* Re: [PATCH net v2 2/2] net: stmmac: preserve real_num_tx_queues on mqprio setup failure
@ 2026-09-10 15:07 netdev-bot+sashiko
2026-09-11 8:56 ` Lorenzo Bianconi
0 siblings, 1 reply; 3+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 15:07 UTC (permalink / raw)
To: lorenzo.bianconi
Cc: maxime.chevallier, andrew+netdev, davem, edumazet, kuba, pabeni,
mcoquelin.stm32, alexandre.torgue, 0x1207, olteanv, netdev,
linux-stm32, linux-arm-kernel, linux-kernel
Thank you for your contribution! Sashiko AI review found 5 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 3 · Low: 2
- [Medium] The reproducer quoted in the commit message cannot demonstrate
the bug being fixed.
- [Medium] The new error path no longer reprograms the MAC's FPE
preemption-class register.
- [Medium] Spurious kernel warning triggered during mqprio teardown path
in `tc_setup_dwmac510_mqprio`
- [Low] The driver-side restore of dev->num_tc / tc_to_txq[] /
prio_tc_map[] on the FPE-failure path is immediately overridden in…
- [Low] The newly added rollback in tc_setup_dwmac510_mqprio() discards
the return value of netif_set_real_num_tx_queues(ndev,…
--
net: stmmac: preserve real_num_tx_queues on mqprio setup failure
This patch saves ndev->real_num_tx_queues along with the TC-to-queue and
priority-to-TC mappings before an mqprio offload is applied, and restores
them if stmmac_fpe_map_preemption_class() fails, instead of calling
stmmac_reset_tc_mqprio() which reset the queue count to the platform
maximum.
Some questions about the commit message and the new rollback path below.
> The issue can be triggered using the following configuration:
>
> # First mqprio config lowers the hw queue count.
> $tc qdisc add dev eth0 root handle 1: mqprio queues 4@0 4@4
[Severity: Medium]
Does this reproducer actually show the difference between the old and new
error paths?
"queues 4@0 4@4" totals 8 TX queues, and mqprio only accepts it if
real_num_tx_queues is already >= 8:
net/sched/sch_mqprio_lib.c:mqprio_validate_queue_counts() {
...
if (qopt->offset[i] >= dev->real_num_tx_queues ||
last > dev->real_num_tx_queues) {
...
}
stmmac opts into that check via caps->validate_queue_counts = true in
tc_query_caps(), allocates MTL_MAX_TX_QUEUES (8) TX queues, and
stmmac_hw_setup() sets:
netif_set_real_num_tx_queues(dev, priv->plat->tx_queues_to_use);
So the first command is only accepted when tx_queues_to_use == 8, i.e.
real_num_tx_queues is already at the platform maximum, and the comment
"First mqprio config lowers the hw queue count" does not match what
happens.
With real_num_tx_queues == 8, the old path
stmmac_reset_tc_mqprio() -> netif_set_real_num_tx_queues(ndev,
priv->plat->tx_queues_to_use) restores exactly the same 8 that the new
code restores from ndev_num_tx_queues, so the two behave identically for
the quoted commands.
Would a first configuration that totals fewer queues than
tx_queues_to_use, for example "queues 2@0 2@2" on an 8-queue MAC followed
by the failing "queues 2@0 2@2 fp E P", be a better reproducer?
> Note that a failed setup makes the qdisc layer run mqprio_destroy() on
> the new qdisc, which resets dev->num_tc to 0; that is qdisc-layer handling,
> out of the driver's control, and the driver-side restore remains correct
> for the still-active previous configuration.
[Severity: Low]
Is "the driver-side restore remains correct for the still-active previous
configuration" accurate for num_tc and the two map arrays?
mqprio_enable_offload() only assigns priv->hw_offload after ndo_setup_tc()
succeeds:
net/sched/sch_mqprio.c:mqprio_enable_offload() {
err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_MQPRIO, &mqprio);
if (err)
return err;
priv->hw_offload = mqprio.qopt.hw;
}
so on the failure path mqprio_destroy() takes the else branch:
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);
}
netdev_set_num_tc() writes num_tc only, so the netdev ends up with num_tc
== 0 while the driver-restored tc_to_txq[] and prio_tc_map[] stay
populated, a combination netdev_reset_tc() never produces on its own.
Since skb_tx_hash(), netif_setup_tc(), mqprio_qopt_reconstruct() and XPS
all gate on num_tc, is the newly added save/restore of num_tc and of the
two 16-entry maps doing anything observable, and is priority
classification for the still-installed previous qdisc actually preserved?
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
> index 5398616fcdfe..7d73b0c16f50 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
[ ... ]
> @@ -1251,43 +1275,59 @@ static int stmmac_reset_tc_mqprio(struct net_device *ndev,
[ ... ]
> + 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);
> + for (i = 0; i < ARRAY_SIZE(ndev_prio_tc_map); i++)
> + netdev_set_prio_tc_map(ndev, i, ndev_prio_tc_map[i]);
[Severity: Medium]
Is the rollback order at these two labels right when the new mqprio
configuration asks for more TX queues than the one it replaces?
When error_reset_num_tx_queues is reached, the netdev still carries
the mapping that stmmac_set_ndev_tcs(ndev, qopt->num_tc, tc_to_txq)
just installed: dev->num_tc is qopt->num_tc and tc_to_txq[] describes
num_tx_queues queues. The restore call then shrinks
real_num_tx_queues back to ndev_num_tx_queues, and because
dev->num_tc is non-zero netif_set_real_num_tx_queues() goes through
netif_setup_tc():
net/core/dev.c:netif_setup_tc() {
struct netdev_tc_txq *tc = &dev->tc_to_txq[0];
/* If TC0 is invalidated disable TC mapping */
if (tc->offset + tc->count > txq) {
netdev_warn(dev, "Number of in use tx queues changed"
" invalidating tc mappings...\n");
dev->num_tc = 0;
return;
}
for (i = 1; i < TC_BITMASK + 1; i++) {
...
if (tc->offset + tc->count > txq) {
netdev_warn(dev, "... Setting map to 0\n");
netdev_set_prio_tc_map(dev, i, 0);
}
}
}
So for a sequence such as "queues 2@0 2@2" followed by a failing
"queues 4@0 4@4 fp E P", the driver's own rollback makes the core
print "Priority traffic classification disabled!" (and possibly the
per-priority warnings), zero dev->num_tc and rewrite prio_tc_map[]
entries, purely as a side effect of the intermediate state. The
following stmmac_set_ndev_tcs(ndev, ndev_ntc, ndev_tc_to_txq) and
the netdev_set_prio_tc_map() loop do repair all of that, so the
end state looks correct, but users see a scary warning for an
internal undo step and the tc mappings are transiently invalid.
Would reversing the two rollback steps, restoring ndev_ntc /
ndev_tc_to_txq / ndev_prio_tc_map first and only then calling
netif_set_real_num_tx_queues(ndev, ndev_num_tx_queues), avoid this?
The saved mapping by construction fits ndev_num_tx_queues, so
netif_setup_tc() should have nothing to complain about. If there is
a reason the queue count has to be restored first, could that be
spelled out in a comment?
[Severity: Low]
Should the return value of the restoring netif_set_real_num_tx_queues() be
checked here?
On this path the call always grows the queue count, because
mqprio_validate_queue_counts() already ensured num_tx_queues <=
ndev_num_tx_queues, and growing can fail:
net/core/dev.c:netif_set_real_num_tx_queues() {
...
rc = netdev_queue_update_kobjects(dev, dev->real_num_tx_queues,
txq);
if (rc)
return rc;
...
}
net/core/net-sysfs.c:netdev_queue_update_kobjects() {
for (i = old_num; i < new_num; i++) {
error = netdev_queue_add_kobject(dev, i);
if (error) {
new_num = old_num;
break;
}
}
}
The early return happens before dev->real_num_tx_queues is assigned, so on
-ENOMEM the device keeps the lower queue count while the following
stmmac_set_ndev_tcs() reinstates a tc_to_txq[] whose offsets and counts
describe queues beyond real_num_tx_queues. The function still returns only
the original FPE error, so nothing above sees the partial restore.
[Severity: Medium]
Does this error path need to reprogram the FPE preemption-class register?
The previous code reached the disarm through:
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c:stmmac_reset_tc_mqprio() {
netdev_reset_tc(ndev);
netif_set_real_num_tx_queues(ndev, priv->plat->tx_queues_to_use);
return stmmac_fpe_map_preemption_class(priv, ndev, extack, 0);
}
dwmac5_fpe_map_preemption_class() does all of its validation before its
single read-modify-write:
drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c {
if (priv->plat->tx_sched_algorithm == MTL_TX_ALGORITHM_SP) {
NL_SET_ERR_MSG_MOD(extack, ALG_ERR_MSG);
return -EINVAL;
}
...
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);
}
so on the -EINVAL path GMAC5_MTL_FPE_CTRL_STS still holds the mask
programmed by the previous successful configuration.
The new error_reset_num_tx_queues/error_reset_tc labels touch no MAC
register, and the qdisc layer then zeroes dev->num_tc via
mqprio_destroy() -> netdev_set_num_tc(dev, 0). Is the hardware then left
with a per-TXQ preemptible mask armed while the driver's only description
of which TXQs belong to which TC says there are no traffic classes, so
express-intended frames can land on TXQs the MAC merge layer will
fragment?
The driver recomputes the mask only from netdev_get_num_tc() and
tc_to_txq[] on later calls, so it appears the stale mask stays until the
previous qdisc is deleted or replaced. Would it be worth mentioning this
hardware-state change in the commit message?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909-stmmac-tc_setup_dwmac510_mqprio-error-path-v2-0-8158b8333b3d%40oss.qualcomm.com
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net v2 2/2] net: stmmac: preserve real_num_tx_queues on mqprio setup failure
2026-09-10 15:07 [PATCH net v2 2/2] net: stmmac: preserve real_num_tx_queues on mqprio setup failure netdev-bot+sashiko
@ 2026-09-11 8:56 ` Lorenzo Bianconi
0 siblings, 0 replies; 3+ messages in thread
From: Lorenzo Bianconi @ 2026-09-11 8:56 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: maxime.chevallier, andrew+netdev, davem, edumazet, kuba, pabeni,
mcoquelin.stm32, alexandre.torgue, 0x1207, olteanv, netdev,
linux-stm32, linux-arm-kernel, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 11377 bytes --]
> Thank you for your contribution! Sashiko AI review found 5 potential
> issue(s) to consider:
>
> Critical: 0 · High: 0 · Medium: 3 · Low: 2
>
> - [Medium] The reproducer quoted in the commit message cannot demonstrate
> the bug being fixed.
> - [Medium] The new error path no longer reprograms the MAC's FPE
> preemption-class register.
> - [Medium] Spurious kernel warning triggered during mqprio teardown path
> in `tc_setup_dwmac510_mqprio`
> - [Low] The driver-side restore of dev->num_tc / tc_to_txq[] /
> prio_tc_map[] on the FPE-failure path is immediately overridden in…
> - [Low] The newly added rollback in tc_setup_dwmac510_mqprio() discards
> the return value of netif_set_real_num_tx_queues(ndev,…
>
> --
>
> net: stmmac: preserve real_num_tx_queues on mqprio setup failure
>
> This patch saves ndev->real_num_tx_queues along with the TC-to-queue and
> priority-to-TC mappings before an mqprio offload is applied, and restores
> them if stmmac_fpe_map_preemption_class() fails, instead of calling
> stmmac_reset_tc_mqprio() which reset the queue count to the platform
> maximum.
>
> Some questions about the commit message and the new rollback path below.
>
> > The issue can be triggered using the following configuration:
> >
> > # First mqprio config lowers the hw queue count.
> > $tc qdisc add dev eth0 root handle 1: mqprio queues 4@0 4@4
>
> [Severity: Medium]
> Does this reproducer actually show the difference between the old and new
> error paths?
>
> "queues 4@0 4@4" totals 8 TX queues, and mqprio only accepts it if
> real_num_tx_queues is already >= 8:
ack, I will fix it in v3.
>
> net/sched/sch_mqprio_lib.c:mqprio_validate_queue_counts() {
> ...
> if (qopt->offset[i] >= dev->real_num_tx_queues ||
> last > dev->real_num_tx_queues) {
> ...
> }
>
> stmmac opts into that check via caps->validate_queue_counts = true in
> tc_query_caps(), allocates MTL_MAX_TX_QUEUES (8) TX queues, and
> stmmac_hw_setup() sets:
>
> netif_set_real_num_tx_queues(dev, priv->plat->tx_queues_to_use);
>
> So the first command is only accepted when tx_queues_to_use == 8, i.e.
> real_num_tx_queues is already at the platform maximum, and the comment
> "First mqprio config lowers the hw queue count" does not match what
> happens.
>
> With real_num_tx_queues == 8, the old path
> stmmac_reset_tc_mqprio() -> netif_set_real_num_tx_queues(ndev,
> priv->plat->tx_queues_to_use) restores exactly the same 8 that the new
> code restores from ndev_num_tx_queues, so the two behave identically for
> the quoted commands.
>
> Would a first configuration that totals fewer queues than
> tx_queues_to_use, for example "queues 2@0 2@2" on an 8-queue MAC followed
> by the failing "queues 2@0 2@2 fp E P", be a better reproducer?
>
> > Note that a failed setup makes the qdisc layer run mqprio_destroy() on
> > the new qdisc, which resets dev->num_tc to 0; that is qdisc-layer handling,
> > out of the driver's control, and the driver-side restore remains correct
> > for the still-active previous configuration.
>
> [Severity: Low]
> Is "the driver-side restore remains correct for the still-active previous
> configuration" accurate for num_tc and the two map arrays?
>
> mqprio_enable_offload() only assigns priv->hw_offload after ndo_setup_tc()
> succeeds:
>
> net/sched/sch_mqprio.c:mqprio_enable_offload() {
> err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_MQPRIO, &mqprio);
> if (err)
> return err;
>
> priv->hw_offload = mqprio.qopt.hw;
> }
>
> so on the failure path mqprio_destroy() takes the else branch:
>
> 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);
> }
>
> netdev_set_num_tc() writes num_tc only, so the netdev ends up with num_tc
> == 0 while the driver-restored tc_to_txq[] and prio_tc_map[] stay
> populated, a combination netdev_reset_tc() never produces on its own.
>
> Since skb_tx_hash(), netif_setup_tc(), mqprio_qopt_reconstruct() and XPS
> all gate on num_tc, is the newly added save/restore of num_tc and of the
> two 16-entry maps doing anything observable, and is priority
> classification for the still-installed previous qdisc actually preserved?
I think mqprio replace error path is broken since the user would assume if
'qdisc replace' fails, the old qdisc will be properly working, right? So I
guess this issue should be fixed in sch_mqprio.
>
> > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
> > index 5398616fcdfe..7d73b0c16f50 100644
> > --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
> > +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
>
> [ ... ]
>
> > @@ -1251,43 +1275,59 @@ static int stmmac_reset_tc_mqprio(struct net_device *ndev,
>
> [ ... ]
>
> > + 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);
> > + for (i = 0; i < ARRAY_SIZE(ndev_prio_tc_map); i++)
> > + netdev_set_prio_tc_map(ndev, i, ndev_prio_tc_map[i]);
>
> [Severity: Medium]
> Is the rollback order at these two labels right when the new mqprio
> configuration asks for more TX queues than the one it replaces?
>
> When error_reset_num_tx_queues is reached, the netdev still carries
> the mapping that stmmac_set_ndev_tcs(ndev, qopt->num_tc, tc_to_txq)
> just installed: dev->num_tc is qopt->num_tc and tc_to_txq[] describes
> num_tx_queues queues. The restore call then shrinks
> real_num_tx_queues back to ndev_num_tx_queues, and because
> dev->num_tc is non-zero netif_set_real_num_tx_queues() goes through
> netif_setup_tc():
>
> net/core/dev.c:netif_setup_tc() {
> struct netdev_tc_txq *tc = &dev->tc_to_txq[0];
>
> /* If TC0 is invalidated disable TC mapping */
> if (tc->offset + tc->count > txq) {
> netdev_warn(dev, "Number of in use tx queues changed"
> " invalidating tc mappings...\n");
> dev->num_tc = 0;
> return;
> }
>
> for (i = 1; i < TC_BITMASK + 1; i++) {
> ...
> if (tc->offset + tc->count > txq) {
> netdev_warn(dev, "... Setting map to 0\n");
> netdev_set_prio_tc_map(dev, i, 0);
> }
> }
> }
>
> So for a sequence such as "queues 2@0 2@2" followed by a failing
> "queues 4@0 4@4 fp E P", the driver's own rollback makes the core
> print "Priority traffic classification disabled!" (and possibly the
> per-priority warnings), zero dev->num_tc and rewrite prio_tc_map[]
> entries, purely as a side effect of the intermediate state. The
> following stmmac_set_ndev_tcs(ndev, ndev_ntc, ndev_tc_to_txq) and
> the netdev_set_prio_tc_map() loop do repair all of that, so the
> end state looks correct, but users see a scary warning for an
> internal undo step and the tc mappings are transiently invalid.
>
> Would reversing the two rollback steps, restoring ndev_ntc /
> ndev_tc_to_txq / ndev_prio_tc_map first and only then calling
> netif_set_real_num_tx_queues(ndev, ndev_num_tx_queues), avoid this?
> The saved mapping by construction fits ndev_num_tx_queues, so
> netif_setup_tc() should have nothing to complain about. If there is
> a reason the queue count has to be restored first, could that be
> spelled out in a comment?
Can this really happen? mqprio_validate_queue_counts() should avoid that.
>
> [Severity: Low]
> Should the return value of the restoring netif_set_real_num_tx_queues() be
> checked here?
>
> On this path the call always grows the queue count, because
> mqprio_validate_queue_counts() already ensured num_tx_queues <=
> ndev_num_tx_queues, and growing can fail:
>
> net/core/dev.c:netif_set_real_num_tx_queues() {
> ...
> rc = netdev_queue_update_kobjects(dev, dev->real_num_tx_queues,
> txq);
> if (rc)
> return rc;
> ...
> }
>
> net/core/net-sysfs.c:netdev_queue_update_kobjects() {
> for (i = old_num; i < new_num; i++) {
> error = netdev_queue_add_kobject(dev, i);
> if (error) {
> new_num = old_num;
> break;
> }
> }
> }
>
> The early return happens before dev->real_num_tx_queues is assigned, so on
> -ENOMEM the device keeps the lower queue count while the following
> stmmac_set_ndev_tcs() reinstates a tc_to_txq[] whose offsets and counts
> describe queues beyond real_num_tx_queues. The function still returns only
> the original FPE error, so nothing above sees the partial restore.
ack, I will fix it in v3.
>
> [Severity: Medium]
> Does this error path need to reprogram the FPE preemption-class register?
>
> The previous code reached the disarm through:
>
> drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c:stmmac_reset_tc_mqprio() {
> netdev_reset_tc(ndev);
> netif_set_real_num_tx_queues(ndev, priv->plat->tx_queues_to_use);
>
> return stmmac_fpe_map_preemption_class(priv, ndev, extack, 0);
> }
>
> dwmac5_fpe_map_preemption_class() does all of its validation before its
> single read-modify-write:
>
> drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c {
> if (priv->plat->tx_sched_algorithm == MTL_TX_ALGORITHM_SP) {
> NL_SET_ERR_MSG_MOD(extack, ALG_ERR_MSG);
> return -EINVAL;
> }
> ...
> 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);
> }
>
> so on the -EINVAL path GMAC5_MTL_FPE_CTRL_STS still holds the mask
> programmed by the previous successful configuration.
>
> The new error_reset_num_tx_queues/error_reset_tc labels touch no MAC
> register, and the qdisc layer then zeroes dev->num_tc via
> mqprio_destroy() -> netdev_set_num_tc(dev, 0). Is the hardware then left
> with a per-TXQ preemptible mask armed while the driver's only description
> of which TXQs belong to which TC says there are no traffic classes, so
> express-intended frames can land on TXQs the MAC merge layer will
> fragment?
>
> The driver recomputes the mask only from netdev_get_num_tc() and
> tc_to_txq[] on later calls, so it appears the stale mask stays until the
> previous qdisc is deleted or replaced. Would it be worth mentioning this
> hardware-state change in the commit message?
as pointed out above, I guess this is a problem in sch_mqprio error path, and
it should be fixed there.
>
> --
> Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909-stmmac-tc_setup_dwmac510_mqprio-error-path-v2-0-8158b8333b3d%40oss.qualcomm.com
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH net v2 2/2] net: stmmac: preserve real_num_tx_queues on mqprio setup failure
2026-09-09 14:26 [PATCH net v2 0/2] net: stmmac: restore previous state if tc_setup_dwmac510_mqprio() fails Lorenzo Bianconi
@ 2026-09-09 14:26 ` Lorenzo Bianconi
0 siblings, 0 replies; 3+ messages in thread
From: Lorenzo Bianconi @ 2026-09-09 14:26 UTC (permalink / raw)
To: Maxime Chevallier, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Furong Xu, Vladimir Oltean
Cc: netdev, linux-stm32, linux-arm-kernel, linux-kernel, Lorenzo Bianconi
With the FPE preemption-class mapping error now propagated from
stmmac_fpe_map_preemption_class(), tc_setup_dwmac510_mqprio() can fail
on the mapping step. The error path used to call stmmac_reset_tc_mqprio(),
which resets the number of real TX queues to priv->plat->tx_queues_to_use
(the platform maximum), overwriting the value that was active before the
offload was attempted (for example a lower count left over from a previous
mqprio configuration).
The issue can be triggered using the following configuration:
# First mqprio config lowers the hw queue count.
$tc qdisc add dev eth0 root handle 1: mqprio queues 4@0 4@4
# Replace mqprio configuration with a second one that fails FPE
# preemption-class mapping. stmmac driver resets the real_num_tx_queues
# to the platform maximum, losing the previous configuration.
$tc qdisc replace dev eth0 root handle 2: mqprio queues 2@0 2@2 fp E P
Save ndev->real_num_tx_queues before lowering it and restore it,
together with the TC-to-queue and priority-to-TC mappings, when the FPE
preemption-class mapping fails, instead of resetting the queue count to
the platform maximum.
Note that a failed setup makes the qdisc layer run mqprio_destroy() on
the new qdisc, which resets dev->num_tc to 0; that is qdisc-layer handling,
out of the driver's control, and the driver-side restore remains correct
for the still-active previous configuration.
Fixes: 195e4f409a40 ("net: stmmac: support fp parameter of tc-mqprio")
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 80 ++++++++++++++++++-------
1 file changed, 60 insertions(+), 20 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index 5398616fcdfe..7d73b0c16f50 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)
+ return 0;
+
+ 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 int stmmac_reset_tc_mqprio(struct net_device *ndev,
struct netlink_ext_ack *extack)
{
@@ -1251,43 +1275,59 @@ static int stmmac_reset_tc_mqprio(struct net_device *ndev,
static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,
struct tc_mqprio_qopt_offload *mqprio)
{
+ unsigned int ndev_num_tx_queues, num_tx_queues = 0;
+ struct netdev_tc_txq ndev_tc_to_txq[TC_MAX_QUEUE];
+ struct netdev_tc_txq tc_to_txq[TC_MAX_QUEUE] = {};
struct netlink_ext_ack *extack = mqprio->extack;
struct tc_mqprio_qopt *qopt = &mqprio->qopt;
- u32 offset, count, num_stack_tx_queues = 0;
struct net_device *ndev = priv->dev;
- u32 num_tc = qopt->num_tc;
- int err;
+ u8 ndev_prio_tc_map[TC_BITMASK + 1];
+ int i, err, ndev_ntc;
- if (!num_tc)
+ if (!qopt->num_tc)
return stmmac_reset_tc_mqprio(ndev, extack);
- err = netdev_set_num_tc(ndev, num_tc);
- if (err)
- return err;
-
- for (u32 tc = 0; tc < num_tc; tc++) {
- offset = qopt->offset[tc];
- count = qopt->count[tc];
- num_stack_tx_queues += count;
+ if (qopt->num_tc > ARRAY_SIZE(tc_to_txq))
+ return -EINVAL;
- err = netdev_set_tc_queue(ndev, tc, count, offset);
- if (err)
- goto err_reset_tc;
+ /* save current tc values for reset */
+ ndev_ntc = netdev_get_num_tc(ndev);
+ 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);
+ for (i = 0; i < ARRAY_SIZE(ndev_prio_tc_map); i++)
+ ndev_prio_tc_map[i] = READ_ONCE(ndev->prio_tc_map[i]);
+
+ 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);
+ for (i = 0; i < ARRAY_SIZE(ndev_prio_tc_map); i++)
+ netdev_set_prio_tc_map(ndev, i, ndev_prio_tc_map[i]);
return err;
}
--
2.55.0
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-11 8:57 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-10 15:07 [PATCH net v2 2/2] net: stmmac: preserve real_num_tx_queues on mqprio setup failure netdev-bot+sashiko
2026-09-11 8:56 ` Lorenzo Bianconi
-- strict thread matches above, loose matches on Subject: below --
2026-09-09 14:26 [PATCH net v2 0/2] net: stmmac: restore previous state if tc_setup_dwmac510_mqprio() fails Lorenzo Bianconi
2026-09-09 14:26 ` [PATCH net v2 2/2] net: stmmac: preserve real_num_tx_queues on mqprio setup failure Lorenzo Bianconi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®