* [PATCH v3 0/4] nvme-fc: fix blktests nvme/041
@ 2025-08-29 15:37 Daniel Wagner
2025-08-29 15:37 ` [PATCH v3 1/4] nvme-fabrics: introduce ref-counting for nvmf_ctrl_options Daniel Wagner
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Daniel Wagner @ 2025-08-29 15:37 UTC (permalink / raw)
To: Keith Busch, Christoph Hellwig, Sagi Grimberg, James Smart
Cc: Shinichiro Kawasaki, Hannes Reinecke, linux-nvme, linux-kernel,
Daniel Wagner
Another attempt to get the nvme/041 fixed. I've decided to make the
synchronous connect an opt-in feature, so that we don't break existing
users. I need to do some changes for libnvme and nvme-cli so all works
fine when users update either the userspace or kernel.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
v3:
- rebased to current master
- use explicit initial connect flag to distiguish between retry and
initial connect. Necessary to keep nvme/061 working.
- added 'nvme-fc: refactore nvme_fc_reconnect_or_delete'
- updated commit messages
v2:
- renamed flag to connect_async, default false
- add info to commit message why nvme-fc is different
- merged connect_async with 'nvme-fc: wait for
initial connect attempt to finish'
- https://lore.kernel.org/all/20240221132404.6311-1-dwagner@suse.de/
v1:
- renamed 'nvme-fc: redesign locking and refcounting'
to 'nvme-fc: reorder ctrl ref counting and cleanup code path'
- testing with scsi/nvme dev_loss_tmo on real hw
- removed rport ref counting part
- collected RB tags
- https://lore.kernel.org/linux-nvme/20240219131531.15134-1-dwagner@suse.de/
v0:
- initial version
- https://lore.kernel.org/linux-nvme/20240216084526.14133-1-dwagner@suse.de/
---
Daniel Wagner (4):
nvme-fabrics: introduce ref-counting for nvmf_ctrl_options
nvme-fc: reorganize ctrl ref-counting and cleanup code
nvme-fc: refactore nvme_fc_reconnect_or_delete
nvme-fc: wait for initial connect attempt to finish
drivers/nvme/host/fabrics.c | 40 ++++++++-
drivers/nvme/host/fabrics.h | 9 +-
drivers/nvme/host/fc.c | 212 ++++++++++++++++++++++----------------------
drivers/nvme/host/rdma.c | 18 ++--
drivers/nvme/host/tcp.c | 21 +++--
drivers/nvme/target/loop.c | 19 ++--
6 files changed, 192 insertions(+), 127 deletions(-)
---
base-commit: c17b750b3ad9f45f2b6f7e6f7f4679844244f0b9
change-id: 20250828-nvme-fc-sync-bbc73a36255d
Best regards,
--
Daniel Wagner <wagi@kernel.org>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 1/4] nvme-fabrics: introduce ref-counting for nvmf_ctrl_options
2025-08-29 15:37 [PATCH v3 0/4] nvme-fc: fix blktests nvme/041 Daniel Wagner
@ 2025-08-29 15:37 ` Daniel Wagner
2025-08-29 15:37 ` [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code Daniel Wagner
` (2 subsequent siblings)
3 siblings, 0 replies; 11+ messages in thread
From: Daniel Wagner @ 2025-08-29 15:37 UTC (permalink / raw)
To: Keith Busch, Christoph Hellwig, Sagi Grimberg, James Smart
Cc: Shinichiro Kawasaki, Hannes Reinecke, linux-nvme, linux-kernel,
Daniel Wagner
nvmf_create_ctrl allocates the options object and frees it when the
create_ctrl function fails. TCP and RDMA are doing the initial connect
synchronous, while the FC transport offloads the connect attempt to a
workqueue. Thus, nvme_fc_init_ctrl has to take owner ship in the error
case to avoid double free.
But there are several places in the nvme core code which want to access
the options pointer in the cleanup path and this is racing with the FC
reconnect or delete state machine. E.g
nvme_ctrl_free
nvme_auth_free
ctrl_max_dhchaps
ctrl->opts->dhchaps
nvme_do_delete_ctrl
nvmf_ctrl_subsysnqn
ctrl->opts->subsysnqn
There are also a few workaround in the existing code to avoid
de-referencing a NULL pointer:
nvme_class_uevent
if (opts) {
add_uevent_var(env, "NVME_TRADDR");
add_uevent_var(env, "NVME_TRSVID");
add_uevent_var(env, "NVME_HOST_TRADDR");
add_uevent_var(env, "NVME_HOST_IFACE");
}
Furthermore, user space is able to trigger a crash because
nvmf_ctrl_options are exposed to sysfs and expects ctrl->opts to be
valid always:
nvme_sysfs_show_hostnqn
ctrl->opts->host->nqn
nvme_sysfs_show_hostid
ctrl->opts->host->id
By introducing ref-counting for the options, it decouples the options
lifetime from the controller objects, which avoids all the races
mentioned above.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/host/fabrics.c | 22 +++++++++++++++++++---
drivers/nvme/host/fabrics.h | 6 +++++-
drivers/nvme/host/fc.c | 14 +++++++++-----
drivers/nvme/host/rdma.c | 18 +++++++++++++-----
drivers/nvme/host/tcp.c | 21 ++++++++++++++-------
drivers/nvme/target/loop.c | 19 +++++++++++++------
6 files changed, 73 insertions(+), 27 deletions(-)
diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index 2e58a7ce109052ecdda433f13162911459a81fb3..cb9311c399856de54a2e4d41d41d295dd1aef31e 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -1279,8 +1279,11 @@ static int nvmf_check_allowed_opts(struct nvmf_ctrl_options *opts,
return 0;
}
-void nvmf_free_options(struct nvmf_ctrl_options *opts)
+static void nvmf_free_options(struct kref *ref)
{
+ struct nvmf_ctrl_options *opts =
+ container_of(ref, struct nvmf_ctrl_options, ref);
+
nvmf_host_put(opts->host);
key_put(opts->keyring);
key_put(opts->tls_key);
@@ -1294,7 +1297,18 @@ void nvmf_free_options(struct nvmf_ctrl_options *opts)
kfree(opts->dhchap_ctrl_secret);
kfree(opts);
}
-EXPORT_SYMBOL_GPL(nvmf_free_options);
+
+int nvmf_ctrl_options_get(struct nvmf_ctrl_options *opts)
+{
+ return kref_get_unless_zero(&opts->ref);
+}
+EXPORT_SYMBOL_GPL(nvmf_ctrl_options_get);
+
+void nvmf_ctrl_options_put(struct nvmf_ctrl_options *opts)
+{
+ kref_put(&opts->ref, nvmf_free_options);
+}
+EXPORT_SYMBOL_GPL(nvmf_ctrl_options_put);
#define NVMF_REQUIRED_OPTS (NVMF_OPT_TRANSPORT | NVMF_OPT_NQN)
#define NVMF_ALLOWED_OPTS (NVMF_OPT_QUEUE_SIZE | NVMF_OPT_NR_IO_QUEUES | \
@@ -1316,6 +1330,8 @@ nvmf_create_ctrl(struct device *dev, const char *buf)
if (!opts)
return ERR_PTR(-ENOMEM);
+ kref_init(&opts->ref);
+
ret = nvmf_parse_options(opts, buf);
if (ret)
goto out_free_opts;
@@ -1371,7 +1387,7 @@ nvmf_create_ctrl(struct device *dev, const char *buf)
out_unlock:
up_read(&nvmf_transports_rwsem);
out_free_opts:
- nvmf_free_options(opts);
+ nvmf_ctrl_options_put(opts);
return ERR_PTR(ret);
}
diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
index 1b58ee7d0dcee420a1cf7d1a4b8e7e558b69ecae..bcc1d5f97ee5a03852830bf3ba0a15f82c7c2c03 100644
--- a/drivers/nvme/host/fabrics.h
+++ b/drivers/nvme/host/fabrics.h
@@ -72,6 +72,7 @@ enum {
/**
* struct nvmf_ctrl_options - Used to hold the options specified
* with the parsing opts enum.
+ * @ref: for reference count of the data structure
* @mask: Used by the fabrics library to parse through sysfs options
* on adding a NVMe controller.
* @max_reconnects: maximum number of allowed reconnect attempts before removing
@@ -112,6 +113,7 @@ enum {
* @fast_io_fail_tmo: Fast I/O fail timeout in seconds
*/
struct nvmf_ctrl_options {
+ struct kref ref;
unsigned mask;
int max_reconnects;
char *transport;
@@ -142,6 +144,9 @@ struct nvmf_ctrl_options {
int fast_io_fail_tmo;
};
+int nvmf_ctrl_options_get(struct nvmf_ctrl_options *opts);
+void nvmf_ctrl_options_put(struct nvmf_ctrl_options *opts);
+
/*
* struct nvmf_transport_ops - used to register a specific
* fabric implementation of NVMe fabrics.
@@ -225,7 +230,6 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl);
int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid);
int nvmf_register_transport(struct nvmf_transport_ops *ops);
void nvmf_unregister_transport(struct nvmf_transport_ops *ops);
-void nvmf_free_options(struct nvmf_ctrl_options *opts);
int nvmf_get_address(struct nvme_ctrl *ctrl, char *buf, int size);
bool nvmf_should_reconnect(struct nvme_ctrl *ctrl, int status);
bool nvmf_ip_options_match(struct nvme_ctrl *ctrl,
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 3e12d4683ac72f9ef9c6dff64d22d5d97e6f3d60..db5dbbf6aee21db45e91ea6f0c122681b9bdaf6f 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -2372,8 +2372,7 @@ nvme_fc_ctrl_free(struct kref *ref)
nvme_fc_rport_put(ctrl->rport);
ida_free(&nvme_fc_ctrl_cnt, ctrl->cnum);
- if (ctrl->ctrl.opts)
- nvmf_free_options(ctrl->ctrl.opts);
+ nvmf_ctrl_options_put(ctrl->ctrl.opts);
kfree(ctrl);
}
@@ -3437,10 +3436,15 @@ nvme_fc_alloc_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
goto out_fail;
}
+ if (!nvmf_ctrl_options_get(opts)) {
+ ret = -ENOLCK;
+ goto out_free_ctrl;
+ }
+
idx = ida_alloc(&nvme_fc_ctrl_cnt, GFP_KERNEL);
if (idx < 0) {
ret = -ENOSPC;
- goto out_free_ctrl;
+ goto out_free_opts;
}
/*
@@ -3512,6 +3516,8 @@ nvme_fc_alloc_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
out_free_ida:
put_device(ctrl->dev);
ida_free(&nvme_fc_ctrl_cnt, ctrl->cnum);
+out_free_opts:
+ nvmf_ctrl_options_put(opts);
out_free_ctrl:
kfree(ctrl);
out_fail:
@@ -3573,8 +3579,6 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
cancel_work_sync(&ctrl->ctrl.reset_work);
cancel_delayed_work_sync(&ctrl->connect_work);
- ctrl->ctrl.opts = NULL;
-
/* initiate nvme ctrl ref counting teardown */
nvme_uninit_ctrl(&ctrl->ctrl);
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 190a4cfa8a5ee2e6b97a5a1b304c1338dcd748fa..f0692f1132aaef2f8f8c30e9d7824e4bf1f91117 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -976,8 +976,8 @@ static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl)
list_del(&ctrl->list);
mutex_unlock(&nvme_rdma_ctrl_mutex);
- nvmf_free_options(nctrl->opts);
free_ctrl:
+ nvmf_ctrl_options_put(nctrl->opts);
kfree(ctrl->queues);
kfree(ctrl);
}
@@ -2242,6 +2242,12 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return ERR_PTR(-ENOMEM);
+
+ if (!nvmf_ctrl_options_get(opts)) {
+ ret = -ENODEV;
+ goto out_free_ctrl;
+ }
+
ctrl->ctrl.opts = opts;
INIT_LIST_HEAD(&ctrl->list);
@@ -2250,7 +2256,7 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
kstrdup(__stringify(NVME_RDMA_IP_PORT), GFP_KERNEL);
if (!opts->trsvcid) {
ret = -ENOMEM;
- goto out_free_ctrl;
+ goto out_free_opts;
}
opts->mask |= NVMF_OPT_TRSVCID;
}
@@ -2269,13 +2275,13 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
if (ret) {
pr_err("malformed src address passed: %s\n",
opts->host_traddr);
- goto out_free_ctrl;
+ goto out_free_opts;
}
}
if (!opts->duplicate_connect && nvme_rdma_existing_controller(opts)) {
ret = -EALREADY;
- goto out_free_ctrl;
+ goto out_free_opts;
}
INIT_DELAYED_WORK(&ctrl->reconnect_work,
@@ -2292,7 +2298,7 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
ctrl->queues = kcalloc(ctrl->ctrl.queue_count, sizeof(*ctrl->queues),
GFP_KERNEL);
if (!ctrl->queues)
- goto out_free_ctrl;
+ goto out_free_opts;
ret = nvme_init_ctrl(&ctrl->ctrl, dev, &nvme_rdma_ctrl_ops,
0 /* no quirks, we're perfect! */);
@@ -2303,6 +2309,8 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
out_kfree_queues:
kfree(ctrl->queues);
+out_free_opts:
+ nvmf_ctrl_options_put(opts);
out_free_ctrl:
kfree(ctrl);
return ERR_PTR(ret);
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index c0fe8cfb7229e16af41286f5edb01586ad617291..df445b4da39ef134c605ca7ef1ccbeda99a23862 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -2554,8 +2554,8 @@ static void nvme_tcp_free_ctrl(struct nvme_ctrl *nctrl)
list_del(&ctrl->list);
mutex_unlock(&nvme_tcp_ctrl_mutex);
- nvmf_free_options(nctrl->opts);
free_ctrl:
+ nvmf_ctrl_options_put(nctrl->opts);
kfree(ctrl->queues);
kfree(ctrl);
}
@@ -2888,6 +2888,11 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
if (!ctrl)
return ERR_PTR(-ENOMEM);
+ if (!nvmf_ctrl_options_get(opts)) {
+ ret = -ENODEV;
+ goto out_free_ctrl;
+ }
+
INIT_LIST_HEAD(&ctrl->list);
ctrl->ctrl.opts = opts;
ctrl->ctrl.queue_count = opts->nr_io_queues + opts->nr_write_queues +
@@ -2905,7 +2910,7 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
kstrdup(__stringify(NVME_TCP_DISC_PORT), GFP_KERNEL);
if (!opts->trsvcid) {
ret = -ENOMEM;
- goto out_free_ctrl;
+ goto out_free_opts;
}
opts->mask |= NVMF_OPT_TRSVCID;
}
@@ -2915,7 +2920,7 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
if (ret) {
pr_err("malformed address passed: %s:%s\n",
opts->traddr, opts->trsvcid);
- goto out_free_ctrl;
+ goto out_free_opts;
}
if (opts->mask & NVMF_OPT_HOST_TRADDR) {
@@ -2924,7 +2929,7 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
if (ret) {
pr_err("malformed src address passed: %s\n",
opts->host_traddr);
- goto out_free_ctrl;
+ goto out_free_opts;
}
}
@@ -2933,20 +2938,20 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
pr_err("invalid interface passed: %s\n",
opts->host_iface);
ret = -ENODEV;
- goto out_free_ctrl;
+ goto out_free_opts;
}
}
if (!opts->duplicate_connect && nvme_tcp_existing_controller(opts)) {
ret = -EALREADY;
- goto out_free_ctrl;
+ goto out_free_opts;
}
ctrl->queues = kcalloc(ctrl->ctrl.queue_count, sizeof(*ctrl->queues),
GFP_KERNEL);
if (!ctrl->queues) {
ret = -ENOMEM;
- goto out_free_ctrl;
+ goto out_free_opts;
}
ret = nvme_init_ctrl(&ctrl->ctrl, dev, &nvme_tcp_ctrl_ops, 0);
@@ -2956,6 +2961,8 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
return ctrl;
out_kfree_queues:
kfree(ctrl->queues);
+out_free_opts:
+ nvmf_ctrl_options_put(opts);
out_free_ctrl:
kfree(ctrl);
return ERR_PTR(ret);
diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c
index f85a8441bcc6ecdcfb17e9cf1e883d2817cce9fc..22039535ff9d3c8b2b42ecaa843b947446a8ba99 100644
--- a/drivers/nvme/target/loop.c
+++ b/drivers/nvme/target/loop.c
@@ -291,8 +291,8 @@ static void nvme_loop_free_ctrl(struct nvme_ctrl *nctrl)
if (nctrl->tagset)
nvme_remove_io_tag_set(nctrl);
kfree(ctrl->queues);
- nvmf_free_options(nctrl->opts);
free_ctrl:
+ nvmf_ctrl_options_put(nctrl->opts);
kfree(ctrl);
}
@@ -567,6 +567,12 @@ static struct nvme_ctrl *nvme_loop_create_ctrl(struct device *dev,
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return ERR_PTR(-ENOMEM);
+
+ if (!nvmf_ctrl_options_get(opts)) {
+ ret = -ENOLCK;
+ goto out_free_ctrl;
+ }
+
ctrl->ctrl.opts = opts;
INIT_LIST_HEAD(&ctrl->list);
@@ -574,10 +580,8 @@ static struct nvme_ctrl *nvme_loop_create_ctrl(struct device *dev,
ret = nvme_init_ctrl(&ctrl->ctrl, dev, &nvme_loop_ctrl_ops,
0 /* no quirks, we're perfect! */);
- if (ret) {
- kfree(ctrl);
- goto out;
- }
+ if (ret)
+ goto out_free_opts;
ret = nvme_add_ctrl(&ctrl->ctrl);
if (ret)
@@ -641,7 +645,10 @@ static struct nvme_ctrl *nvme_loop_create_ctrl(struct device *dev,
nvme_uninit_ctrl(&ctrl->ctrl);
out_put_ctrl:
nvme_put_ctrl(&ctrl->ctrl);
-out:
+out_free_opts:
+ nvmf_ctrl_options_put(opts);
+out_free_ctrl:
+ kfree(ctrl);
if (ret > 0)
ret = -EIO;
return ERR_PTR(ret);
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code
2025-08-29 15:37 [PATCH v3 0/4] nvme-fc: fix blktests nvme/041 Daniel Wagner
2025-08-29 15:37 ` [PATCH v3 1/4] nvme-fabrics: introduce ref-counting for nvmf_ctrl_options Daniel Wagner
@ 2025-08-29 15:37 ` Daniel Wagner
2025-09-01 11:46 ` Dan Carpenter
2025-09-02 6:45 ` Hannes Reinecke
2025-08-29 15:37 ` [PATCH v3 3/4] nvme-fc: refactore nvme_fc_reconnect_or_delete Daniel Wagner
2025-08-29 15:37 ` [PATCH v3 4/4] nvme-fc: wait for initial connect attempt to finish Daniel Wagner
3 siblings, 2 replies; 11+ messages in thread
From: Daniel Wagner @ 2025-08-29 15:37 UTC (permalink / raw)
To: Keith Busch, Christoph Hellwig, Sagi Grimberg, James Smart
Cc: Shinichiro Kawasaki, Hannes Reinecke, linux-nvme, linux-kernel,
Daniel Wagner
The lifetime of the controller is managed by the upper layers. This
ensures that the controller does not go away as long as there are
commands in flight. Thus, there is no need to take references in the
command handling code.
Currently, the refcounting code is partially open-coded for handling
error paths. By moving the cleanup code fully under refcounting and
releasing references when necessary, the error handling can be
simplified.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/host/fc.c | 102 ++++++++++++++++---------------------------------
1 file changed, 32 insertions(+), 70 deletions(-)
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index db5dbbf6aee21db45e91ea6f0c122681b9bdaf6f..3e6c79bf0bfd3884867c9ebeb24771323a619934 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -228,6 +228,9 @@ static struct device *fc_udev_device;
static void nvme_fc_complete_rq(struct request *rq);
+static void nvme_fc_ctrl_put(struct nvme_fc_ctrl *);
+static int nvme_fc_ctrl_get(struct nvme_fc_ctrl *);
+
/* *********************** FC-NVME Port Management ************************ */
static void __nvme_fc_delete_hw_queue(struct nvme_fc_ctrl *,
@@ -826,7 +829,7 @@ nvme_fc_unregister_remoteport(struct nvme_fc_remote_port *portptr)
dev_warn(ctrl->ctrl.device,
"NVME-FC{%d}: controller connectivity lost.\n",
ctrl->cnum);
- nvme_delete_ctrl(&ctrl->ctrl);
+ nvme_fc_ctrl_put(ctrl);
} else
nvme_fc_ctrl_connectivity_loss(ctrl);
}
@@ -980,9 +983,6 @@ fc_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
/* *********************** FC-NVME LS Handling **************************** */
-static void nvme_fc_ctrl_put(struct nvme_fc_ctrl *);
-static int nvme_fc_ctrl_get(struct nvme_fc_ctrl *);
-
static void nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg);
static void
@@ -1476,8 +1476,6 @@ nvme_fc_match_disconn_ls(struct nvme_fc_rport *rport,
spin_lock_irqsave(&rport->lock, flags);
list_for_each_entry(ctrl, &rport->ctrl_list, ctrl_list) {
- if (!nvme_fc_ctrl_get(ctrl))
- continue;
spin_lock(&ctrl->lock);
if (association_id == ctrl->association_id) {
oldls = ctrl->rcv_disconn;
@@ -1485,10 +1483,6 @@ nvme_fc_match_disconn_ls(struct nvme_fc_rport *rport,
ret = ctrl;
}
spin_unlock(&ctrl->lock);
- if (ret)
- /* leave the ctrl get reference */
- break;
- nvme_fc_ctrl_put(ctrl);
}
spin_unlock_irqrestore(&rport->lock, flags);
@@ -1567,9 +1561,6 @@ nvme_fc_ls_disconnect_assoc(struct nvmefc_ls_rcv_op *lsop)
/* fail the association */
nvme_fc_error_recovery(ctrl, "Disconnect Association LS received");
- /* release the reference taken by nvme_fc_match_disconn_ls() */
- nvme_fc_ctrl_put(ctrl);
-
return false;
}
@@ -2036,7 +2027,6 @@ nvme_fc_fcpio_done(struct nvmefc_fcp_req *req)
__nvme_fc_fcpop_chk_teardowns(ctrl, op, opstate);
atomic_set(&op->state, FCPOP_STATE_IDLE);
op->flags = FCOP_FLAGS_AEN; /* clear other flags */
- nvme_fc_ctrl_put(ctrl);
goto check_error;
}
@@ -2349,37 +2339,18 @@ nvme_fc_init_io_queues(struct nvme_fc_ctrl *ctrl)
}
static void
-nvme_fc_ctrl_free(struct kref *ref)
+nvme_fc_ctrl_delete(struct kref *ref)
{
struct nvme_fc_ctrl *ctrl =
container_of(ref, struct nvme_fc_ctrl, ref);
- unsigned long flags;
-
- if (ctrl->ctrl.tagset)
- nvme_remove_io_tag_set(&ctrl->ctrl);
- /* remove from rport list */
- spin_lock_irqsave(&ctrl->rport->lock, flags);
- list_del(&ctrl->ctrl_list);
- spin_unlock_irqrestore(&ctrl->rport->lock, flags);
-
- nvme_unquiesce_admin_queue(&ctrl->ctrl);
- nvme_remove_admin_tag_set(&ctrl->ctrl);
-
- kfree(ctrl->queues);
-
- put_device(ctrl->dev);
- nvme_fc_rport_put(ctrl->rport);
-
- ida_free(&nvme_fc_ctrl_cnt, ctrl->cnum);
- nvmf_ctrl_options_put(ctrl->ctrl.opts);
- kfree(ctrl);
+ nvme_delete_ctrl(&ctrl->ctrl);
}
static void
nvme_fc_ctrl_put(struct nvme_fc_ctrl *ctrl)
{
- kref_put(&ctrl->ref, nvme_fc_ctrl_free);
+ kref_put(&ctrl->ref, nvme_fc_ctrl_delete);
}
static int
@@ -2397,9 +2368,20 @@ nvme_fc_free_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
- WARN_ON(nctrl != &ctrl->ctrl);
+ if (ctrl->ctrl.tagset)
+ nvme_remove_io_tag_set(&ctrl->ctrl);
+
+ nvme_unquiesce_admin_queue(&ctrl->ctrl);
+ nvme_remove_admin_tag_set(&ctrl->ctrl);
- nvme_fc_ctrl_put(ctrl);
+ kfree(ctrl->queues);
+
+ put_device(ctrl->dev);
+ nvme_fc_rport_put(ctrl->rport);
+
+ ida_free(&nvme_fc_ctrl_cnt, ctrl->cnum);
+ nvmf_ctrl_options_put(ctrl->ctrl.opts);
+ kfree(ctrl);
}
/*
@@ -2649,9 +2631,6 @@ nvme_fc_start_fcp_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_queue *queue,
if (ctrl->rport->remoteport.port_state != FC_OBJSTATE_ONLINE)
return BLK_STS_RESOURCE;
- if (!nvme_fc_ctrl_get(ctrl))
- return BLK_STS_IOERR;
-
/* format the FC-NVME CMD IU and fcp_req */
cmdiu->connection_id = cpu_to_be64(queue->connection_id);
cmdiu->data_len = cpu_to_be32(data_len);
@@ -2696,7 +2675,6 @@ nvme_fc_start_fcp_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_queue *queue,
ret = nvme_fc_map_data(ctrl, op->rq, op);
if (ret < 0) {
nvme_cleanup_cmd(op->rq);
- nvme_fc_ctrl_put(ctrl);
if (ret == -ENOMEM || ret == -EAGAIN)
return BLK_STS_RESOURCE;
return BLK_STS_IOERR;
@@ -2737,8 +2715,6 @@ nvme_fc_start_fcp_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_queue *queue,
nvme_cleanup_cmd(op->rq);
}
- nvme_fc_ctrl_put(ctrl);
-
if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE &&
ret != -EBUSY)
return BLK_STS_IOERR;
@@ -2822,7 +2798,6 @@ nvme_fc_complete_rq(struct request *rq)
nvme_fc_unmap_data(ctrl, rq, op);
nvme_complete_rq(rq);
- nvme_fc_ctrl_put(ctrl);
}
static void nvme_fc_map_queues(struct blk_mq_tag_set *set)
@@ -3251,9 +3226,16 @@ static void
nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
+ unsigned long flags;
cancel_work_sync(&ctrl->ioerr_work);
cancel_delayed_work_sync(&ctrl->connect_work);
+
+ /* remove from rport list */
+ spin_lock_irqsave(&ctrl->rport->lock, flags);
+ list_del(&ctrl->ctrl_list);
+ spin_unlock_irqrestore(&ctrl->rport->lock, flags);
+
/*
* kill the association on the link side. this will block
* waiting for io to terminate
@@ -3307,7 +3289,7 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
ctrl->cnum, min_t(int, portptr->dev_loss_tmo,
(ctrl->ctrl.opts->max_reconnects *
ctrl->ctrl.opts->reconnect_delay)));
- WARN_ON(nvme_delete_ctrl(&ctrl->ctrl));
+ nvme_fc_ctrl_put(ctrl);
}
}
@@ -3521,6 +3503,7 @@ nvme_fc_alloc_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
out_free_ctrl:
kfree(ctrl);
out_fail:
+ nvme_fc_rport_put(rport);
/* exit via here doesn't follow ctlr ref points */
return ERR_PTR(ret);
}
@@ -3539,7 +3522,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
ret = nvme_add_ctrl(&ctrl->ctrl);
if (ret)
- goto out_put_ctrl;
+ goto fail_ctrl;
ret = nvme_alloc_admin_tag_set(&ctrl->ctrl, &ctrl->admin_tag_set,
&nvme_fc_admin_mq_ops,
@@ -3574,26 +3557,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
return &ctrl->ctrl;
fail_ctrl:
- nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING);
- cancel_work_sync(&ctrl->ioerr_work);
- cancel_work_sync(&ctrl->ctrl.reset_work);
- cancel_delayed_work_sync(&ctrl->connect_work);
-
- /* initiate nvme ctrl ref counting teardown */
- nvme_uninit_ctrl(&ctrl->ctrl);
-
-out_put_ctrl:
- /* Remove core ctrl ref. */
- nvme_put_ctrl(&ctrl->ctrl);
-
- /* as we're past the point where we transition to the ref
- * counting teardown path, if we return a bad pointer here,
- * the calling routine, thinking it's prior to the
- * transition, will do an rport put. Since the teardown
- * path also does a rport put, we do an extra get here to
- * so proper order/teardown happens.
- */
- nvme_fc_rport_get(rport);
+ nvme_fc_ctrl_put(ctrl);
return ERR_PTR(-EIO);
}
@@ -3703,8 +3667,6 @@ nvme_fc_create_ctrl(struct device *dev, struct nvmf_ctrl_options *opts)
spin_unlock_irqrestore(&nvme_fc_lock, flags);
ctrl = nvme_fc_init_ctrl(dev, opts, lport, rport);
- if (IS_ERR(ctrl))
- nvme_fc_rport_put(rport);
return ctrl;
}
}
@@ -3929,7 +3891,7 @@ nvme_fc_delete_controllers(struct nvme_fc_rport *rport)
dev_warn(ctrl->ctrl.device,
"NVME-FC{%d}: transport unloading: deleting ctrl\n",
ctrl->cnum);
- nvme_delete_ctrl(&ctrl->ctrl);
+ nvme_fc_ctrl_put(ctrl);
}
spin_unlock(&rport->lock);
}
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 3/4] nvme-fc: refactore nvme_fc_reconnect_or_delete
2025-08-29 15:37 [PATCH v3 0/4] nvme-fc: fix blktests nvme/041 Daniel Wagner
2025-08-29 15:37 ` [PATCH v3 1/4] nvme-fabrics: introduce ref-counting for nvmf_ctrl_options Daniel Wagner
2025-08-29 15:37 ` [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code Daniel Wagner
@ 2025-08-29 15:37 ` Daniel Wagner
2025-09-02 6:47 ` Hannes Reinecke
2025-08-29 15:37 ` [PATCH v3 4/4] nvme-fc: wait for initial connect attempt to finish Daniel Wagner
3 siblings, 1 reply; 11+ messages in thread
From: Daniel Wagner @ 2025-08-29 15:37 UTC (permalink / raw)
To: Keith Busch, Christoph Hellwig, Sagi Grimberg, James Smart
Cc: Shinichiro Kawasaki, Hannes Reinecke, linux-nvme, linux-kernel,
Daniel Wagner
Instead using if else blocks, apply the 'early return' and 'goto out'
pattern. This reduces the overall complexity of this function as the
conditions can be read in a linear order.
The only function change is that always the next reconnect attempt
message will be printed.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/host/fc.c | 64 +++++++++++++++++++++++++++-----------------------
1 file changed, 34 insertions(+), 30 deletions(-)
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 3e6c79bf0bfd3884867c9ebeb24771323a619934..5d9e193bd2525ae1a2f08e2a0a16ca41e08a7304 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -3249,7 +3249,6 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
struct nvme_fc_rport *rport = ctrl->rport;
struct nvme_fc_remote_port *portptr = &rport->remoteport;
unsigned long recon_delay = ctrl->ctrl.opts->reconnect_delay * HZ;
- bool recon = true;
if (nvme_ctrl_state(&ctrl->ctrl) != NVME_CTRL_CONNECTING)
return;
@@ -3259,38 +3258,43 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
"NVME-FC{%d}: reset: Reconnect attempt failed (%d)\n",
ctrl->cnum, status);
} else if (time_after_eq(jiffies, rport->dev_loss_end))
- recon = false;
+ goto delete_log;
- if (recon && nvmf_should_reconnect(&ctrl->ctrl, status)) {
- if (portptr->port_state == FC_OBJSTATE_ONLINE)
- dev_info(ctrl->ctrl.device,
- "NVME-FC{%d}: Reconnect attempt in %ld "
- "seconds\n",
- ctrl->cnum, recon_delay / HZ);
- else if (time_after(jiffies + recon_delay, rport->dev_loss_end))
- recon_delay = rport->dev_loss_end - jiffies;
+ if (!nvmf_should_reconnect(&ctrl->ctrl, status))
+ goto delete_log;
- queue_delayed_work(nvme_wq, &ctrl->connect_work, recon_delay);
- } else {
- if (portptr->port_state == FC_OBJSTATE_ONLINE) {
- if (status > 0 && (status & NVME_STATUS_DNR))
- dev_warn(ctrl->ctrl.device,
- "NVME-FC{%d}: reconnect failure\n",
- ctrl->cnum);
- else
- dev_warn(ctrl->ctrl.device,
- "NVME-FC{%d}: Max reconnect attempts "
- "(%d) reached.\n",
- ctrl->cnum, ctrl->ctrl.nr_reconnects);
- } else
+ if (portptr->port_state != FC_OBJSTATE_ONLINE &&
+ time_after(jiffies + recon_delay, rport->dev_loss_end))
+ recon_delay = rport->dev_loss_end - jiffies;
+
+ dev_info(ctrl->ctrl.device,
+ "NVME-FC{%d}: Reconnect attempt in %ld seconds\n",
+ ctrl->cnum, recon_delay / HZ);
+
+ queue_delayed_work(nvme_wq, &ctrl->connect_work, recon_delay);
+
+ return;
+
+delete_log:
+ if (portptr->port_state == FC_OBJSTATE_ONLINE) {
+ if (status > 0 && (status & NVME_STATUS_DNR))
dev_warn(ctrl->ctrl.device,
- "NVME-FC{%d}: dev_loss_tmo (%d) expired "
- "while waiting for remoteport connectivity.\n",
- ctrl->cnum, min_t(int, portptr->dev_loss_tmo,
- (ctrl->ctrl.opts->max_reconnects *
- ctrl->ctrl.opts->reconnect_delay)));
- nvme_fc_ctrl_put(ctrl);
- }
+ "NVME-FC{%d}: reconnect failure\n",
+ ctrl->cnum);
+ else
+ dev_warn(ctrl->ctrl.device,
+ "NVME-FC{%d}: Max reconnect attempts "
+ "(%d) reached.\n",
+ ctrl->cnum, ctrl->ctrl.nr_reconnects);
+ } else
+ dev_warn(ctrl->ctrl.device,
+ "NVME-FC{%d}: dev_loss_tmo (%d) expired "
+ "while waiting for remoteport connectivity.\n",
+ ctrl->cnum, min_t(int, portptr->dev_loss_tmo,
+ (ctrl->ctrl.opts->max_reconnects *
+ ctrl->ctrl.opts->reconnect_delay)));
+
+ nvme_fc_ctrl_put(ctrl);
}
static void
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 4/4] nvme-fc: wait for initial connect attempt to finish
2025-08-29 15:37 [PATCH v3 0/4] nvme-fc: fix blktests nvme/041 Daniel Wagner
` (2 preceding siblings ...)
2025-08-29 15:37 ` [PATCH v3 3/4] nvme-fc: refactore nvme_fc_reconnect_or_delete Daniel Wagner
@ 2025-08-29 15:37 ` Daniel Wagner
2025-09-02 9:13 ` Hannes Reinecke
3 siblings, 1 reply; 11+ messages in thread
From: Daniel Wagner @ 2025-08-29 15:37 UTC (permalink / raw)
To: Keith Busch, Christoph Hellwig, Sagi Grimberg, James Smart
Cc: Shinichiro Kawasaki, Hannes Reinecke, linux-nvme, linux-kernel,
Daniel Wagner
The TCP and RDMA transport are doing synchronous connects. Thus the
write to /dev/nvme-fabrics blocks and will return either success or
fail. The FC transport offloads the connect attempt to a workqueue and
thus it's an asynchronous operation. The write will succeeds even though
the connection attempt is ongoing.
This async connect feature was introduced to mitigate problems with
transient connect errors and the task to coordinate retries with
userspace (nvme-cli).
Unfortunately, this makes the transports behave differently. Streamline
nvme-fc to wait for the initial connection attempt.
In order to support also the async connection attempt introduce a new
flag for userspace, which is an opt-in feature. This avoids breaking
existing users which are not ready for the FC transport behavior change.
Link: https://lore.kernel.org/linux-nvme/0605ac36-16d5-2026-d3c6-62d346db6dfb@gmail.com/
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/host/fabrics.c | 18 +++++++++++++++++-
drivers/nvme/host/fabrics.h | 3 +++
drivers/nvme/host/fc.c | 36 +++++++++++++++++++++++++++++++++++-
3 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index cb9311c399856de54a2e4d41d41d295dd1aef31e..73e7a1e7925ef2e8ad633e8e2bd36207181dcbb6 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -709,6 +709,7 @@ static const match_table_t opt_tokens = {
{ NVMF_OPT_TLS, "tls" },
{ NVMF_OPT_CONCAT, "concat" },
#endif
+ { NVMF_OPT_CONNECT_ASYNC, "connect_async=%d" },
{ NVMF_OPT_ERR, NULL }
};
@@ -738,6 +739,8 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
opts->tls_key = NULL;
opts->keyring = NULL;
opts->concat = false;
+ /* keep backward compatibility with older nvme-cli */
+ opts->connect_async = true;
options = o = kstrdup(buf, GFP_KERNEL);
if (!options)
@@ -1064,6 +1067,19 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
}
opts->concat = true;
break;
+ case NVMF_OPT_CONNECT_ASYNC:
+ if (match_int(args, &token)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (token < 0 || token > 1) {
+ pr_err("Invalid connect_async %d value\n",
+ token);
+ ret = -EINVAL;
+ goto out;
+ }
+ opts->connect_async = token;
+ break;
default:
pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n",
p);
@@ -1316,7 +1332,7 @@ EXPORT_SYMBOL_GPL(nvmf_ctrl_options_put);
NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\
NVMF_OPT_DISABLE_SQFLOW | NVMF_OPT_DISCOVERY |\
NVMF_OPT_FAIL_FAST_TMO | NVMF_OPT_DHCHAP_SECRET |\
- NVMF_OPT_DHCHAP_CTRL_SECRET)
+ NVMF_OPT_DHCHAP_CTRL_SECRET | NVMF_OPT_CONNECT_ASYNC)
static struct nvme_ctrl *
nvmf_create_ctrl(struct device *dev, const char *buf)
diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
index bcc1d5f97ee5a03852830bf3ba0a15f82c7c2c03..9015bb3f63e1d0c412cf4af5194a42411fbb516c 100644
--- a/drivers/nvme/host/fabrics.h
+++ b/drivers/nvme/host/fabrics.h
@@ -67,6 +67,7 @@ enum {
NVMF_OPT_KEYRING = 1 << 26,
NVMF_OPT_TLS_KEY = 1 << 27,
NVMF_OPT_CONCAT = 1 << 28,
+ NVMF_OPT_CONNECT_ASYNC = 1 << 29,
};
/**
@@ -111,6 +112,7 @@ enum {
* @nr_poll_queues: number of queues for polling I/O
* @tos: type of service
* @fast_io_fail_tmo: Fast I/O fail timeout in seconds
+ * @connect_async: Don't wait for the initial connect attempt to succeed or fail
*/
struct nvmf_ctrl_options {
struct kref ref;
@@ -142,6 +144,7 @@ struct nvmf_ctrl_options {
unsigned int nr_poll_queues;
int tos;
int fast_io_fail_tmo;
+ bool connect_async;
};
int nvmf_ctrl_options_get(struct nvmf_ctrl_options *opts);
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 5d9e193bd2525ae1a2f08e2a0a16ca41e08a7304..ccd67acb55e216602010933914afca77248b3de0 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -148,6 +148,7 @@ struct nvme_fc_rport {
#define ASSOC_ACTIVE 0
#define ASSOC_FAILED 1
#define FCCTRL_TERMIO 2
+#define INITIAL_SYNC_CONNECT 3
struct nvme_fc_ctrl {
spinlock_t lock;
@@ -168,6 +169,7 @@ struct nvme_fc_ctrl {
struct work_struct ioerr_work;
struct delayed_work connect_work;
+ struct completion connect_completion;
struct kref ref;
unsigned long flags;
@@ -829,6 +831,7 @@ nvme_fc_unregister_remoteport(struct nvme_fc_remote_port *portptr)
dev_warn(ctrl->ctrl.device,
"NVME-FC{%d}: controller connectivity lost.\n",
ctrl->cnum);
+ complete(&ctrl->connect_completion);
nvme_fc_ctrl_put(ctrl);
} else
nvme_fc_ctrl_connectivity_loss(ctrl);
@@ -3253,6 +3256,9 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
if (nvme_ctrl_state(&ctrl->ctrl) != NVME_CTRL_CONNECTING)
return;
+ if (test_bit(INITIAL_SYNC_CONNECT, &ctrl->flags))
+ goto delete;
+
if (portptr->port_state == FC_OBJSTATE_ONLINE) {
dev_info(ctrl->ctrl.device,
"NVME-FC{%d}: reset: Reconnect attempt failed (%d)\n",
@@ -3294,6 +3300,8 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
(ctrl->ctrl.opts->max_reconnects *
ctrl->ctrl.opts->reconnect_delay)));
+delete:
+ complete(&ctrl->connect_completion);
nvme_fc_ctrl_put(ctrl);
}
@@ -3353,10 +3361,14 @@ nvme_fc_connect_ctrl_work(struct work_struct *work)
ret = nvme_fc_create_association(ctrl);
if (ret)
nvme_fc_reconnect_or_delete(ctrl, ret);
- else
+ else {
dev_info(ctrl->ctrl.device,
"NVME-FC{%d}: controller connect complete\n",
ctrl->cnum);
+ pr_info("%s:%d clear initial sync connect bit\n", __func__, __LINE__);
+ clear_bit(INITIAL_SYNC_CONNECT, &ctrl->flags);
+ complete(&ctrl->connect_completion);
+ }
}
@@ -3461,6 +3473,7 @@ nvme_fc_alloc_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
INIT_WORK(&ctrl->ctrl.reset_work, nvme_fc_reset_ctrl_work);
INIT_DELAYED_WORK(&ctrl->connect_work, nvme_fc_connect_ctrl_work);
+ init_completion(&ctrl->connect_completion);
INIT_WORK(&ctrl->ioerr_work, nvme_fc_ctrl_ioerr_work);
spin_lock_init(&ctrl->lock);
@@ -3539,6 +3552,12 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
list_add_tail(&ctrl->ctrl_list, &rport->ctrl_list);
spin_unlock_irqrestore(&rport->lock, flags);
+ if (!opts->connect_async) {
+ pr_info("%s:%d set initial connect bit\n", __func__, __LINE__);
+ set_bit(INITIAL_SYNC_CONNECT, &ctrl->flags);
+ nvme_fc_ctrl_get(ctrl);
+ }
+
if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) {
dev_err(ctrl->ctrl.device,
"NVME-FC{%d}: failed to init ctrl state\n", ctrl->cnum);
@@ -3554,6 +3573,20 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
flush_delayed_work(&ctrl->connect_work);
+ if (!opts->connect_async) {
+ enum nvme_ctrl_state state;
+
+ wait_for_completion(&ctrl->connect_completion);
+ state = nvme_ctrl_state(&ctrl->ctrl);
+ nvme_fc_ctrl_put(ctrl);
+
+ if (state != NVME_CTRL_LIVE) {
+ /* Cleanup is handled by the connect state machine */
+ pr_info("%s:%d ctrl state %d\n", __func__, __LINE__, state);
+ return ERR_PTR(-EIO);
+ }
+ }
+
dev_info(ctrl->ctrl.device,
"NVME-FC{%d}: new ctrl: NQN \"%s\", hostnqn: %s\n",
ctrl->cnum, nvmf_ctrl_subsysnqn(&ctrl->ctrl), opts->host->nqn);
@@ -3895,6 +3928,7 @@ nvme_fc_delete_controllers(struct nvme_fc_rport *rport)
dev_warn(ctrl->ctrl.device,
"NVME-FC{%d}: transport unloading: deleting ctrl\n",
ctrl->cnum);
+ complete(&ctrl->connect_completion);
nvme_fc_ctrl_put(ctrl);
}
spin_unlock(&rport->lock);
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code
2025-08-29 15:37 ` [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code Daniel Wagner
@ 2025-09-01 11:46 ` Dan Carpenter
2025-09-03 7:34 ` Daniel Wagner
2025-09-02 6:45 ` Hannes Reinecke
1 sibling, 1 reply; 11+ messages in thread
From: Dan Carpenter @ 2025-09-01 11:46 UTC (permalink / raw)
To: oe-kbuild, Daniel Wagner, Keith Busch, Christoph Hellwig,
Sagi Grimberg, James Smart
Cc: lkp, oe-kbuild-all, Shinichiro Kawasaki, Hannes Reinecke,
linux-nvme, linux-kernel, Daniel Wagner
Hi Daniel,
kernel test robot noticed the following build warnings:
url: https://github.com/intel-lab-lkp/linux/commits/Daniel-Wagner/nvme-fabrics-introduce-ref-counting-for-nvmf_ctrl_options/20250829-234513
base: c17b750b3ad9f45f2b6f7e6f7f4679844244f0b9
patch link: https://lore.kernel.org/r/20250829-nvme-fc-sync-v3-2-d69c87e63aee%40kernel.org
patch subject: [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code
config: x86_64-randconfig-161-20250830 (https://download.01.org/0day-ci/archive/20250831/202508310442.lBWBZ5AC-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202508310442.lBWBZ5AC-lkp@intel.com/
New smatch warnings:
drivers/nvme/host/fc.c:1492 nvme_fc_match_disconn_ls() warn: iterator used outside loop: 'ctrl'
Old smatch warnings:
drivers/nvme/host/fc.c:3180 nvme_fc_delete_association() warn: mixing irqsave and irq
vim +/ctrl +1492 drivers/nvme/host/fc.c
14fd1e98afafc0 James Smart 2020-03-31 1465 static struct nvme_fc_ctrl *
14fd1e98afafc0 James Smart 2020-03-31 1466 nvme_fc_match_disconn_ls(struct nvme_fc_rport *rport,
14fd1e98afafc0 James Smart 2020-03-31 1467 struct nvmefc_ls_rcv_op *lsop)
14fd1e98afafc0 James Smart 2020-03-31 1468 {
14fd1e98afafc0 James Smart 2020-03-31 1469 struct fcnvme_ls_disconnect_assoc_rqst *rqst =
14fd1e98afafc0 James Smart 2020-03-31 1470 &lsop->rqstbuf->rq_dis_assoc;
14fd1e98afafc0 James Smart 2020-03-31 1471 struct nvme_fc_ctrl *ctrl, *ret = NULL;
14fd1e98afafc0 James Smart 2020-03-31 1472 struct nvmefc_ls_rcv_op *oldls = NULL;
14fd1e98afafc0 James Smart 2020-03-31 1473 u64 association_id = be64_to_cpu(rqst->associd.association_id);
14fd1e98afafc0 James Smart 2020-03-31 1474 unsigned long flags;
14fd1e98afafc0 James Smart 2020-03-31 1475
14fd1e98afafc0 James Smart 2020-03-31 1476 spin_lock_irqsave(&rport->lock, flags);
14fd1e98afafc0 James Smart 2020-03-31 1477
14fd1e98afafc0 James Smart 2020-03-31 1478 list_for_each_entry(ctrl, &rport->ctrl_list, ctrl_list) {
14fd1e98afafc0 James Smart 2020-03-31 1479 spin_lock(&ctrl->lock);
14fd1e98afafc0 James Smart 2020-03-31 1480 if (association_id == ctrl->association_id) {
14fd1e98afafc0 James Smart 2020-03-31 1481 oldls = ctrl->rcv_disconn;
14fd1e98afafc0 James Smart 2020-03-31 1482 ctrl->rcv_disconn = lsop;
14fd1e98afafc0 James Smart 2020-03-31 1483 ret = ctrl;
There should maybe be a break statement here?
14fd1e98afafc0 James Smart 2020-03-31 1484 }
14fd1e98afafc0 James Smart 2020-03-31 1485 spin_unlock(&ctrl->lock);
14fd1e98afafc0 James Smart 2020-03-31 1486 }
14fd1e98afafc0 James Smart 2020-03-31 1487
14fd1e98afafc0 James Smart 2020-03-31 1488 spin_unlock_irqrestore(&rport->lock, flags);
14fd1e98afafc0 James Smart 2020-03-31 1489
14fd1e98afafc0 James Smart 2020-03-31 1490 /* transmit a response for anything that was pending */
14fd1e98afafc0 James Smart 2020-03-31 1491 if (oldls) {
14fd1e98afafc0 James Smart 2020-03-31 @1492 dev_info(rport->lport->dev,
14fd1e98afafc0 James Smart 2020-03-31 1493 "NVME-FC{%d}: Multiple Disconnect Association "
14fd1e98afafc0 James Smart 2020-03-31 1494 "LS's received\n", ctrl->cnum);
ctrl->cnum is always an invalid pointer on this line. Another
option would be to print ret->cnum instead ctrl->nmum.
14fd1e98afafc0 James Smart 2020-03-31 1495 /* overwrite good response with bogus failure */
14fd1e98afafc0 James Smart 2020-03-31 1496 oldls->lsrsp->rsplen = nvme_fc_format_rjt(oldls->rspbuf,
14fd1e98afafc0 James Smart 2020-03-31 1497 sizeof(*oldls->rspbuf),
14fd1e98afafc0 James Smart 2020-03-31 1498 rqst->w0.ls_cmd,
14fd1e98afafc0 James Smart 2020-03-31 1499 FCNVME_RJT_RC_UNAB,
14fd1e98afafc0 James Smart 2020-03-31 1500 FCNVME_RJT_EXP_NONE, 0);
14fd1e98afafc0 James Smart 2020-03-31 1501 nvme_fc_xmt_ls_rsp(oldls);
14fd1e98afafc0 James Smart 2020-03-31 1502 }
14fd1e98afafc0 James Smart 2020-03-31 1503
14fd1e98afafc0 James Smart 2020-03-31 1504 return ret;
14fd1e98afafc0 James Smart 2020-03-31 1505 }
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code
2025-08-29 15:37 ` [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code Daniel Wagner
2025-09-01 11:46 ` Dan Carpenter
@ 2025-09-02 6:45 ` Hannes Reinecke
1 sibling, 0 replies; 11+ messages in thread
From: Hannes Reinecke @ 2025-09-02 6:45 UTC (permalink / raw)
To: Daniel Wagner, Keith Busch, Christoph Hellwig, Sagi Grimberg,
James Smart
Cc: Shinichiro Kawasaki, linux-nvme, linux-kernel
On 8/29/25 17:37, Daniel Wagner wrote:
> The lifetime of the controller is managed by the upper layers. This
> ensures that the controller does not go away as long as there are
> commands in flight. Thus, there is no need to take references in the
> command handling code.
>
> Currently, the refcounting code is partially open-coded for handling
> error paths. By moving the cleanup code fully under refcounting and
> releasing references when necessary, the error handling can be
> simplified.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/host/fc.c | 102 ++++++++++++++++---------------------------------
> 1 file changed, 32 insertions(+), 70 deletions(-)
>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 3/4] nvme-fc: refactore nvme_fc_reconnect_or_delete
2025-08-29 15:37 ` [PATCH v3 3/4] nvme-fc: refactore nvme_fc_reconnect_or_delete Daniel Wagner
@ 2025-09-02 6:47 ` Hannes Reinecke
0 siblings, 0 replies; 11+ messages in thread
From: Hannes Reinecke @ 2025-09-02 6:47 UTC (permalink / raw)
To: Daniel Wagner, Keith Busch, Christoph Hellwig, Sagi Grimberg,
James Smart
Cc: Shinichiro Kawasaki, linux-nvme, linux-kernel
I guess the subject should be 'refactor nvme_fc_reconnect_or_delete'
On 8/29/25 17:37, Daniel Wagner wrote:
> Instead using if else blocks, apply the 'early return' and 'goto out'
> pattern. This reduces the overall complexity of this function as the
> conditions can be read in a linear order.
>
> The only function change is that always the next reconnect attempt
> message will be printed.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/host/fc.c | 64 +++++++++++++++++++++++++++-----------------------
> 1 file changed, 34 insertions(+), 30 deletions(-)
>
Otherwise:
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 4/4] nvme-fc: wait for initial connect attempt to finish
2025-08-29 15:37 ` [PATCH v3 4/4] nvme-fc: wait for initial connect attempt to finish Daniel Wagner
@ 2025-09-02 9:13 ` Hannes Reinecke
2025-09-03 7:45 ` Daniel Wagner
0 siblings, 1 reply; 11+ messages in thread
From: Hannes Reinecke @ 2025-09-02 9:13 UTC (permalink / raw)
To: Daniel Wagner, Keith Busch, Christoph Hellwig, Sagi Grimberg,
James Smart
Cc: Shinichiro Kawasaki, linux-nvme, linux-kernel
On 8/29/25 17:37, Daniel Wagner wrote:
> The TCP and RDMA transport are doing synchronous connects. Thus the
> write to /dev/nvme-fabrics blocks and will return either success or
> fail. The FC transport offloads the connect attempt to a workqueue and
> thus it's an asynchronous operation. The write will succeeds even though
> the connection attempt is ongoing.
>
> This async connect feature was introduced to mitigate problems with
> transient connect errors and the task to coordinate retries with
> userspace (nvme-cli).
>
> Unfortunately, this makes the transports behave differently. Streamline
> nvme-fc to wait for the initial connection attempt.
>
> In order to support also the async connection attempt introduce a new
> flag for userspace, which is an opt-in feature. This avoids breaking
> existing users which are not ready for the FC transport behavior change.
>
> Link: https://lore.kernel.org/linux-nvme/0605ac36-16d5-2026-d3c6-62d346db6dfb@gmail.com/
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/host/fabrics.c | 18 +++++++++++++++++-
> drivers/nvme/host/fabrics.h | 3 +++
> drivers/nvme/host/fc.c | 36 +++++++++++++++++++++++++++++++++++-
> 3 files changed, 55 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
> index cb9311c399856de54a2e4d41d41d295dd1aef31e..73e7a1e7925ef2e8ad633e8e2bd36207181dcbb6 100644
> --- a/drivers/nvme/host/fabrics.c
> +++ b/drivers/nvme/host/fabrics.c
> @@ -709,6 +709,7 @@ static const match_table_t opt_tokens = {
> { NVMF_OPT_TLS, "tls" },
> { NVMF_OPT_CONCAT, "concat" },
> #endif
> + { NVMF_OPT_CONNECT_ASYNC, "connect_async=%d" },
> { NVMF_OPT_ERR, NULL }
> };
>
> @@ -738,6 +739,8 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
> opts->tls_key = NULL;
> opts->keyring = NULL;
> opts->concat = false;
> + /* keep backward compatibility with older nvme-cli */
> + opts->connect_async = true;
>
> options = o = kstrdup(buf, GFP_KERNEL);
> if (!options)
> @@ -1064,6 +1067,19 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
> }
> opts->concat = true;
> break;
> + case NVMF_OPT_CONNECT_ASYNC:
> + if (match_int(args, &token)) {
> + ret = -EINVAL;
> + goto out;
> + }
> + if (token < 0 || token > 1) {
> + pr_err("Invalid connect_async %d value\n",
> + token);
> + ret = -EINVAL;
> + goto out;
> + }
> + opts->connect_async = token;
> + break;
> default:
> pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n",
> p);
> @@ -1316,7 +1332,7 @@ EXPORT_SYMBOL_GPL(nvmf_ctrl_options_put);
> NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\
> NVMF_OPT_DISABLE_SQFLOW | NVMF_OPT_DISCOVERY |\
> NVMF_OPT_FAIL_FAST_TMO | NVMF_OPT_DHCHAP_SECRET |\
> - NVMF_OPT_DHCHAP_CTRL_SECRET)
> + NVMF_OPT_DHCHAP_CTRL_SECRET | NVMF_OPT_CONNECT_ASYNC)
>
> static struct nvme_ctrl *
> nvmf_create_ctrl(struct device *dev, const char *buf)
> diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
> index bcc1d5f97ee5a03852830bf3ba0a15f82c7c2c03..9015bb3f63e1d0c412cf4af5194a42411fbb516c 100644
> --- a/drivers/nvme/host/fabrics.h
> +++ b/drivers/nvme/host/fabrics.h
> @@ -67,6 +67,7 @@ enum {
> NVMF_OPT_KEYRING = 1 << 26,
> NVMF_OPT_TLS_KEY = 1 << 27,
> NVMF_OPT_CONCAT = 1 << 28,
> + NVMF_OPT_CONNECT_ASYNC = 1 << 29,
> };
>
> /**
> @@ -111,6 +112,7 @@ enum {
> * @nr_poll_queues: number of queues for polling I/O
> * @tos: type of service
> * @fast_io_fail_tmo: Fast I/O fail timeout in seconds
> + * @connect_async: Don't wait for the initial connect attempt to succeed or fail
> */
> struct nvmf_ctrl_options {
> struct kref ref;
> @@ -142,6 +144,7 @@ struct nvmf_ctrl_options {
> unsigned int nr_poll_queues;
> int tos;
> int fast_io_fail_tmo;
> + bool connect_async;
> };
>
> int nvmf_ctrl_options_get(struct nvmf_ctrl_options *opts);
> diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
> index 5d9e193bd2525ae1a2f08e2a0a16ca41e08a7304..ccd67acb55e216602010933914afca77248b3de0 100644
> --- a/drivers/nvme/host/fc.c
> +++ b/drivers/nvme/host/fc.c
> @@ -148,6 +148,7 @@ struct nvme_fc_rport {
> #define ASSOC_ACTIVE 0
> #define ASSOC_FAILED 1
> #define FCCTRL_TERMIO 2
> +#define INITIAL_SYNC_CONNECT 3
>
> struct nvme_fc_ctrl {
> spinlock_t lock;
> @@ -168,6 +169,7 @@ struct nvme_fc_ctrl {
>
> struct work_struct ioerr_work;
> struct delayed_work connect_work;
> + struct completion connect_completion;
>
> struct kref ref;
> unsigned long flags;
> @@ -829,6 +831,7 @@ nvme_fc_unregister_remoteport(struct nvme_fc_remote_port *portptr)
> dev_warn(ctrl->ctrl.device,
> "NVME-FC{%d}: controller connectivity lost.\n",
> ctrl->cnum);
> + complete(&ctrl->connect_completion);
> nvme_fc_ctrl_put(ctrl);
> } else
> nvme_fc_ctrl_connectivity_loss(ctrl);
> @@ -3253,6 +3256,9 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
> if (nvme_ctrl_state(&ctrl->ctrl) != NVME_CTRL_CONNECTING)
> return;
>
> + if (test_bit(INITIAL_SYNC_CONNECT, &ctrl->flags))
> + goto delete;
> +
> if (portptr->port_state == FC_OBJSTATE_ONLINE) {
> dev_info(ctrl->ctrl.device,
> "NVME-FC{%d}: reset: Reconnect attempt failed (%d)\n",
> @@ -3294,6 +3300,8 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
> (ctrl->ctrl.opts->max_reconnects *
> ctrl->ctrl.opts->reconnect_delay)));
>
> +delete:
> + complete(&ctrl->connect_completion);
> nvme_fc_ctrl_put(ctrl);
> }
>
> @@ -3353,10 +3361,14 @@ nvme_fc_connect_ctrl_work(struct work_struct *work)
> ret = nvme_fc_create_association(ctrl);
> if (ret)
> nvme_fc_reconnect_or_delete(ctrl, ret);
> - else
> + else {
> dev_info(ctrl->ctrl.device,
> "NVME-FC{%d}: controller connect complete\n",
> ctrl->cnum);
> + pr_info("%s:%d clear initial sync connect bit\n", __func__, __LINE__);
> + clear_bit(INITIAL_SYNC_CONNECT, &ctrl->flags);
> + complete(&ctrl->connect_completion);
> + }
> }
>
>
> @@ -3461,6 +3473,7 @@ nvme_fc_alloc_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
>
> INIT_WORK(&ctrl->ctrl.reset_work, nvme_fc_reset_ctrl_work);
> INIT_DELAYED_WORK(&ctrl->connect_work, nvme_fc_connect_ctrl_work);
> + init_completion(&ctrl->connect_completion);
> INIT_WORK(&ctrl->ioerr_work, nvme_fc_ctrl_ioerr_work);
> spin_lock_init(&ctrl->lock);
>
> @@ -3539,6 +3552,12 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
> list_add_tail(&ctrl->ctrl_list, &rport->ctrl_list);
> spin_unlock_irqrestore(&rport->lock, flags);
>
> + if (!opts->connect_async) {
> + pr_info("%s:%d set initial connect bit\n", __func__, __LINE__);
> + set_bit(INITIAL_SYNC_CONNECT, &ctrl->flags);
> + nvme_fc_ctrl_get(ctrl);
> + }
> +
> if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) {
> dev_err(ctrl->ctrl.device,
> "NVME-FC{%d}: failed to init ctrl state\n", ctrl->cnum);
> @@ -3554,6 +3573,20 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
>
> flush_delayed_work(&ctrl->connect_work);
>
> + if (!opts->connect_async) {
> + enum nvme_ctrl_state state;
> +
> + wait_for_completion(&ctrl->connect_completion);
> + state = nvme_ctrl_state(&ctrl->ctrl);
> + nvme_fc_ctrl_put(ctrl);
> +
> + if (state != NVME_CTRL_LIVE) {
> + /* Cleanup is handled by the connect state machine */
> + pr_info("%s:%d ctrl state %d\n", __func__, __LINE__, state);
> + return ERR_PTR(-EIO);
We really should return the correct status (and not just -EIO).
Guess we'll need to introduce another variable in struct nvme_fc_ctrl
to hold the connect status...
> + }
> + }
> +
> dev_info(ctrl->ctrl.device,
> "NVME-FC{%d}: new ctrl: NQN \"%s\", hostnqn: %s\n",
> ctrl->cnum, nvmf_ctrl_subsysnqn(&ctrl->ctrl), opts->host->nqn);
> @@ -3895,6 +3928,7 @@ nvme_fc_delete_controllers(struct nvme_fc_rport *rport)
> dev_warn(ctrl->ctrl.device,
> "NVME-FC{%d}: transport unloading: deleting ctrl\n",
> ctrl->cnum);
> + complete(&ctrl->connect_completion);
> nvme_fc_ctrl_put(ctrl);
> }
> spin_unlock(&rport->lock);
>
And I wonder: what about the udev rules?
Do they need to be modified?
(IE: should we call udev with --connect-async or without?)
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code
2025-09-01 11:46 ` Dan Carpenter
@ 2025-09-03 7:34 ` Daniel Wagner
0 siblings, 0 replies; 11+ messages in thread
From: Daniel Wagner @ 2025-09-03 7:34 UTC (permalink / raw)
To: Dan Carpenter
Cc: oe-kbuild, Daniel Wagner, Keith Busch, Christoph Hellwig,
Sagi Grimberg, James Smart, lkp, oe-kbuild-all,
Shinichiro Kawasaki, Hannes Reinecke, linux-nvme, linux-kernel
Hi Dan,
On Mon, Sep 01, 2025 at 02:46:07PM +0300, Dan Carpenter wrote:
> Hi Daniel,
>
> kernel test robot noticed the following build warnings:
>
> url: https://github.com/intel-lab-lkp/linux/commits/Daniel-Wagner/nvme-fabrics-introduce-ref-counting-for-nvmf_ctrl_options/20250829-234513
> base: c17b750b3ad9f45f2b6f7e6f7f4679844244f0b9
> patch link: https://lore.kernel.org/r/20250829-nvme-fc-sync-v3-2-d69c87e63aee%40kernel.org
> patch subject: [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code
> config: x86_64-randconfig-161-20250830 (https://download.01.org/0day-ci/archive/20250831/202508310442.lBWBZ5AC-lkp@intel.com/config)
> compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
> | Closes: https://lore.kernel.org/r/202508310442.lBWBZ5AC-lkp@intel.com/
>
> New smatch warnings:
> drivers/nvme/host/fc.c:1492 nvme_fc_match_disconn_ls() warn: iterator used outside loop: 'ctrl'
>
> Old smatch warnings:
> drivers/nvme/host/fc.c:3180 nvme_fc_delete_association() warn: mixing irqsave and irq
>
> vim +/ctrl +1492 drivers/nvme/host/fc.c
>
> 14fd1e98afafc0 James Smart 2020-03-31 1465 static struct nvme_fc_ctrl *
> 14fd1e98afafc0 James Smart 2020-03-31 1466 nvme_fc_match_disconn_ls(struct nvme_fc_rport *rport,
> 14fd1e98afafc0 James Smart 2020-03-31 1467 struct nvmefc_ls_rcv_op *lsop)
> 14fd1e98afafc0 James Smart 2020-03-31 1468 {
> 14fd1e98afafc0 James Smart 2020-03-31 1469 struct fcnvme_ls_disconnect_assoc_rqst *rqst =
> 14fd1e98afafc0 James Smart 2020-03-31 1470 &lsop->rqstbuf->rq_dis_assoc;
> 14fd1e98afafc0 James Smart 2020-03-31 1471 struct nvme_fc_ctrl *ctrl, *ret = NULL;
> 14fd1e98afafc0 James Smart 2020-03-31 1472 struct nvmefc_ls_rcv_op *oldls = NULL;
> 14fd1e98afafc0 James Smart 2020-03-31 1473 u64 association_id = be64_to_cpu(rqst->associd.association_id);
> 14fd1e98afafc0 James Smart 2020-03-31 1474 unsigned long flags;
> 14fd1e98afafc0 James Smart 2020-03-31 1475
> 14fd1e98afafc0 James Smart 2020-03-31 1476 spin_lock_irqsave(&rport->lock, flags);
> 14fd1e98afafc0 James Smart 2020-03-31 1477
> 14fd1e98afafc0 James Smart 2020-03-31 1478 list_for_each_entry(ctrl, &rport->ctrl_list, ctrl_list) {
> 14fd1e98afafc0 James Smart 2020-03-31 1479 spin_lock(&ctrl->lock);
> 14fd1e98afafc0 James Smart 2020-03-31 1480 if (association_id == ctrl->association_id) {
> 14fd1e98afafc0 James Smart 2020-03-31 1481 oldls = ctrl->rcv_disconn;
> 14fd1e98afafc0 James Smart 2020-03-31 1482 ctrl->rcv_disconn = lsop;
> 14fd1e98afafc0 James Smart 2020-03-31 1483 ret = ctrl;
>
> There should maybe be a break statement here?
Hmm, the commit 14fd1e98afafc0 has a break after the spin_unlock.
+ spin_unlock(&ctrl->lock);
+ if (ret)
+ /* leave the ctrl get reference */
+ break;
And since then there aren't any changes applied.
> 14fd1e98afafc0 James Smart 2020-03-31 1484 }
> 14fd1e98afafc0 James Smart 2020-03-31 1485 spin_unlock(&ctrl->lock);
> 14fd1e98afafc0 James Smart 2020-03-31 1486 }
> 14fd1e98afafc0 James Smart 2020-03-31 1487
> 14fd1e98afafc0 James Smart 2020-03-31 1488 spin_unlock_irqrestore(&rport->lock, flags);
> 14fd1e98afafc0 James Smart 2020-03-31 1489
> 14fd1e98afafc0 James Smart 2020-03-31 1490 /* transmit a response for anything that was pending */
> 14fd1e98afafc0 James Smart 2020-03-31 1491 if (oldls) {
> 14fd1e98afafc0 James Smart 2020-03-31 @1492 dev_info(rport->lport->dev,
> 14fd1e98afafc0 James Smart 2020-03-31 1493 "NVME-FC{%d}: Multiple Disconnect Association "
> 14fd1e98afafc0 James Smart 2020-03-31 1494 "LS's received\n", ctrl->cnum);
>
> ctrl->cnum is always an invalid pointer on this line. Another
> option would be to print ret->cnum instead ctrl->nmum.
Yes, this makes sense. I'll send a patch for this. Thanks!
Thanks,
Daniel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 4/4] nvme-fc: wait for initial connect attempt to finish
2025-09-02 9:13 ` Hannes Reinecke
@ 2025-09-03 7:45 ` Daniel Wagner
0 siblings, 0 replies; 11+ messages in thread
From: Daniel Wagner @ 2025-09-03 7:45 UTC (permalink / raw)
To: Hannes Reinecke
Cc: Daniel Wagner, Keith Busch, Christoph Hellwig, Sagi Grimberg,
James Smart, Shinichiro Kawasaki, linux-nvme, linux-kernel
On Tue, Sep 02, 2025 at 11:13:41AM +0200, Hannes Reinecke wrote:
> > + if (!opts->connect_async) {
> > + enum nvme_ctrl_state state;
> > +
> > + wait_for_completion(&ctrl->connect_completion);
> > + state = nvme_ctrl_state(&ctrl->ctrl);
> > + nvme_fc_ctrl_put(ctrl);
> > +
> > + if (state != NVME_CTRL_LIVE) {
> > + /* Cleanup is handled by the connect state machine */
> > + pr_info("%s:%d ctrl state %d\n", __func__, __LINE__, state);
> > + return ERR_PTR(-EIO);
>
> We really should return the correct status (and not just -EIO).
> Guess we'll need to introduce another variable in struct nvme_fc_ctrl
> to hold the connect status...
(forgot to remove the debug prints)
But this is what the current return value is if something goes wrong.
The other transport map all status codes to -EIO or return the negative
error code from the function which fails. I'd rather not change this in
this series.
> > dev_info(ctrl->ctrl.device,
> > "NVME-FC{%d}: new ctrl: NQN \"%s\", hostnqn: %s\n",
> > ctrl->cnum, nvmf_ctrl_subsysnqn(&ctrl->ctrl), opts->host->nqn);
> > @@ -3895,6 +3928,7 @@ nvme_fc_delete_controllers(struct nvme_fc_rport *rport)
> > dev_warn(ctrl->ctrl.device,
> > "NVME-FC{%d}: transport unloading: deleting ctrl\n",
> > ctrl->cnum);
> > + complete(&ctrl->connect_completion);
> > nvme_fc_ctrl_put(ctrl);
> > }
> > spin_unlock(&rport->lock);
> >
>
> And I wonder: what about the udev rules?
> Do they need to be modified?
> (IE: should we call udev with --connect-async or without?)
Without changing user space, the old behavior will be used. It's an
opt-in feature. I haven't finished the libnvme/nvme-cli changes but I
expect for getting all working the udev rules need to be modified as
well. My plan is to get the kernel bits finished up first before
tackling all the user bits in detail, e.g. also creating blktests for
this (in a way we have the first one which is nvme/041)
I'll post those changes here as it needs wider review for sure.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-09-03 8:36 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-29 15:37 [PATCH v3 0/4] nvme-fc: fix blktests nvme/041 Daniel Wagner
2025-08-29 15:37 ` [PATCH v3 1/4] nvme-fabrics: introduce ref-counting for nvmf_ctrl_options Daniel Wagner
2025-08-29 15:37 ` [PATCH v3 2/4] nvme-fc: reorganize ctrl ref-counting and cleanup code Daniel Wagner
2025-09-01 11:46 ` Dan Carpenter
2025-09-03 7:34 ` Daniel Wagner
2025-09-02 6:45 ` Hannes Reinecke
2025-08-29 15:37 ` [PATCH v3 3/4] nvme-fc: refactore nvme_fc_reconnect_or_delete Daniel Wagner
2025-09-02 6:47 ` Hannes Reinecke
2025-08-29 15:37 ` [PATCH v3 4/4] nvme-fc: wait for initial connect attempt to finish Daniel Wagner
2025-09-02 9:13 ` Hannes Reinecke
2025-09-03 7:45 ` Daniel Wagner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).