net/sfc: support VLAN presence match in transfer rules
authorIvan Malov <ivan.malov@oktetlabs.ru>
Mon, 24 May 2021 11:48:33 +0000 (14:48 +0300)
committerDavid Marchand <david.marchand@redhat.com>
Wed, 7 Jul 2021 09:54:21 +0000 (11:54 +0200)
Take into account VLAN presence fields in items ETH and VLAN.

Provided that the item ETH does not match on the EtherType,
the pattern behaviour will be as follows:

- ETH (mask->has_vlan = 0) | IPv4        = match both tagged and untagged;
- ETH (mask->has_vlan = 1) | IPv4        = match as per spec->has_vlan;
- ETH (mask->has_vlan = 0) | VLAN | IPv4 = match only tagged.

Similar logic applies to double tagging.

Signed-off-by: Ivan Malov <ivan.malov@oktetlabs.ru>
Reviewed-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Reviewed-by: Andy Moreton <amoreton@xilinx.com>
drivers/net/sfc/sfc_mae.c
drivers/net/sfc/sfc_mae.h

index a2c0aa1..33bff27 100644 (file)
@@ -728,6 +728,7 @@ sfc_mae_rule_process_pattern_data(struct sfc_mae_parse_ctx *ctx,
                RTE_BE16(RTE_ETHER_TYPE_QINQ2),
                RTE_BE16(RTE_ETHER_TYPE_QINQ3),
        };
+       bool enforce_tag_presence[SFC_MAE_MATCH_VLAN_MAX_NTAGS] = {0};
        unsigned int nb_supported_tpids = RTE_DIM(supported_tpids);
        unsigned int ethertype_idx;
        const uint8_t *valuep;
