From ed7092069a3fdef2e520199ae7b835ec42948d79 Mon Sep 17 00:00:00 2001 From: Rahul Lakkireddy Date: Sat, 28 Sep 2019 02:00:11 +0530 Subject: [PATCH] net/cxgbe: support matching VLAN via flow API Add support for matching VLAN fields via rte_flow API. When matching VLAN pattern, the ethertype field in hardware filter specification must contain VLAN header's ethertype, and not Ethernet header's ethertype. The hardware automatically searches for ethertype 0x8100 in Ethernet header, when parsing incoming packet against VLAN pattern. Signed-off-by: Rahul Lakkireddy --- drivers/net/cxgbe/base/t4_regs_values.h | 9 ++ drivers/net/cxgbe/cxgbe_filter.c | 11 +- drivers/net/cxgbe/cxgbe_flow.c | 145 +++++++++++++++++++++++- drivers/net/cxgbe/cxgbe_main.c | 10 +- 4 files changed, 162 insertions(+), 13 deletions(-) diff --git a/drivers/net/cxgbe/base/t4_regs_values.h b/drivers/net/cxgbe/base/t4_regs_values.h index a9414d2027..e3f549e517 100644 --- a/drivers/net/cxgbe/base/t4_regs_values.h +++ b/drivers/net/cxgbe/base/t4_regs_values.h @@ -143,4 +143,13 @@ #define W_FT_MPSHITTYPE 3 #define W_FT_FRAGMENTATION 1 +/* + * Some of the Compressed Filter Tuple fields have internal structure. These + * bit shifts/masks describe those structures. All shifts are relative to the + * base position of the fields within the Compressed Filter Tuple + */ +#define S_FT_VLAN_VLD 16 +#define V_FT_VLAN_VLD(x) ((x) << S_FT_VLAN_VLD) +#define F_FT_VLAN_VLD V_FT_VLAN_VLD(1U) + #endif /* __T4_REGS_VALUES_H__ */ diff --git a/drivers/net/cxgbe/cxgbe_filter.c b/drivers/net/cxgbe/cxgbe_filter.c index 33b95a69ab..b9d9d5d391 100644 --- a/drivers/net/cxgbe/cxgbe_filter.c +++ b/drivers/net/cxgbe/cxgbe_filter.c @@ -69,7 +69,8 @@ int cxgbe_validate_filter(struct adapter *adapter, (!(fconf & (_mask)) && S(_field)) if (U(F_PORT, iport) || U(F_ETHERTYPE, ethtype) || - U(F_PROTOCOL, proto) || U(F_MACMATCH, macidx)) + U(F_PROTOCOL, proto) || U(F_MACMATCH, macidx) || + U(F_VLAN, ivlan_vld)) return -EOPNOTSUPP; #undef S @@ -292,6 +293,9 @@ static u64 hash_filter_ntuple(const struct filter_entry *f) ntuple |= (u64)(f->fs.val.ethtype) << tp->ethertype_shift; if (tp->macmatch_shift >= 0 && f->fs.mask.macidx) ntuple |= (u64)(f->fs.val.macidx) << tp->macmatch_shift; + if (tp->vlan_shift >= 0 && f->fs.mask.ivlan) + ntuple |= (u64)(F_FT_VLAN_VLD | f->fs.val.ivlan) << + tp->vlan_shift; return ntuple; } @@ -769,6 +773,9 @@ static int set_filter_wr(struct rte_eth_dev *dev, unsigned int fidx) V_FW_FILTER_WR_L2TIX(f->l2t ? f->l2t->idx : 0)); fwr->ethtype = cpu_to_be16(f->fs.val.ethtype); fwr->ethtypem = cpu_to_be16(f->fs.mask.ethtype); + fwr->frag_to_ovlan_vldm = + (V_FW_FILTER_WR_IVLAN_VLD(f->fs.val.ivlan_vld) | + V_FW_FILTER_WR_IVLAN_VLDM(f->fs.mask.ivlan_vld)); fwr->smac_sel = 0; fwr->rx_chan_rx_rpl_iq = cpu_to_be16(V_FW_FILTER_WR_RX_CHAN(0) | @@ -781,6 +788,8 @@ static int set_filter_wr(struct rte_eth_dev *dev, unsigned int fidx) V_FW_FILTER_WR_PORTM(f->fs.mask.iport)); fwr->ptcl = f->fs.val.proto; fwr->ptclm = f->fs.mask.proto; + fwr->ivlan = cpu_to_be16(f->fs.val.ivlan); + fwr->ivlanm = cpu_to_be16(f->fs.mask.ivlan); rte_memcpy(fwr->lip, f->fs.val.lip, sizeof(fwr->lip)); rte_memcpy(fwr->lipm, f->fs.mask.lip, sizeof(fwr->lipm)); rte_memcpy(fwr->fip, f->fs.val.fip, sizeof(fwr->fip)); diff --git a/drivers/net/cxgbe/cxgbe_flow.c b/drivers/net/cxgbe/cxgbe_flow.c index 4c85530398..4b72e64225 100644 --- a/drivers/net/cxgbe/cxgbe_flow.c +++ b/drivers/net/cxgbe/cxgbe_flow.c @@ -46,6 +46,53 @@ cxgbe_validate_item(const struct rte_flow_item *i, struct rte_flow_error *e) return 0; } +/** + * Apart from the 4-tuple IPv4/IPv6 - TCP/UDP information, + * there's only 40-bits available to store match fields. + * So, to save space, optimize filter spec for some common + * known fields that hardware can parse against incoming + * packets automatically. + */ +static void +cxgbe_tweak_filter_spec(struct adapter *adap, + struct ch_filter_specification *fs) +{ + /* Save 16-bit ethertype field space, by setting corresponding + * 1-bit flags in the filter spec for common known ethertypes. + * When hardware sees these flags, it automatically infers and + * matches incoming packets against the corresponding ethertype. + */ + if (fs->mask.ethtype == 0xffff) { + switch (fs->val.ethtype) { + case RTE_ETHER_TYPE_IPV4: + if (adap->params.tp.ethertype_shift < 0) { + fs->type = FILTER_TYPE_IPV4; + fs->val.ethtype = 0; + fs->mask.ethtype = 0; + } + break; + case RTE_ETHER_TYPE_IPV6: + if (adap->params.tp.ethertype_shift < 0) { + fs->type = FILTER_TYPE_IPV6; + fs->val.ethtype = 0; + fs->mask.ethtype = 0; + } + break; + case RTE_ETHER_TYPE_VLAN: + if (adap->params.tp.ethertype_shift < 0 && + adap->params.tp.vlan_shift >= 0) { + fs->val.ivlan_vld = 1; + fs->mask.ivlan_vld = 1; + fs->val.ethtype = 0; + fs->mask.ethtype = 0; + } + break; + default: + break; + } + } +} + static void cxgbe_fill_filter_region(struct adapter *adap, struct ch_filter_specification *fs) @@ -95,6 +142,9 @@ cxgbe_fill_filter_region(struct adapter *adap, ntuple_mask |= (u64)fs->mask.iport << tp->port_shift; if (tp->macmatch_shift >= 0) ntuple_mask |= (u64)fs->mask.macidx << tp->macmatch_shift; + if (tp->vlan_shift >= 0 && fs->mask.ivlan_vld) + ntuple_mask |= (u64)(F_FT_VLAN_VLD | fs->mask.ivlan) << + tp->vlan_shift; if (ntuple_mask != hash_filter_mask) return; @@ -114,6 +164,25 @@ ch_rte_parsetype_eth(const void *dmask, const struct rte_flow_item *item, /* If user has not given any mask, then use chelsio supported mask. */ mask = umask ? umask : (const struct rte_flow_item_eth *)dmask; + if (!spec) + return 0; + + /* Chelsio hardware supports matching on only one ethertype + * (i.e. either the outer or inner ethertype, but not both). If + * we already encountered VLAN item, then ensure that the outer + * ethertype is VLAN (0x8100) and don't overwrite the inner + * ethertype stored during VLAN item parsing. Note that if + * 'ivlan_vld' bit is set in Chelsio filter spec, then the + * hardware automatically only matches packets with outer + * ethertype having VLAN (0x8100). + */ + if (fs->mask.ivlan_vld && + be16_to_cpu(spec->type) != RTE_ETHER_TYPE_VLAN) + return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Already encountered VLAN item," + " but outer ethertype is not 0x8100"); + /* we don't support SRC_MAC filtering*/ if (!rte_is_zero_ether_addr(&mask->src)) return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, @@ -137,8 +206,13 @@ ch_rte_parsetype_eth(const void *dmask, const struct rte_flow_item *item, CXGBE_FILL_FS(idx, 0x1ff, macidx); } - CXGBE_FILL_FS(be16_to_cpu(spec->type), - be16_to_cpu(mask->type), ethtype); + /* Only set outer ethertype, if we didn't encounter VLAN item yet. + * Otherwise, the inner ethertype set by VLAN item will get + * overwritten. + */ + if (!fs->mask.ivlan_vld) + CXGBE_FILL_FS(be16_to_cpu(spec->type), + be16_to_cpu(mask->type), ethtype); return 0; } @@ -163,6 +237,50 @@ ch_rte_parsetype_port(const void *dmask, const struct rte_flow_item *item, return 0; } +static int +ch_rte_parsetype_vlan(const void *dmask, const struct rte_flow_item *item, + struct ch_filter_specification *fs, + struct rte_flow_error *e) +{ + const struct rte_flow_item_vlan *spec = item->spec; + const struct rte_flow_item_vlan *umask = item->mask; + const struct rte_flow_item_vlan *mask; + + /* If user has not given any mask, then use chelsio supported mask. */ + mask = umask ? umask : (const struct rte_flow_item_vlan *)dmask; + + CXGBE_FILL_FS(1, 1, ivlan_vld); + if (!spec) + return 0; /* Wildcard, match all VLAN */ + + /* Chelsio hardware supports matching on only one ethertype + * (i.e. either the outer or inner ethertype, but not both). + * If outer ethertype is already set and is not VLAN (0x8100), + * then don't proceed further. Otherwise, reset the outer + * ethertype, so that it can be replaced by inner ethertype. + * Note that the hardware will automatically match on outer + * ethertype 0x8100, if 'ivlan_vld' bit is set in Chelsio + * filter spec. + */ + if (fs->mask.ethtype) { + if (fs->val.ethtype != RTE_ETHER_TYPE_VLAN) + return rte_flow_error_set(e, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Outer ethertype not 0x8100"); + + fs->val.ethtype = 0; + fs->mask.ethtype = 0; + } + + CXGBE_FILL_FS(be16_to_cpu(spec->tci), be16_to_cpu(mask->tci), ivlan); + if (spec->inner_type) + CXGBE_FILL_FS(be16_to_cpu(spec->inner_type), + be16_to_cpu(mask->inner_type), ethtype); + + return 0; +} + static int ch_rte_parsetype_udp(const void *dmask, const struct rte_flow_item *item, struct ch_filter_specification *fs, @@ -232,8 +350,13 @@ ch_rte_parsetype_ipv4(const void *dmask, const struct rte_flow_item *item, return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, item, "ttl/tos are not supported"); + if (fs->mask.ethtype && + (fs->val.ethtype != RTE_ETHER_TYPE_VLAN && + fs->val.ethtype != RTE_ETHER_TYPE_IPV4)) + return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Couldn't find IPv4 ethertype"); fs->type = FILTER_TYPE_IPV4; - CXGBE_FILL_FS(RTE_ETHER_TYPE_IPV4, 0xffff, ethtype); if (!val) return 0; /* ipv4 wild card */ @@ -261,8 +384,13 @@ ch_rte_parsetype_ipv6(const void *dmask, const struct rte_flow_item *item, item, "tc/flow/hop are not supported"); + if (fs->mask.ethtype && + (fs->val.ethtype != RTE_ETHER_TYPE_VLAN && + fs->val.ethtype != RTE_ETHER_TYPE_IPV6)) + return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Couldn't find IPv6 ethertype"); fs->type = FILTER_TYPE_IPV6; - CXGBE_FILL_FS(RTE_ETHER_TYPE_IPV6, 0xffff, ethtype); if (!val) return 0; /* ipv6 wild card */ @@ -700,6 +828,14 @@ static struct chrte_fparse parseitem[] = { } }, + [RTE_FLOW_ITEM_TYPE_VLAN] = { + .fptr = ch_rte_parsetype_vlan, + .dmask = &(const struct rte_flow_item_vlan){ + .tci = 0xffff, + .inner_type = 0xffff, + } + }, + [RTE_FLOW_ITEM_TYPE_IPV4] = { .fptr = ch_rte_parsetype_ipv4, .dmask = &rte_flow_item_ipv4_mask, @@ -773,6 +909,7 @@ cxgbe_rtef_parse_items(struct rte_flow *flow, } cxgbe_fill_filter_region(adap, &flow->fs); + cxgbe_tweak_filter_spec(adap, &flow->fs); return 0; } diff --git a/drivers/net/cxgbe/cxgbe_main.c b/drivers/net/cxgbe/cxgbe_main.c index 4701518a64..f6967a3e4b 100644 --- a/drivers/net/cxgbe/cxgbe_main.c +++ b/drivers/net/cxgbe/cxgbe_main.c @@ -753,12 +753,6 @@ static void configure_vlan_types(struct adapter *adapter) V_OVLAN_ETYPE(M_OVLAN_ETYPE), V_OVLAN_MASK(M_OVLAN_MASK) | V_OVLAN_ETYPE(0x9100)); - /* OVLAN Type 0x8100 */ - t4_set_reg_field(adapter, MPS_PORT_RX_OVLAN_REG(i, A_RX_OVLAN2), - V_OVLAN_MASK(M_OVLAN_MASK) | - V_OVLAN_ETYPE(M_OVLAN_ETYPE), - V_OVLAN_MASK(M_OVLAN_MASK) | - V_OVLAN_ETYPE(0x8100)); /* IVLAN 0X8100 */ t4_set_reg_field(adapter, MPS_PORT_RX_IVLAN(i), @@ -767,9 +761,9 @@ static void configure_vlan_types(struct adapter *adapter) t4_set_reg_field(adapter, MPS_PORT_RX_CTL(i), F_OVLAN_EN0 | F_OVLAN_EN1 | - F_OVLAN_EN2 | F_IVLAN_EN, + F_IVLAN_EN, F_OVLAN_EN0 | F_OVLAN_EN1 | - F_OVLAN_EN2 | F_IVLAN_EN); + F_IVLAN_EN); } t4_tp_wr_bits_indirect(adapter, A_TP_INGRESS_CONFIG, V_RM_OVLAN(1), -- 2.20.1