|
|
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 |
|