* [PATCH net v2] net: stmmac: ethtool: validate TX coalesce before reprogramming RX
@ 2026-09-20 1:56 Linkui Xiao
2026-09-21 2:06 ` netdev-bot+sashiko
0 siblings, 1 reply; 3+ messages in thread
From: Linkui Xiao @ 2026-09-20 1:56 UTC (permalink / raw)
To: maxime.chevallier, andrew+netdev, davem, edumazet, kuba, pabeni,
mcoquelin.stm32, alexandre.torgue
Cc: netdev, linux-stm32, linux-arm-kernel, linux-kernel, Linkui Xiao, stable
From: Linkui Xiao <xiaolinkui@kylinos.cn>
__stmmac_set_coalesce() applies the RX part of the request first and
only afterwards checks the TX parameters. The RX path already calls
stmmac_rx_watchdog() and stores rx_riwt[] and rx_coal_frames[], so when
the TX check rejects the request the driver returns -EINVAL after having
silently changed the hardware. A following ethtool -c then reports the
new RX values even though the command failed.
This became easy to hit once the per-queue interface was added.
__stmmac_get_coalesce() reports tx-usecs and tx-frames as 0 for a queue
index that is RX-only, and ethtool applies per-queue coalesce by reading
the current values first and sending them straight back. The next set is
therefore guaranteed to trip the test for both TX fields being zero,
right after the RX watchdog has been reprogrammed.
Move both TX checks in front of the RX block so a request is either
applied completely or rejected without touching the device.
Fixes: db2f2842e6f5 ("net: stmmac: add per-queue TX & RX coalesce ethtool support")
Cc: stable@vger.kernel.org
Signed-off-by: Linkui Xiao <xiaolinkui@kylinos.cn>
---
v2:
- Trim the comment in front of the TX checks to one line; the reasoning
belongs in the changelog. (Andrew Lunn)
.../ethernet/stmicro/stmmac/stmmac_ethtool.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index 154cc0c7623d..e06d05c3b4c7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -850,6 +850,16 @@ static int __stmmac_set_coalesce(struct net_device *dev,
else if (queue >= max_cnt)
return -EINVAL;
+ /* Check TX parameters before applying any RX setting */
+
+ if (ec->tx_coalesce_usecs == 0 &&
+ ec->tx_max_coalesced_frames == 0)
+ return -EINVAL;
+
+ if (ec->tx_coalesce_usecs > STMMAC_MAX_COAL_TX_TICK ||
+ ec->tx_max_coalesced_frames > STMMAC_TX_MAX_FRAMES)
+ return -EINVAL;
+
if (priv->use_riwt) {
rx_riwt = stmmac_usec2riwt(ec->rx_coalesce_usecs, priv);
@@ -875,14 +885,6 @@ static int __stmmac_set_coalesce(struct net_device *dev,
}
}
- if ((ec->tx_coalesce_usecs == 0) &&
- (ec->tx_max_coalesced_frames == 0))
- return -EINVAL;
-
- if ((ec->tx_coalesce_usecs > STMMAC_MAX_COAL_TX_TICK) ||
- (ec->tx_max_coalesced_frames > STMMAC_TX_MAX_FRAMES))
- return -EINVAL;
-
if (all_queues) {
int i;
--
2.25.1
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH net v2] net: stmmac: ethtool: validate TX coalesce before reprogramming RX 2026-09-20 1:56 [PATCH net v2] net: stmmac: ethtool: validate TX coalesce before reprogramming RX Linkui Xiao @ 2026-09-21 2:06 ` netdev-bot+sashiko 2026-09-21 13:31 ` Linkui Xiao 0 siblings, 1 reply; 3+ messages in thread From: netdev-bot+sashiko @ 2026-09-21 2:06 UTC (permalink / raw) To: xiaolinkui Cc: maxime.chevallier, andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32, alexandre.torgue, netdev, linux-stm32, linux-arm-kernel, linux-kernel, xiaolinkui, stable Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider. Critical: 0 · High: 1 (1) · Medium: 0 · Low: 0 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [High] Incomplete fix: the TX validation relocated to the top of __stmmac_set_coalesce()… Pre-existing issues: - [High] __stmmac_set_coalesce() accepts tx_max_coalesced_frames == 0 as long as tx_coalesce_usecs != 0 (the check at stmmac_ethtool.c:855 only… -- net: stmmac: ethtool: validate TX coalesce before reprogramming RX The two TX parameter checks in __stmmac_set_coalesce() are moved ahead of the RX block so that a request rejected because of its TX values no longer reprograms the RX watchdog and rx_riwt[]/rx_coal_frames[] first. The changelog motivates this with the per-queue path, where ethtool reads the current values back and sends them straight in again. > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c > index 1be5310ca766..193b65f9650f 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c > +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c > @@ -850,6 +850,16 @@ static int __stmmac_set_coalesce(struct net_device *dev, > else if (queue >= max_cnt) > return -EINVAL; > > + /* Check TX parameters before applying any RX setting */ > + > + if (ec->tx_coalesce_usecs == 0 && > + ec->tx_max_coalesced_frames == 0) > + return -EINVAL; [Severity: High] Does this reordering actually fix the per-queue case described in the changelog? The relocated check runs for every queue index, but the TX apply block further down is gated on the queue having a TX ring: } else if (queue < tx_cnt) { priv->tx_coal_frames[queue] = ec->tx_max_coalesced_frames; priv->tx_coal_timer[queue] = ec->tx_coalesce_usecs; } and __stmmac_get_coalesce() deliberately reports zeros for an index that has no TX ring: if (queue < tx_cnt) { ec->tx_coalesce_usecs = priv->tx_coal_timer[queue]; ec->tx_max_coalesced_frames = priv->tx_coal_frames[queue]; } else { ec->tx_coalesce_usecs = 0; ec->tx_max_coalesced_frames = 0; } On a device with rx_queues_to_use > tx_queues_to_use (dwmac-intel tgl_common_data sets 6 RX and 4 TX queues, so queues 4 and 5 are RX-only), does ethtool -Q ethX queue_mask 0x10 --coalesce rx-usecs N still return -EINVAL here? If so, is the net effect for that queue not worse than before? Previously the RX watchdog and rx_riwt[]/rx_coal_frames[] were programmed and -EINVAL was returned; now the early return means the RX part is never applied, so per-queue RX coalescing on an RX-only queue index has no effect at all. There is a second consequence in the ethtool core. ethtool_set_per_queue_coalesce() snapshots the getter output per queue and replays it on failure, discarding the return code: net/ethtool/ioctl.c:ethtool_set_per_queue_coalesce() { ... roll_back: if (ret != 0) { tmp = backup; for_each_set_bit(i, queue_mask, bit) { dev->ethtool_ops->set_per_queue_coalesce(dev, i, tmp); tmp++; } } ... } For an RX-only queue the saved backup necessarily carries tx_coalesce_usecs == 0 and tx_max_coalesced_frames == 0, so the new first check rejects the replay before the RX block runs. Can the RX watchdog and rx_riwt[] of that queue therefore stay at the values of the failed request after a later queue in the mask fails? > + > + if (ec->tx_coalesce_usecs > STMMAC_MAX_COAL_TX_TICK || > + ec->tx_max_coalesced_frames > STMMAC_TX_MAX_FRAMES) > + return -EINVAL; > + > if (priv->use_riwt) { > rx_riwt = stmmac_usec2riwt(ec->rx_coalesce_usecs, priv); > > @@ -875,14 +885,6 @@ static int __stmmac_set_coalesce(struct net_device *dev, The mirror of the same problem is left in place for a TX-only index. With priv->use_riwt set and queue >= rx_cnt, the getter reports rx_coalesce_usecs = 0, stmmac_usec2riwt() returns 0 and if ((rx_riwt > MAX_DMA_RIWT) || (rx_riwt < MIN_DMA_RIWT)) return -EINVAL; rejects the request before any TX value is stored. include/linux/ethtool.h documents both halves of the contract: * @get_per_queue_coalesce: ... If only a RX queue or a TX * queue has this number, set the inapplicable fields to ~0 and return 0. * @set_per_queue_coalesce: ... If only a RX queue or a TX * queue has this number, ignore the inapplicable fields. Would making the validation direction-aware, so the TX checks only apply when all_queues || queue < tx_cnt and the rx_riwt check only when all_queues || queue < rx_cnt (plus the getter reporting ~0 instead of 0 for the inapplicable fields), address both directions rather than reordering alone? [Severity: High] This isn't a bug introduced by this patch, but since the validation is being moved anyway, is the zero tx_max_coalesced_frames case worth handling here too? The check above only rejects both TX fields being zero, so ethtool -C ethX tx-usecs 10 tx-frames 0 stores priv->tx_coal_frames[queue] == 0, and stmmac_xdp_xmit_xdpf() divides by it with no zero test: drivers/net/ethernet/stmicro/stmmac/stmmac_main.c:stmmac_xdp_xmit_xdpf() { ... if (tx_q->tx_count_frames % priv->tx_coal_frames[queue] == 0) set_ic = true; ... } That is reached from stmmac_xdp_xmit_back() for XDP_TX and from stmmac_xdp_xmit() for ndo_xdp_xmit, so does this divide by zero in softirq context on x86? The other TX sites do test for zero, but the guard and the divisor are separate unannotated loads of the same plain u32 with no lock, for example in stmmac_xmit() and stmmac_tso_xmit(): else if (!priv->tx_coal_frames[queue]) set_ic = false; ... else if ((tx_q->tx_count_frames % priv->tx_coal_frames[queue]) < tx_packets) and in stmmac_xdp_xmit_zc(): if (!priv->tx_coal_frames[queue]) set_ic = false; else if (tx_q->tx_count_frames % priv->tx_coal_frames[queue] == 0) Can a concurrent ethtool store of 0 land between the guard and the modulo reload? Would a single READ_ONCE() snapshot per transmit path be appropriate? -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260920015647.1783574-1-xiaolinkui%40126.com ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net v2] net: stmmac: ethtool: validate TX coalesce before reprogramming RX 2026-09-21 2:06 ` netdev-bot+sashiko @ 2026-09-21 13:31 ` Linkui Xiao 0 siblings, 0 replies; 3+ messages in thread From: Linkui Xiao @ 2026-09-21 13:31 UTC (permalink / raw) To: netdev-bot+sashiko Cc: maxime.chevallier, andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32, alexandre.torgue, netdev, linux-stm32, linux-arm-kernel, linux-kernel, xiaolinkui, stable > Thank you for your contribution! Sashiko AI review found 1 potential > issue(s) to consider. > > Critical: 0 · High: 1 (1) · Medium: 0 · Low: 0 Both issues are legitimate, thanks for the detailed analysis. v3 reworks the fix and adds a second patch; details inline. > - [High] Incomplete fix: the TX validation relocated to the top of > __stmmac_set_coalesce()... > > Does this reordering actually fix the per-queue case described in the > changelog? [...] > On a device with rx_queues_to_use > tx_queues_to_use (dwmac-intel > tgl_common_data sets 6 RX and 4 TX queues, so queues 4 and 5 are RX-only), > does ethtool -Q ethX queue_mask 0x10 --coalesce rx-usecs N still return > -EINVAL here? Yes, it still does. tgl_common_data() sets rx_queues_to_use = 6 and tx_queues_to_use = 4, __stmmac_get_coalesce() reports tx-usecs and tx-frames as 0 for index 4 and 5, ethtool feeds those zeros straight back into the setter and the relocated test rejects the request. v2 changed when such a request fails, not whether it succeeds. > If so, is the net effect for that queue not worse than before? Previously > the RX watchdog and rx_riwt[]/rx_coal_frames[] were programmed and -EINVAL > was returned; now the early return means the RX part is never applied, so > per-queue RX coalescing on an RX-only queue index has no effect at all. > > There is a second consequence in the ethtool core. [...] > For an RX-only queue the saved backup necessarily carries tx_coalesce_usecs > == 0 and tx_max_coalesced_frames == 0, so the new first check rejects the > replay before the RX block runs. Can the RX watchdog and rx_riwt[] of that > queue therefore stay at the values of the failed request after a later queue > in the mask fails? Correct on both counts, and the roll-back one is the more serious of the two: ethtool_set_per_queue_coalesce() discards the return code of the replay, so an early -EINVAL there silently leaves the RX watchdog of an already-applied queue at the value of the failed request. > Would making the validation direction-aware, so the TX checks only apply when > all_queues || queue < tx_cnt and the rx_riwt check only when > all_queues || queue < rx_cnt (plus the getter reporting ~0 instead of 0 for > the inapplicable fields), address both directions rather than reordering > alone? That is what v3 patch 1/2 does, minus the getter change: has_rx = all_queues || queue < rx_cnt; has_tx = all_queues || queue < tx_cnt; if (has_tx && ec->tx_coalesce_usecs == 0 && ec->tx_max_coalesced_frames == 0) return -EINVAL; if (has_tx && (ec->tx_coalesce_usecs > STMMAC_MAX_COAL_TX_TICK || ec->tx_max_coalesced_frames > STMMAC_TX_MAX_FRAMES)) return -EINVAL; if (has_rx && priv->use_riwt) { has_rx also gates the application, so the "else if (queue < rx_cnt)" inside the RIWT block becomes a plain "else", and the TX store keeps its own has_tx test. All validation still runs before the first stmmac_rx_watchdog() call, so the property v2 was after is retained: a request is either applied completely or rejected without touching the device. The mirrored case is fixed by the same change. With use_riwt set and queue >= rx_cnt, has_rx is false, so stmmac_usec2riwt() is never asked to convert the rx-usecs 0 that the getter reported and the MIN_DMA_RIWT test no longer rejects per-queue TX requests for a TX-only index. tx_queues_to_use > rx_queues_to_use does not occur in the in-tree platform data - dwmac-intel is the other way round - but stmmac_probe_config_dt() parses snps,rx-queues-to-use and snps,tx-queues-to-use independently and clamps each one on its own, so DT can produce it. I left __stmmac_get_coalesce() reporting 0 rather than ~0. Once the setter ignores the inapplicable fields the read-modify-write round trip works either way, and ~0 would show up as 4294967295 in "ethtool --per-queue ... --show-coalesce" for the indices that print 0 today. If the ~0 convention is wanted it should be a separate change, together with whatever displays it. > [Severity: High] > This isn't a bug introduced by this patch, but since the validation is being > moved anyway, is the zero tx_max_coalesced_frames case worth handling here > too? [...] > That is reached from stmmac_xdp_xmit_back() for XDP_TX and from > stmmac_xdp_xmit() for ndo_xdp_xmit, so does this divide by zero in softirq > context on x86? The divide by zero is real and reachable. tx_coal_frames[] starts at STMMAC_TX_FRAMES in stmmac_init_coalesce() and can only become 0 through ethtool, and dwmac-intel TGL/EHL is exactly the x86 platform where the modulo traps. The fix belongs in stmmac_xdp_xmit_xdpf() rather than in the setter, because tx-frames 0 is a supported request: it only stops the frame count from raising TX completion interrupts, while the coalescing timer that stmmac_xmit(), stmmac_tso_xmit(), stmmac_finalize_xdp_rx() and stmmac_xdp_xmit() arm through stmmac_tx_timer_arm() keeps reclaiming the descriptors. That is why those paths test the divisor - the test came with c2837423cb54 ("net: stmmac: Rework TX Coalesce logic") and was extended to the zero-copy path by 132c32ee5bc0 ("net: stmmac: Add TX via XDP zero-copy socket") - and why __stmmac_set_coalesce() rejects the request only when tx-usecs is zero as well, since then nothing would complete the transmissions. Rejecting tx-frames 0 outright would break that configuration. So v3 adds patch 2/2, giving stmmac_xdp_xmit_xdpf() the missing test, with be8b38a722e6 ("net: stmmac: Add support for XDP_TX action") as the Fixes tag. > The other TX sites do test for zero, but the guard and the divisor are > separate unannotated loads of the same plain u32 with no lock [...] > Can a concurrent ethtool store of 0 land between the guard and the modulo > reload? Would a single READ_ONCE() snapshot per transmit path be > appropriate? In principle yes: nothing forces the two loads to observe the same value. They are adjacent in all four chains with no call or barrier in between, so compilers are likely to CSE them, but that is a favour and not a guarantee. Annotating it properly means one snapshot per transmit path plus WRITE_ONCE() on the stores in __stmmac_set_coalesce(), i.e. a conversion of all four sites at once, which does not belong in a stable fix. Patch 2/2 therefore uses the plain test that matches the existing three; I will send the READ_ONCE()/WRITE_ONCE() conversion separately if it is wanted. pw-bot: cr ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-21 13:35 UTC | newest] Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-20 1:56 [PATCH net v2] net: stmmac: ethtool: validate TX coalesce before reprogramming RX Linkui Xiao 2026-09-21 2:06 ` netdev-bot+sashiko 2026-09-21 13:31 ` Linkui Xiao
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®