From 900d0c1b7196074b00c840b7a45222994b9abdca Mon Sep 17 00:00:00 2001 From: Ivan Malov Date: Tue, 20 Oct 2020 10:13:27 +0100 Subject: [PATCH] net/sfc: support flow item VLAN in transfer rules Add support for this flow item to MAE-specific RTE flow implementation. In a pattern, a L2 item preceding an item VLAN must have correct "type" ("inner_type") set depending on the total number of VLAN tags (double-tagging is supported): "pattern eth type is X / vlan / end", X = 0x8100, or 0x88a8, or 0x9100, or 0x9200, or 0x9300 "pattern eth type is X / vlan inner_type is 0x8100 / vlan / end" X = 0x88a8, or 0x9100, or 0x9200, or 0x9300 Signed-off-by: Ivan Malov Signed-off-by: Andrew Rybchenko Reviewed-by: Andy Moreton --- doc/guides/nics/sfc_efx.rst | 2 + drivers/net/sfc/sfc_mae.c | 265 +++++++++++++++++++++++++++++++++++- drivers/net/sfc/sfc_mae.h | 47 +++++++ 3 files changed, 311 insertions(+), 3 deletions(-) diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst index e372627a50..cd61b4d7b6 100644 --- a/doc/guides/nics/sfc_efx.rst +++ b/doc/guides/nics/sfc_efx.rst @@ -200,6 +200,8 @@ Supported pattern items (***transfer*** rules): - ETH +- VLAN (double-tagging is supported) + Supported actions (***transfer*** rules): - OF_POP_VLAN diff --git a/drivers/net/sfc/sfc_mae.c b/drivers/net/sfc/sfc_mae.c index 14e6d33c55..cc22fee6fe 100644 --- a/drivers/net/sfc/sfc_mae.c +++ b/drivers/net/sfc/sfc_mae.c @@ -260,6 +260,122 @@ sfc_mae_flow_cleanup(struct sfc_adapter *sa, efx_mae_match_spec_fini(sa->nic, spec_mae->match_spec); } +static int +sfc_mae_set_ethertypes(struct sfc_mae_parse_ctx *ctx) +{ + efx_mae_match_spec_t *efx_spec = ctx->match_spec_action; + struct sfc_mae_pattern_data *pdata = &ctx->pattern_data; + const efx_mae_field_id_t field_ids[] = { + EFX_MAE_FIELD_VLAN0_PROTO_BE, + EFX_MAE_FIELD_VLAN1_PROTO_BE, + }; + const struct sfc_mae_ethertype *et; + unsigned int i; + int rc; + + /* + * In accordance with RTE flow API convention, the innermost L2 + * item's "type" ("inner_type") is a L3 EtherType. If there is + * no L3 item, it's 0x0000/0x0000. + */ + et = &pdata->ethertypes[pdata->nb_vlan_tags]; + rc = efx_mae_match_spec_field_set(efx_spec, EFX_MAE_FIELD_ETHER_TYPE_BE, + sizeof(et->value), + (const uint8_t *)&et->value, + sizeof(et->mask), + (const uint8_t *)&et->mask); + if (rc != 0) + return rc; + + /* + * sfc_mae_rule_parse_item_vlan() has already made sure + * that pdata->nb_vlan_tags does not exceed this figure. + */ + RTE_BUILD_BUG_ON(SFC_MAE_MATCH_VLAN_MAX_NTAGS != 2); + + for (i = 0; i < pdata->nb_vlan_tags; ++i) { + et = &pdata->ethertypes[i]; + + rc = efx_mae_match_spec_field_set(efx_spec, field_ids[i], + sizeof(et->value), + (const uint8_t *)&et->value, + sizeof(et->mask), + (const uint8_t *)&et->mask); + if (rc != 0) + return rc; + } + + return 0; +} + +static int +sfc_mae_rule_process_pattern_data(struct sfc_mae_parse_ctx *ctx, + struct rte_flow_error *error) +{ + struct sfc_mae_pattern_data *pdata = &ctx->pattern_data; + struct sfc_mae_ethertype *ethertypes = pdata->ethertypes; + const rte_be16_t supported_tpids[] = { + /* VLAN standard TPID (always the first element) */ + RTE_BE16(RTE_ETHER_TYPE_VLAN), + + /* Double-tagging TPIDs */ + RTE_BE16(RTE_ETHER_TYPE_QINQ), + RTE_BE16(RTE_ETHER_TYPE_QINQ1), + RTE_BE16(RTE_ETHER_TYPE_QINQ2), + RTE_BE16(RTE_ETHER_TYPE_QINQ3), + }; + unsigned int nb_supported_tpids = RTE_DIM(supported_tpids); + unsigned int ethertype_idx; + int rc; + + /* + * sfc_mae_rule_parse_item_vlan() has already made sure + * that pdata->nb_vlan_tags does not exceed this figure. + */ + RTE_BUILD_BUG_ON(SFC_MAE_MATCH_VLAN_MAX_NTAGS != 2); + + for (ethertype_idx = 0; + ethertype_idx < pdata->nb_vlan_tags; ++ethertype_idx) { + unsigned int tpid_idx; + + /* Exact match is supported only. */ + if (ethertypes[ethertype_idx].mask != RTE_BE16(0xffff)) { + rc = EINVAL; + goto fail; + } + + for (tpid_idx = pdata->nb_vlan_tags - ethertype_idx - 1; + tpid_idx < nb_supported_tpids; ++tpid_idx) { + if (ethertypes[ethertype_idx].value == + supported_tpids[tpid_idx]) + break; + } + + if (tpid_idx == nb_supported_tpids) { + rc = EINVAL; + goto fail; + } + + nb_supported_tpids = 1; + } + + /* + * Now, when the number of VLAN tags is known, set fields + * ETHER_TYPE, VLAN0_PROTO and VLAN1_PROTO so that the first + * one is either a valid L3 EtherType (or 0x0000/0x0000), + * and the last two are valid TPIDs (or 0x0000/0x0000). + */ + rc = sfc_mae_set_ethertypes(ctx); + if (rc != 0) + goto fail; + + return 0; + +fail: + return rte_flow_error_set(error, rc, RTE_FLOW_ERROR_TYPE_ITEM, NULL, + "Failed to process pattern data"); +} + static int sfc_mae_rule_parse_item_port_id(const struct rte_flow_item *item, struct sfc_flow_parse_ctx *ctx, @@ -486,6 +602,16 @@ sfc_mae_rule_parse_item_vf(const struct rte_flow_item *item, return 0; } +/* + * Having this field ID in a field locator means that this + * locator cannot be used to actually set the field at the + * time when the corresponding item gets encountered. Such + * fields get stashed in the parsing context instead. This + * is required to resolve dependencies between the stashed + * fields. See sfc_mae_rule_process_pattern_data(). + */ +#define SFC_MAE_FIELD_HANDLING_DEFERRED EFX_MAE_FIELD_NIDS + struct sfc_mae_field_locator { efx_mae_field_id_t field_id; size_t size; @@ -522,6 +648,9 @@ sfc_mae_parse_item(const struct sfc_mae_field_locator *field_locators, for (i = 0; i < nb_field_locators; ++i) { const struct sfc_mae_field_locator *fl = &field_locators[i]; + if (fl->field_id == SFC_MAE_FIELD_HANDLING_DEFERRED) + continue; + rc = efx_mae_match_spec_field_set(efx_spec, fl->field_id, fl->size, spec + fl->ofst, fl->size, mask + fl->ofst); @@ -539,7 +668,11 @@ sfc_mae_parse_item(const struct sfc_mae_field_locator *field_locators, static const struct sfc_mae_field_locator flocs_eth[] = { { - EFX_MAE_FIELD_ETHER_TYPE_BE, + /* + * This locator is used only for building supported fields mask. + * The field is handled by sfc_mae_rule_process_pattern_data(). + */ + SFC_MAE_FIELD_HANDLING_DEFERRED, RTE_SIZEOF_FIELD(struct rte_flow_item_eth, type), offsetof(struct rte_flow_item_eth, type), }, @@ -577,14 +710,128 @@ sfc_mae_rule_parse_item_eth(const struct rte_flow_item *item, if (rc != 0) return rc; - /* If "spec" is not set, could be any Ethernet */ - if (spec == NULL) + if (spec != NULL) { + struct sfc_mae_pattern_data *pdata = &ctx_mae->pattern_data; + struct sfc_mae_ethertype *ethertypes = pdata->ethertypes; + const struct rte_flow_item_eth *item_spec; + const struct rte_flow_item_eth *item_mask; + + item_spec = (const struct rte_flow_item_eth *)spec; + item_mask = (const struct rte_flow_item_eth *)mask; + + ethertypes[0].value = item_spec->type; + ethertypes[0].mask = item_mask->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. + */ return 0; + } return sfc_mae_parse_item(flocs_eth, RTE_DIM(flocs_eth), spec, mask, ctx_mae->match_spec_action, error); } +static const struct sfc_mae_field_locator flocs_vlan[] = { + /* Outermost tag */ + { + EFX_MAE_FIELD_VLAN0_TCI_BE, + RTE_SIZEOF_FIELD(struct rte_flow_item_vlan, tci), + offsetof(struct rte_flow_item_vlan, tci), + }, + { + /* + * This locator is used only for building supported fields mask. + * The field is handled by sfc_mae_rule_process_pattern_data(). + */ + SFC_MAE_FIELD_HANDLING_DEFERRED, + RTE_SIZEOF_FIELD(struct rte_flow_item_vlan, inner_type), + offsetof(struct rte_flow_item_vlan, inner_type), + }, + + /* Innermost tag */ + { + EFX_MAE_FIELD_VLAN1_TCI_BE, + RTE_SIZEOF_FIELD(struct rte_flow_item_vlan, tci), + offsetof(struct rte_flow_item_vlan, tci), + }, + { + /* + * This locator is used only for building supported fields mask. + * The field is handled by sfc_mae_rule_process_pattern_data(). + */ + SFC_MAE_FIELD_HANDLING_DEFERRED, + RTE_SIZEOF_FIELD(struct rte_flow_item_vlan, inner_type), + offsetof(struct rte_flow_item_vlan, inner_type), + }, +}; + +static int +sfc_mae_rule_parse_item_vlan(const struct rte_flow_item *item, + struct sfc_flow_parse_ctx *ctx, + struct rte_flow_error *error) +{ + struct sfc_mae_parse_ctx *ctx_mae = ctx->mae; + struct sfc_mae_pattern_data *pdata = &ctx_mae->pattern_data; + const struct sfc_mae_field_locator *flocs; + struct rte_flow_item_vlan supp_mask; + const uint8_t *spec = NULL; + const uint8_t *mask = NULL; + unsigned int nb_flocs; + int rc; + + RTE_BUILD_BUG_ON(SFC_MAE_MATCH_VLAN_MAX_NTAGS != 2); + + 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 match that many VLAN tags"); + } + + 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)); + + rc = sfc_flow_parse_init(item, + (const void **)&spec, (const void **)&mask, + (const void *)&supp_mask, + &rte_flow_item_vlan_mask, + sizeof(struct rte_flow_item_vlan), error); + if (rc != 0) + return rc; + + if (spec != NULL) { + struct sfc_mae_ethertype *ethertypes = 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. + */ + return 0; + } + + return sfc_mae_parse_item(flocs, nb_flocs, spec, mask, + ctx_mae->match_spec_action, error); +} + static const struct sfc_flow_item sfc_flow_items[] = { { .type = RTE_FLOW_ITEM_TYPE_PORT_ID, @@ -637,6 +884,13 @@ static const struct sfc_flow_item sfc_flow_items[] = { .ctx_type = SFC_FLOW_PARSE_CTX_MAE, .parse = sfc_mae_rule_parse_item_eth, }, + { + .type = RTE_FLOW_ITEM_TYPE_VLAN, + .prev_layer = SFC_FLOW_ITEM_L2, + .layer = SFC_FLOW_ITEM_L2, + .ctx_type = SFC_FLOW_PARSE_CTX_MAE, + .parse = sfc_mae_rule_parse_item_vlan, + }, }; int @@ -670,6 +924,10 @@ sfc_mae_rule_parse_pattern(struct sfc_adapter *sa, if (rc != 0) goto fail_parse_pattern; + rc = sfc_mae_rule_process_pattern_data(&ctx_mae, error); + if (rc != 0) + goto fail_process_pattern_data; + if (!efx_mae_match_spec_is_valid(sa->nic, ctx_mae.match_spec_action)) { rc = rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, NULL, @@ -682,6 +940,7 @@ sfc_mae_rule_parse_pattern(struct sfc_adapter *sa, return 0; fail_validate_match_spec_action: +fail_process_pattern_data: fail_parse_pattern: efx_mae_match_spec_fini(sa->nic, ctx_mae.match_spec_action); diff --git a/drivers/net/sfc/sfc_mae.h b/drivers/net/sfc/sfc_mae.h index f92e62dcbe..e4e8ab67a5 100644 --- a/drivers/net/sfc/sfc_mae.h +++ b/drivers/net/sfc/sfc_mae.h @@ -62,10 +62,57 @@ struct sfc_mae { struct sfc_adapter; struct sfc_flow_spec; +/** This implementation supports double-tagging */ +#define SFC_MAE_MATCH_VLAN_MAX_NTAGS (2) + +/** It is possible to keep track of one item ETH and two items VLAN */ +#define SFC_MAE_L2_MAX_NITEMS (SFC_MAE_MATCH_VLAN_MAX_NTAGS + 1) + +/** Auxiliary entry format to keep track of L2 "type" ("inner_type") */ +struct sfc_mae_ethertype { + rte_be16_t value; + rte_be16_t mask; +}; + +struct sfc_mae_pattern_data { + /** + * Keeps track of "type" ("inner_type") mask and value for each + * parsed L2 item in a pattern. These values/masks get filled + * in MAE match specification at the end of parsing. Also, this + * information is used to conduct consistency checks: + * + * - 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). + * + * - 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. + * + * In turn, mapping between RTE convention (above requirements) and + * MAE fields is non-trivial. The following scheme indicates + * which item EtherTypes go to which MAE fields in the case + * of single tag: + * + * ETH (0x8100) --> VLAN0_PROTO_BE + * VLAN (L3 EtherType) --> ETHER_TYPE_BE + * + * Similarly, in the case of double tagging: + * + * ETH (0x88a8) --> VLAN0_PROTO_BE + * VLAN (0x8100) --> VLAN1_PROTO_BE + * VLAN (L3 EtherType) --> ETHER_TYPE_BE + */ + struct sfc_mae_ethertype ethertypes[SFC_MAE_L2_MAX_NITEMS]; + unsigned int nb_vlan_tags; +}; + struct sfc_mae_parse_ctx { struct sfc_adapter *sa; efx_mae_match_spec_t *match_spec_action; bool match_mport_set; + struct sfc_mae_pattern_data pattern_data; }; int sfc_mae_attach(struct sfc_adapter *sa); -- 2.20.1