From: Daniel Gomez <[email protected]>

When submitting large IO beyond IOV_MAX, ensure the last dma mapped
chunk ends on an aligned boundary and unmap the remainder so the next
chunk submission takes it. Otherwise the discard path at the end of
dma_blk_cb() drops the unaligned tail and those bytes are lost.

Reproducer: Write 8 MiB through unaligned path and read it back using
the aligned path:

fio --name=demo --filename=/dev/nvme0n1 --rw=write --bs=8M --size=8M \
    --direct=1 --iomem_align=4 --verify=crc32c

fio --name=demo --filename=/dev/nvme0n1 --rw=write --bs=8M --size=8M \
    --direct=1 --verify=crc32c --verify_only

Signed-off-by: Daniel Gomez <[email protected]>
---
 system/dma-helpers.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/system/dma-helpers.c b/system/dma-helpers.c
index 8ee83ce9e75..9004ad50ae3 100644
--- a/system/dma-helpers.c
+++ b/system/dma-helpers.c
@@ -158,6 +158,17 @@ static void dma_blk_cb(void *opaque, int ret)
         }
         if (!mem)
             break;
+
+        /* Ensure the last slot in the chunk ends on an aligned boundary */
+        if (dbs->iov.niov == IOV_MAX - 1) {
+            dma_addr_t aligned = QEMU_ALIGN_DOWN(dbs->iov.size + cur_len,
+                                                 dbs->align);
+            if (aligned <= dbs->iov.size) {
+                dma_memory_unmap(dbs->sg->as, mem, cur_len, dbs->dir, 0);
+                break;
+          }
+          cur_len = aligned - dbs->iov.size;
+        }
         qemu_iovec_add(&dbs->iov, mem, cur_len);
         dbs->sg_cur_byte += cur_len;
         if (dbs->sg_cur_byte == dbs->sg->sg[dbs->sg_cur_index].len) {

-- 
2.55.0


Reply via email to