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 fd27da40f2..ea2df31515 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -193,30 +193,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);
 
@@ -225,7 +227,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



Reply via email to