[PATCH net] net: stmmac: rebuild VLAN priority steering when filters change
James Hilliard
james.hilliard1 at gmail.com
Mon Nov 2 17:07:30 PST 2026
The RX queue priority callbacks preserve previously assigned priorities.
Passing zero from tc_del_vlan_flow() therefore leaves the deleted rule's
priority mapped to its RX queue. Clearing the whole queue instead would
also remove priorities belonging to other installed rules.
Save each VLAN rule's priority and rebuild the complete per-queue masks
after insertion and deletion, starting from the platform assignments.
Resolve overlapping platform masks with the same last-queue-wins ordering
used during initialization.
Make the GMAC4 and XGMAC callbacks replace the target queue's mask while
retaining their exclusion of its priorities from other queues. Program a
priority's new destination before clearing its previous assignment.
Keep VLAN rules in programming order. Flower installs a replacement under
a new cookie before deleting the old one, so the newer assignment must
remain effective while both are present, including when an earlier array
slot is reused. Removing a rejected replacement restores the older rule,
and removing the last override restores the platform assignment.
Reoffloading an existing cookie reuses its entry without incrementing the
rule count. Validate the selected RX queue before indexing the masks:
tc_classid_to_hwtc() checks the number of traffic classes, which can exceed
the number of RX queues.
Found by code inspection while working on STMMAC reset/recovery fixes.
Host-side tests exercising the driver's VLAN rule handlers and priority
callbacks with mocked MMIO reproduce the stale mapping after deletion
and pass with this fix. No hardware testing was performed.
Fixes: b3da86d432b7 ("net: stmmac: fix rx queue priority assignment")
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 2 +
.../net/ethernet/stmicro/stmmac/dwxgmac2_core.c | 2 +
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 4 ++
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 54 +++++++++++++++++++---
4 files changed, 56 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index 259e2ebed8c1..efbe2ec4c213 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -126,6 +126,7 @@ static void dwmac4_rx_queue_priority(struct mac_device_info *hw,
* clear them from others queues
*/
if (queue < 4) {
+ ctrl2 &= ~GMAC_RXQCTRL_PSRQX_MASK(queue);
ctrl2 |= (prio << GMAC_RXQCTRL_PSRQX_SHIFT(queue)) &
GMAC_RXQCTRL_PSRQX_MASK(queue);
@@ -134,6 +135,7 @@ static void dwmac4_rx_queue_priority(struct mac_device_info *hw,
} else {
queue -= 4;
+ ctrl3 &= ~GMAC_RXQCTRL_PSRQX_MASK(queue);
ctrl3 |= (prio << GMAC_RXQCTRL_PSRQX_SHIFT(queue)) &
GMAC_RXQCTRL_PSRQX_MASK(queue);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index 1a88cbaed70c..7ad08651675e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -119,6 +119,7 @@ static void dwxgmac2_rx_queue_prio(struct mac_device_info *hw, u32 prio,
* clear them from others queues
*/
if (queue < 4) {
+ ctrl2 &= ~XGMAC_PSRQ(queue);
ctrl2 |= (prio << XGMAC_PSRQ_SHIFT(queue)) &
XGMAC_PSRQ(queue);
@@ -127,6 +128,7 @@ static void dwxgmac2_rx_queue_prio(struct mac_device_info *hw, u32 prio,
} else {
queue -= 4;
+ ctrl3 &= ~XGMAC_PSRQ(queue);
ctrl3 |= (prio << XGMAC_PSRQ_SHIFT(queue)) &
XGMAC_PSRQ(queue);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index b76acfe0d958..fcc395321816 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -14,6 +14,7 @@
#include <linux/clk.h>
#include <linux/hrtimer.h>
#include <linux/if_vlan.h>
+#include <linux/list.h>
#include <linux/stmmac.h>
#include <linux/phylink.h>
#include <linux/pci.h>
@@ -210,6 +211,8 @@ enum stmmac_rfs_type {
struct stmmac_rfs_entry {
unsigned long cookie;
u16 etype;
+ u8 vlan_prio;
+ struct list_head vlan_node;
int in_use;
int type;
int tc;
@@ -382,6 +385,7 @@ struct stmmac_priv {
unsigned int rfs_entries_cnt[STMMAC_RFS_T_MAX];
unsigned int rfs_entries_total;
struct stmmac_rfs_entry *rfs_entries;
+ struct list_head vlan_prio;
/* Pulse Per Second output */
struct stmmac_pps_cfg pps[STMMAC_PPS_MAX];
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index 10b61fa2c27c..6707f7621f3b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
@@ -250,6 +250,8 @@ static int tc_rfs_init(struct stmmac_priv *priv)
if (!priv->rfs_entries)
return -ENOMEM;
+ INIT_LIST_HEAD(&priv->vlan_prio);
+
dev_info(priv->device, "Enabled RFS Flow TC (entries=%d)\n",
priv->rfs_entries_total);
@@ -685,6 +687,41 @@ static struct stmmac_rfs_entry *tc_find_rfs(struct stmmac_priv *priv,
#define VLAN_PRIO_FULL_MASK (0x07)
+static void tc_update_vlan_prio(struct stmmac_priv *priv, u8 prio)
+{
+ u8 prios[MTL_MAX_RX_QUEUES] = {};
+ struct stmmac_rfs_entry *entry;
+ unsigned int queue, i;
+
+ /* Restore platform assignments with the same precedence as setup. */
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ if (!priv->plat->rx_queues_cfg[queue].use_prio)
+ continue;
+
+ prios[queue] = priv->plat->rx_queues_cfg[queue].prio;
+ for (i = 0; i < queue; i++)
+ prios[i] &= ~prios[queue];
+ }
+
+ /*
+ * Flower installs a replacement before destroying the old cookie.
+ * Preserve the latest assignment while both rules are present.
+ */
+ list_for_each_entry(entry, &priv->vlan_prio, vlan_node) {
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++)
+ prios[queue] &= ~entry->vlan_prio;
+ prios[entry->tc] |= entry->vlan_prio;
+ }
+
+ /* Install the new assignment before clearing the previous queue. */
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++)
+ if (prios[queue] & prio)
+ stmmac_rx_queue_prio(priv, priv->hw, prios[queue], queue);
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++)
+ if (!(prios[queue] & prio))
+ stmmac_rx_queue_prio(priv, priv->hw, prios[queue], queue);
+}
+
static int tc_add_vlan_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls)
{
@@ -700,7 +737,7 @@ static int tc_add_vlan_flow(struct stmmac_priv *priv,
return -ENOENT;
}
- if (priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN] >=
+ if (!entry->in_use && priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN] >=
priv->rfs_entries_max[STMMAC_RFS_T_VLAN])
return -ENOENT;
@@ -708,7 +745,7 @@ static int tc_add_vlan_flow(struct stmmac_priv *priv,
if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_VLAN))
return -EINVAL;
- if (tc < 0) {
+ if (tc < 0 || tc >= priv->plat->rx_queues_to_use) {
netdev_err(priv->dev, "Invalid traffic class\n");
return -EINVAL;
}
@@ -724,13 +761,18 @@ static int tc_add_vlan_flow(struct stmmac_priv *priv,
}
prio = BIT(match.key->vlan_priority);
- stmmac_rx_queue_prio(priv, priv->hw, prio, tc);
+ if (entry->in_use)
+ list_del(&entry->vlan_node);
+ else
+ priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN]++;
entry->in_use = true;
entry->cookie = cls->cookie;
entry->tc = tc;
entry->type = STMMAC_RFS_T_VLAN;
- priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN]++;
+ entry->vlan_prio = prio;
+ list_add_tail(&entry->vlan_node, &priv->vlan_prio);
+ tc_update_vlan_prio(priv, entry->vlan_prio);
}
return 0;
@@ -744,14 +786,14 @@ static int tc_del_vlan_flow(struct stmmac_priv *priv,
if (!entry || !entry->in_use || entry->type != STMMAC_RFS_T_VLAN)
return -ENOENT;
- stmmac_rx_queue_prio(priv, priv->hw, 0, entry->tc);
-
+ list_del(&entry->vlan_node);
entry->in_use = false;
entry->cookie = 0;
entry->tc = 0;
entry->type = 0;
priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN]--;
+ tc_update_vlan_prio(priv, entry->vlan_prio);
return 0;
}
---
base-commit: c3e67bd82b9b9b1a5118f09a429ccefe2aab0741
change-id: 20261102-submit-stmmac-vlan-prio-v1-8e5f674f1201
Best regards,
--
James Hilliard <james.hilliard1 at gmail.com>
More information about the linux-arm-kernel
mailing list