Signed-off-by: Nelio Laranjeiro <nelio.laranje...@6wind.com>
---
 drivers/net/mlx5/mlx5_flow.c | 114 +++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)

diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index 8c6309188..ed27914aa 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -368,6 +368,117 @@ mlx5_flow_item_eth(const struct rte_flow_item *item, 
struct rte_flow *flow,
        return 0;
 }
 
+/**
+ * Update the VLAN tag in the Ethernet spec.
+ *
+ * @param attr[in, out]
+ *   Pointer to Verbs attributes structure.
+ * @param eth[in]
+ *   Verbs structure containing the VLAN information to copy.
+ */
+static void
+mlx5_flow_item_vlan_update(struct ibv_flow_attr *attr,
+                          struct ibv_flow_spec_eth *eth)
+{
+       unsigned int i;
+       enum ibv_flow_spec_type search = IBV_FLOW_SPEC_ETH;
+       struct ibv_spec_header *hdr = (struct ibv_spec_header *)
+               ((uint8_t *)attr + sizeof(struct ibv_flow_attr));
+
+       for (i = 0; i != attr->num_of_specs; ++i) {
+               if (hdr->type == search) {
+                       struct ibv_flow_spec_eth *e =
+                               (struct ibv_flow_spec_eth *)hdr;
+
+                       e->val.vlan_tag = eth->val.vlan_tag;
+                       e->mask.vlan_tag = eth->mask.vlan_tag;
+                       break;
+               }
+               hdr = (struct ibv_spec_header *)((uint8_t *)hdr + hdr->size);
+       }
+}
+
+/**
+ * Validate VLAN layer and possibly create/modify the Verbs specification.
+ *
+ * @param item[in]
+ *   Item specification.
+ * @param flow[in, out]
+ *   Pointer to flow structure.
+ * @param error
+ *   Pointer to error structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+mlx5_flow_item_vlan(const struct rte_flow_item *item, struct rte_flow *flow,
+                   struct rte_flow_error *error)
+{
+       const struct rte_flow_item_vlan *spec = item->spec;
+       const struct rte_flow_item_vlan *mask = item->mask;
+       const struct rte_flow_item_vlan nic_mask = {
+               .tci = RTE_BE16(0x0fff),
+       };
+       const unsigned int size = sizeof(struct ibv_flow_spec_eth);
+       struct ibv_flow_spec_eth eth = {
+               .type = IBV_FLOW_SPEC_ETH,
+               .size = size,
+       };
+       int ret;
+       const uint32_t lm = (MLX5_FLOW_LAYER_L3 | MLX5_FLOW_LAYER_L4);
+       const uint32_t vlanm = MLX5_FLOW_LAYER_VLAN;
+       const uint32_t l2m = MLX5_FLOW_LAYER_L2;
+
+       if (flow->verbs.layers & vlanm)
+               return rte_flow_error_set(error, ENOTSUP,
+                                         RTE_FLOW_ERROR_TYPE_ITEM,
+                                         item,
+                                         "L2 layers already configured");
+       else if ((flow->verbs.layers & lm) != 0)
+               return rte_flow_error_set(error, ENOTSUP,
+                                         RTE_FLOW_ERROR_TYPE_ITEM,
+                                         item,
+                                         "L2 layer cannot follow L3/L4 layer");
+       if (!mask)
+               mask = &rte_flow_item_vlan_mask;
+       ret = mlx5_flow_item_validate(item, (const uint8_t *)mask,
+                                     (const uint8_t *)&nic_mask,
+                                     sizeof(struct rte_flow_item_vlan), error);
+       if (ret)
+               return ret;
+       if (spec) {
+               eth.val.vlan_tag = spec->tci;
+               eth.mask.vlan_tag = mask->tci;
+               eth.val.vlan_tag &= eth.mask.vlan_tag;
+               eth.val.ether_type = spec->inner_type;
+               eth.mask.ether_type = mask->inner_type;
+               eth.val.ether_type &= eth.mask.ether_type;
+       }
+       /*
+        * From verbs perspective an empty VLAN is equivalent
+        * to a packet without VLAN layer.
+        */
+       if (!eth.mask.vlan_tag)
+               return rte_flow_error_set(error, EINVAL,
+                                         RTE_FLOW_ERROR_TYPE_ITEM_SPEC,
+                                         item->spec,
+                                         "VLAN cannot be empty");
+       /* Outer TPID cannot be matched. */
+       if (eth.mask.ether_type)
+               return rte_flow_error_set(error, ENOTSUP,
+                                         RTE_FLOW_ERROR_TYPE_ITEM_SPEC,
+                                         item->spec,
+                                         "VLAN TPID matching is not"
+                                         " supported");
+       if (!(flow->verbs.layers & l2m))
+               mlx5_flow_spec_verbs_add(flow, &eth, size);
+       else
+               mlx5_flow_item_vlan_update(flow->verbs.attr, &eth);
+       flow->verbs.layers |= MLX5_FLOW_LAYER_L2 | MLX5_FLOW_LAYER_VLAN;
+       return 0;
+}
+
 /**
  * Validate items provided by the user.
  *
@@ -395,6 +506,9 @@ mlx5_flow_items(const struct rte_flow_item items[],
                case RTE_FLOW_ITEM_TYPE_ETH:
                        ret = mlx5_flow_item_eth(items, flow, error);
                        break;
+               case RTE_FLOW_ITEM_TYPE_VLAN:
+                       ret = mlx5_flow_item_vlan(items, flow, error);
+                       break;
                default:
                        return rte_flow_error_set(error, ENOTSUP,
                                                  RTE_FLOW_ERROR_TYPE_ITEM,
-- 
2.17.0

Reply via email to