[PATCH v3 10/15] ata: ahci: fix zero-length DMA handling

Luca Lauro via B4 Relay <[email protected]> Sun, 02 Aug 2026 15:16:26 +0200
Newsgroups org.infradead.lists.barebox,org.kernel.feeds.b4-sent
Message-ID <[email protected]>
From: Luca Lauro <[email protected]>

ata: ahci: fix zero-length DMA handling

Commands without a data buffer must not trigger DMA setup. The previous
code unconditionally programmed PRDT entries and attempted DMA mapping
even when buf_len was zero, leading to invalid PRD tables and spurious
DMA operations.

This patch ensures that DMA mapping, PRDT setup and unmapping are only
performed when buf_len > 0.

Signed-off-by: Luca Lauro <[email protected]>
---
 drivers/ata/ahci.c | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 0ac1a9dead..a080de236e 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -198,30 +198,32 @@ static int ahci_io(struct ahci_port *ahci_port, u8 *fis, int fis_len, void *rbuf
 		const void *wbuf, int buf_len)
 {
 	u32 opts;
-	int sg_count;
+	int sg_count = 0;
 	int ret;
-	void *buf;
-	dma_addr_t buf_dma;
-	enum dma_data_direction dma_dir;
+	void *buf = NULL;
+	dma_addr_t buf_dma = 0;
+	enum dma_data_direction dma_dir = DMA_NONE;
 
 	if (!ahci_link_ok(ahci_port, 1))
 		return -EIO;
 
-	if (wbuf) {
-		buf = (void *)wbuf;
-		dma_dir = DMA_TO_DEVICE;
-	} else {
-		buf = rbuf;
-		dma_dir = DMA_FROM_DEVICE;
-	}
+	if (buf_len > 0) {
+		if (wbuf) {
+			buf = (void *)wbuf;
+			dma_dir = DMA_TO_DEVICE;
+		} else {
+			buf = rbuf;
+			dma_dir = DMA_FROM_DEVICE;
+		}
 
-	buf_dma = dma_map_single(ahci_port->ahci->dev, buf, buf_len, dma_dir);
+		buf_dma = dma_map_single(ahci_port->ahci->dev, buf, buf_len, dma_dir);
+		sg_count = ahci_fill_sg(ahci_port, buf_dma, buf_len);
+	}
 
 	memcpy(ahci_port->cmd_tbl, fis, fis_len);
 
-	sg_count = ahci_fill_sg(ahci_port, buf_dma, buf_len);
 	opts = (fis_len >> 2) | (sg_count << 16);
-	if (wbuf)
+	if (wbuf && buf_len > 0)
 		opts |= CMD_LIST_OPTS_WRITE;
 	ahci_fill_cmd_slot(ahci_port, opts);
 
@@ -230,7 +232,8 @@ static int ahci_io(struct ahci_port *ahci_port, u8 *fis, int fis_len, void *rbuf
 	ret = wait_on_timeout(WAIT_DATAIO,
 			(ahci_port_read(ahci_port, PORT_CMD_ISSUE) & 0x1) == 0);
 
-	dma_unmap_single(ahci_port->ahci->dev, buf_dma, buf_len, dma_dir);
+	if (buf_len > 0)
+		dma_unmap_single(ahci_port->ahci->dev, buf_dma, buf_len, dma_dir);
 
 	return ret;
 }

-- 
2.47.3