From: BeaCox <0xbeac@gmail.com>
To: Andrew Lunn <andrew+netdev@lunn.ch>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org
Subject: [PATCH net 3/3] net: xgmac: disable timeout work before teardown
Date: Wed, 4 Nov 2026 14:32:45 +0800 [thread overview]
Message-ID: <20261104063245.23402-4-0xbeac@gmail.com> (raw)
In-Reply-To: <20261104063245.23402-1-0xbeac@gmail.com>
xgmac_remove() frees the IRQ lines and unmaps the registers before
freeing the netdev, but tx_timeout_work can still be pending or running.
The work is queued from ndo_tx_timeout() and from the DMA error
interrupt path, and its handler disables NAPI, programs the DMA
registers and walks the TX ring.
xgmac_stop() has the same lifetime problem: it frees the DMA descriptor
rings while a queued timeout work can still run.
Disable the work in probe after INIT_WORK() so open/resume can enable it,
disable it synchronously in xgmac_stop() before the rings are freed and
in xgmac_suspend() before NAPI/interrupts are torn down, enable it in
xgmac_open() and xgmac_resume(), and disable it in xgmac_remove() before
the IRQs and MMIO are released. The enable/disable pairing avoids the
underflow warning that enable_work() emits when the work was never
disabled. disable_work_sync() also rejects queue_work() from a late
interrupt, which cancellation alone would not.
The patch does not change the existing free_irq()/unregister_netdev()
ordering in xgmac_remove(); it only prevents the timeout work from
outliving the netdev.
Fixes: 8746f671ef04 ("net: calxedaxgmac: fix race between xgmac_tx_complete and xgmac_tx_err")
Cc: stable@vger.kernel.org # 6.10+
Signed-off-by: BeaCox <0xbeac@gmail.com>
---
drivers/net/ethernet/calxeda/xgmac.c | 30 ++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c
index a2410fba6b..9b7aee56f2 100644
--- a/drivers/net/ethernet/calxeda/xgmac.c
+++ b/drivers/net/ethernet/calxeda/xgmac.c
@@ -1023,6 +1023,9 @@ static int xgmac_open(struct net_device *dev)
if (ret < 0)
return ret;
+ /* xgmac_stop() disables this work before freeing the rings. */
+ enable_work(&priv->tx_timeout_work);
+
/* Enable the MAC Rx/Tx */
xgmac_mac_enable(ioaddr);
@@ -1046,6 +1049,13 @@ static int xgmac_stop(struct net_device *dev)
{
struct xgmac_priv *priv = netdev_priv(dev);
+ /*
+ * The timeout work programs the DMA rings that are freed below, and
+ * it toggles NAPI. Disable it synchronously before touching either;
+ * xgmac_open() and xgmac_resume() enable it again.
+ */
+ disable_work_sync(&priv->tx_timeout_work);
+
if (readl(priv->base + XGMAC_DMA_INTR_ENA))
napi_disable(&priv->napi);
@@ -1713,6 +1723,12 @@ static int xgmac_probe(struct platform_device *pdev)
ndev->ethtool_ops = &xgmac_ethtool_ops;
spin_lock_init(&priv->stats_lock);
INIT_WORK(&priv->tx_timeout_work, xgmac_tx_timeout_work);
+ /*
+ * The work is enabled while the interface is up. Start it disabled
+ * so the enable_work() in xgmac_open()/xgmac_resume() has a matching
+ * disable and never underflows.
+ */
+ disable_work(&priv->tx_timeout_work);
priv->device = &pdev->dev;
priv->dev = ndev;
@@ -1823,6 +1839,13 @@ static void xgmac_remove(struct platform_device *pdev)
struct xgmac_priv *priv = netdev_priv(ndev);
struct resource *res;
+ /*
+ * The timeout work can be queued from ndo_tx_timeout() and from the
+ * DMA error interrupt. Disable it before the IRQs are released and
+ * the registers are unmapped.
+ */
+ disable_work_sync(&priv->tx_timeout_work);
+
xgmac_mac_disable(priv->base);
/* Free the IRQ lines */
@@ -1862,6 +1885,12 @@ static int xgmac_suspend(struct device *dev)
return 0;
netif_device_detach(ndev);
+ /*
+ * The timeout work touches the DMA registers and toggles NAPI.
+ * Disable it before NAPI and the interrupts are torn down;
+ * xgmac_resume() enables it again.
+ */
+ disable_work_sync(&priv->tx_timeout_work);
napi_disable(&priv->napi);
writel(0, priv->base + XGMAC_DMA_INTR_ENA);
@@ -1891,6 +1920,7 @@ static int xgmac_resume(struct device *dev)
/* Enable the MAC and DMA */
xgmac_mac_enable(ioaddr);
+ enable_work(&priv->tx_timeout_work);
writel(DMA_INTR_DEFAULT_MASK, ioaddr + XGMAC_DMA_STATUS);
writel(DMA_INTR_DEFAULT_MASK, ioaddr + XGMAC_DMA_INTR_ENA);
--
2.43.0
next prev parent reply other threads:[~2026-11-04 6:33 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 6:32 [PATCH net 0/3] net: ethernet: disable async work before teardown BeaCox
2026-11-04 6:32 ` [PATCH net 1/3] net: atl1: disable work before freeing the netdev BeaCox
2026-11-08 6:59 ` netdev-bot+sashiko
2026-11-04 6:32 ` [PATCH net 2/3] net: enc28j60: " BeaCox
2026-11-08 6:59 ` netdev-bot+sashiko
2026-11-04 6:32 ` BeaCox [this message]
2026-11-08 6:59 ` [PATCH net 3/3] net: xgmac: disable timeout work before teardown netdev-bot+sashiko
2026-11-04 6:38 ` [PATCH net 0/3] net: ethernet: disable async " netdev-bot+sinfo
2026-11-04 15:55 ` BeaCox
2026-11-04 21:20 ` Andrew Lunn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261104063245.23402-4-0xbeac@gmail.com \
--to=0xbeac@gmail.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.