* [PATCH] nvme: bump genctr when cancelling a request
@ 2026-09-13 12:41 Mateusz Nowicki
2026-09-14 14:39 ` Keith Busch
0 siblings, 1 reply; 3+ messages in thread
From: Mateusz Nowicki @ 2026-09-13 12:41 UTC (permalink / raw)
To: kbusch, axboe, hch, sagi; +Cc: linux-nvme, linux-kernel, Mateusz Nowicki
nvme_find_rq() rejects a completion whose genctr does not match the
request, so a command completed once cannot be completed again by a
stale CQE. nvme_try_complete_req() bumps genctr for that reason.
nvme_cancel_request() completes the command too, but leaves genctr
alone. A CQE the controller posts for the cancelled command later still
matches and gets applied.
The cancel path is taken when the host stopped waiting for the
controller: CSTS.CFS set (dead path in nvme_dev_disable(), no CC.EN=0,
no wait), or CSTS.RDY not cleared within CAP.TO. Nothing stops the
controller from posting completions after that, and the second
nvme_dev_disable() from nvme_reset_work() reaps them in
nvme_reap_pending_cqes().
The cancelled request sits on the requeue list after the RETRY
disposition. The late CQE ends and frees it from there, and the next
dispatch hits req->mq_hctx == NULL:
BUG: kernel NULL pointer dereference, address: 0000000000000158
RIP: nvme_prep_rq+0x1a6
nvme_queue_rq
blk_mq_dispatch_rq_list
__blk_mq_sched_dispatch_requests
blk_mq_run_work_fn
Bump genctr in nvme_cancel_request() like a real completion does.
Reproduced with vnvme (https://github.com/Mateusz-Nowicki-Embedded/vnvme),
a virtual NVMe endpoint that holds completions back under I/O, sets
CSTS.CFS, and releases them after the tagset was cancelled.
Signed-off-by: Mateusz Nowicki <mateusz.nowicki@posteo.net>
---
drivers/nvme/host/core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -532,6 +532,8 @@ bool nvme_cancel_request(struct request *req, void *data)
if (blk_mq_rq_state(req) != MQ_RQ_IN_FLIGHT)
return true;
+ if (!(nvme_req(req)->ctrl->quirks & NVME_QUIRK_SKIP_CID_GEN))
+ nvme_req(req)->genctr++;
nvme_req(req)->status = NVME_SC_HOST_ABORTED_CMD;
nvme_req(req)->flags |= NVME_REQ_CANCELLED;
blk_mq_complete_request(req);
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] nvme: bump genctr when cancelling a request
2026-09-13 12:41 [PATCH] nvme: bump genctr when cancelling a request Mateusz Nowicki
@ 2026-09-14 14:39 ` Keith Busch
2026-09-14 18:49 ` Mateusz Nowicki
0 siblings, 1 reply; 3+ messages in thread
From: Keith Busch @ 2026-09-14 14:39 UTC (permalink / raw)
To: Mateusz Nowicki; +Cc: axboe, hch, sagi, linux-nvme, linux-kernel
On Sun, Sep 13, 2026 at 12:41:01PM +0000, Mateusz Nowicki wrote:
> The cancel path is taken when the host stopped waiting for the
> controller: CSTS.CFS set (dead path in nvme_dev_disable(), no CC.EN=0,
> no wait), or CSTS.RDY not cleared within CAP.TO. Nothing stops the
> controller from posting completions after that, and the second
> nvme_dev_disable() from nvme_reset_work() reaps them in
> nvme_reap_pending_cqes().
I think it was supposed to be that we don't cancel a request while it's
still possible to see a natural completion. So with that in mind,
perhaps the call to "nvme_disable_ctrl" should be called unconditionally
instead of only on a live controller.
The sequence you're describing is weird, though. Your controller
has fatal status, the driver hasn't done anything yet to recover the
device, so why were there no completions available the first go around,
then completions became available on the second? The controller
shouldn't have done anything in that time, nor should it have been able
to since Bus Master Enable was off.
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] nvme: bump genctr when cancelling a request
2026-09-14 14:39 ` Keith Busch
@ 2026-09-14 18:49 ` Mateusz Nowicki
0 siblings, 0 replies; 3+ messages in thread
From: Mateusz Nowicki @ 2026-09-14 18:49 UTC (permalink / raw)
To: Keith Busch, Mateusz Nowicki; +Cc: axboe, hch, sagi, linux-nvme, linux-kernel
> The sequence you're describing is weird, though. Your controller
> has fatal status, the driver hasn't done anything yet to recover the
> device, so why were there no completions available the first go around,
> then completions became available on the second? The controller
> shouldn't have done anything in that time, nor should it have been able
> to since Bus Master Enable was off.
Thanks Keith for the feedback. You're right, vnvme (a virtual NVMe
endpoint I use to create corner cases that are hard to reproduce with
publicly available drives, like delayed completions) ignored BME, and
that is what produced the late completions.
I have seen firmware that ignores BME too. Unlikely in the field, but
the question is whether the driver wants to be resilient to that class
of firmware bug. If not, I'll drop this and can test the unconditional
nvme_disable_ctrl() variant you suggested instead.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-14 18:49 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-13 12:41 [PATCH] nvme: bump genctr when cancelling a request Mateusz Nowicki
2026-09-14 14:39 ` Keith Busch
2026-09-14 18:49 ` Mateusz Nowicki
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®