@@ -753,6 +754,22 @@ sfc_mae_rule_process_pattern_data(struct sfc_mae_parse_ctx *ctx,
             ethertype_idx < pdata->nb_vlan_tags; ++ethertype_idx) {
                unsigned int tpid_idx;
 
+               /*
+                * This loop can have only two iterations. On the second one,
+                * drop outer tag presence enforcement bit because the inner
+                * tag presence automatically assumes that for the outer tag.
+                */
+               enforce_tag_presence[0] = B_FALSE;
+
+               if (ethertypes[ethertype_idx].mask == RTE_BE16(0)) {
+                       if (pdata->tci_masks[ethertype_idx] == RTE_BE16(0))
+                               enforce_tag_presence[ethertype_idx] = B_TRUE;
+
+                       /* No match on this field, and no value check. */
+                       nb_supported_tpids = 1;
+                       continue;
+               }
+
                /* Exact match is supported only. */
                if (ethertypes[ethertype_idx].mask != RTE_BE16(0xffff)) {
                        rc = EINVAL;
@@ -812,6 +829,24 @@ sfc_mae_rule_process_pattern_data(struct sfc_mae_parse_ctx *ctx,
                }
        }
 
+       if (enforce_tag_presence[0] || pdata->has_ovlan_mask) {
+               rc = efx_mae_match_spec_bit_set(ctx->match_spec,
+                                               fremap[EFX_MAE_FIELD_HAS_OVLAN],
+                                               enforce_tag_presence[0] ||
+                                               pdata->has_ovlan_value);
+               if (rc != 0)
+                       goto fail;
+       }
+
+       if (enforce_tag_presence[1] || pdata->has_ivlan_mask) {
+               rc = efx_mae_match_spec_bit_set(ctx->match_spec,
+                                               fremap[EFX_MAE_FIELD_HAS_IVLAN],
+                                               enforce_tag_presence[1] ||
+                                               pdata->has_ivlan_value);
+               if (rc != 0)
+                       goto fail;
+       }
+
        valuep = (const uint8_t *)&pdata->l3_next_proto_value;
        maskp = (const uint8_t *)&pdata->l3_next_proto_mask;
        rc = efx_mae_match_spec_field_set(ctx->match_spec,
@@ -1154,6 +1189,7 @@ sfc_mae_rule_parse_item_eth(const struct rte_flow_item *item,
 
        sfc_mae_item_build_supp_mask(flocs_eth, RTE_DIM(flocs_eth),
                                     &supp_mask, sizeof(supp_mask));
+       supp_mask.has_vlan = 1;
 
        rc = sfc_flow_parse_init(item,
                                 (const void **)&spec, (const void **)&mask,
@@ -1172,14 +1208,23 @@ sfc_mae_rule_parse_item_eth(const struct rte_flow_item *item,
                item_spec = (const struct rte_flow_item_eth *)spec;
                item_mask = (const struct rte_flow_item_eth *)mask;
 
+               /*
+                * Remember various match criteria in the parsing context.
+                * sfc_mae_rule_process_pattern_data() will consider them
+                * altogether when the rest of the items have been parsed.
+                */
                ethertypes[0].value = item_spec->type;
                ethertypes[0].mask = item_mask->type;
+               if (item_mask->has_vlan) {
+                       pdata->has_ovlan_mask = B_TRUE;
+                       if (item_spec->has_vlan)
+                               pdata->has_ovlan_value = B_TRUE;
+               }
        } else {
                /*
-                * The specification is empty. This is wrong in the case
-                * when there are more network patterns in line. Other
-                * than that, any Ethernet can match. All of that is
-                * checked at the end of parsing.
+                * The specification is empty. The overall pattern
+                * validity will be enforced at the end of parsing.
+                * See sfc_mae_rule_process_pattern_data().
                 */
                return 0;
        }
@@ -1229,6 +1274,16 @@ sfc_mae_rule_parse_item_vlan(const struct rte_flow_item *item,
 {
        struct sfc_mae_parse_ctx *ctx_mae = ctx->mae;
        struct sfc_mae_pattern_data *pdata = &ctx_mae->pattern_data;
+       boolean_t *has_vlan_mp_by_nb_tags[SFC_MAE_MATCH_VLAN_MAX_NTAGS] = {
+               &pdata->has_ovlan_mask,
+               &pdata->has_ivlan_mask,
+       };
+       boolean_t *has_vlan_vp_by_nb_tags[SFC_MAE_MATCH_VLAN_MAX_NTAGS] = {
+               &pdata->has_ovlan_value,
+               &pdata->has_ivlan_value,
+       };
+       boolean_t *cur_tag_presence_bit_mp;
+       boolean_t *cur_tag_presence_bit_vp;
        const struct sfc_mae_field_locator *flocs;
        struct rte_flow_item_vlan supp_mask;
        const uint8_t *spec = NULL;
@@ -1244,14 +1299,27 @@ sfc_mae_rule_parse_item_vlan(const struct rte_flow_item *item,
                                "Can't match that many VLAN tags");
        }
 
+       cur_tag_presence_bit_mp = has_vlan_mp_by_nb_tags[pdata->nb_vlan_tags];
+       cur_tag_presence_bit_vp = has_vlan_vp_by_nb_tags[pdata->nb_vlan_tags];
+
+       if (*cur_tag_presence_bit_mp == B_TRUE &&
+           *cur_tag_presence_bit_vp == B_FALSE) {
+               return rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ITEM, item,
+                               "The previous item enforces no (more) VLAN, "
+                               "so the current item (VLAN) must not exist");
+       }
+
        nb_flocs = RTE_DIM(flocs_vlan) / SFC_MAE_MATCH_VLAN_MAX_NTAGS;
        flocs = flocs_vlan + pdata->nb_vlan_tags * nb_flocs;
 
-       /* If parsing fails, this can remain incremented. */
-       ++pdata->nb_vlan_tags;
-
        sfc_mae_item_build_supp_mask(flocs, nb_flocs,
                                     &supp_mask, sizeof(supp_mask));
+       /*
+        * This only means that the field is supported by the driver and libefx.
+        * Support on NIC level will be checked when all items have been parsed.
+        */
+       supp_mask.has_more_vlan = 1;
 
        rc = sfc_flow_parse_init(item,
                                 (const void **)&spec, (const void **)&mask,
@@ -1262,26 +1330,44 @@ sfc_mae_rule_parse_item_vlan(const struct rte_flow_item *item,
                return rc;
 
        if (spec != NULL) {
-               struct sfc_mae_ethertype *ethertypes = pdata->ethertypes;
+               struct sfc_mae_ethertype *et = pdata->ethertypes;
                const struct rte_flow_item_vlan *item_spec;
                const struct rte_flow_item_vlan *item_mask;
 
                item_spec = (const struct rte_flow_item_vlan *)spec;
                item_mask = (const struct rte_flow_item_vlan *)mask;
 
-               ethertypes[pdata->nb_vlan_tags].value = item_spec->inner_type;
-               ethertypes[pdata->nb_vlan_tags].mask = item_mask->inner_type;
-       } else {
                /*
-                * The specification is empty. This is wrong in the case
-                * when there are more network patterns in line. Other
-                * than that, any Ethernet can match. All of that is
-                * checked at the end of parsing.
+                * Remember various match criteria in the parsing context.
+                * sfc_mae_rule_process_pattern_data() will consider them
+                * altogether when the rest of the items have been parsed.
                 */
-               return 0;
+               et[pdata->nb_vlan_tags + 1].value = item_spec->inner_type;
+               et[pdata->nb_vlan_tags + 1].mask = item_mask->inner_type;
+               pdata->tci_masks[pdata->nb_vlan_tags] = item_mask->tci;
+               if (item_mask->has_more_vlan) {
+                       if (pdata->nb_vlan_tags ==
+                           SFC_MAE_MATCH_VLAN_MAX_NTAGS) {
+                               return rte_flow_error_set(error, ENOTSUP,
+                                       RTE_FLOW_ERROR_TYPE_ITEM, item,
+                                       "Can't use 'has_more_vlan' in "
+                                       "the second item VLAN");
+                       }
+                       pdata->has_ivlan_mask = B_TRUE;
+                       if (item_spec->has_more_vlan)
+                               pdata->has_ivlan_value = B_TRUE;
+               }
+
+               /* Convert TCI to MAE representation right now. */
+               rc = sfc_mae_parse_item(flocs, nb_flocs, spec, mask,
+                                       ctx_mae, error);
+               if (rc != 0)
+                       return rc;
        }
 
-       return sfc_mae_parse_item(flocs, nb_flocs, spec, mask, ctx_mae, error);
+       ++(pdata->nb_vlan_tags);
+
+       return 0;
 }
 
 static const struct sfc_mae_field_locator flocs_ipv4[] = {
@@ -1612,6 +1698,8 @@ static const efx_mae_field_id_t field_ids_no_remap[] = {
        FIELD_ID_NO_REMAP(L4_SPORT_BE),
        FIELD_ID_NO_REMAP(L4_DPORT_BE),
        FIELD_ID_NO_REMAP(TCP_FLAGS_BE),
+       FIELD_ID_NO_REMAP(HAS_OVLAN),
+       FIELD_ID_NO_REMAP(HAS_IVLAN),
 
 #undef FIELD_ID_NO_REMAP
 };
@@ -1642,6 +1730,8 @@ static const efx_mae_field_id_t field_ids_remap_to_encap[] = {
        FIELD_ID_REMAP_TO_ENCAP(DST_IP6_BE),
        FIELD_ID_REMAP_TO_ENCAP(L4_SPORT_BE),
        FIELD_ID_REMAP_TO_ENCAP(L4_DPORT_BE),
+       FIELD_ID_REMAP_TO_ENCAP(HAS_OVLAN),
+       FIELD_ID_REMAP_TO_ENCAP(HAS_IVLAN),
 
 #undef FIELD_ID_REMAP_TO_ENCAP
 };
index 9740e54..0241fe3 100644 (file)
@@ -130,12 +130,14 @@ struct sfc_mae_pattern_data {
         *
         * - If an item ETH is followed by a single item VLAN,
         *   the former must have "type" set to one of supported
-        *   TPID values (0x8100, 0x88a8, 0x9100, 0x9200, 0x9300).
+        *   TPID values (0x8100, 0x88a8, 0x9100, 0x9200, 0x9300),
+        *   or 0x0000/0x0000.
         *
         * - If an item ETH is followed by two items VLAN, the
         *   item ETH must have "type" set to one of supported TPID
-        *   values (0x88a8, 0x9100, 0x9200, 0x9300), and the outermost
-        *   VLAN item must have "inner_type" set to TPID value 0x8100.
+        *   values (0x88a8, 0x9100, 0x9200, 0x9300), or 0x0000/0x0000,
+        *   and the outermost VLAN item must have "inner_type" set
+        *   to TPID value 0x8100, or 0x0000/0x0000
         *
         * - If a L2 item is followed by a L3 one, the former must
         *   indicate "type" ("inner_type") which corresponds to
@@ -156,6 +158,9 @@ struct sfc_mae_pattern_data {
         * VLAN (L3 EtherType)  --> ETHER_TYPE_BE
         */
        struct sfc_mae_ethertype        ethertypes[SFC_MAE_L2_MAX_NITEMS];
+
+       rte_be16_t                      tci_masks[SFC_MAE_MATCH_VLAN_MAX_NTAGS];
+
        unsigned int                    nb_vlan_tags;
 
        /**
@@ -191,6 +196,14 @@ struct sfc_mae_pattern_data {
         */
        uint8_t                         l3_next_proto_restriction_value;
        uint8_t                         l3_next_proto_restriction_mask;
+
+       /* Projected state of EFX_MAE_FIELD_HAS_OVLAN match bit */
+       bool                            has_ovlan_value;
+       bool                            has_ovlan_mask;
+
+       /* Projected state of EFX_MAE_FIELD_HAS_IVLAN match bit */
+       bool                            has_ivlan_value;
+       bool                            has_ivlan_mask;
 };
 
 struct sfc_mae_parse_ctx {