[1/9] net/cxgbe: add rte_flow support for matching Q-in-Q VLAN

Message ID 67a7f133daaa4dae254208bc86c9c2b999688715.1583906144.git.kaara.satwik@chelsio.com (mailing list archive)
State Accepted, archived
Delegated to: Ferruh Yigit
Headers
Series net/cxgbe: updates for rte_flow support |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation fail Compilation issues
ci/iol-testing success Testing PASS
ci/iol-intel-Performance fail Performance Testing issues
ci/iol-mellanox-Performance success Performance Testing PASS

Commit Message

Rahul Lakkireddy March 11, 2020, 9:05 a.m. UTC
  From: Karra Satwik <kaara.satwik@chelsio.com>

Add support to match fields in 802.1ad Q-in-Q VLAN packets.
Relax check for repeated pattern items for RTE_FLOW_ITEM_TYPE_VLAN
item, since the same item is used to represent both QinQ and VLAN
packets.

When QinQ match is enabled, the ethertype field in the hardware
spec must contain the innermost VLAN header's ethertype field,
and not the Ethernet header's ethertype field. The hardware
automatically searches for ethertype 0x88A8/0x8100 in Ethernet
header, when parsing incoming packet against QinQ/VLAN pattern,
respectively.

Signed-off-by: Karra Satwik <kaara.satwik@chelsio.com>
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
---
 drivers/net/cxgbe/base/t4_hw.c   |   7 ---
 drivers/net/cxgbe/base/t4_regs.h |   4 ++
 drivers/net/cxgbe/cxgbe_filter.c |  26 +++++++-
 drivers/net/cxgbe/cxgbe_flow.c   | 102 +++++++++++++++++--------------
 4 files changed, 82 insertions(+), 57 deletions(-)
  

Patch

diff --git a/drivers/net/cxgbe/base/t4_hw.c b/drivers/net/cxgbe/base/t4_hw.c
index 71ad1cb0f..f6bf57c75 100644
--- a/drivers/net/cxgbe/base/t4_hw.c
+++ b/drivers/net/cxgbe/base/t4_hw.c
@@ -5254,13 +5254,6 @@  int t4_init_tp_params(struct adapter *adap)
 	adap->params.tp.macmatch_shift = t4_filter_field_shift(adap,
 							       F_MACMATCH);
 
-	/*
-	 * If TP_INGRESS_CONFIG.VNID == 0, then TP_VLAN_PRI_MAP.VNIC_ID
-	 * represents the presense of an Outer VLAN instead of a VNIC ID.
-	 */
-	if ((adap->params.tp.ingress_config & F_VNIC) == 0)
-		adap->params.tp.vnic_shift = -1;
-
 	v = t4_read_reg(adap, LE_3_DB_HASH_MASK_GEN_IPV4_T6_A);
 	adap->params.tp.hash_filter_mask = v;
 	v = t4_read_reg(adap, LE_4_DB_HASH_MASK_GEN_IPV4_T6_A);
diff --git a/drivers/net/cxgbe/base/t4_regs.h b/drivers/net/cxgbe/base/t4_regs.h
index af8c741e2..97cf49a48 100644
--- a/drivers/net/cxgbe/base/t4_regs.h
+++ b/drivers/net/cxgbe/base/t4_regs.h
@@ -572,6 +572,10 @@ 
 
 #define A_TP_INGRESS_CONFIG 0x141
 
+#define S_USE_ENC_IDX    13
+#define V_USE_ENC_IDX(x) ((x) << S_USE_ENC_IDX)
+#define F_USE_ENC_IDX    V_USE_ENC_IDX(1U)
+
 #define S_VNIC    11
 #define V_VNIC(x) ((x) << S_VNIC)
 #define F_VNIC    V_VNIC(1U)
