[PATCH v4 02/12] dmaengine: switchtec-dma: fix resource leak in alloc_chan_resources
Logan Gunthorpe <[email protected]> Tue, 28 Jul 2026 11:15:13 -0600
| Newsgroups | org.kernel.vger.dmaengine,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci |
|---|---|
| Message-ID | <[email protected]> |
switchtec_dma_alloc_chan_resources() returns directly on any later failure, without ever freeing the descriptor rings and coherent DMA memory it just allocated. The dmaengine core does not call device_free_chan_resources() when device_alloc_chan_resources() fails, so the driver has to unwind its own partial state. The device-removed check also runs after ring_active and comp_ring_active have already been set true, so a failure there left the channel marked active despite alloc_chan_resources() reporting failure. Add an error-unwind path that disables the channel and frees the descriptor rings on every failure after allocation. ring_active and comp_ring_active are cleared under the same locks switchtec_dma_free_chan_resources() already uses, since the completion tasklet checks comp_ring_active under complete_lock before touching the completion ring, and a stale IRQ can still be in flight when this unwind path runs. Reported-by: Sashiko <[email protected]> Link: https://lore.kernel.org/dmaengine/[email protected] Fixes: 30eba9df76ad ("dmaengine: switchtec-dma: Implement hardware initialization and cleanup") Reviewed-by: Frank Li <[email protected]> Signed-off-by: Logan Gunthorpe <[email protected]> --- drivers/dma/switchtec_dma.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c index a4a7d66d042d..f77da31aeb65 100644 --- a/drivers/dma/switchtec_dma.c +++ b/drivers/dma/switchtec_dma.c @@ -988,15 +988,15 @@ static int switchtec_dma_alloc_chan_resources(struct dma_chan *chan) rc = enable_channel(swdma_chan); if (rc) - return rc; + goto err_free_desc; rc = reset_channel(swdma_chan); if (rc) - return rc; + goto err_disable_channel; rc = unhalt_channel(swdma_chan); if (rc) - return rc; + goto err_disable_channel; swdma_chan->ring_active = true; swdma_chan->comp_ring_active = true; @@ -1007,7 +1007,8 @@ static int switchtec_dma_alloc_chan_resources(struct dma_chan *chan) rcu_read_lock(); if (!rcu_dereference(swdma_dev->pdev)) { rcu_read_unlock(); - return -ENODEV; + rc = -ENODEV; + goto err_ring_inactive; } perf_cfg = readl(&swdma_chan->mmio_chan_fw->perf_cfg); @@ -1029,6 +1030,20 @@ static int switchtec_dma_alloc_chan_resources(struct dma_chan *chan) FIELD_GET(PERF_MRRS_MASK, perf_cfg)); return SWITCHTEC_DMA_SQ_SIZE; + +err_ring_inactive: + spin_lock_bh(&swdma_chan->submit_lock); + swdma_chan->ring_active = false; + spin_unlock_bh(&swdma_chan->submit_lock); + + spin_lock_bh(&swdma_chan->complete_lock); + swdma_chan->comp_ring_active = false; + spin_unlock_bh(&swdma_chan->complete_lock); +err_disable_channel: + disable_channel(swdma_chan); +err_free_desc: + switchtec_dma_free_desc(swdma_chan); + return rc; } static void switchtec_dma_free_chan_resources(struct dma_chan *chan) -- 2.47.3