[PATCH RFC 09/11] nvmet: Add helpers to find and get static controllers

Mike Christie
2025-03-13T05:22:56Z

The new nvmet_mdev_pci driver lives on the mdev bus and does not have
direct access to the configfs interface. For the
nvmet_fabrics_ops.add_port callout we will add a mdev port. However, the
mdev bus driver design requires the device under that to be added in a
separate step (from the driver->probe callout). For this callout we need
a way to find the controllers under a port, so this patch adds some
helpers to be able to loop over the static controllers created under
a port.

The nvmet-mdev-pci driver can then loop over the controllers under
a port and create a mdev/vfio device for the nvmet controller.

Signed-off-by: Mike Christie <michael.christie@oracle.com>
---
drivers/nvme/target/configfs.c | 10 ++++-
drivers/nvme/target/core.c | 71 ++++++++++++++++++++++++++++++++++
drivers/nvme/target/nvmet.h | 8 ++++
3 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index a946a879b9d6..31c484d51a69 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -2069,16 +2069,19 @@ static ssize_t nvmet_ctrl_enable_store(struct config_item *item,
ret = -EBUSY;
goto out_put_ctrl;
}
+ list_add_tail(&ctrl->port_entry, &port->static_ctrls);

ret = nvmet_enable_port(port);
if (ret)
- goto out_put_ctrl;
+ goto out_del_entry;

conf->ctrl = ctrl;
up_read(&nvmet_config_sem);

return count;

+out_del_entry:
+ list_del(&ctrl->port_entry);
out_put_ctrl:
up_read(&nvmet_config_sem);
nvmet_ctrl_put(ctrl);
@@ -2169,6 +2172,10 @@ static void nvmet_ctrl_release(struct config_item *item)
mod = conf->args.ops->owner;

if (conf->ctrl) {
+ down_write(&nvmet_config_sem);
+ list_del(&conf->ctrl->port_entry);
+ up_write(&nvmet_config_sem);
+
conf->args.ops->delete_ctrl(conf->ctrl);
nvmet_ctrl_put(conf->ctrl);
}
@@ -2401,6 +2408,7 @@ static struct config_group *nvmet_ports_make(struct config_group *group,

INIT_LIST_HEAD(&port->entry);
INIT_LIST_HEAD(&port->subsystems);
+ INIT_LIST_HEAD(&port->static_ctrls);
INIT_LIST_HEAD(&port->referrals);
port->inline_data_size = -1; /* < 0 == let the transport choose */
port->max_queue_size = -1; /* < 0 == let the transport choose */
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 1385368270de..6dab9d0f6b2f 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -1551,6 +1551,76 @@ static void nvmet_fatal_error_handler(struct work_struct *work)
ctrl->ops->delete_ctrl(ctrl);
}

+/**
+ * nvmet_find_get_static_ctrl - find a static controller by port and cntlid
+ * @port: port to search under
+ * @trtype: transport type of the controller
+ * @cntlid: controller ID
+ *
+ * Returns: On success this returns a nvmet_ctrl with the refcount increased
+ * by one that the caller must drop.
+ */
+struct nvmet_ctrl *nvmet_find_get_static_ctrl(struct nvmet_port *port,
+ int trtype, u16 cntlid)
+{
+ struct nvmet_ctrl *ctrl;
+
+ down_read(&nvmet_config_sem);
+
+ list_for_each_entry(ctrl, &port->static_ctrls, port_entry) {
+ if (ctrl->ops->type != trtype)
+ continue;
+
+ if (ctrl->cntlid == cntlid) {
+ if (!kref_get_unless_zero(&ctrl->ref))
+ ctrl = NULL;
+
+ up_read(&nvmet_config_sem);
+ return ctrl;
+ }
+ }
+
+ up_read(&nvmet_config_sem);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(nvmet_find_get_static_ctrl);
+
+/**
+ * nvmet_for_each_static_ctrl - execute fn over matching static controllers
+ * @port: port that the controller is using.
+ * @trtype: transport type of the controller.
+ * @fn: function to execute on each matching controller.
+ * @priv: driver specific struct passed to fn.
+ *
+ * This must be called with the nvmet_config_sem.
+ *
+ * Returns: passes fn's return value to caller.
+ */
+int nvmet_for_each_static_ctrl(struct nvmet_port *port, int trtype,
+ nvmet_ctlr_iter_fn *fn, void *priv)
+{
+ struct nvmet_ctrl *ctrl;
+ int ret = 0;
+
+ lockdep_assert_held(&nvmet_config_sem);
+
+ list_for_each_entry(ctrl, &port->static_ctrls, port_entry) {
+ if (ctrl->ops->type != trtype)
+ continue;
+
+ if (!kref_get_unless_zero(&ctrl->ref))
+ continue;
+
+ ret = fn(priv, port, ctrl);
+ nvmet_ctrl_put(ctrl);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nvmet_for_each_static_ctrl);
+
struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
{
struct nvmet_subsys *subsys;
@@ -1599,6 +1669,7 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)

INIT_WORK(&ctrl->async_event_work, nvmet_async_event_work);
INIT_LIST_HEAD(&ctrl->async_events);
+ INIT_LIST_HEAD(&ctrl->port_entry);
INIT_RADIX_TREE(&ctrl->p2p_ns_map, GFP_KERNEL);
INIT_WORK(&ctrl->fatal_err_work, nvmet_fatal_error_handler);
INIT_DELAYED_WORK(&ctrl->ka_work, nvmet_keep_alive_timer);
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 2b0e624b80e1..a16d1c74e3d9 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -196,6 +196,7 @@ struct nvmet_port {
struct config_group group;
struct config_group subsys_group;
struct list_head subsystems;
+ struct list_head static_ctrls;
struct config_group referrals_group;
struct list_head referrals;
struct list_head global_entry;
@@ -268,6 +269,7 @@ struct nvmet_ctrl {
struct list_head async_events;
struct work_struct async_event_work;

+ struct list_head port_entry;
struct list_head subsys_entry;
struct kref ref;
struct delayed_work ka_work;
@@ -603,6 +605,12 @@ struct nvmet_alloc_ctrl_args {
u16 status;
};

+typedef int (nvmet_ctlr_iter_fn)(void *priv, struct nvmet_port *port,
+ struct nvmet_ctrl *ctrl);
+int nvmet_for_each_static_ctrl(struct nvmet_port *port, int trtype,
+ nvmet_ctlr_iter_fn *fn, void *priv);
+struct nvmet_ctrl *nvmet_find_get_static_ctrl(struct nvmet_port *port,
+ int trtype, u16 cntlid);
struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args);
struct nvmet_ctrl *nvmet_ctrl_find_get(const char *subsysnqn,
const char *hostnqn, u16 cntlid,
--
2.43.0