diff --git a/drivers/net/cxgbe/cxgbe_filter.c b/drivers/net/cxgbe/cxgbe_filter.c
index b9d9d5d39..d26be3cd7 100644
--- a/drivers/net/cxgbe/cxgbe_filter.c
+++ b/drivers/net/cxgbe/cxgbe_filter.c
@@ -56,13 +56,15 @@  int cxgbe_init_hash_filter(struct adapter *adap)
 int cxgbe_validate_filter(struct adapter *adapter,
 			  struct ch_filter_specification *fs)
 {
-	u32 fconf;
+	u32 fconf, iconf;
 
 	/*
 	 * Check for unconfigured fields being used.
 	 */
 	fconf = adapter->params.tp.vlan_pri_map;
 
+	iconf = adapter->params.tp.ingress_config;
+
 #define S(_field) \
 	(fs->val._field || fs->mask._field)
 #define U(_mask, _field) \
@@ -70,7 +72,15 @@  int cxgbe_validate_filter(struct adapter *adapter,
 
 	if (U(F_PORT, iport) || U(F_ETHERTYPE, ethtype) ||
 	    U(F_PROTOCOL, proto) || U(F_MACMATCH, macidx) ||
-	    U(F_VLAN, ivlan_vld))
+	    U(F_VLAN, ivlan_vld) || U(F_VNIC_ID, ovlan_vld))
+		return -EOPNOTSUPP;
+
+	/* Ensure OVLAN match is enabled in hardware */
+	if (S(ovlan_vld) && (iconf & F_VNIC))
+		return -EOPNOTSUPP;
+
+	/* To use OVLAN, L4 encapsulation match must not be enabled */
+	if (S(ovlan_vld) && (iconf & F_USE_ENC_IDX))
 		return -EOPNOTSUPP;
 
 #undef S
@@ -296,6 +306,12 @@  static u64 hash_filter_ntuple(const struct filter_entry *f)
 	if (tp->vlan_shift >= 0 && f->fs.mask.ivlan)
 		ntuple |= (u64)(F_FT_VLAN_VLD | f->fs.val.ivlan) <<
 			  tp->vlan_shift;
+	if (tp->vnic_shift >= 0) {
+		if (!(adap->params.tp.ingress_config & F_VNIC) &&
+		    f->fs.mask.ovlan_vld)
+			ntuple |= (u64)(f->fs.val.ovlan_vld << 16 |
+					f->fs.val.ovlan) << tp->vnic_shift;
+	}
 
 	return ntuple;
 }
@@ -775,7 +791,9 @@  static int set_filter_wr(struct rte_eth_dev *dev, unsigned int fidx)
 	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));
+		 V_FW_FILTER_WR_IVLAN_VLDM(f->fs.mask.ivlan_vld) |
+		 V_FW_FILTER_WR_OVLAN_VLD(f->fs.val.ovlan_vld) |
+		 V_FW_FILTER_WR_OVLAN_VLDM(f->fs.mask.ovlan_vld));
 	fwr->smac_sel = 0;
 	fwr->rx_chan_rx_rpl_iq =
 		cpu_to_be16(V_FW_FILTER_WR_RX_CHAN(0) |
@@ -790,6 +808,8 @@  static int set_filter_wr(struct rte_eth_dev *dev, unsigned int fidx)
 	fwr->ptclm = f->fs.mask.proto;
 	fwr->ivlan = cpu_to_be16(f->fs.val.ivlan);
 	fwr->ivlanm = cpu_to_be16(f->fs.mask.ivlan);
+	fwr->ovlan = cpu_to_be16(f->fs.val.ovlan);
+	fwr->ovlanm = cpu_to_be16(f->fs.mask.ovlan);
 	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 9070f4960..cd833d095 100644
--- a/drivers/net/cxgbe/cxgbe_flow.c
+++ b/drivers/net/cxgbe/cxgbe_flow.c
@@ -87,6 +87,15 @@  cxgbe_tweak_filter_spec(struct adapter *adap,
 				fs->mask.ethtype = 0;
 			}
 			break;
+		case RTE_ETHER_TYPE_QINQ:
+			if (adap->params.tp.ethertype_shift < 0 &&
+			    adap->params.tp.vnic_shift >= 0) {
+				fs->val.ovlan_vld = 1;
+				fs->mask.ovlan_vld = 1;
+				fs->val.ethtype = 0;
+				fs->mask.ethtype = 0;
+			}
+			break;
 		default:
 			break;
 		}
