* [PATCH v2 1/7] dma-helpers: fix unaligned discard_back
2026-08-19 15:24 [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path Daniel Gomez
@ 2026-08-19 15:24 ` Daniel Gomez
2026-09-04 21:57 ` Jesper Wendel Devantier
2026-08-19 15:24 ` [PATCH v2 2/7] dma-helpers: ensure IOV_MAX chunks end aligned Daniel Gomez
` (6 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Daniel Gomez @ 2026-08-19 15:24 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé, Keith Busch,
Klaus Jensen, Klaus Jensen, Jesper Devantier, qemu-block,
Daniel Gomez, Daniel Gomez, GOST
From: Daniel Gomez <da.gomez@samsung.com>
Commit 99868af3d0 ("dma-helpers: explicitly pass alignment into DMA
helpers") replaced the sub-sector remainder with the aligned size as the
amount passed to qemu_iovec_discard_back(), effectively discarding the
wrong chunk. Go back to discarding the tail bytes of unaligned IO.
It was found while chunking dma_blk_cb() at IOV_MAX to batch large IO:
a chunk boundary that is not a multiple of the alignment lands here and
dma_blk_cb() submits the wrong bytes.
Reproducer (with dma_blk_cb() chunked at IOV_MAX and mdts >= 10):
fio --name=demo --filename=/dev/nvme0n1 --rw=write --bs=8M --direct=1 \
--iomem_align=4 --size=64M --verify=crc32c --do_verify=1
Logs:
demo: (g=0): rw=write, bs=(R) 8192KiB-8192KiB, (W) 8192KiB-8192KiB, (T) 8192KiB-8192KiB, ioengine=psync, iodepth=1
fio-3.41
Starting 1 process
crc32c: verify failed at file /dev/nvme0n1 offset 0, length 8388608 (requested block: offset=0, length=8388608, flags=88)
Expected CRC: db932ec1
Received CRC: d3b6f2dc
fio: pid=2755, err=84/file:io_u.c:2280, func=io_u_sync_complete, error=Invalid or incomplete multibyte or wide character
Fixes: 99868af3d0 ("dma-helpers: explicitly pass alignment into DMA helpers")
Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
---
system/dma-helpers.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/system/dma-helpers.c b/system/dma-helpers.c
index 0d592f64680..8ee83ce9e75 100644
--- a/system/dma-helpers.c
+++ b/system/dma-helpers.c
@@ -174,8 +174,7 @@ static void dma_blk_cb(void *opaque, int ret)
}
if (!QEMU_IS_ALIGNED(dbs->iov.size, dbs->align)) {
- qemu_iovec_discard_back(&dbs->iov,
- QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align));
+ qemu_iovec_discard_back(&dbs->iov, dbs->iov.size % dbs->align);
}
dbs->acb = dbs->io_func(dbs->offset, &dbs->iov,
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v2 1/7] dma-helpers: fix unaligned discard_back
2026-08-19 15:24 ` [PATCH v2 1/7] dma-helpers: fix unaligned discard_back Daniel Gomez
@ 2026-09-04 21:57 ` Jesper Wendel Devantier
2026-09-09 12:17 ` Daniel Gomez
0 siblings, 1 reply; 17+ messages in thread
From: Jesper Wendel Devantier @ 2026-09-04 21:57 UTC (permalink / raw)
To: Daniel Gomez
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé,
Keith Busch, Klaus Jensen, Klaus Jensen, qemu-block, Daniel Gomez,
GOST
On 2026-08-19T17:24:03+02:00, Daniel Gomez <da.gomez@kernel.org> wrote:
> From: Daniel Gomez <da.gomez@samsung.com>
>
> Commit 99868af3d0 ("dma-helpers: explicitly pass alignment into DMA
> helpers") replaced the sub-sector remainder with the aligned size as the
> amount passed to qemu_iovec_discard_back(), effectively discarding the
> wrong chunk. Go back to discarding the tail bytes of unaligned IO.
>
> It was found while chunking dma_blk_cb() at IOV_MAX to batch large IO:
> a chunk boundary that is not a multiple of the alignment lands here and
> dma_blk_cb() submits the wrong bytes.
>
> Reproducer (with dma_blk_cb() chunked at IOV_MAX and mdts >= 10):
> fio --name=demo --filename=/dev/nvme0n1 --rw=write --bs=8M --direct=1 \
> --iomem_align=4 --size=64M --verify=crc32c --do_verify=1
>
> Logs:
> demo: (g=0): rw=write, bs=(R) 8192KiB-8192KiB, (W) 8192KiB-8192KiB, (T) 8192KiB-8192KiB, ioengine=psync, iodepth=1
> fio-3.41
> Starting 1 process
> crc32c: verify failed at file /dev/nvme0n1 offset 0, length 8388608 (requested block: offset=0, length=8388608, flags=88)
> Expected CRC: db932ec1
> Received CRC: d3b6f2dc
> fio: pid=2755, err=84/file:io_u.c:2280, func=io_u_sync_complete, error=Invalid or incomplete multibyte or wide character
>
> Fixes: 99868af3d0 ("dma-helpers: explicitly pass alignment into DMA helpers")
> Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
> ---
> system/dma-helpers.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/system/dma-helpers.c b/system/dma-helpers.c
> index 0d592f64680..8ee83ce9e75 100644
> --- a/system/dma-helpers.c
> +++ b/system/dma-helpers.c
> @@ -174,8 +174,7 @@ static void dma_blk_cb(void *opaque, int ret)
> }
>
> if (!QEMU_IS_ALIGNED(dbs->iov.size, dbs->align)) {
> - qemu_iovec_discard_back(&dbs->iov,
> - QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align));
> + qemu_iovec_discard_back(&dbs->iov, dbs->iov.size % dbs->align);
> }
>
> dbs->acb = dbs->io_func(dbs->offset, &dbs->iov,
>
> --
> 2.55.0
>
>
>
It is obviously locally correct.
(I am more confused by the code, broadly. How come the IO isn't rejected
if unaligned?).
Acked-by: Jesper Wendel Devantier <foss@defmacro.it>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v2 1/7] dma-helpers: fix unaligned discard_back
2026-09-04 21:57 ` Jesper Wendel Devantier
@ 2026-09-09 12:17 ` Daniel Gomez
0 siblings, 0 replies; 17+ messages in thread
From: Daniel Gomez @ 2026-09-09 12:17 UTC (permalink / raw)
To: Jesper Wendel Devantier
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé,
Keith Busch, Klaus Jensen, Klaus Jensen, qemu-block, Daniel Gomez,
GOST
On 2026-09-04T23:57:16+02:00, Jesper Wendel Devantier <foss@defmacro.it> wrote:
>
>
> On 2026-08-19T17:24:03+02:00, Daniel Gomez <da.gomez@kernel.org> wrote:
> > From: Daniel Gomez <da.gomez@samsung.com>
> It is obviously locally correct.
>
> (I am more confused by the code, broadly. How come the IO isn't rejected
> if unaligned?).
IOs were never unaligned from nvme side. However, chunking introduces an
unalignment case that patch 2 fixes by ensuring that each IOV_MAX chunk
boundary is sector-aligned (not present in v1), which is what led me to
this fio test and unalignment check.
But your question made me look at the discard path more closely. And
looking at the commit introducing it, 58f423fbd5f7 "dma-helpers: Fix too
long qiov", it was meant to fix something unrelated: "This fixes the IDE
qtest case /x86_64/ide/bmdma/short_prdt." So, I guess it's fine for that
case.
I will drop the reproducer from the commit message as it belongs to
patch 2.
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 2/7] dma-helpers: ensure IOV_MAX chunks end aligned
2026-08-19 15:24 [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path Daniel Gomez
2026-08-19 15:24 ` [PATCH v2 1/7] dma-helpers: fix unaligned discard_back Daniel Gomez
@ 2026-08-19 15:24 ` Daniel Gomez
2026-08-19 15:24 ` [PATCH v2 3/7] dma-helpers: cap iovec allocation at IOV_MAX Daniel Gomez
` (5 subsequent siblings)
7 siblings, 0 replies; 17+ messages in thread
From: Daniel Gomez @ 2026-08-19 15:24 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé, Keith Busch,
Klaus Jensen, Klaus Jensen, Jesper Devantier, qemu-block,
Daniel Gomez, Daniel Gomez, GOST
From: Daniel Gomez <da.gomez@samsung.com>
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 <da.gomez@samsung.com>
---
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
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v2 3/7] dma-helpers: cap iovec allocation at IOV_MAX
2026-08-19 15:24 [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path Daniel Gomez
2026-08-19 15:24 ` [PATCH v2 1/7] dma-helpers: fix unaligned discard_back Daniel Gomez
2026-08-19 15:24 ` [PATCH v2 2/7] dma-helpers: ensure IOV_MAX chunks end aligned Daniel Gomez
@ 2026-08-19 15:24 ` Daniel Gomez
2026-09-04 21:57 ` Jesper Wendel Devantier
2026-08-19 15:24 ` [PATCH v2 4/7] dma-helpers: chunk dma_blk_cb " Daniel Gomez
` (4 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Daniel Gomez @ 2026-08-19 15:24 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé, Keith Busch,
Klaus Jensen, Klaus Jensen, Jesper Devantier, qemu-block,
Daniel Gomez, Daniel Gomez, GOST
From: Daniel Gomez <da.gomez@samsung.com>
Cap the initial iovec allocation at IOV_MAX instead of sizing it for the
whole sg list, which over-allocates for large IO where the number of sg
entries exceeds IOV_MAX.
This is preparatory work for chunking dma_blk_cb() at IOV_MAX to batch
large IO: once chunked, dbs->iov never holds more than IOV_MAX iovecs.
Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
---
system/dma-helpers.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/system/dma-helpers.c b/system/dma-helpers.c
index 9004ad50ae3..d4fd33a3d4c 100644
--- a/system/dma-helpers.c
+++ b/system/dma-helpers.c
@@ -242,7 +242,7 @@ BlockAIOCB *dma_blk_io(
dbs->io_func = io_func;
dbs->io_func_opaque = io_func_opaque;
dbs->bh = NULL;
- qemu_iovec_init(&dbs->iov, sg->nsg);
+ qemu_iovec_init(&dbs->iov, MIN(sg->nsg, IOV_MAX));
dma_blk_cb(dbs, 0);
return &dbs->common;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v2 3/7] dma-helpers: cap iovec allocation at IOV_MAX
2026-08-19 15:24 ` [PATCH v2 3/7] dma-helpers: cap iovec allocation at IOV_MAX Daniel Gomez
@ 2026-09-04 21:57 ` Jesper Wendel Devantier
0 siblings, 0 replies; 17+ messages in thread
From: Jesper Wendel Devantier @ 2026-09-04 21:57 UTC (permalink / raw)
To: Daniel Gomez
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé,
Keith Busch, Klaus Jensen, Klaus Jensen, qemu-block, Daniel Gomez,
GOST
On 2026-08-19T17:24:05+02:00, Daniel Gomez <da.gomez@kernel.org> wrote:
> From: Daniel Gomez <da.gomez@samsung.com>
>
> Cap the initial iovec allocation at IOV_MAX instead of sizing it for the
> whole sg list, which over-allocates for large IO where the number of sg
> entries exceeds IOV_MAX.
>
> This is preparatory work for chunking dma_blk_cb() at IOV_MAX to batch
> large IO: once chunked, dbs->iov never holds more than IOV_MAX iovecs.
>
> Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
> ---
> system/dma-helpers.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/system/dma-helpers.c b/system/dma-helpers.c
> index 9004ad50ae3..d4fd33a3d4c 100644
> --- a/system/dma-helpers.c
> +++ b/system/dma-helpers.c
> @@ -242,7 +242,7 @@ BlockAIOCB *dma_blk_io(
> dbs->io_func = io_func;
> dbs->io_func_opaque = io_func_opaque;
> dbs->bh = NULL;
> - qemu_iovec_init(&dbs->iov, sg->nsg);
> + qemu_iovec_init(&dbs->iov, MIN(sg->nsg, IOV_MAX));
> dma_blk_cb(dbs, 0);
> return &dbs->common;
> }
>
> --
> 2.55.0
>
>
>
Sensible, given the next patch in the series.
Acked-by: Jesper Wendel Devantier <foss@defmacro.it>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 4/7] dma-helpers: chunk dma_blk_cb at IOV_MAX
2026-08-19 15:24 [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path Daniel Gomez
` (2 preceding siblings ...)
2026-08-19 15:24 ` [PATCH v2 3/7] dma-helpers: cap iovec allocation at IOV_MAX Daniel Gomez
@ 2026-08-19 15:24 ` Daniel Gomez
2026-09-04 21:58 ` Jesper Wendel Devantier
2026-08-19 15:24 ` [PATCH v2 5/7] hw/nvme: clamp mdts and zasl shifts Daniel Gomez
` (3 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Daniel Gomez @ 2026-08-19 15:24 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé, Keith Busch,
Klaus Jensen, Klaus Jensen, Jesper Devantier, qemu-block,
Daniel Gomez, Daniel Gomez, GOST
From: Daniel Gomez <da.gomez@samsung.com>
dma_blk_cb() submits dbs->iov to io_func() in one shot, so callers
must keep dbs->iov.niov below IOV_MAX or the host preadv()/pwritev()
rejects the call.
Break the accumulation loop when niov reaches IOV_MAX. The existing
re-entry path resumes mapping and submits a follow-up chunk under
the same AIOCB, so callers no longer carry that constraint.
Suggested-by: Klaus Jensen <k.jensen@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
---
system/dma-helpers.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/system/dma-helpers.c b/system/dma-helpers.c
index d4fd33a3d4c..613fa548669 100644
--- a/system/dma-helpers.c
+++ b/system/dma-helpers.c
@@ -175,6 +175,9 @@ static void dma_blk_cb(void *opaque, int ret)
dbs->sg_cur_byte = 0;
++dbs->sg_cur_index;
}
+ if (dbs->iov.niov >= IOV_MAX) {
+ break;
+ }
}
if (dbs->iov.size == 0) {
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v2 4/7] dma-helpers: chunk dma_blk_cb at IOV_MAX
2026-08-19 15:24 ` [PATCH v2 4/7] dma-helpers: chunk dma_blk_cb " Daniel Gomez
@ 2026-09-04 21:58 ` Jesper Wendel Devantier
0 siblings, 0 replies; 17+ messages in thread
From: Jesper Wendel Devantier @ 2026-09-04 21:58 UTC (permalink / raw)
To: Daniel Gomez
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé,
Keith Busch, Klaus Jensen, Klaus Jensen, qemu-block, Daniel Gomez,
GOST
On 2026-08-19T17:24:06+02:00, Daniel Gomez <da.gomez@kernel.org> wrote:
> From: Daniel Gomez <da.gomez@samsung.com>
>
> dma_blk_cb() submits dbs->iov to io_func() in one shot, so callers
> must keep dbs->iov.niov below IOV_MAX or the host preadv()/pwritev()
> rejects the call.
>
> Break the accumulation loop when niov reaches IOV_MAX. The existing
> re-entry path resumes mapping and submits a follow-up chunk under
> the same AIOCB, so callers no longer carry that constraint.
>
> Suggested-by: Klaus Jensen <k.jensen@samsung.com>
> Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
> ---
> system/dma-helpers.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/system/dma-helpers.c b/system/dma-helpers.c
> index d4fd33a3d4c..613fa548669 100644
> --- a/system/dma-helpers.c
> +++ b/system/dma-helpers.c
> @@ -175,6 +175,9 @@ static void dma_blk_cb(void *opaque, int ret)
> dbs->sg_cur_byte = 0;
> ++dbs->sg_cur_index;
> }
> + if (dbs->iov.niov >= IOV_MAX) {
> + break;
> + }
> }
>
> if (dbs->iov.size == 0) {
>
> --
> 2.55.0
>
>
>
Acked-by: Jesper Wendel Devantier <foss@defmacro.it>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 5/7] hw/nvme: clamp mdts and zasl shifts
2026-08-19 15:24 [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path Daniel Gomez
` (3 preceding siblings ...)
2026-08-19 15:24 ` [PATCH v2 4/7] dma-helpers: chunk dma_blk_cb " Daniel Gomez
@ 2026-08-19 15:24 ` Daniel Gomez
2026-09-04 21:58 ` Jesper Wendel Devantier
2026-08-19 15:24 ` [PATCH v2 6/7] hw/nvme: drop DMA-path IOV_MAX guard Daniel Gomez
` (2 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Daniel Gomez @ 2026-08-19 15:24 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé, Keith Busch,
Klaus Jensen, Klaus Jensen, Jesper Devantier, qemu-block,
Daniel Gomez, Daniel Gomez, GOST
From: Daniel Gomez <da.gomez@samsung.com>
Once dma_blk_cb() chunks at IOV_MAX, a later patch drops the current
mdts 2 MiB cap, allowing mdts (and zasl, which may be as large as mdts)
to essentially be 32 or more. Ensure shifting doesn't go out of range
by adding a static inline helper that clamps to UINT64_MAX when mdts or
zasl shift operations exceed uint64_t width.
Fixes error with ubsan enabled and large mdts:
../hw/nvme/ctrl.c:1685:36: runtime error: shift exponent 32 is too large
for 32-bit type 'unsigned int'
Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
---
hw/nvme/ctrl.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 4893cf7e741..a24a674a4c5 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -1713,11 +1713,18 @@ static void nvme_clear_events(NvmeCtrl *n, uint8_t event_type)
}
}
+static inline uint64_t nvme_max_data_transfer_size(NvmeCtrl *n, uint8_t exp)
+{
+ unsigned shift = n->page_bits + exp;
+
+ return shift >= 64 ? UINT64_MAX : 1ULL << shift;
+}
+
static inline uint16_t nvme_check_mdts(NvmeCtrl *n, size_t len)
{
uint8_t mdts = n->params.mdts;
- if (mdts && len > n->page_size << mdts) {
+ if (mdts && len > nvme_max_data_transfer_size(n, mdts)) {
trace_pci_nvme_err_mdts(len);
return NVME_INVALID_FIELD | NVME_DNR;
}
@@ -3809,7 +3816,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
}
if (n->params.zasl &&
- data_size > (uint64_t)n->page_size << n->params.zasl) {
+ data_size > nvme_max_data_transfer_size(n, n->params.zasl)) {
trace_pci_nvme_err_zasl(data_size);
return NVME_INVALID_FIELD | NVME_DNR;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v2 5/7] hw/nvme: clamp mdts and zasl shifts
2026-08-19 15:24 ` [PATCH v2 5/7] hw/nvme: clamp mdts and zasl shifts Daniel Gomez
@ 2026-09-04 21:58 ` Jesper Wendel Devantier
0 siblings, 0 replies; 17+ messages in thread
From: Jesper Wendel Devantier @ 2026-09-04 21:58 UTC (permalink / raw)
To: Daniel Gomez
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé,
Keith Busch, Klaus Jensen, Klaus Jensen, qemu-block, Daniel Gomez,
GOST
On 2026-08-19T17:24:07+02:00, Daniel Gomez <da.gomez@kernel.org> wrote:
> From: Daniel Gomez <da.gomez@samsung.com>
>
> Once dma_blk_cb() chunks at IOV_MAX, a later patch drops the current
> mdts 2 MiB cap, allowing mdts (and zasl, which may be as large as mdts)
> to essentially be 32 or more. Ensure shifting doesn't go out of range
> by adding a static inline helper that clamps to UINT64_MAX when mdts or
> zasl shift operations exceed uint64_t width.
>
> Fixes error with ubsan enabled and large mdts:
> ../hw/nvme/ctrl.c:1685:36: runtime error: shift exponent 32 is too large
> for 32-bit type 'unsigned int'
>
> Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
> ---
> hw/nvme/ctrl.c | 11 +++++++++--
> 1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
> index 4893cf7e741..a24a674a4c5 100644
> --- a/hw/nvme/ctrl.c
> +++ b/hw/nvme/ctrl.c
> @@ -1713,11 +1713,18 @@ static void nvme_clear_events(NvmeCtrl *n, uint8_t event_type)
> }
> }
>
> +static inline uint64_t nvme_max_data_transfer_size(NvmeCtrl *n, uint8_t exp)
> +{
> + unsigned shift = n->page_bits + exp;
> +
> + return shift >= 64 ? UINT64_MAX : 1ULL << shift;
> +}
> +
> static inline uint16_t nvme_check_mdts(NvmeCtrl *n, size_t len)
> {
> uint8_t mdts = n->params.mdts;
>
> - if (mdts && len > n->page_size << mdts) {
> + if (mdts && len > nvme_max_data_transfer_size(n, mdts)) {
> trace_pci_nvme_err_mdts(len);
> return NVME_INVALID_FIELD | NVME_DNR;
> }
> @@ -3809,7 +3816,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
> }
>
> if (n->params.zasl &&
> - data_size > (uint64_t)n->page_size << n->params.zasl) {
> + data_size > nvme_max_data_transfer_size(n, n->params.zasl)) {
> trace_pci_nvme_err_zasl(data_size);
> return NVME_INVALID_FIELD | NVME_DNR;
> }
>
> --
> 2.55.0
>
>
>
Reviewed-by: Jesper Wendel Devantier <foss@defmacro.it>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 6/7] hw/nvme: drop DMA-path IOV_MAX guard
2026-08-19 15:24 [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path Daniel Gomez
` (4 preceding siblings ...)
2026-08-19 15:24 ` [PATCH v2 5/7] hw/nvme: clamp mdts and zasl shifts Daniel Gomez
@ 2026-08-19 15:24 ` Daniel Gomez
2026-09-04 21:59 ` Jesper Wendel Devantier
2026-08-19 15:24 ` [PATCH v2 7/7] hw/nvme: cap mdts for CMB/PMR-only Daniel Gomez
2026-09-04 22:00 ` [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path Keith Busch
7 siblings, 1 reply; 17+ messages in thread
From: Daniel Gomez @ 2026-08-19 15:24 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé, Keith Busch,
Klaus Jensen, Klaus Jensen, Jesper Devantier, qemu-block,
Daniel Gomez, Daniel Gomez, GOST
From: Daniel Gomez <da.gomez@samsung.com>
dma_blk_cb() now chunks at IOV_MAX, so the per-mapping cap on sg->qsg
in nvme_map_addr() is redundant. Drop it.
Suggested-by: Klaus Jensen <k.jensen@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
---
hw/nvme/ctrl.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index a24a674a4c5..7861d8f2521 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -872,10 +872,6 @@ static uint16_t nvme_map_addr(NvmeCtrl *n, NvmeSg *sg, hwaddr addr, size_t len)
return NVME_INVALID_USE_OF_CMB | NVME_DNR;
}
- if (sg->qsg.nsg + 1 > IOV_MAX) {
- goto max_mappings_exceeded;
- }
-
qemu_sglist_add(&sg->qsg, addr, len);
return NVME_SUCCESS;
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v2 6/7] hw/nvme: drop DMA-path IOV_MAX guard
2026-08-19 15:24 ` [PATCH v2 6/7] hw/nvme: drop DMA-path IOV_MAX guard Daniel Gomez
@ 2026-09-04 21:59 ` Jesper Wendel Devantier
0 siblings, 0 replies; 17+ messages in thread
From: Jesper Wendel Devantier @ 2026-09-04 21:59 UTC (permalink / raw)
To: Daniel Gomez
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé,
Keith Busch, Klaus Jensen, Klaus Jensen, qemu-block, Daniel Gomez,
GOST
On 2026-08-19T17:24:08+02:00, Daniel Gomez <da.gomez@kernel.org> wrote:
> From: Daniel Gomez <da.gomez@samsung.com>
>
> dma_blk_cb() now chunks at IOV_MAX, so the per-mapping cap on sg->qsg
> in nvme_map_addr() is redundant. Drop it.
>
> Suggested-by: Klaus Jensen <k.jensen@samsung.com>
> Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
> ---
> hw/nvme/ctrl.c | 4 ----
> 1 file changed, 4 deletions(-)
>
> diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
> index a24a674a4c5..7861d8f2521 100644
> --- a/hw/nvme/ctrl.c
> +++ b/hw/nvme/ctrl.c
> @@ -872,10 +872,6 @@ static uint16_t nvme_map_addr(NvmeCtrl *n, NvmeSg *sg, hwaddr addr, size_t len)
> return NVME_INVALID_USE_OF_CMB | NVME_DNR;
> }
>
> - if (sg->qsg.nsg + 1 > IOV_MAX) {
> - goto max_mappings_exceeded;
> - }
> -
> qemu_sglist_add(&sg->qsg, addr, len);
>
> return NVME_SUCCESS;
>
> --
> 2.55.0
>
>
>
Reviewed-by: Jesper Wendel Devantier <foss@defmacro.it>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 7/7] hw/nvme: cap mdts for CMB/PMR-only
2026-08-19 15:24 [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path Daniel Gomez
` (5 preceding siblings ...)
2026-08-19 15:24 ` [PATCH v2 6/7] hw/nvme: drop DMA-path IOV_MAX guard Daniel Gomez
@ 2026-08-19 15:24 ` Daniel Gomez
2026-09-04 22:02 ` Jesper Wendel Devantier
2026-09-04 22:00 ` [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path Keith Busch
7 siblings, 1 reply; 17+ messages in thread
From: Daniel Gomez @ 2026-08-19 15:24 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé, Keith Busch,
Klaus Jensen, Klaus Jensen, Jesper Devantier, qemu-block,
Daniel Gomez, Daniel Gomez, GOST
From: Daniel Gomez <da.gomez@samsung.com>
Commit 53493c1f83 ("hw/nvme: cap MDTS value for internal limitation")
capped MDTS so the worst-case PRP count would fit in IOV_MAX, leaving
transfers limited to 2 MiB.
Now that dma_blk_cb() can batch IOs up to IOV_MAX instead of limiting
to IOV_MAX, remove it, except for CMB/PMR-only where the limit still
applies.
In addition, fix UB when mdts >= 31 by dropping the shift and making the
cap explicit. Fixes error with ubsan:
../hw/nvme/ctrl.c:8638:33: runtime error: shift exponent 32 is too
large for 32-bit type 'int'
Suggested-by: Klaus Jensen <k.jensen@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
---
hw/nvme/ctrl.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 7861d8f2521..ff7e4a055b8 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -8802,8 +8802,10 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp)
host_memory_backend_set_mapped(n->pmr.dev, true);
}
- if (!n->params.mdts || ((1 << n->params.mdts) + 1) > IOV_MAX) {
- error_setg(errp, "mdts exceeds IOV_MAX");
+ /* 2^mdts + 1 must fit IOV_MAX */
+ if ((n->params.cmb_size_mb || n->pmr.dev) &&
+ (!n->params.mdts || (params->mdts > 9))) {
+ error_setg(errp, "mdts=%u is incompatible with CMB/PMR", params->mdts);
return false;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v2 7/7] hw/nvme: cap mdts for CMB/PMR-only
2026-08-19 15:24 ` [PATCH v2 7/7] hw/nvme: cap mdts for CMB/PMR-only Daniel Gomez
@ 2026-09-04 22:02 ` Jesper Wendel Devantier
2026-09-09 12:35 ` Daniel Gomez
0 siblings, 1 reply; 17+ messages in thread
From: Jesper Wendel Devantier @ 2026-09-04 22:02 UTC (permalink / raw)
To: Daniel Gomez
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé,
Keith Busch, Klaus Jensen, Klaus Jensen, qemu-block, Daniel Gomez,
GOST
On 2026-08-19T17:24:09+02:00, Daniel Gomez <da.gomez@kernel.org> wrote:
> From: Daniel Gomez <da.gomez@samsung.com>
>
> Commit 53493c1f83 ("hw/nvme: cap MDTS value for internal limitation")
> capped MDTS so the worst-case PRP count would fit in IOV_MAX, leaving
> transfers limited to 2 MiB.
>
> Now that dma_blk_cb() can batch IOs up to IOV_MAX instead of limiting
> to IOV_MAX, remove it, except for CMB/PMR-only where the limit still
> applies.
>
> In addition, fix UB when mdts >= 31 by dropping the shift and making the
> cap explicit. Fixes error with ubsan:
> ../hw/nvme/ctrl.c:8638:33: runtime error: shift exponent 32 is too
> large for 32-bit type 'int'
>
> Suggested-by: Klaus Jensen <k.jensen@samsung.com>
> Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
> ---
> hw/nvme/ctrl.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
> index 7861d8f2521..ff7e4a055b8 100644
> --- a/hw/nvme/ctrl.c
> +++ b/hw/nvme/ctrl.c
> @@ -8802,8 +8802,10 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp)
> host_memory_backend_set_mapped(n->pmr.dev, true);
> }
>
> - if (!n->params.mdts || ((1 << n->params.mdts) + 1) > IOV_MAX) {
> - error_setg(errp, "mdts exceeds IOV_MAX");
> + /* 2^mdts + 1 must fit IOV_MAX */
> + if ((n->params.cmb_size_mb || n->pmr.dev) &&
> + (!n->params.mdts || (params->mdts > 9))) {
> + error_setg(errp, "mdts=%u is incompatible with CMB/PMR", params->mdts);
> return false;
> }
>
>
> --
> 2.55.0
>
>
>
I generally agree except for a single nit-pick, if you will permit me.
(params->mdts > 9) -- it seems a bit indirect and could break if,
for some reason, IOV_MAX is different.
```c
static inline uint64_t nvme_mdts_max_iovs(uint8_t mdts)
{
return mdts >= 64 ? UINT64_MAX : (1ULL << mdts) + 1;
}
```
Then the check becomes
```c
if ((params->cmb_size_mb || n->pmr.dev) &&
(!params->mdts || nvme_mdts_max_iovs(params->mdts) > IOV_MAX)) {
error_setg(errp, "mdts=%u is incompatible with CMB/PMR", params->mdts);
return false;
}
```
?
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v2 7/7] hw/nvme: cap mdts for CMB/PMR-only
2026-09-04 22:02 ` Jesper Wendel Devantier
@ 2026-09-09 12:35 ` Daniel Gomez
0 siblings, 0 replies; 17+ messages in thread
From: Daniel Gomez @ 2026-09-09 12:35 UTC (permalink / raw)
To: Jesper Wendel Devantier
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé,
Keith Busch, Klaus Jensen, Klaus Jensen, qemu-block, Daniel Gomez,
GOST
On 2026-09-05T00:02:22+02:00, Jesper Wendel Devantier <foss@defmacro.it> wrote:
> On 2026-08-19T17:24:09+02:00, Daniel Gomez <da.gomez@kernel.org> wrote:
> ```c
> static inline uint64_t nvme_mdts_max_iovs(uint8_t mdts)
> {
> return mdts >= 64 ? UINT64_MAX : (1ULL << mdts) + 1;
> }
> ```
>
> Then the check becomes
> ```c
> if ((params->cmb_size_mb || n->pmr.dev) &&
> (!params->mdts || nvme_mdts_max_iovs(params->mdts) > IOV_MAX)) {
> error_setg(errp, "mdts=%u is incompatible with CMB/PMR", params->mdts);
> return false;
> }
> ```
>
> ?
Looks good. I'll apply it together with the commit 1 message update and
send a new series.
Thanks!
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path
2026-08-19 15:24 [PATCH v2 0/7] hw/nvme: lift IOV_MAX limit in DMA path Daniel Gomez
` (6 preceding siblings ...)
2026-08-19 15:24 ` [PATCH v2 7/7] hw/nvme: cap mdts for CMB/PMR-only Daniel Gomez
@ 2026-09-04 22:00 ` Keith Busch
7 siblings, 0 replies; 17+ messages in thread
From: Keith Busch @ 2026-09-04 22:00 UTC (permalink / raw)
To: Daniel Gomez
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé,
Klaus Jensen, Klaus Jensen, Jesper Devantier, qemu-block,
Daniel Gomez, GOST
On Wed, Aug 19, 2026 at 05:24:02PM +0200, Daniel Gomez wrote:
> Raise the QEMU NVMe controller's MDTS beyond the 2 MiB cap.
>
> Commit 53493c1f83 ("hw/nvme: cap MDTS value for internal limitation")
> needed the 2 MiB cap because dma_blk_io() submitted the full sglist
> in one preadv()/pwritev() call, which the host kernel rejects when the
> iovec count exceeds IOV_MAX. This series moves the IOV_MAX bound down to
> dma_blk_cb(), where we batch in IOV_MAX chunks when necessary.
Series looks good to me:
Reviewed-by: Keith Busch <kbusch@kernel.org>
^ permalink raw reply [flat|nested] 17+ messages in thread