[PATCH] nvme-rdma: fix NULL queue->ctrl dereference on reconnect
Christian Aurich Zanettini Martins
christian.aurichzm at gmail.com
Wed Oct 28 23:05:21 PDT 2026
Since commit 2a8513091d2f ("nvme-rdma: parallelize I/O queue allocation
and startup"), a reconnect that gets more I/O queues than before grows
the tag set before the new queues are set up. queue->ctrl is only set
by nvme_rdma_setup_io_queues(), so I/O submitted in between reaches
nvme_rdma_queue_rq() on a queue that was never allocated and
dereferences a NULL queue->ctrl:
BUG: kernel NULL pointer dereference, address: 000000000000034c
RIP: 0010:nvme_rdma_queue_rq+0x52/0xb90
Add nvme_rdma_init_queue() for the queue fields that do not depend on
the connection and call it for every queue when allocating the
controller. Such requests are then requeued like on any other queue
that is not live. queue_lock moves there as well, and
nvme_rdma_exit_queue() destroys it when the controller is freed.
Fixes: 2a8513091d2f ("nvme-rdma: parallelize I/O queue allocation and startup")
Cc: stable at vger.kernel.org
Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm at gmail.com>
---
Based on nvme-7.4 (89480d137bf0), also applies to mainline and v7.3.
Same approach as "nvme-tcp: fix NULL queue->ctrl dereference on
reconnect" in nvme-7.4.
pi_support, cmnd_capsule_len, queue_size and cm_done are still set on
each connect, as they depend on the RDMA device or on the connect.
Reproduced in QEMU with nvmet-rdma over rxe and
CONFIG_NVME_MULTIPATH=n: connect with attr_qid_max=2, keep direct reads
running on the CPUs that map to the new hw queues, then raise
attr_qid_max to 4. The window is short, so the readers run at
SCHED_FIFO with nvme-wq pinned to their CPUs. Without the patch the
host oopses within two tries in all six boots. With it, 64 of 64 tries
go live and 30 resets under I/O pass, with and without lockdep.
drivers/nvme/host/rdma.c | 36 +++++++++++++++++++++++++-----------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 07216890f75c..79ef8927d3f0 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -90,6 +90,9 @@ enum nvme_rdma_queue_flags {
NVME_RDMA_Q_TR_READY = 2,
};
+#define NVME_RDMA_OPT_NR_QUEUES(opts) ((opts)->nr_io_queues + \
+ (opts)->nr_write_queues + (opts)->nr_poll_queues + 1)
+
struct nvme_rdma_queue {
struct nvme_rdma_qe *rsp_ring;
int queue_size;
@@ -576,6 +579,19 @@ static int nvme_rdma_create_queue_ib(struct nvme_rdma_queue *queue)
return ret;
}
+static void nvme_rdma_init_queue(struct nvme_rdma_ctrl *ctrl, int idx)
+{
+ struct nvme_rdma_queue *queue = &ctrl->queues[idx];
+
+ queue->ctrl = ctrl;
+ mutex_init(&queue->queue_lock);
+}
+
+static void nvme_rdma_exit_queue(struct nvme_rdma_ctrl *ctrl, int idx)
+{
+ mutex_destroy(&ctrl->queues[idx].queue_lock);
+}
+
static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
{
struct nvme_rdma_ctrl *ctrl = queue->ctrl;
@@ -583,7 +599,6 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
struct sockaddr *src_addr = NULL;
int ret;
- mutex_init(&queue->queue_lock);
if (idx && ctrl->ctrl.max_integrity_segments)
queue->pi_support = true;
else
@@ -600,8 +615,7 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
if (IS_ERR(queue->cm_id)) {
dev_info(ctrl->ctrl.device,
"failed to create CM ID: %ld\n", PTR_ERR(queue->cm_id));
- ret = PTR_ERR(queue->cm_id);
- goto out_destroy_mutex;
+ return PTR_ERR(queue->cm_id);
}
if (ctrl->ctrl.opts->mask & NVMF_OPT_HOST_TRADDR)
@@ -631,8 +645,6 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
out_destroy_cm_id:
rdma_destroy_id(queue->cm_id);
nvme_rdma_destroy_queue_ib(queue);
-out_destroy_mutex:
- mutex_destroy(&queue->queue_lock);
return ret;
}
@@ -660,7 +672,6 @@ static void nvme_rdma_free_queue(struct nvme_rdma_queue *queue)
rdma_destroy_id(queue->cm_id);
nvme_rdma_destroy_queue_ib(queue);
- mutex_destroy(&queue->queue_lock);
}
static void nvme_rdma_free_io_queues(struct nvme_rdma_ctrl *ctrl)
@@ -735,7 +746,6 @@ static int nvme_rdma_setup_io_queues(struct nvme_rdma_ctrl *ctrl,
for (i = 0; i < nr_queues; i++) {
struct nvme_rdma_queue *queue = &ctrl->queues[first + i];
- queue->ctrl = ctrl;
queue->queue_size = queue_size;
ctxs[i].queue = queue;
@@ -798,7 +808,6 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl,
bool pi_capable = false;
int error;
- ctrl->queues[0].ctrl = ctrl;
ctrl->queues[0].queue_size = NVME_AQ_DEPTH;
error = nvme_rdma_alloc_queue(&ctrl->queues[0]);
if (error)
@@ -1001,6 +1010,7 @@ static void nvme_rdma_stop_ctrl(struct nvme_ctrl *nctrl)
static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl);
+ int i, nr_queues = NVME_RDMA_OPT_NR_QUEUES(nctrl->opts);
if (list_empty_careful(&ctrl->list))
goto free_ctrl;
@@ -1011,6 +1021,8 @@ static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl)
nvmf_free_options(nctrl->opts);
free_ctrl:
+ for (i = 0; i < nr_queues; i++)
+ nvme_rdma_exit_queue(ctrl, i);
kfree(ctrl->queues);
kfree(ctrl);
}
@@ -2284,7 +2296,7 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
struct nvmf_ctrl_options *opts)
{
struct nvme_rdma_ctrl *ctrl;
- int ret;
+ int ret, i;
ctrl = kzalloc_obj(*ctrl);
if (!ctrl)
@@ -2333,8 +2345,7 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
INIT_WORK(&ctrl->err_work, nvme_rdma_error_recovery_work);
INIT_WORK(&ctrl->ctrl.reset_work, nvme_rdma_reset_ctrl_work);
- ctrl->ctrl.queue_count = opts->nr_io_queues + opts->nr_write_queues +
- opts->nr_poll_queues + 1;
+ ctrl->ctrl.queue_count = NVME_RDMA_OPT_NR_QUEUES(opts);
ctrl->ctrl.sqsize = opts->queue_size - 1;
ctrl->ctrl.kato = opts->kato;
@@ -2343,6 +2354,9 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
if (!ctrl->queues)
goto out_free_ctrl;
+ for (i = 0; i < ctrl->ctrl.queue_count; i++)
+ nvme_rdma_init_queue(ctrl, i);
+
ret = nvme_init_ctrl(&ctrl->ctrl, dev, &nvme_rdma_ctrl_ops,
0 /* no quirks, we're perfect! */);
if (ret)
base-commit: 89480d137bf0d943a82c64f86ae2407a15276b5e
--
2.43.0
More information about the Linux-nvme
mailing list