* Re: [PATCH net] net: calxeda: cancel timeout work before freeing rings
2026-09-28 13:41 [PATCH net] net: calxeda: cancel timeout work before freeing rings Hongyan Xu
@ 2026-09-28 13:45 ` netdev-bot+sinfo
2026-10-01 13:42 ` netdev-bot+sashiko
1 sibling, 0 replies; 3+ messages in thread
From: netdev-bot+sinfo @ 2026-09-28 13:45 UTC (permalink / raw)
To: Hongyan Xu
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, o-takashi, kees,
u.kleine-koenig, bhelgaas, netdev, linux-kernel, jianhao.xu
Hi!
This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:
- How the issue was discovered, e.g. hit in production, hit during
development, syzbot report, manual code inspection, LLM or static
analysis tool scan.
- Whether the issue was actually triggered, or is only theoretical
(e.g. found by code inspection). If it was triggered please include
the symptoms, like the stack trace or error messages.
- What hardware the change was tested on. For driver fixes please
mention the device (and if relevant firmware version) used for
testing, or say that the change was not tested on real hardware.
Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.
The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net] net: calxeda: cancel timeout work before freeing rings
2026-09-28 13:41 [PATCH net] net: calxeda: cancel timeout work before freeing rings Hongyan Xu
2026-09-28 13:45 ` netdev-bot+sinfo
@ 2026-10-01 13:42 ` netdev-bot+sashiko
1 sibling, 0 replies; 3+ messages in thread
From: netdev-bot+sashiko @ 2026-10-01 13:42 UTC (permalink / raw)
To: getshell
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, o-takashi, kees,
u.kleine-koenig, bhelgaas, netdev, linux-kernel, jianhao.xu
Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider.
Critical: 0 · High: 3 · Medium: 1 · 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] The fix is incomplete: tx_timeout_work can be queued again after
the new cancel_work_sync() in xgmac_stop()…
- [High] Two more paths leave the worker lifetime bug reachable.
- [High] The patch moves netif_tx_disable() before napi_disable() in
xgmac_stop() (line 1049 vs 1052).
- [Medium] The patch removes the `if (readl(priv->base +
XGMAC_DMA_INTR_ENA)) napi_disable()` guard and calls…
--
net: calxeda: cancel timeout work before freeing rings
This commit reorders xgmac_stop(). Transmit is now stopped and the DMA
interrupts are masked first, then tx_timeout_work is cancelled, and only
then is NAPI disabled and the rings freed. It also removes the check on
XGMAC_DMA_INTR_ENA that decided whether napi_disable() was called.
> diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c
> index a2410fba6be2..63296469bd85 100644
> --- a/drivers/net/ethernet/calxeda/xgmac.c
> +++ b/drivers/net/ethernet/calxeda/xgmac.c
> @@ -1046,12 +1046,10 @@ static int xgmac_stop(struct net_device *dev)
> {
> struct xgmac_priv *priv = netdev_priv(dev);
>
> - if (readl(priv->base + XGMAC_DMA_INTR_ENA))
> - napi_disable(&priv->napi);
> -
[Severity: Medium]
Is it safe to drop this guard? napi_disable() now runs every time, but
xgmac_open() can fail and leave NAPI disabled:
xgmac_open() {
...
xgmac_hw_init(dev); <- writes 0 to XGMAC_DMA_INTR_ENA
...
ret = xgmac_dma_desc_rings_init(dev);
if (ret < 0)
return ret;
...
napi_enable(&priv->napi);
...
}
On the xgmac_change_mtu() path, xgmac_stop() has already disabled NAPI.
If xgmac_dma_desc_rings_init() then returns -ENOMEM, netif_set_mtu_ext()
returns the error and the device stays up.
The next ifdown or MTU change calls xgmac_stop() again. Would that
napi_disable() on an already disabled NAPI spin forever in
napi_disable_locked() while holding RTNL and netdev_lock?
The old readl() check saw 0 in INTR_ENA in this state and skipped the
second disable.
> - writel(0, priv->base + XGMAC_DMA_INTR_ENA);
> -
> netif_tx_disable(dev);
> + writel(0, priv->base + XGMAC_DMA_INTR_ENA);
[Severity: High]
Can XGMAC_DMA_INTR_ENA be turned back on after this write? This is the
only place xgmac_stop() masks the interrupts. Both contexts it then waits
for write the mask back.
A poll that is still running when the mask is written finishes with:
xgmac_poll() {
...
if (work_done < budget) {
napi_complete_done(napi, work_done);
__raw_writel(DMA_INTR_DEFAULT_MASK, priv->base + XGMAC_DMA_INTR_ENA);
}
...
}
With the old order, napi_disable() ran before writel(0), so a poll could
not undo the mask.
A tx_timeout_work that is already running, which cancel_work_sync() waits
for, ends with:
xgmac_tx_timeout_work() {
...
netif_wake_queue(priv->dev);
napi_enable(&priv->napi);
/* Enable interrupts */
writel(DMA_INTR_DEFAULT_MASK, priv->base + XGMAC_DMA_STATUS);
writel(DMA_INTR_DEFAULT_MASK, priv->base + XGMAC_DMA_INTR_ENA);
}
An xgmac_interrupt() still running on another CPU can also write
DMA_INTR_ABNORMAL to INTR_ENA, or call schedule_work() itself. There is no
synchronize_irq() in xgmac_stop().
Both masks include DMA_INTR_ENA_TSE. xgmac_mac_disable() then clears
DMA_CONTROL_ST, which stops the TX process and latches DMA_STATUS_TPS.
The IRQ is still registered, so:
xgmac_interrupt()
if (intr_status & DMA_STATUS_TPS) {
...
schedule_work(&priv->tx_timeout_work);
}
This queues the work again after the cancel, just before
xgmac_free_dma_desc_rings() frees the rings.
The late worker calls napi_disable() on the NAPI that xgmac_stop() has
just disabled. napi_disable_locked() then waits forever for
NAPIF_STATE_SCHED and NAPIF_STATE_NPSVC to clear, and it holds
netdev_lock the whole time. After that, xgmac_open()->napi_enable()
blocks under RTNL.
If the interface stays down and the device is removed, the pending work
can also outlive priv. See the comment on cancel_work_sync() below.
Would this need the interrupts masked, synchronize_irq(), NAPI disabled
and the mask written again before the work is cancelled or disabled with
disable_work_sync()?
> + cancel_work_sync(&priv->tx_timeout_work);
[Severity: High]
Can the watchdog queue tx_timeout_work again after this cancel?
xgmac_change_mtu() calls stop and open directly on a running device:
xgmac_change_mtu() {
...
/* Bring interface down, change mtu and bring interface back up */
xgmac_stop(dev);
WRITE_ONCE(dev->mtu, new_mtu);
return xgmac_open(dev);
}
This path skips dev_deactivate_many(). The dev_watchdog() timer stays
armed, netif_running() stays true, and the carrier stays on.
netif_tx_disable() stops the queue but does not refresh trans_start.
If TX has been idle for longer than watchdog_timeo (5*HZ here, because the
driver doesn't set it), this can run between cancel_work_sync() and the
end of xgmac_open():
dev_watchdog()->xgmac_tx_timeout()->schedule_work(&priv->tx_timeout_work)
The late worker either hangs in napi_disable() on the NAPI that
xgmac_stop() disabled, or races with the napi_disable() in xgmac_stop().
Both cases end in the same netdev_lock/RTNL deadlock.
Separately, xgmac_remove() never cancels or disables tx_timeout_work:
xgmac_remove() {
...
unregister_netdev(ndev);
netif_napi_del(&priv->napi);
...
free_netdev(ndev);
}
netif_close_many() skips ndo_stop for a device that is already down. Work
queued after the last xgmac_stop(), from the watchdog or from the TPS
path above, can therefore still be pending when free_netdev() frees priv.
priv embeds the work_struct.
Should xgmac_remove() also do a final cancel_work_sync() or
disable_work_sync()?
> + napi_disable(&priv->napi);
[Severity: High]
netif_tx_disable() now runs before napi_disable(). Can a poll that runs
in between wake the queue again? That poll goes through:
xgmac_poll()->xgmac_tx_complete() {
...
if (unlikely(netif_queue_stopped(priv->dev) &&
(tx_dma_ring_space(priv) > MAX_SKB_FRAGS)))
netif_wake_queue(priv->dev);
}
xgmac_tx_timeout_work(), which cancel_work_sync() waits for, also calls
netif_wake_queue(). Nothing later in xgmac_stop() stops the queue again.
When xgmac_stop() is called from xgmac_change_mtu(), the device is still
running and the qdisc is still active. After the wake, xgmac_xmit() can
run on another CPU while xgmac_free_dma_desc_rings() frees priv->dma_tx
and priv->tx_skbuff, or after it has set them to NULL.
Would this lead to a use-after-free or a NULL dereference at
priv->tx_skbuff[entry] = skb in xgmac_xmit()? It would also write
XGMAC_DMA_TX_POLL to start DMA on freed descriptors.
With the old order, napi_disable() ran before netif_tx_disable(), so a
poll could not wake the queue here.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260928134115.1957-1-getshell%40seu.edu.cn
^ permalink raw reply [flat|nested] 3+ messages in thread