[PATCH v2 3/3] spi: tegra210-quad: Unmap DMA buffers on transfer timeout
From: Vishwaroop A
Date: Mon Oct 26 2026 - 07:23:33 EST
tegra_qspi_start_dma_based_transfer() maps the client TX/RX buffers with
dma_map_single() via tegra_qspi_dma_map_xfer(). The matching unmap only
happens in handle_dma_based_xfer(), which runs from the interrupt bottom
half.
When wait_for_completion_timeout() expires and tegra_qspi_handle_timeout()
cannot establish that the hardware finished, it returns an error without
the bottom half ever having run. Both transfer paths then abort the
transfer with tegra_qspi_dma_stop(), reset the controller and clear
curr_xfer, but never unmap the buffers. Every genuine transfer timeout
therefore leaks the streaming DMA mappings for that chunk, which
gradually exhausts the IOVA space on systems with an IOMMU in front of
QSPI.
Unmap the transfer after stopping DMA on both timeout error paths. The
unmap is placed after tegra_qspi_dma_stop() so the DMA engine is
quiesced before the mappings go away.
The recovery paths that do reach handle_dma_based_xfer() unmap there
themselves. The preceding patch makes tegra_qspi_dma_unmap_xfer() a no-op
once the mapping is released, so this cleanup cannot unmap twice or run
against a chunk that was never mapped.
Fixes: 921fc1838fb0 ("spi: tegra210-quad: Add support for Tegra210 QSPI controller")
Cc: stable@xxxxxxxxxxxxxxx # with "spi: tegra210-quad: Track DMA mapping state"
Signed-off-by: Vishwaroop A <va@xxxxxxxxxx>
---
drivers/spi/spi-tegra210-quad.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c
index d74aa46636ec..58c87c45431a 100644
--- a/drivers/spi/spi-tegra210-quad.c
+++ b/drivers/spi/spi-tegra210-quad.c
@@ -1507,10 +1507,12 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
dev_err(tqspi->dev, "transfer timeout\n");
/* Abort transfer by resetting pio/dma bit */
- if (tqspi->is_curr_dma_xfer)
+ if (tqspi->is_curr_dma_xfer) {
tegra_qspi_dma_stop(tqspi);
- else
+ tegra_qspi_dma_unmap_xfer(tqspi, xfer);
+ } else {
tegra_qspi_pio_stop(tqspi);
+ }
/* Reset controller if timeout happens */
tegra_qspi_reset(tqspi);
@@ -1624,8 +1626,10 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi,
WARN_ON(1);
dev_err(tqspi->dev, "transfer timeout\n");
- if (tqspi->is_curr_dma_xfer)
+ if (tqspi->is_curr_dma_xfer) {
tegra_qspi_dma_stop(tqspi);
+ tegra_qspi_dma_unmap_xfer(tqspi, xfer);
+ }
tegra_qspi_handle_error(tqspi);
ret = -EIO;
--
2.17.1