net/mlx5: extend IPv6 flow item
[dpdk.git] / drivers / net / mlx5 / mlx5_flow.c
index 093c140..a885f6e 100644 (file)
@@ -50,6 +50,7 @@
 #include <rte_malloc.h>
 
 #include "mlx5.h"
+#include "mlx5_prm.h"
 
 static int
 mlx5_flow_create_eth(const struct rte_flow_item *item,
@@ -95,6 +96,7 @@ struct rte_flow {
        struct ibv_exp_wq *wq; /**< Verbs work queue. */
        struct ibv_cq *cq; /**< Verbs completion queue. */
        struct rxq *rxq; /**< Pointer to the queue, NULL if drop queue. */
+       uint32_t mark:1; /**< Set if the flow is marked. */
 };
 
 /** Static initializer for items. */
@@ -109,6 +111,12 @@ struct mlx5_flow_items {
        const enum rte_flow_action_type *const actions;
        /** Bit-masks corresponding to the possibilities for the item. */
        const void *mask;
+       /**
+        * Default bit-masks to use when item->mask is not provided. When
+        * \default_mask is also NULL, the full supported bit-mask (\mask) is
+        * used instead.
+        */
+       const void *default_mask;
        /** Bit-masks size in bytes. */
        const unsigned int mask_sz;
        /**
@@ -137,6 +145,7 @@ struct mlx5_flow_items {
 static const enum rte_flow_action_type valid_actions[] = {
        RTE_FLOW_ACTION_TYPE_DROP,
        RTE_FLOW_ACTION_TYPE_QUEUE,
+       RTE_FLOW_ACTION_TYPE_MARK,
        RTE_FLOW_ACTION_TYPE_END,
 };
 
@@ -154,7 +163,9 @@ static const struct mlx5_flow_items mlx5_flow_items[] = {
                .mask = &(const struct rte_flow_item_eth){
                        .dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
                        .src.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+                       .type = -1,
                },
+               .default_mask = &rte_flow_item_eth_mask,
                .mask_sz = sizeof(struct rte_flow_item_eth),
                .convert = mlx5_flow_create_eth,
                .dst_sz = sizeof(struct ibv_exp_flow_spec_eth),
@@ -166,6 +177,7 @@ static const struct mlx5_flow_items mlx5_flow_items[] = {
                .mask = &(const struct rte_flow_item_vlan){
                        .tci = -1,
                },
+               .default_mask = &rte_flow_item_vlan_mask,
                .mask_sz = sizeof(struct rte_flow_item_vlan),
                .convert = mlx5_flow_create_vlan,
                .dst_sz = 0,
@@ -178,11 +190,14 @@ static const struct mlx5_flow_items mlx5_flow_items[] = {
                        .hdr = {
                                .src_addr = -1,
                                .dst_addr = -1,
+                               .type_of_service = -1,
+                               .next_proto_id = -1,
                        },
                },
+               .default_mask = &rte_flow_item_ipv4_mask,
                .mask_sz = sizeof(struct rte_flow_item_ipv4),
                .convert = mlx5_flow_create_ipv4,
-               .dst_sz = sizeof(struct ibv_exp_flow_spec_ipv4),
+               .dst_sz = sizeof(struct ibv_exp_flow_spec_ipv4_ext),
        },
        [RTE_FLOW_ITEM_TYPE_IPV6] = {
                .items = ITEMS(RTE_FLOW_ITEM_TYPE_UDP,
@@ -202,11 +217,15 @@ static const struct mlx5_flow_items mlx5_flow_items[] = {
                                        0xff, 0xff, 0xff, 0xff,
                                        0xff, 0xff, 0xff, 0xff,
                                },
+                               .vtc_flow = -1,
+                               .proto = -1,
+                               .hop_limits = -1,
                        },
                },
+               .default_mask = &rte_flow_item_ipv6_mask,
                .mask_sz = sizeof(struct rte_flow_item_ipv6),
                .convert = mlx5_flow_create_ipv6,
-               .dst_sz = sizeof(struct ibv_exp_flow_spec_ipv6),
+               .dst_sz = sizeof(struct ibv_exp_flow_spec_ipv6_ext),
        },
        [RTE_FLOW_ITEM_TYPE_UDP] = {
                .items = ITEMS(RTE_FLOW_ITEM_TYPE_VXLAN),
@@ -217,6 +236,7 @@ static const struct mlx5_flow_items mlx5_flow_items[] = {
                                .dst_port = -1,
                        },
                },
+               .default_mask = &rte_flow_item_udp_mask,
                .mask_sz = sizeof(struct rte_flow_item_udp),
                .convert = mlx5_flow_create_udp,
                .dst_sz = sizeof(struct ibv_exp_flow_spec_tcp_udp),
@@ -229,6 +249,7 @@ static const struct mlx5_flow_items mlx5_flow_items[] = {
                                .dst_port = -1,
                        },
                },
+               .default_mask = &rte_flow_item_tcp_mask,
                .mask_sz = sizeof(struct rte_flow_item_tcp),
                .convert = mlx5_flow_create_tcp,
                .dst_sz = sizeof(struct ibv_exp_flow_spec_tcp_udp),
@@ -239,6 +260,7 @@ static const struct mlx5_flow_items mlx5_flow_items[] = {
                .mask = &(const struct rte_flow_item_vxlan){
                        .vni = "\xff\xff\xff",
                },
+               .default_mask = &rte_flow_item_vxlan_mask,
                .mask_sz = sizeof(struct rte_flow_item_vxlan),
                .convert = mlx5_flow_create_vxlan,
                .dst_sz = sizeof(struct ibv_exp_flow_spec_tunnel),
@@ -255,7 +277,9 @@ struct mlx5_flow {
 struct mlx5_flow_action {
        uint32_t queue:1; /**< Target is a receive queue. */
        uint32_t drop:1; /**< Target is a drop queue. */
+       uint32_t mark:1; /**< Mark is present in the flow. */
        uint32_t queue_id; /**< Identifier of the queue. */
+       uint32_t mark_id; /**< Mark identifier. */
 };
 
 /**
@@ -352,6 +376,7 @@ priv_flow_validate(struct priv *priv,
        struct mlx5_flow_action action = {
                .queue = 0,
                .drop = 0,
+               .mark = 0,
        };
 
        (void)priv;
@@ -390,17 +415,6 @@ priv_flow_validate(struct priv *priv,
 
                if (items->type == RTE_FLOW_ITEM_TYPE_VOID)
                        continue;
-               /* Handle special situation for VLAN. */
-               if (items->type == RTE_FLOW_ITEM_TYPE_VLAN) {
-                       if (((const struct rte_flow_item_vlan *)items)->tci >
-                           ETHER_MAX_VLAN_ID) {
-                               rte_flow_error_set(error, ENOTSUP,
-                                                  RTE_FLOW_ERROR_TYPE_ITEM,
-                                                  items,
-                                                  "wrong VLAN id value");
-                               return -rte_errno;
-                       }
-               }
                for (i = 0;
                     cur_item->items &&
                     cur_item->items[i] != RTE_FLOW_ITEM_TYPE_END;
@@ -415,11 +429,15 @@ priv_flow_validate(struct priv *priv,
                cur_item = token;
                err = mlx5_flow_item_validate(items,
                                              (const uint8_t *)cur_item->mask,
-                                             sizeof(cur_item->mask_sz));
+                                             cur_item->mask_sz);
                if (err)
                        goto exit_item_not_supported;
                if (flow->ibv_attr && cur_item->convert) {
-                       err = cur_item->convert(items, cur_item->mask, flow);
+                       err = cur_item->convert(items,
+                                               (cur_item->default_mask ?
+                                                cur_item->default_mask :
+                                                cur_item->mask),
+                                               flow);
                        if (err)
                                goto exit_item_not_supported;
                }
@@ -438,10 +456,32 @@ priv_flow_validate(struct priv *priv,
                        if (!queue || (queue->index > (priv->rxqs_n - 1)))
                                goto exit_action_not_supported;
                        action.queue = 1;
+               } else if (actions->type == RTE_FLOW_ACTION_TYPE_MARK) {
+                       const struct rte_flow_action_mark *mark =
+                               (const struct rte_flow_action_mark *)
+                               actions->conf;
+
+                       if (!mark) {
+                               rte_flow_error_set(error, EINVAL,
+                                                  RTE_FLOW_ERROR_TYPE_ACTION,
+                                                  actions,
+                                                  "mark must be defined");
+                               return -rte_errno;
+                       } else if (mark->id >= MLX5_FLOW_MARK_MAX) {
+                               rte_flow_error_set(error, ENOTSUP,
+                                                  RTE_FLOW_ERROR_TYPE_ACTION,
+                                                  actions,
+                                                  "mark must be between 0"
+                                                  " and 16777199");
+                               return -rte_errno;
+                       }
+                       action.mark = 1;
                } else {
                        goto exit_action_not_supported;
                }
        }
+       if (action.mark && !flow->ibv_attr && !action.drop)
+               flow->offset += sizeof(struct ibv_exp_flow_spec_action_tag);
        if (!action.queue && !action.drop) {
                rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
                                   NULL, "no valid action");
@@ -516,13 +556,16 @@ mlx5_flow_create_eth(const struct rte_flow_item *item,
                mask = default_mask;
        memcpy(eth->val.dst_mac, spec->dst.addr_bytes, ETHER_ADDR_LEN);
        memcpy(eth->val.src_mac, spec->src.addr_bytes, ETHER_ADDR_LEN);
+       eth->val.ether_type = spec->type;
        memcpy(eth->mask.dst_mac, mask->dst.addr_bytes, ETHER_ADDR_LEN);
        memcpy(eth->mask.src_mac, mask->src.addr_bytes, ETHER_ADDR_LEN);
+       eth->mask.ether_type = mask->type;
        /* Remove unwanted bits from values. */
        for (i = 0; i < ETHER_ADDR_LEN; ++i) {
                eth->val.dst_mac[i] &= eth->mask.dst_mac[i];
                eth->val.src_mac[i] &= eth->mask.src_mac[i];
        }
+       eth->val.ether_type &= eth->mask.ether_type;
        return 0;
 }
 
@@ -576,31 +619,37 @@ mlx5_flow_create_ipv4(const struct rte_flow_item *item,
        const struct rte_flow_item_ipv4 *spec = item->spec;
        const struct rte_flow_item_ipv4 *mask = item->mask;
        struct mlx5_flow *flow = (struct mlx5_flow *)data;
-       struct ibv_exp_flow_spec_ipv4 *ipv4;
-       unsigned int ipv4_size = sizeof(struct ibv_exp_flow_spec_ipv4);
+       struct ibv_exp_flow_spec_ipv4_ext *ipv4;
+       unsigned int ipv4_size = sizeof(struct ibv_exp_flow_spec_ipv4_ext);
 
        ++flow->ibv_attr->num_of_specs;
        flow->ibv_attr->priority = 1;
        ipv4 = (void *)((uintptr_t)flow->ibv_attr + flow->offset);
-       *ipv4 = (struct ibv_exp_flow_spec_ipv4) {
-               .type = flow->inner | IBV_EXP_FLOW_SPEC_IPV4,
+       *ipv4 = (struct ibv_exp_flow_spec_ipv4_ext) {
+               .type = flow->inner | IBV_EXP_FLOW_SPEC_IPV4_EXT,
                .size = ipv4_size,
        };
        if (!spec)
                return 0;
        if (!mask)
                mask = default_mask;
-       ipv4->val = (struct ibv_exp_flow_ipv4_filter){
+       ipv4->val = (struct ibv_exp_flow_ipv4_ext_filter){
                .src_ip = spec->hdr.src_addr,
                .dst_ip = spec->hdr.dst_addr,
+               .proto = spec->hdr.next_proto_id,
+               .tos = spec->hdr.type_of_service,
        };
-       ipv4->mask = (struct ibv_exp_flow_ipv4_filter){
+       ipv4->mask = (struct ibv_exp_flow_ipv4_ext_filter){
                .src_ip = mask->hdr.src_addr,
                .dst_ip = mask->hdr.dst_addr,
+               .proto = mask->hdr.next_proto_id,
+               .tos = mask->hdr.type_of_service,
        };
        /* Remove unwanted bits from values. */
        ipv4->val.src_ip &= ipv4->mask.src_ip;
        ipv4->val.dst_ip &= ipv4->mask.dst_ip;
+       ipv4->val.proto &= ipv4->mask.proto;
+       ipv4->val.tos &= ipv4->mask.tos;
        return 0;
 }
 
@@ -622,15 +671,14 @@ mlx5_flow_create_ipv6(const struct rte_flow_item *item,
        const struct rte_flow_item_ipv6 *spec = item->spec;
        const struct rte_flow_item_ipv6 *mask = item->mask;
        struct mlx5_flow *flow = (struct mlx5_flow *)data;
-       struct ibv_exp_flow_spec_ipv6 *ipv6;
-       unsigned int ipv6_size = sizeof(struct ibv_exp_flow_spec_ipv6);
-       unsigned int i;
+       struct ibv_exp_flow_spec_ipv6_ext *ipv6;
+       unsigned int ipv6_size = sizeof(struct ibv_exp_flow_spec_ipv6_ext);
 
        ++flow->ibv_attr->num_of_specs;
        flow->ibv_attr->priority = 1;
        ipv6 = (void *)((uintptr_t)flow->ibv_attr + flow->offset);
-       *ipv6 = (struct ibv_exp_flow_spec_ipv6) {
-               .type = flow->inner | IBV_EXP_FLOW_SPEC_IPV6,
+       *ipv6 = (struct ibv_exp_flow_spec_ipv6_ext) {
+               .type = flow->inner | IBV_EXP_FLOW_SPEC_IPV6_EXT,
                .size = ipv6_size,
        };
        if (!spec)
@@ -645,11 +693,12 @@ mlx5_flow_create_ipv6(const struct rte_flow_item *item,
               RTE_DIM(ipv6->mask.src_ip));
        memcpy(ipv6->mask.dst_ip, mask->hdr.dst_addr,
               RTE_DIM(ipv6->mask.dst_ip));
-       /* Remove unwanted bits from values. */
-       for (i = 0; i < RTE_DIM(ipv6->val.src_ip); ++i) {
-               ipv6->val.src_ip[i] &= ipv6->mask.src_ip[i];
-               ipv6->val.dst_ip[i] &= ipv6->mask.dst_ip[i];
-       }
+       ipv6->mask.flow_label = mask->hdr.vtc_flow;
+       ipv6->mask.next_hdr = mask->hdr.proto;
+       ipv6->mask.hop_limit = mask->hdr.hop_limits;
+       ipv6->val.flow_label &= ipv6->mask.flow_label;
+       ipv6->val.next_hdr &= ipv6->mask.next_hdr;
+       ipv6->val.hop_limit &= ipv6->mask.hop_limit;
        return 0;
 }
 
@@ -784,6 +833,30 @@ mlx5_flow_create_vxlan(const struct rte_flow_item *item,
        return 0;
 }
 
+/**
+ * Convert mark/flag action to Verbs specification.
+ *
+ * @param flow
+ *   Pointer to MLX5 flow structure.
+ * @param mark_id
+ *   Mark identifier.
+ */
+static int
+mlx5_flow_create_flag_mark(struct mlx5_flow *flow, uint32_t mark_id)
+{
+       struct ibv_exp_flow_spec_action_tag *tag;
+       unsigned int size = sizeof(struct ibv_exp_flow_spec_action_tag);
+
+       tag = (void *)((uintptr_t)flow->ibv_attr + flow->offset);
+       *tag = (struct ibv_exp_flow_spec_action_tag){
+               .type = IBV_EXP_FLOW_SPEC_ACTION_TAG,
+               .size = size,
+               .tag_id = mlx5_flow_mark_set(mark_id),
+       };
+       ++flow->ibv_attr->num_of_specs;
+       return 0;
+}
+
 /**
  * Complete flow rule creation.
  *
@@ -836,12 +909,20 @@ priv_flow_create_action_queue(struct priv *priv,
                                                 .pd = priv->pd,
                                                 .cq = rte_flow->cq,
                                                 });
+               if (!rte_flow->wq) {
+                       rte_flow_error_set(error, ENOMEM,
+                                          RTE_FLOW_ERROR_TYPE_HANDLE,
+                                          NULL, "cannot allocate WQ");
+                       goto error;
+               }
        } else {
                rxq = container_of((*priv->rxqs)[action->queue_id],
                                   struct rxq_ctrl, rxq);
                rte_flow->rxq = &rxq->rxq;
+               rxq->rxq.mark |= action->mark;
                rte_flow->wq = rxq->wq;
        }
+       rte_flow->mark = action->mark;
        rte_flow->ibv_attr = ibv_attr;
        rte_flow->ind_table = ibv_exp_create_rwq_ind_table(
                priv->ctx,
@@ -880,6 +961,8 @@ priv_flow_create_action_queue(struct priv *priv,
                                   NULL, "cannot allocate QP");
                goto error;
        }
+       if (!priv->started)
+               return rte_flow;
        rte_flow->ibv_flow = ibv_exp_create_flow(rte_flow->qp,
                                                 rte_flow->ibv_attr);
        if (!rte_flow->ibv_flow) {
@@ -898,7 +981,6 @@ error:
                ibv_exp_destroy_wq(rte_flow->wq);
        if (!rte_flow->rxq && rte_flow->cq)
                ibv_destroy_cq(rte_flow->cq);
-       rte_free(rte_flow->ibv_attr);
        rte_free(rte_flow);
        return NULL;
 }
@@ -957,6 +1039,8 @@ priv_flow_create(struct priv *priv,
        action = (struct mlx5_flow_action){
                .queue = 0,
                .drop = 0,
+               .mark = 0,
+               .mark_id = MLX5_FLOW_MARK_DEFAULT,
        };
        for (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {
                if (actions->type == RTE_FLOW_ACTION_TYPE_VOID) {
@@ -968,6 +1052,15 @@ priv_flow_create(struct priv *priv,
                                 actions->conf)->index;
                } else if (actions->type == RTE_FLOW_ACTION_TYPE_DROP) {
                        action.drop = 1;
+                       action.mark = 0;
+               } else if (actions->type == RTE_FLOW_ACTION_TYPE_MARK) {
+                       const struct rte_flow_action_mark *mark =
+                               (const struct rte_flow_action_mark *)
+                               actions->conf;
+
+                       if (mark)
+                               action.mark_id = mark->id;
+                       action.mark = !action.drop;
                } else {
                        rte_flow_error_set(error, ENOTSUP,
                                           RTE_FLOW_ERROR_TYPE_ACTION,
@@ -975,8 +1068,14 @@ priv_flow_create(struct priv *priv,
                        goto exit;
                }
        }
+       if (action.mark) {
+               mlx5_flow_create_flag_mark(&flow, action.mark_id);
+               flow.offset += sizeof(struct ibv_exp_flow_spec_action_tag);
+       }
        rte_flow = priv_flow_create_action_queue(priv, flow.ibv_attr,
                                                 &action, error);
+       if (!rte_flow)
+               goto exit;
        return rte_flow;
 exit:
        rte_free(flow.ibv_attr);
@@ -1033,6 +1132,18 @@ priv_flow_destroy(struct priv *priv,
                claim_zero(ibv_exp_destroy_wq(flow->wq));
        if (!flow->rxq && flow->cq)
                claim_zero(ibv_destroy_cq(flow->cq));
+       if (flow->mark) {
+               struct rte_flow *tmp;
+               uint32_t mark_n = 0;
+
+               for (tmp = LIST_FIRST(&priv->flows);
+                    tmp;
+                    tmp = LIST_NEXT(tmp, next)) {
+                       if ((flow->rxq == tmp->rxq) && tmp->mark)
+                               ++mark_n;
+               }
+               flow->rxq->mark = !!mark_n;
+       }
        rte_free(flow->ibv_attr);
        DEBUG("Flow destroyed %p", (void *)flow);
        rte_free(flow);
@@ -1112,6 +1223,8 @@ priv_flow_stop(struct priv *priv)
             flow = LIST_NEXT(flow, next)) {
                claim_zero(ibv_exp_destroy_flow(flow->ibv_flow));
                flow->ibv_flow = NULL;
+               if (flow->mark)
+                       flow->rxq->mark = 0;
                DEBUG("Flow %p removed", (void *)flow);
        }
 }
@@ -1141,6 +1254,8 @@ priv_flow_start(struct priv *priv)
                        return rte_errno;
                }
                DEBUG("Flow %p applied", (void *)flow);
+               if (flow->rxq)
+                       flow->rxq->mark |= flow->mark;
        }
        return 0;
 }