From: Felix Gu <ustc.gu@gmail.com>
To: Ryan Wanner <ryan.wanner@microchip.com>,
Mark Brown <broonie@kernel.org>,
Nicolas Ferre <nicolas.ferre@microchip.com>,
Alexandre Belloni <alexandre.belloni@bootlin.com>,
Claudiu Beznea <claudiu.beznea@tuxon.dev>,
Radu Pirea <radu.pirea@microchip.com>,
Richard Genoud <richard.genoud@bootlin.com>,
Wenyou Yang <wenyou.yang@atmel.com>
Cc: linux-spi@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org,
Mark Brown <broonie@opensource.wolfsonmicro.com>,
Felix Gu <ustc.gu@gmail.com>
Subject: [PATCH 1/2] spi: atmel: fix resource leak on DMA buffer allocation failure
Date: Sat, 16 May 2026 01:20:34 +0800 [thread overview]
Message-ID: <20260516-atmel-v1-1-674fb4707af6@gmail.com> (raw)
In-Reply-To: <20260516-atmel-v1-0-674fb4707af6@gmail.com>
The original code set use_dma to false when dma_alloc_coherent() fails,
so DMA channels allocated earlier were never freed, causing a resource
leak.
Fix by moving the bounce buffer allocation into
atmel_spi_configure_dma() and extending atmel_spi_release_dma() to
also free the bounce buffers. Any allocation failure in the DMA
configuration path now rolls back both channels and buffers through
the same release function.
Fixes: a9889ed62d06 ("spi: atmel: Implements transfers with bounce buffer")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
drivers/spi/spi-atmel.c | 113 ++++++++++++++++++++++++------------------------
1 file changed, 57 insertions(+), 56 deletions(-)
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 25aa294631c8..e519a86a2b45 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -559,6 +559,34 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, u8 bits_per_word)
return err;
}
+static void atmel_spi_release_dma(struct spi_controller *host,
+ struct atmel_spi *as)
+{
+ if (host->dma_rx) {
+ dma_release_channel(host->dma_rx);
+ host->dma_rx = NULL;
+ }
+ if (host->dma_tx) {
+ dma_release_channel(host->dma_tx);
+ host->dma_tx = NULL;
+ }
+
+ if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
+ if (as->addr_tx_bbuf) {
+ dma_free_coherent(&as->pdev->dev, SPI_MAX_DMA_XFER,
+ as->addr_tx_bbuf,
+ as->dma_addr_tx_bbuf);
+ as->addr_tx_bbuf = NULL;
+ }
+ if (as->addr_rx_bbuf) {
+ dma_free_coherent(&as->pdev->dev, SPI_MAX_DMA_XFER,
+ as->addr_rx_bbuf,
+ as->dma_addr_rx_bbuf);
+ as->addr_rx_bbuf = NULL;
+ }
+ }
+}
+
static int atmel_spi_configure_dma(struct spi_controller *host,
struct atmel_spi *as)
{
@@ -569,7 +597,8 @@ static int atmel_spi_configure_dma(struct spi_controller *host,
if (IS_ERR(host->dma_tx)) {
err = PTR_ERR(host->dma_tx);
dev_dbg(dev, "No TX DMA channel, DMA is disabled\n");
- goto error_clear;
+ host->dma_tx = NULL;
+ return err;
}
host->dma_rx = dma_request_chan(dev, "rx");
@@ -580,12 +609,31 @@ static int atmel_spi_configure_dma(struct spi_controller *host,
* requested tx channel.
*/
dev_dbg(dev, "No RX DMA channel, DMA is disabled\n");
- goto error;
+ host->dma_rx = NULL;
+ goto err_release_dma;
}
err = atmel_spi_dma_slave_config(as, 8);
if (err)
- goto error;
+ goto err_release_dma;
+
+ if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
+ as->addr_tx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER,
+ &as->dma_addr_tx_bbuf,
+ GFP_KERNEL | GFP_DMA);
+ if (!as->addr_tx_bbuf) {
+ err = -ENOMEM;
+ goto err_release_dma;
+ }
+
+ as->addr_rx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER,
+ &as->dma_addr_rx_bbuf,
+ GFP_KERNEL | GFP_DMA);
+ if (!as->addr_rx_bbuf) {
+ err = -ENOMEM;
+ goto err_release_dma;
+ }
+ }
dev_info(&as->pdev->dev,
"Using %s (tx) and %s (rx) for DMA transfers\n",
@@ -593,13 +641,10 @@ static int atmel_spi_configure_dma(struct spi_controller *host,
dma_chan_name(host->dma_rx));
return 0;
-error:
- if (!IS_ERR(host->dma_rx))
- dma_release_channel(host->dma_rx);
- if (!IS_ERR(host->dma_tx))
- dma_release_channel(host->dma_tx);
-error_clear:
- host->dma_tx = host->dma_rx = NULL;
+
+err_release_dma:
+ atmel_spi_release_dma(host, as);
+
return err;
}
@@ -611,18 +656,6 @@ static void atmel_spi_stop_dma(struct spi_controller *host)
dmaengine_terminate_all(host->dma_tx);
}
-static void atmel_spi_release_dma(struct spi_controller *host)
-{
- if (host->dma_rx) {
- dma_release_channel(host->dma_rx);
- host->dma_rx = NULL;
- }
- if (host->dma_tx) {
- dma_release_channel(host->dma_tx);
- host->dma_tx = NULL;
- }
-}
-
/* This function is called by the DMA driver from tasklet context */
static void dma_callback(void *data)
{
@@ -1581,30 +1614,6 @@ static int atmel_spi_probe(struct platform_device *pdev)
as->use_pdc = true;
}
- if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
- as->addr_rx_bbuf = dma_alloc_coherent(&pdev->dev,
- SPI_MAX_DMA_XFER,
- &as->dma_addr_rx_bbuf,
- GFP_KERNEL | GFP_DMA);
- if (!as->addr_rx_bbuf) {
- as->use_dma = false;
- } else {
- as->addr_tx_bbuf = dma_alloc_coherent(&pdev->dev,
- SPI_MAX_DMA_XFER,
- &as->dma_addr_tx_bbuf,
- GFP_KERNEL | GFP_DMA);
- if (!as->addr_tx_bbuf) {
- as->use_dma = false;
- dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER,
- as->addr_rx_bbuf,
- as->dma_addr_rx_bbuf);
- }
- }
- if (!as->use_dma)
- dev_info(host->dev.parent,
- " can not allocate dma coherent memory\n");
- }
-
if (as->caps.has_dma_support && !as->use_dma)
dev_info(&pdev->dev, "Atmel SPI Controller using PIO only\n");
@@ -1666,7 +1675,7 @@ static int atmel_spi_probe(struct platform_device *pdev)
pm_runtime_set_suspended(&pdev->dev);
if (as->use_dma)
- atmel_spi_release_dma(host);
+ atmel_spi_release_dma(host, as);
spi_writel(as, CR, SPI_BIT(SWRST));
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
@@ -1689,15 +1698,7 @@ static void atmel_spi_remove(struct platform_device *pdev)
/* reset the hardware and block queue progress */
if (as->use_dma) {
atmel_spi_stop_dma(host);
- atmel_spi_release_dma(host);
- if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
- dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER,
- as->addr_tx_bbuf,
- as->dma_addr_tx_bbuf);
- dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER,
- as->addr_rx_bbuf,
- as->dma_addr_rx_bbuf);
- }
+ atmel_spi_release_dma(host, as);
}
spin_lock_irq(&as->lock);
--
2.43.0
next prev parent reply other threads:[~2026-05-15 17:20 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-15 17:20 [PATCH 0/2] spi: atmel: two fixes Felix Gu
2026-05-15 17:20 ` Felix Gu [this message]
2026-05-15 17:20 ` [PATCH 2/2] spi: atmel: fix DMA resource leak on probe error paths Felix Gu
2026-05-16 2:33 ` [PATCH 0/2] spi: atmel: two fixes Mark Brown
2026-05-16 4:41 ` Felix Gu
2026-05-16 6:36 ` Mark Brown
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=20260516-atmel-v1-1-674fb4707af6@gmail.com \
--to=ustc.gu@gmail.com \
--cc=alexandre.belloni@bootlin.com \
--cc=broonie@kernel.org \
--cc=broonie@opensource.wolfsonmicro.com \
--cc=claudiu.beznea@tuxon.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-spi@vger.kernel.org \
--cc=nicolas.ferre@microchip.com \
--cc=radu.pirea@microchip.com \
--cc=richard.genoud@bootlin.com \
--cc=ryan.wanner@microchip.com \
--cc=wenyou.yang@atmel.com \
/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.