New upstream version 18.11-rc3
[deb_dpdk.git] / drivers / net / mlx5 / mlx5_flow_tcf.c
index fb817b2..97d2a54 100644 (file)
@@ -3846,30 +3846,6 @@ flow_tcf_alloc_nlcmd(struct tcf_nlcb_context *ctx, uint32_t size)
        return nlh;
 }
 
-/**
- * Set NLM_F_ACK flags in the last netlink command in buffer.
- * Only last command in the buffer will be acked by system.
- *
- * @param[in, out] buf
- *   Pointer to buffer with netlink commands.
- */
-static void
-flow_tcf_setack_nlcmd(struct tcf_nlcb_buf *buf)
-{
-       struct nlmsghdr *nlh;
-       uint32_t size = 0;
-
-       assert(buf->size);
-       do {
-               nlh = (struct nlmsghdr *)&buf->msg[size];
-               size += NLMSG_ALIGN(nlh->nlmsg_len);
-               if (size >= buf->size) {
-                       nlh->nlmsg_flags |= NLM_F_ACK;
-                       break;
-               }
-       } while (true);
-}
-
 /**
  * Send the buffers with prepared netlink commands. Scans the list and
  * sends all found buffers. Buffers are sent and freed anyway in order
@@ -3888,21 +3864,35 @@ static int
 flow_tcf_send_nlcmd(struct mlx5_flow_tcf_context *tcf,
                    struct tcf_nlcb_context *ctx)
 {
-       struct tcf_nlcb_buf *bc, *bn;
-       struct nlmsghdr *nlh;
+       struct tcf_nlcb_buf *bc = LIST_FIRST(&ctx->nlbuf);
        int ret = 0;
 
-       bc = LIST_FIRST(&ctx->nlbuf);
        while (bc) {
+               struct tcf_nlcb_buf *bn = LIST_NEXT(bc, next);
+               struct nlmsghdr *nlh;
+               uint32_t msg = 0;
                int rc;
 
-               bn = LIST_NEXT(bc, next);
-               if (bc->size) {
-                       flow_tcf_setack_nlcmd(bc);
-                       nlh = (struct nlmsghdr *)&bc->msg;
-                       rc = flow_tcf_nl_ack(tcf, nlh, bc->size, NULL, NULL);
-                       if (rc && !ret)
-                               ret = rc;
+               while (msg < bc->size) {
+                       /*
+                        * Send Netlink commands from buffer in one by one
+                        * fashion. If we send multiple rule deletion commands
+                        * in one Netlink message and some error occurs it may
+                        * cause multiple ACK error messages and break sequence
+                        * numbers of Netlink communication, because we expect
+                        * the only one ACK reply.
+                        */
+                       assert((bc->size - msg) >= sizeof(struct nlmsghdr));
+                       nlh = (struct nlmsghdr *)&bc->msg[msg];
+                       assert((bc->size - msg) >= nlh->nlmsg_len);
+                       msg += nlh->nlmsg_len;
+                       rc = flow_tcf_nl_ack(tcf, nlh, 0, NULL, NULL);
+                       if (rc) {
+                               DRV_LOG(WARNING,
+                                       "netlink: cleanup error %d", rc);
+                               if (!ret)
+                                       ret = rc;
+                       }
                }
                rte_free(bc);
                bc = bn;
@@ -3935,6 +3925,7 @@ flow_tcf_collect_local_cb(const struct nlmsghdr *nlh, void *arg)
        struct nlattr *na_local = NULL;
        struct nlattr *na_peer = NULL;
        unsigned char family;
+       uint32_t size;
 
        if (nlh->nlmsg_type != RTM_NEWADDR) {
                rte_errno = EINVAL;
@@ -3962,11 +3953,11 @@ flow_tcf_collect_local_cb(const struct nlmsghdr *nlh, void *arg)
        if (!na_local || !na_peer)
                return 1;
        /* Local rule found with scope link, permanent and assigned peer. */
-       cmd = flow_tcf_alloc_nlcmd(ctx, MNL_ALIGN(sizeof(struct nlmsghdr)) +
-                                       MNL_ALIGN(sizeof(struct ifaddrmsg)) +
-                                       (family == AF_INET6
-                                       ? 2 * SZ_NLATTR_DATA_OF(IPV6_ADDR_LEN)
-                                       : 2 * SZ_NLATTR_TYPE_OF(uint32_t)));
+       size = MNL_ALIGN(sizeof(struct nlmsghdr)) +
+              MNL_ALIGN(sizeof(struct ifaddrmsg)) +
+              (family == AF_INET6 ? 2 * SZ_NLATTR_DATA_OF(IPV6_ADDR_LEN)
+                                  : 2 * SZ_NLATTR_TYPE_OF(uint32_t));
+       cmd = flow_tcf_alloc_nlcmd(ctx, size);
        if (!cmd) {
                rte_errno = ENOMEM;
                return -rte_errno;
@@ -3991,6 +3982,7 @@ flow_tcf_collect_local_cb(const struct nlmsghdr *nlh, void *arg)
                mnl_attr_put(cmd, IFA_ADDRESS, IPV6_ADDR_LEN,
                        mnl_attr_get_payload(na_peer));
        }
+       assert(size == cmd->nlmsg_len);
        return 1;
 }
 
