Re: [net,1/2] net: stmmac: preserve real_num_tx_queues on mqprio setup failure
From: netdev-bot+sashiko@kernel.org
Date: 2026-09-02 12:30:45
Also in:
linux-arm-kernel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: Critical: 0 · High: 2 · Medium: 1 · Low: 0 - [High] The error path this patch is built around cannot be taken: `stmmac_fpe_map_preemption_class()` is dispatched through… - [High] The new helper `stmmac_set_ndev_tcs()` (drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c:1240) is a verbatim… - [Medium] The commit message justifies dropping stmmac_reset_tc_mqprio() from the error path with 'the TC-to-queue mapping is restored to the… -- net: stmmac: preserve real_num_tx_queues on mqprio setup failure This reworks tc_setup_dwmac510_mqprio() so that the previous real_num_tx_queues and TC-to-queue mapping are snapshotted and restored if stmmac_fpe_map_preemption_class() fails, instead of falling back to stmmac_reset_tc_mqprio(). A new stmmac_set_ndev_tcs() helper is added to program and restore the netdev TC state. A few questions below.
quoted hunk ↗ jump to hunk
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 14cabe76e53e..be8ddda2aa76 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c@@ -1237,6 +1237,30 @@ static int tc_query_caps(struct stmmac_priv *priv, } } +static int stmmac_set_ndev_tcs(struct net_device *ndev, u8 ntc, + struct netdev_tc_txq *tc_to_txq) +{ + int i, err; + + netdev_reset_tc(ndev); + if (ntc == 1) + return 0;
[Severity: High]
Is the "ntc == 1" early return correct for stmmac?
This shortcut matches mlx5e_netdev_set_tcs() in
drivers/net/ethernet/mellanox/mlx5/core/en_main.c, where one TC means "no
TC differentiation" and mlx5 keeps its own queue/TC bookkeeping. In
stmmac the netdev TC state is the only input to the FPE programming
callbacks.
For a legal single-TC offload request, for example
tc qdisc replace dev eth0 root mqprio num_tc 1 map 0 0 0 0 0 0 0 0 \
queues 4@0 fp P hw 1
mqprio_validate_qopt() accepts num_tc 1 and mqprio_fp_to_offload() sets
mqprio->preemptible_tcs = BIT(0). stmmac_set_ndev_tcs(ndev, 1, tc_to_txq)
then leaves dev->num_tc == 0 and tc_to_txq[] zeroed, and
tc_setup_dwmac510_mqprio() still returns 0.
Before this patch the same request ran netdev_set_num_tc(ndev, 1) followed
by netdev_set_tc_queue(ndev, 0, count, offset).
Does that change the hardware programming? dwmac5_fpe_map_preemption_class()
derives everything from netdev_get_num_tc():
drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c:dwmac5_fpe_map_preemption_class() {
int num_tc = netdev_get_num_tc(ndev);
...
for (u32 tc = 0; tc < num_tc; tc++) {
...
if (pclass & BIT(tc))
preemptible_txqs |= GENMASK(offset + count - 1, offset);
...
}
update_mapping:
val = readl(priv->ioaddr + GMAC5_MTL_FPE_CTRL_STS);
writel(u32_replace_bits(val, preemptible_txqs, FPE_MTL_PREEMPTION_CLASS),
priv->ioaddr + GMAC5_MTL_FPE_CTRL_STS);
}
With num_tc == 0 the loop body never runs, so FPE_MTL_PREEMPTION_CLASS is
written as 0 and the TC0 the user marked preemptible is never armed. The
one-to-many SP-algorithm and equal-weight checks in the same loop are
skipped as well.
On XGMAC the effect is the opposite direction:
drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c:dwxgmac3_fpe_map_preemption_class() {
if (!num_tc) {
/* Restore default TC:Queue mapping */
for (u32 i = 0; i < priv->plat->tx_queues_to_use; i++) {
val = readl(priv->ioaddr + XGMAC_MTL_TXQ_OPMODE(i));
writel(u32_replace_bits(val, i, XGMAC_Q2TCMAP),
priv->ioaddr + XGMAC_MTL_TXQ_OPMODE(i));
}
}
so the identity queue-to-TC map is reprogrammed even though the offload of
N queues into TC0 was reported as successful. The netdev also ends up
reporting num_tc 0 to mqprio_dump() while the mqprio qdisc is installed.
+
+ err = netdev_set_num_tc(ndev, ntc);
+ if (err)
+ return err;
+
+ for (i = 0; i < ntc; i++) {
+ u16 count, offset;
+
+ count = tc_to_txq[i].count;
+ offset = tc_to_txq[i].offset;
+ netdev_set_tc_queue(ndev, i, count, offset);
+ }
+
+ return 0;
+}
+
static void stmmac_reset_tc_mqprio(struct net_device *ndev,
struct netlink_ext_ack *extack)
{[ ... ]
quoted hunk ↗ jump to hunk
@@ -1250,45 +1274,56 @@ static void stmmac_reset_tc_mqprio(struct net_device *ndev, static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv, struct tc_mqprio_qopt_offload *mqprio) {
[ ... ]
+ /* save current tc values for reset */ + ndev_ntc = netdev_get_num_tc(ndev) ? : 1; + for (i = 0; i < ARRAY_SIZE(ndev->tc_to_txq); i++) + ndev_tc_to_txq[i].combined = + READ_ONCE(ndev->tc_to_txq[i].combined);
[Severity: Medium]
The snapshot covers num_tc and tc_to_txq[], but not ndev->prio_tc_map[].
stmmac_set_ndev_tcs() starts with netdev_reset_tc(), which clears all
three:
net/core/dev.c:netdev_reset_tc() {
WRITE_ONCE(dev->num_tc, 0);
for (i = 0; i < TC_MAX_QUEUE; i++)
WRITE_ONCE(dev->tc_to_txq[i].combined, 0);
for (i = 0; i <= TC_BITMASK; i++)
WRITE_ONCE(dev->prio_tc_map[i], 0);
}
The core only repopulates prio_tc_map after a successful driver callback:
net/sched/sch_mqprio.c:mqprio_init() {
...
/* Always use supplied priority mappings */
for (i = 0; i < TC_BITMASK + 1; i++)
netdev_set_prio_tc_map(dev, i, qopt->prio_tc_map[i]);
}
so on the error path the previously active priority-to-TC map is lost.
Should prio_tc_map[] be saved and restored too?
Also, the "? : 1" encoding collapses an existing num_tc == 1 into the
helper's reset case, so a device that legitimately had one TC ends up with
num_tc == 0 after a failed attempt.
Separately, the commit message says "the TC-to-queue mapping is restored to
the previously saved values". Is the num_tc restore observable in the
scenario the message describes (a new mqprio configuration over a working
one)? qdisc_create() calls ops->destroy() even when ops->init() failed,
and mqprio_destroy() takes the non-offload branch because priv->hw_offload
was never set:
net/sched/sch_mqprio.c:mqprio_destroy() {
if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc)
mqprio_disable_offload(sch);
else
netdev_set_num_tc(dev, 0);
}
which zeroes dev->num_tc again right after the driver restored it.
+
+ for (i = 0; i < qopt->num_tc; i++) {
+ tc_to_txq[i] = (struct netdev_tc_txq) {
+ .count = qopt->count[i],
+ .offset = qopt->offset[i],
+ };
+ num_tx_queues += qopt->count[i];
}
- err = netif_set_real_num_tx_queues(ndev, num_stack_tx_queues);
+ err = stmmac_set_ndev_tcs(ndev, qopt->num_tc, tc_to_txq);
+ if (err)
+ goto error_reset_tc;
+
+ ndev_num_tx_queues = ndev->real_num_tx_queues;
+ err = netif_set_real_num_tx_queues(ndev, num_tx_queues);
if (err)
- goto err_reset_tc;
+ goto error_reset_tc;
err = stmmac_fpe_map_preemption_class(priv, ndev, extack,
mqprio->preemptible_tcs);
if (err)
- goto err_reset_tc;
+ goto error_reset_num_tx_queues;
return 0;
-err_reset_tc:
- stmmac_reset_tc_mqprio(ndev, extack);
+error_reset_num_tx_queues:
+ netif_set_real_num_tx_queues(ndev, ndev_num_tx_queues);
+error_reset_tc:
+ stmmac_set_ndev_tcs(ndev, ndev_ntc, ndev_tc_to_txq);
return err;
}
[Severity: High]
Can err here ever be non-zero on a MAC that reaches this function?
stmmac_fpe_map_preemption_class() is dispatched through the void wrapper:
drivers/net/ethernet/stmicro/stmmac/hwif.h {
#define stmmac_do_void_callback(__priv, __module, __cname, __arg0, __args...) \
({ \
int __result = -EINVAL; \
if ((__priv)->hw->__module && (__priv)->hw->__module->__cname) { \
(__priv)->hw->__module->__cname((__arg0), ##__args); \
__result = 0; \
} \
__result; \
})
...
#define stmmac_fpe_map_preemption_class(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, fpe_map_preemption_class, __args)
}
The op is declared as returning int in struct stmmac_ops, but the wrapper
calls it as a bare statement and forces 0 whenever the pointer is set.
Every ops table paired with .tc = &dwmac510_tc_ops in hwif.c (dwmac410_ops,
dwmac510_ops, dwxgmac210_ops, dwxlgmac2_ops) populates
.fpe_map_preemption_class, so err is always 0 and the new
error_reset_num_tx_queues label looks unreachable.
If so, does that mean the failure described in the commit message,
"if stmmac_fpe_map_preemption_class() fails after the number of real TX
queues has been set to num_tx_queues"
cannot currently happen, and the Fixes: 195e4f409a40 tag is pointing at a
path that is not taken?
The same wrapper also drops the real rejections in
dwmac5_fpe_map_preemption_class():
drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c:dwmac5_fpe_map_preemption_class() {
if (priv->plat->tx_sched_algorithm == MTL_TX_ALGORITHM_SP) {
NL_SET_ERR_MSG_MOD(extack, ALG_ERR_MSG);
return -EINVAL;
}
...
if (priv->plat->tx_queues_cfg[offset + i].weight !=
queue_weight) {
NL_SET_ERR_MSG_FMT_MOD(extack, WEIGHT_ERR_MSG,
queue_weight, tc);
return -EINVAL;
}
}
Both returns happen before the GMAC5_MTL_FPE_CTRL_STS write, so the
preemption-class register keeps its previous value while the qdisc layer is
told the offload succeeded. Would switching the wrapper to
stmmac_do_callback() be the prerequisite change here, so that this new
error path can actually run?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260829-stmmac-fix-graft-overwrite-v1-0-a4f84c4c264e%40oss.qualcomm.com