* [PULL 1/4] hw/nvme: drop AER requests without aiocb in nvme_del_sq()
2026-08-03 22:44 [PULL 0/4] hw/nvme queue Klaus Jensen
@ 2026-08-03 22:44 ` Klaus Jensen
2026-08-03 22:44 ` [PULL 2/4] hw/nvme: factor out nvme_sq_cancel_inflight() Klaus Jensen
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Klaus Jensen @ 2026-08-03 22:44 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Minwoo Im, qemu-stable, Minwoo Im, Klaus Jensen,
Keith Busch, Klaus Jensen, Jesper Devantier, qemu-block
From: Minwoo Im <minwoo.im.dev@gmail.com>
nvme_del_sq() asserted r->aiocb was always set when canceling a
queue's inflight requests. A pending Async Event Request has no
aiocb (nvme_aer() parks it without issuing any block I/O), so
deleting a queue with an outstanding AER trips the assert instead of
just dropping the request.
Cc: qemu-stable@nongnu.org
Signed-off-by: Minwoo Im <minwoo.im@samsung.com>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
hw/nvme/ctrl.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index bd6ad64b2000..e3eadf3d1dd0 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -4862,12 +4862,14 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeRequest *req)
sq = n->sq[qid];
while (!QTAILQ_EMPTY(&sq->out_req_list)) {
r = QTAILQ_FIRST(&sq->out_req_list);
- assert(r->aiocb);
r->status = NVME_CMD_ABORT_SQ_DEL;
- blk_aio_cancel(r->aiocb);
- }
- assert(QTAILQ_EMPTY(&sq->out_req_list));
+ if (r->aiocb) {
+ blk_aio_cancel(r->aiocb);
+ } else {
+ QTAILQ_REMOVE(&sq->out_req_list, r, entry);
+ }
+ }
if (!nvme_check_cqid(n, sq->cqid)) {
cq = n->cq[sq->cqid];
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PULL 2/4] hw/nvme: factor out nvme_sq_cancel_inflight()
2026-08-03 22:44 [PULL 0/4] hw/nvme queue Klaus Jensen
2026-08-03 22:44 ` [PULL 1/4] hw/nvme: drop AER requests without aiocb in nvme_del_sq() Klaus Jensen
@ 2026-08-03 22:44 ` Klaus Jensen
2026-08-03 22:44 ` [PULL 3/4] hw/nvme: cancel inflight requests on controller reset Klaus Jensen
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Klaus Jensen @ 2026-08-03 22:44 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Minwoo Im, qemu-stable, Minwoo Im, Klaus Jensen,
Keith Busch, Klaus Jensen, Jesper Devantier, qemu-block
From: Minwoo Im <minwoo.im.dev@gmail.com>
Factor the cancel-and-wait loop used by nvme_del_sq() into
nvme_sq_cancel_inflight(), so it can be reused to drain queues on
controller reset.
Cc: qemu-stable@nongnu.org
Signed-off-by: Minwoo Im <minwoo.im@samsung.com>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
hw/nvme/ctrl.c | 31 +++++++++++++++++++++----------
1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index e3eadf3d1dd0..e355c256a561 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -4826,6 +4826,26 @@ static int nvme_init_sq_ioeventfd(NvmeSQueue *sq)
return 0;
}
+/*
+ * A pending Async Event Request has no aiocb (nvme_aer() parks it without
+ * issuing any block I/O), so there is nothing to cancel; just drop it.
+ */
+static void nvme_sq_cancel_inflight(NvmeSQueue *sq, uint16_t status)
+{
+ NvmeRequest *r;
+
+ while (!QTAILQ_EMPTY(&sq->out_req_list)) {
+ r = QTAILQ_FIRST(&sq->out_req_list);
+ r->status = status;
+
+ if (r->aiocb) {
+ blk_aio_cancel(r->aiocb);
+ } else {
+ QTAILQ_REMOVE(&sq->out_req_list, r, entry);
+ }
+ }
+}
+
static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n)
{
uint16_t offset = sq->sqid << 3;
@@ -4860,16 +4880,7 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeRequest *req)
trace_pci_nvme_del_sq(qid);
sq = n->sq[qid];
- while (!QTAILQ_EMPTY(&sq->out_req_list)) {
- r = QTAILQ_FIRST(&sq->out_req_list);
- r->status = NVME_CMD_ABORT_SQ_DEL;
-
- if (r->aiocb) {
- blk_aio_cancel(r->aiocb);
- } else {
- QTAILQ_REMOVE(&sq->out_req_list, r, entry);
- }
- }
+ nvme_sq_cancel_inflight(sq, NVME_CMD_ABORT_SQ_DEL);
if (!nvme_check_cqid(n, sq->cqid)) {
cq = n->cq[sq->cqid];
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PULL 3/4] hw/nvme: cancel inflight requests on controller reset
2026-08-03 22:44 [PULL 0/4] hw/nvme queue Klaus Jensen
2026-08-03 22:44 ` [PULL 1/4] hw/nvme: drop AER requests without aiocb in nvme_del_sq() Klaus Jensen
2026-08-03 22:44 ` [PULL 2/4] hw/nvme: factor out nvme_sq_cancel_inflight() Klaus Jensen
@ 2026-08-03 22:44 ` Klaus Jensen
2026-08-03 22:44 ` [PULL 4/4] hw/nvme: fix leak on copy ranges Klaus Jensen
2026-08-05 0:54 ` [PULL 0/4] hw/nvme queue Stefan Hajnoczi
4 siblings, 0 replies; 6+ messages in thread
From: Klaus Jensen @ 2026-08-03 22:44 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Minwoo Im, qemu-stable, Minwoo Im, Klaus Jensen,
Keith Busch, Klaus Jensen, Jesper Devantier, qemu-block
From: Minwoo Im <minwoo.im.dev@gmail.com>
nvme_ctrl_reset() freed every SQ/CQ right after nvme_ns_drain(), which
only waits out requests on a per-namespace BlockBackend. That is safe
as long as the guest first tore down I/O queues gracefully (Delete
I/O SQ/CQ), since nvme_del_sq() already cancels and waits for
anything left on a queue before freeing it.
A reset that happens without that graceful sequence first (e.g. an
abrupt/asynchronous controller reset) can still have commands
inflight on blk_aio_*. Freeing sq/cq before those complete leaves
their completion callbacks (nvme_rw_cb() and friends) to run against
already-freed NvmeRequest/NvmeSQueue/NvmeCQueue memory via
nvme_enqueue_req_completion(), causing a use-after-free/segfault.
Run nvme_sq_cancel_inflight() over every queue in nvme_ctrl_reset()
before the free loops, so no in-flight blk_aio_* callback can fire
after sq/cq memory is freed.
Cc: qemu-stable@nongnu.org
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3398
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3883
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4068
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4072
Signed-off-by: Minwoo Im <minwoo.im@samsung.com>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
hw/nvme/ctrl.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index e355c256a561..8461b6fbdeae 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -8040,6 +8040,18 @@ static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst)
nvme_ns_drain(ns);
}
+ /*
+ * Cancel and wait out every inflight command on every queue first. A
+ * reset is not required to be preceded by the guest's graceful
+ * Delete I/O SQ/CQ sequence, so sq/cq must not be freed below while a
+ * blk_aio_* completion for them could still be in flight.
+ */
+ for (i = 0; i < n->num_queues; i++) {
+ if (n->sq[i] != NULL) {
+ nvme_sq_cancel_inflight(n->sq[i], NVME_CMD_ABORT_SQ_DEL);
+ }
+ }
+
for (i = 0; i < n->num_queues; i++) {
if (n->sq[i] != NULL) {
nvme_free_sq(n->sq[i], n);
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PULL 4/4] hw/nvme: fix leak on copy ranges
2026-08-03 22:44 [PULL 0/4] hw/nvme queue Klaus Jensen
` (2 preceding siblings ...)
2026-08-03 22:44 ` [PULL 3/4] hw/nvme: cancel inflight requests on controller reset Klaus Jensen
@ 2026-08-03 22:44 ` Klaus Jensen
2026-08-05 0:54 ` [PULL 0/4] hw/nvme queue Stefan Hajnoczi
4 siblings, 0 replies; 6+ messages in thread
From: Klaus Jensen @ 2026-08-03 22:44 UTC (permalink / raw)
To: qemu-devel
Cc: Peter Maydell, Klaus Jensen, qemu-stable, Jesper Wendel Devantier,
Keith Busch, Klaus Jensen, qemu-block
From: Klaus Jensen <k.jensen@samsung.com>
The buffer holding the ranges for the copy command is not correctly
deallocated.
Fix this.
Cc: qemu-stable@nongnu.org
Link: https://gitlab.com/qemu-project/qemu/-/work_items/4072
Fixes: 796d20681d9b ("hw/nvme: reimplement the copy command to allow aio cancellation")
Reviewed-by: Jesper Wendel Devantier <foss@defmacro.it>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
hw/nvme/ctrl.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 8461b6fbdeae..4893cf7e7418 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -2811,6 +2811,7 @@ static void nvme_copy_done(NvmeCopyAIOCB *iocb)
qemu_iovec_destroy(&iocb->iov);
g_free(iocb->bounce);
+ g_free(iocb->ranges);
if (iocb->ret < 0) {
block_acct_failed(stats, &iocb->acct.read);
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PULL 0/4] hw/nvme queue
2026-08-03 22:44 [PULL 0/4] hw/nvme queue Klaus Jensen
` (3 preceding siblings ...)
2026-08-03 22:44 ` [PULL 4/4] hw/nvme: fix leak on copy ranges Klaus Jensen
@ 2026-08-05 0:54 ` Stefan Hajnoczi
4 siblings, 0 replies; 6+ messages in thread
From: Stefan Hajnoczi @ 2026-08-05 0:54 UTC (permalink / raw)
To: Klaus Jensen; +Cc: qemu-devel, Peter Maydell, Klaus Jensen
[-- Attachment #1: Type: text/plain, Size: 116 bytes --]
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread