[PATCH net] net: xilinx: axienet: Free outstanding DMA buffers on dmaengine stop
From: Suraj Gupta <hidden>
Date: 2026-09-10 14:20:21
Also in:
lkml, netdev
Subsystem:
networking drivers, the rest, xilinx axi ethernet driver · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds, Radhey Shyam Pandey
In the dmaengine path the driver pre-submits RX buffers (and holds
in-flight TX buffers) whose SKBs are allocated and DMA-mapped by the
driver and reclaimed only in the completion callbacks. On ndo_stop()
the driver calls dmaengine_terminate_sync(), which aborts these
descriptors without running their callbacks, and then frees only the
skbuf_dma_descriptor ring shells with kfree().
As a result every SKB still owned by the DMA engine, together with its
DMA mapping, is leaked on each interface down. With RX_BUF_NUM_DEFAULT
(128) RX buffers pre-posted per channel, an ifup/ifdown cycle leaks up
to 128 SKBs and their DMA mappings; on systems with a limited IOMMU
aperture the mapping leak can eventually exhaust the address space.
Clear the ring slot's skb pointer in the TX and RX completion callbacks
so that a non-NULL skb reliably marks a slot that still owns a live,
DMA-mapped buffer. On stop, after terminating the channels, walk the
whole ring and unmap and free every such buffer before releasing the
rings, mirroring what the completion callbacks do.
Fixes: 6a91b846af85 ("net: axienet: Introduce dmaengine support")
Cc: stable@vger.kernel.org
Signed-off-by: Suraj Gupta <redacted>
---
.../net/ethernet/xilinx/xilinx_axienet_main.c | 34 ++++++++++++++++---
1 file changed, 30 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 1722b7038f34..7828fbb09dc8 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c@@ -881,6 +881,7 @@ static void axienet_dma_tx_cb(void *data, const struct dmaengine_result *result) u64_stats_update_end(&lp->tx_stat_sync); dma_unmap_sg(lp->dev, skbuf_dma->sgl, skbuf_dma->sg_len, DMA_TO_DEVICE); dev_consume_skb_any(skbuf_dma->skb); + skbuf_dma->skb = NULL; netif_txq_completed_wake(txq, 1, len, CIRC_SPACE(lp->tx_ring_head, lp->tx_ring_tail, TX_BD_NUM_MAX), 2);
@@ -1171,6 +1172,7 @@ static void axienet_dma_rx_cb(void *data, const struct dmaengine_result *result) &meta_max_len); dma_unmap_single(lp->dev, skbuf_dma->dma_address, lp->max_frm_size, DMA_FROM_DEVICE); + skbuf_dma->skb = NULL; if (IS_ERR(app_metadata)) { if (net_ratelimit())
@@ -1752,16 +1754,40 @@ static int axienet_stop(struct net_device *ndev) free_irq(lp->rx_irq, ndev); axienet_dma_bd_release(ndev); } else { + struct skbuf_dma_descriptor *skbuf_dma; + dmaengine_terminate_sync(lp->tx_chan); dmaengine_synchronize(lp->tx_chan); dmaengine_terminate_sync(lp->rx_chan); dmaengine_synchronize(lp->rx_chan); - for (i = 0; i < TX_BD_NUM_MAX; i++) - kfree(lp->tx_skb_ring[i]); + /* dmaengine_terminate_sync() aborts the descriptors still owned + * by the DMA engine without running their completion callbacks. + * A ring slot owns a live, DMA-mapped SKB iff its skb pointer is + * non-NULL (the callbacks clear it on completion), so unmap and + * free those here. Otherwise every outstanding TX/RX SKB and its + * DMA mapping is leaked on ifdown. + */ + for (i = 0; i < TX_BD_NUM_MAX; i++) { + skbuf_dma = lp->tx_skb_ring[i]; + if (skbuf_dma && skbuf_dma->skb) { + dma_unmap_sg(lp->dev, skbuf_dma->sgl, + skbuf_dma->sg_len, DMA_TO_DEVICE); + dev_kfree_skb_any(skbuf_dma->skb); + } + kfree(skbuf_dma); + } kfree(lp->tx_skb_ring); - for (i = 0; i < RX_BUF_NUM_DEFAULT; i++) - kfree(lp->rx_skb_ring[i]); + + for (i = 0; i < RX_BUF_NUM_DEFAULT; i++) { + skbuf_dma = lp->rx_skb_ring[i]; + if (skbuf_dma && skbuf_dma->skb) { + dma_unmap_single(lp->dev, skbuf_dma->dma_address, + lp->max_frm_size, DMA_FROM_DEVICE); + dev_kfree_skb_any(skbuf_dma->skb); + } + kfree(skbuf_dma); + } kfree(lp->rx_skb_ring); dma_release_channel(lp->rx_chan);
--
2.25.1