@@ -4059,6 +4051,7 @@ flow_tcf_collect_neigh_cb(const struct nlmsghdr *nlh, void *arg)
        struct nlattr *na_ip = NULL;
        struct nlattr *na_mac = NULL;
        unsigned char family;
+       uint32_t size;
 
        if (nlh->nlmsg_type != RTM_NEWNEIGH) {
                rte_errno = EINVAL;
@@ -4085,12 +4078,12 @@ flow_tcf_collect_neigh_cb(const struct nlmsghdr *nlh, void *arg)
        if (!na_mac || !na_ip)
                return 1;
        /* Neigh rule with permenent attribute found. */
-       cmd = flow_tcf_alloc_nlcmd(ctx, MNL_ALIGN(sizeof(struct nlmsghdr)) +
-                                       MNL_ALIGN(sizeof(struct ndmsg)) +
-                                       SZ_NLATTR_DATA_OF(ETHER_ADDR_LEN) +
-                                       (family == AF_INET6
-                                       ? SZ_NLATTR_DATA_OF(IPV6_ADDR_LEN)
-                                       : SZ_NLATTR_TYPE_OF(uint32_t)));
+       size = MNL_ALIGN(sizeof(struct nlmsghdr)) +
+              MNL_ALIGN(sizeof(struct ndmsg)) +
+              SZ_NLATTR_DATA_OF(ETHER_ADDR_LEN) +
+              (family == AF_INET6 ? SZ_NLATTR_DATA_OF(IPV6_ADDR_LEN)
+                                  : SZ_NLATTR_TYPE_OF(uint32_t));
+       cmd = flow_tcf_alloc_nlcmd(ctx, size);
        if (!cmd) {
                rte_errno = ENOMEM;
                return -rte_errno;
@@ -4113,6 +4106,7 @@ flow_tcf_collect_neigh_cb(const struct nlmsghdr *nlh, void *arg)
        }
        mnl_attr_put(cmd, NDA_LLADDR, ETHER_ADDR_LEN,
                     mnl_attr_get_payload(na_mac));
+       assert(size == cmd->nlmsg_len);
        return 1;
 }
 
@@ -4179,6 +4173,7 @@ flow_tcf_collect_vxlan_cb(const struct nlmsghdr *nlh, void *arg)
        struct nlattr *na_vxlan = NULL;
        bool found = false;
        unsigned int vxindex;
+       uint32_t size;
 
        if (nlh->nlmsg_type != RTM_NEWLINK) {
                rte_errno = EINVAL;
@@ -4224,9 +4219,10 @@ flow_tcf_collect_vxlan_cb(const struct nlmsghdr *nlh, void *arg)
                return 1;
        /* Attached VXLAN device found, store the command to delete. */
        vxindex = ifm->ifi_index;
-       cmd = flow_tcf_alloc_nlcmd(ctx, MNL_ALIGN(sizeof(struct nlmsghdr)) +
-                                       MNL_ALIGN(sizeof(struct ifinfomsg)));
-       if (!nlh) {
+       size = MNL_ALIGN(sizeof(struct nlmsghdr)) +
+              MNL_ALIGN(sizeof(struct ifinfomsg));
+       cmd = flow_tcf_alloc_nlcmd(ctx, size);
+       if (!cmd) {
                rte_errno = ENOMEM;
                return -rte_errno;
        }
@@ -4236,6 +4232,7 @@ flow_tcf_collect_vxlan_cb(const struct nlmsghdr *nlh, void *arg)
        ifm = mnl_nlmsg_put_extra_header(cmd, sizeof(*ifm));
        ifm->ifi_family = AF_UNSPEC;
        ifm->ifi_index = vxindex;
+       assert(size == cmd->nlmsg_len);
        return 1;
 }
 
@@ -5127,6 +5124,13 @@ flow_tcf_apply(struct rte_eth_dev *dev, struct rte_flow *flow,
                dev_flow->tcf.applied = 1;
                return 0;
        }
+       if (dev_flow->tcf.tunnel) {
+               /* Rollback the VTEP configuration if rule apply failed. */
+               assert(dev_flow->tcf.tunnel->vtep);
+               flow_tcf_vtep_release(ctx, dev_flow->tcf.tunnel->vtep,
+                                     dev_flow);
+               dev_flow->tcf.tunnel->vtep = NULL;
+       }
        return rte_flow_error_set(error, rte_errno,
                                  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
                                  "netlink: failed to create TC flow rule");