c13727
From 8d729f0fcae5a80505126a5409b2d1a9f6563e90 Mon Sep 17 00:00:00 2001
c13727
From: Ong Boon Leong <boon.leong.ong@intel.com>
c13727
Date: Sat, 11 Dec 2021 22:51:34 +0800
c13727
Subject: [PATCH 19/37] net: stmmac: fix tc flower deletion for VLAN priority
c13727
 Rx steering
c13727
Git-commit: aeb7c75cb77478fdbf821628e9c95c4baa9adc63
c13727
Patch-mainline: v5.16-rc6
c13727
References: git-fixes
c13727
c13727
To replicate the issue:-
c13727
c13727
1) Add 1 flower filter for VLAN Priority based frame steering:-
c13727
$ IFDEVNAME=eth0
c13727
$ tc qdisc add dev $IFDEVNAME ingress
c13727
$ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \
c13727
   map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \
c13727
   queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0
c13727
$ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \
c13727
   flower vlan_prio 0 hw_tc 0
c13727
c13727
2) Get the 'pref' id
c13727
$ tc filter show dev $IFDEVNAME ingress
c13727
c13727
3) Delete a specific tc flower record (say pref 49151)
c13727
$ tc filter del dev $IFDEVNAME parent ffff: pref 49151
c13727
c13727
From dmesg, we will observe kernel NULL pointer ooops
c13727
c13727
[  197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000
c13727
[  197.171367] #PF: supervisor read access in kernel mode
c13727
[  197.171367] #PF: error_code(0x0000) - not-present page
c13727
[  197.171367] PGD 0 P4D 0
c13727
[  197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI
c13727
c13727
<snip>
c13727
c13727
[  197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac]
c13727
c13727
<snip>
c13727
c13727
[  197.171367] Call Trace:
c13727
[  197.171367]  <TASK>
c13727
[  197.171367]  ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac]
c13727
[  197.171367]  stmmac_setup_tc_block_cb+0x70/0x110 [stmmac]
c13727
[  197.171367]  tc_setup_cb_destroy+0xb3/0x180
c13727
[  197.171367]  fl_hw_destroy_filter+0x94/0xc0 [cls_flower]
c13727
c13727
The above issue is due to previous incorrect implementation of
c13727
tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule()
c13727
to get struct flow_rule *rule which is no longer valid for tc filter
c13727
delete operation.
c13727
c13727
  struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
c13727
  struct flow_dissector *dissector = rule->match.dissector;
c13727
c13727
So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for
c13727
earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(),
c13727
this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload
c13727
record for 'RX frame steering' tc flower, currently used for VLAN
c13727
priority. The implementation has taken consideration for future extension
c13727
to include other type RX frame steering such as EtherType based.
c13727
c13727
v2:
c13727
 - Clean up overly extensive backtrace and rewrite git message to better
c13727
   explain the kernel NULL pointer issue.
c13727
c13727
Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower")
c13727
Tested-by: Kurt Kanzenbach <kurt@linutronix.de>
c13727
Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com>
c13727
Signed-off-by: David S. Miller <davem@davemloft.net>
c13727
Signed-off-by: Denis Kirjanov <denis.kirjanov@suse.com>
c13727
---
c13727
 drivers/net/ethernet/stmicro/stmmac/stmmac.h    | 17 +++++
c13727
 drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 86 +++++++++++++++++++++----
c13727
 2 files changed, 90 insertions(+), 13 deletions(-)
c13727
c13727
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
c13727
index 5f129733aabd..873b9e3e5da2 100644
c13727
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
c13727
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
c13727
@@ -172,6 +172,19 @@ struct stmmac_flow_entry {
c13727
 	int is_l4;
c13727
 };
c13727
 
c13727
+/* Rx Frame Steering */
c13727
+enum stmmac_rfs_type {
c13727
+	STMMAC_RFS_T_VLAN,
c13727
+	STMMAC_RFS_T_MAX,
c13727
+};
c13727
+
c13727
+struct stmmac_rfs_entry {
c13727
+	unsigned long cookie;
c13727
+	int in_use;
c13727
+	int type;
c13727
+	int tc;
c13727
+};
c13727
+
c13727
 struct stmmac_priv {
c13727
 	/* Frequently used values are kept adjacent for cache effect */
c13727
 	u32 tx_coal_frames[MTL_MAX_TX_QUEUES];
c13727
@@ -289,6 +302,10 @@ struct stmmac_priv {
c13727
 	struct stmmac_tc_entry *tc_entries;
c13727
 	unsigned int flow_entries_max;
c13727
 	struct stmmac_flow_entry *flow_entries;
c13727
+	unsigned int rfs_entries_max[STMMAC_RFS_T_MAX];
c13727
+	unsigned int rfs_entries_cnt[STMMAC_RFS_T_MAX];
c13727
+	unsigned int rfs_entries_total;
c13727
+	struct stmmac_rfs_entry *rfs_entries;
c13727
 
c13727
 	/* Pulse Per Second output */
c13727
 	struct stmmac_pps_cfg pps[STMMAC_PPS_MAX];
c13727
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
c13727
index 1c4ea0b1b845..d0a2b289f460 100644
c13727
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
c13727
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
c13727
@@ -232,11 +232,33 @@ static int tc_setup_cls_u32(struct stmmac_priv *priv,
c13727
 	}
c13727
 }
c13727
 
c13727
+static int tc_rfs_init(struct stmmac_priv *priv)
c13727
+{
c13727
+	int i;
c13727
+
c13727
+	priv->rfs_entries_max[STMMAC_RFS_T_VLAN] = 8;
c13727
+
c13727
+	for (i = 0; i < STMMAC_RFS_T_MAX; i++)
c13727
+		priv->rfs_entries_total += priv->rfs_entries_max[i];
c13727
+
c13727
+	priv->rfs_entries = devm_kcalloc(priv->device,
c13727
+					 priv->rfs_entries_total,
c13727
+					 sizeof(*priv->rfs_entries),
c13727
+					 GFP_KERNEL);
c13727
+	if (!priv->rfs_entries)
c13727
+		return -ENOMEM;
c13727
+
c13727
+	dev_info(priv->device, "Enabled RFS Flow TC (entries=%d)\n",
c13727
+		 priv->rfs_entries_total);
c13727
+
c13727
+	return 0;
c13727
+}
c13727
+
c13727
 static int tc_init(struct stmmac_priv *priv)
c13727
 {
c13727
 	struct dma_features *dma_cap = &priv->dma_cap;
c13727
 	unsigned int count;
c13727
-	int i;
c13727
+	int ret, i;
c13727
 
c13727
 	if (dma_cap->l3l4fnum) {
c13727
 		priv->flow_entries_max = dma_cap->l3l4fnum;
c13727
@@ -250,10 +272,14 @@ static int tc_init(struct stmmac_priv *priv)
c13727
 		for (i = 0; i < priv->flow_entries_max; i++)
c13727
 			priv->flow_entries[i].idx = i;
c13727
 
c13727
-		dev_info(priv->device, "Enabled Flow TC (entries=%d)\n",
c13727
+		dev_info(priv->device, "Enabled L3L4 Flow TC (entries=%d)\n",
c13727
 			 priv->flow_entries_max);
c13727
 	}
c13727
 
c13727
+	ret = tc_rfs_init(priv);
c13727
+	if (ret)
c13727
+		return -ENOMEM;
c13727
+
c13727
 	if (!priv->plat->fpe_cfg) {
c13727
 		priv->plat->fpe_cfg = devm_kzalloc(priv->device,
c13727
 						   sizeof(*priv->plat->fpe_cfg),
c13727
@@ -607,16 +633,45 @@ static int tc_del_flow(struct stmmac_priv *priv,
c13727
 	return ret;
c13727
 }
c13727
 
c13727
+static struct stmmac_rfs_entry *tc_find_rfs(struct stmmac_priv *priv,
c13727
+					    struct flow_cls_offload *cls,
c13727
+					    bool get_free)
c13727
+{
c13727
+	int i;
c13727
+
c13727
+	for (i = 0; i < priv->rfs_entries_total; i++) {
c13727
+		struct stmmac_rfs_entry *entry = &priv->rfs_entries[i];
c13727
+
c13727
+		if (entry->cookie == cls->cookie)
c13727
+			return entry;
c13727
+		if (get_free && entry->in_use == false)
c13727
+			return entry;
c13727
+	}
c13727
+
c13727
+	return NULL;
c13727
+}
c13727
+
c13727
 #define VLAN_PRIO_FULL_MASK (0x07)
c13727
 
c13727
 static int tc_add_vlan_flow(struct stmmac_priv *priv,
c13727
 			    struct flow_cls_offload *cls)
c13727
 {
c13727
+	struct stmmac_rfs_entry *entry = tc_find_rfs(priv, cls, false);
c13727
 	struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
c13727
 	struct flow_dissector *dissector = rule->match.dissector;
c13727
 	int tc = tc_classid_to_hwtc(priv->dev, cls->classid);
c13727
 	struct flow_match_vlan match;
c13727
 
c13727
+	if (!entry) {
c13727
+		entry = tc_find_rfs(priv, cls, true);
c13727
+		if (!entry)
c13727
+			return -ENOENT;
c13727
+	}
c13727
+
c13727
+	if (priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN] >=
c13727
+	    priv->rfs_entries_max[STMMAC_RFS_T_VLAN])
c13727
+		return -ENOENT;
c13727
+
c13727
 	/* Nothing to do here */
c13727
 	if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_VLAN))
c13727
 		return -EINVAL;
c13727
@@ -638,6 +693,12 @@ static int tc_add_vlan_flow(struct stmmac_priv *priv,
c13727
 
c13727
 		prio = BIT(match.key->vlan_priority);
c13727
 		stmmac_rx_queue_prio(priv, priv->hw, prio, tc);
c13727
+
c13727
+		entry->in_use = true;
c13727
+		entry->cookie = cls->cookie;
c13727
+		entry->tc = tc;
c13727
+		entry->type = STMMAC_RFS_T_VLAN;
c13727
+		priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN]++;
c13727
 	}
c13727
 
c13727
 	return 0;
c13727
@@ -646,20 +707,19 @@ static int tc_add_vlan_flow(struct stmmac_priv *priv,
c13727
 static int tc_del_vlan_flow(struct stmmac_priv *priv,
c13727
 			    struct flow_cls_offload *cls)
c13727
 {
c13727
-	struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
c13727
-	struct flow_dissector *dissector = rule->match.dissector;
c13727
-	int tc = tc_classid_to_hwtc(priv->dev, cls->classid);
c13727
+	struct stmmac_rfs_entry *entry = tc_find_rfs(priv, cls, false);
c13727
 
c13727
-	/* Nothing to do here */
c13727
-	if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_VLAN))
c13727
-		return -EINVAL;
c13727
+	if (!entry || !entry->in_use || entry->type != STMMAC_RFS_T_VLAN)
c13727
+		return -ENOENT;
c13727
 
c13727
-	if (tc < 0) {
c13727
-		netdev_err(priv->dev, "Invalid traffic class\n");
c13727
-		return -EINVAL;
c13727
-	}
c13727
+	stmmac_rx_queue_prio(priv, priv->hw, 0, entry->tc);
c13727
+
c13727
+	entry->in_use = false;
c13727
+	entry->cookie = 0;
c13727
+	entry->tc = 0;
c13727
+	entry->type = 0;
c13727
 
c13727
-	stmmac_rx_queue_prio(priv, priv->hw, 0, tc);
c13727
+	priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN]--;
c13727
 
c13727
 	return 0;
c13727
 }
c13727
-- 
c13727
2.16.4
c13727