[PATCH v4 05/12] dmaengine: switchtec-dma: fix channel leak on registration failure

From: Logan Gunthorpe

Date: Tue Jul 28 2026 - 13:31:31 EST


switchtec_dma_chans_release() is called in three places but the
underlying memory is not freed in all of those places. In order to
clean this up, introduce a switchtec_dma_chans_free() helper that
will free the memory.

Ensure each call to switchtec_dma_chans_release() has a corresponding
switchtec_dma_chans_free() call. (The release in switchtec_dma_remove()
pairs with the free in switchtec_dma_release()).

swdma_dev->chan_cnt is now set to the number of channels that succeeded
when one fails to initialise, so switchtec_dma_chans_free() can still
be used if not all channels succeed in being allocated.

switchtec_dma_chans_free() also removes each channel from
dma_dev->channels before freeing it, since the channel status ISR
walks that list and would otherwise dereference freed memory.

Fixes: 30eba9df76ad ("dmaengine: switchtec-dma: Implement hardware initialization and cleanup")
Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Link: https://lore.kernel.org/dmaengine/20260717223024.9BB8A1F000E9@xxxxxxxxxxxxxxx
Link: https://lore.kernel.org/dmaengine/20260727190604.397DB1F000E9@xxxxxxxxxxxxxxx
Reviewed-by: Frank Li <Frank.Li@xxxxxxx>
Signed-off-by: Logan Gunthorpe <logang@xxxxxxxxxxxx>
---
drivers/dma/switchtec_dma.c | 27 +++++++++++++++++----------
1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c
index 25b988e9b4af..48da269198f3 100644
--- a/drivers/dma/switchtec_dma.c
+++ b/drivers/dma/switchtec_dma.c
@@ -1188,6 +1188,18 @@ static int switchtec_dma_chans_release(struct pci_dev *pdev,
return 0;
}

+static void switchtec_dma_chans_free(struct switchtec_dma_dev *swdma_dev)
+{
+ int i;
+
+ for (i = 0; i < swdma_dev->chan_cnt; i++) {
+ list_del(&swdma_dev->swdma_chans[i]->dma_chan.device_node);
+ kfree(swdma_dev->swdma_chans[i]);
+ }
+
+ kfree(swdma_dev->swdma_chans);
+}
+
static int switchtec_dma_chans_enumerate(struct switchtec_dma_dev *swdma_dev,
struct pci_dev *pdev, int chan_cnt)
{
@@ -1213,7 +1225,7 @@ static int switchtec_dma_chans_enumerate(struct switchtec_dma_dev *swdma_dev,
if (rc) {
dev_err(&pdev->dev, "Channel %d: init channel failed\n",
i);
- chan_cnt = i;
+ swdma_dev->chan_cnt = i;
goto err_exit;
}
}
@@ -1221,10 +1233,8 @@ static int switchtec_dma_chans_enumerate(struct switchtec_dma_dev *swdma_dev,
return chan_cnt;

err_exit:
- for (i = 0; i < chan_cnt; i++)
- switchtec_dma_chan_free(pdev, swdma_dev->swdma_chans[i]);
-
- kfree(swdma_dev->swdma_chans);
+ switchtec_dma_chans_release(pdev, swdma_dev);
+ switchtec_dma_chans_free(swdma_dev);

return rc;
}
@@ -1233,12 +1243,8 @@ static void switchtec_dma_release(struct dma_device *dma_dev)
{
struct switchtec_dma_dev *swdma_dev =
container_of(dma_dev, struct switchtec_dma_dev, dma_dev);
- int i;

- for (i = 0; i < swdma_dev->chan_cnt; i++)
- kfree(swdma_dev->swdma_chans[i]);
-
- kfree(swdma_dev->swdma_chans);
+ switchtec_dma_chans_free(swdma_dev);

put_device(dma_dev->dev);
kfree(swdma_dev);
@@ -1329,6 +1335,7 @@ static int switchtec_dma_create(struct pci_dev *pdev)

err_chans_release_exit:
switchtec_dma_chans_release(pdev, swdma_dev);
+ switchtec_dma_chans_free(swdma_dev);

err_exit:
if (swdma_dev->chan_status_irq)
--
2.47.3