@@ -145,6 +154,9 @@  cxgbe_fill_filter_region(struct adapter *adap,
 	if (tp->vlan_shift >= 0 && fs->mask.ivlan_vld)
 		ntuple_mask |= (u64)(F_FT_VLAN_VLD | fs->mask.ivlan) <<
 			       tp->vlan_shift;
+	if (tp->vnic_shift >= 0 && fs->mask.ovlan_vld)
+		ntuple_mask |= (u64)(F_FT_VLAN_VLD | fs->mask.ovlan) <<
+			       tp->vnic_shift;
 
 	if (ntuple_mask != hash_filter_mask)
 		return;
@@ -167,22 +179,6 @@  ch_rte_parsetype_eth(const void *dmask, const struct rte_flow_item *item,
 	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,
@@ -206,13 +202,9 @@  ch_rte_parsetype_eth(const void *dmask, const struct rte_flow_item *item,
 		CXGBE_FILL_FS(idx, 0x1ff, macidx);
 	}
 
-	/* 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);
+	CXGBE_FILL_FS(be16_to_cpu(spec->type),
+		      be16_to_cpu(mask->type), ethtype);
+
 	return 0;
 }
 
@@ -249,32 +241,48 @@  ch_rte_parsetype_vlan(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_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)
+		return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+					  item,
+					  "Can't parse VLAN item without knowing ethertype");
+
+	/* If ethertype is already set and is not VLAN (0x8100) or
+	 * QINQ(0x88A8), then don't proceed further. Otherwise,
+	 * reset the outer ethertype, so that it can be replaced by
+	 * innermost ethertype. Note that hardware will automatically
+	 * match against VLAN or QINQ packets, based on 'ivlan_vld' or
+	 * 'ovlan_vld' bit set in Chelsio filter spec, respectively.
 	 */
 	if (fs->mask.ethtype) {
-		if (fs->val.ethtype != RTE_ETHER_TYPE_VLAN)
+		if (fs->val.ethtype != RTE_ETHER_TYPE_VLAN &&
+		    fs->val.ethtype != RTE_ETHER_TYPE_QINQ)
 			return rte_flow_error_set(e, EINVAL,
 						  RTE_FLOW_ERROR_TYPE_ITEM,
 						  item,
-						  "Outer ethertype not 0x8100");
+						  "Ethertype must be 0x8100 or 0x88a8");
+	}
 
-		fs->val.ethtype = 0;
-		fs->mask.ethtype = 0;
+	if (fs->val.ethtype == RTE_ETHER_TYPE_QINQ) {
+		CXGBE_FILL_FS(1, 1, ovlan_vld);
+		if (spec) {
+			CXGBE_FILL_FS(be16_to_cpu(spec->tci),
+				      be16_to_cpu(mask->tci), ovlan);
+
+			fs->mask.ethtype = 0;
+			fs->val.ethtype = 0;
+		}
+	} else if (fs->val.ethtype == RTE_ETHER_TYPE_VLAN) {
+		CXGBE_FILL_FS(1, 1, ivlan_vld);
+		if (spec) {
+			CXGBE_FILL_FS(be16_to_cpu(spec->tci),
+				      be16_to_cpu(mask->tci), ivlan);
+
+			fs->mask.ethtype = 0;
+			fs->val.ethtype = 0;
+		}
 	}
 
-	CXGBE_FILL_FS(be16_to_cpu(spec->tci), be16_to_cpu(mask->tci), ivlan);
-	if (spec->inner_type)
+	if (spec)
 		CXGBE_FILL_FS(be16_to_cpu(spec->inner_type),
 			      be16_to_cpu(mask->inner_type), ethtype);
 
@@ -351,8 +359,7 @@  ch_rte_parsetype_ipv4(const void *dmask, const struct rte_flow_item *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))
+	    (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");
@@ -385,8 +392,7 @@  ch_rte_parsetype_ipv6(const void *dmask, const struct rte_flow_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))
+	    (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");
@@ -907,10 +913,12 @@  cxgbe_rtef_parse_items(struct rte_flow *flow,
 			continue;
 		default:
 			/* check if item is repeated */
-			if (repeat[i->type])
+			if (repeat[i->type] &&
+			    i->type != RTE_FLOW_ITEM_TYPE_VLAN)
 				return rte_flow_error_set(e, ENOTSUP,
 						RTE_FLOW_ERROR_TYPE_ITEM, i,
-						"parse items cannot be repeated (except void)");
+						"parse items cannot be repeated(except void/vlan)");
+
 			repeat[i->type] = 1;
 
 			/* No spec found for this pattern item. Skip it */