nvmet: add target passthru commands support
Subsystems (3)
v4 cover nvmet: add target passthru commands support
This patchset aims to enable passing through entire physical NVMe drive through an NVMe over fabrics interface. This implies that all namespaces on the target drive are passed through to the initiator one-to-one and all admin commands from the initiator are forwarded back to the target drive. This allows for exposing Vender Unique Commands (VUCs) over the NVMe-OF interface.
Changes since V3:-
1. Add request polling interface to the block layer.
2. Use request polling interface in the NVMEoF target passthru code path.
3. Add checks suggested by Sagi for creating one target ctrl per passthru ctrl.
4. Don't enable the namespace if it belongs to the configured passthru ctrl.
5. Adjust the code latest kernel.
Changes since V2:-
1. Split the addition of passthru command handlers and integration into two different patches since we add guards to create one target controller per passthru controller. This way it will be easier to review the code.
2. Adjust the code for 4.18.
Changes since V1:-
1. Update the new nvme core controller find API naming and changed the string comparison of the ctrl.
2. Get rid of the newly added #defines for target ctrl values.
3. Use the newly added structure members in the same patch where they are used. Aggregate the passthru command handling support and integration with nvmet-core into one patch.
4. Introduce global NVMe Target subsystem list for connected and not connected subsystems on the target side.
5. Add check when configuring the target ns and target passthru ctrl to allow only one target controller to be created for one passthru subsystem.
6. Use the passthru ctrl cntlid when creating the target controller.
Chaitanya Kulkarni (9):
nvme-core: add new interfaces to access nvme-ctrl
nvme-core: export existing ctrl and ns interfaces
nvmet: export nvmet_add_async_event api
nvmet: add global subsystem list
nvmet: add passthru cmd handlers
nvmet: integrate passthru code with core
nvmet: add configfs interface for target passthru
block: add helper to get the request based cookie
nvmet: add request polling support
block/blk-mq.c | 20 +++ drivers/nvme/host/core.c | 81 ++++++++- drivers/nvme/host/nvme.h | 11 ++ drivers/nvme/target/Makefile | 2 +- drivers/nvme/target/configfs.c | 145 ++++++++++++++- drivers/nvme/target/core.c | 197 ++++++++++++++++++-- drivers/nvme/target/nvmet.h | 17 ++ drivers/nvme/target/pt-cmd.c | 398 +++++++++++++++++++++++++++++++++++++++++ include/linux/blk-mq.h | 1 + 9 files changed, 852 insertions(+), 20 deletions(-) create mode 100644 drivers/nvme/target/pt-cmd.c
Signature · 1 line
2.14.1
PATCH v4 1/9 nvme-core: add new interfaces to access nvme-ctrl
The new interface nvme_get_ctrl_by_path() allows to search the nvme ctrl by its pathname. On success, it increments the refcount of the ctrl and associated subsystem. The caller needs to decrement the ctrl and subsystem refcount by calling nvme_put_ctrl_by_path() once the work is done.
This is a preparation patch for implementing NVMe Over Fabric target passthru feature.
--- drivers/nvme/host/core.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/nvme/host/nvme.h | 3 +++ 2 files changed, 66 insertions(+)
drivers/nvme/host/core.c +63 -0
@@ -99,6 +99,7 @@ static struct class *nvme_subsys_class;static void nvme_ns_remove (struct nvme_ns *ns);static int nvme_revalidate_disk (struct gendisk *disk); +static int nvme_get_subsystem (struct nvme_subsystem *subsys);static void nvme_put_subsystem (struct nvme_subsystem *subsys);static void nvme_remove_invalid_namespaces (struct nvme_ctrl *ctrl,unsigned nsid);
@@ -126,6 +127,61 @@ static void nvme_queue_scan(struct nvme_ctrl *ctrl)queue_work (nvme_wq, &ctrl->scan_work); } +int nvme_get_ctrl_by_path (char *path,struct nvme_ctrl **ctrl) +{ +int ret = -ENODEV ; +char *cdev =strrchr (path,'/' ); +struct nvme_ctrl *ictrl =NULL ; +struct nvme_subsystem *isubsys =NULL ; + +if (!cdev) { + *ctrl =NULL ; +return ret; + } + cdev++; + +mutex_lock (&nvme_subsystems_lock); +list_for_each_entry (isubsys, &nvme_subsystems, entry) { +if (!nvme_get_subsystem (isubsys)) { +pr_info ("failed to get the subsystem for ctrl %s\n" , + path); + goto out; + } +mutex_unlock (&nvme_subsystems_lock); + +list_for_each_entry (ictrl, &isubsys->ctrls, subsys_entry) { +spin_lock (&ictrl->lock); +nvme_get_ctrl (ictrl); +if (strcmp (cdev,kobject_name (&ictrl->device->kobj)) + ==0 ) { + *ctrl = ictrl; +if (try_module_get (ictrl->ops->module)) { +spin_unlock (&ictrl->lock); +mutex_lock (&nvme_subsystems_lock); + ret =0 ; + goto out; + } + } +nvme_put_ctrl (ictrl); +spin_unlock (&ictrl->lock); + } +mutex_lock (&nvme_subsystems_lock); +nvme_put_subsystem (isubsys); + } +out: +mutex_unlock (&nvme_subsystems_lock); +return ret; +} +EXPORT_SYMBOL_GPL (nvme_get_ctrl_by_path); + +void nvme_put_ctrl_by_path (struct nvme_ctrl *ctrl) +{ +nvme_put_ctrl (ctrl); +module_put (ctrl->ops->module); +nvme_put_subsystem (ctrl->subsys); +} +EXPORT_SYMBOL_GPL (nvme_put_ctrl_by_path); +int nvme_reset_ctrl (struct nvme_ctrl *ctrl) {if (!nvme_change_ctrl_state (ctrl,NVME_CTRL_RESETTING ))
@@ -2119,6 +2175,13 @@ static void nvme_destroy_subsystem(struct kref *ref)put_device (&subsys->dev); } +static int nvme_get_subsystem (struct nvme_subsystem *subsys) +{ +lockdep_assert_held (&nvme_subsystems_lock); + +return kref_get_unless_zero (&subsys->ref); +} +static void nvme_put_subsystem (struct nvme_subsystem *subsys) {kref_put (&subsys->ref, nvme_destroy_subsystem);
drivers/nvme/host/nvme.h +3 -0
@@ -547,6 +547,9 @@ static inline void nvme_mpath_stop(struct nvme_ctrl *ctrl) }#endif /* CONFIG_NVME_MULTIPATH */ +int nvme_get_ctrl_by_path (char *name,struct nvme_ctrl **ctrl); +void nvme_put_ctrl_by_path (struct nvme_ctrl *ctrl); +#ifdef CONFIG_NVM void nvme_nvm_update_nvm_info (struct nvme_ns *ns);int nvme_nvm_register (struct nvme_ns *ns,char *disk_name,int node);
Signature · 1 line
2.14.1
PATCH v4 1/9 nvme-core: add new interfaces to access nvme-ctrl
Quoted 35 lines · Chaitanya Kulkarni
drivers/nvme/host/core.c +35
-0+int nvme_get_ctrl_by_path (char *path,struct nvme_ctrl **ctrl) +{ +int ret = -ENODEV ; +char *cdev =strrchr (path,'/' ); +struct nvme_ctrl *ictrl =NULL ; +struct nvme_subsystem *isubsys =NULL ; + +if (!cdev) { + *ctrl =NULL ; +return ret; + } + cdev++; + +mutex_lock (&nvme_subsystems_lock); +list_for_each_entry (isubsys, &nvme_subsystems, entry) { +if (!nvme_get_subsystem (isubsys)) { +pr_info ("failed to get the subsystem for ctrl %s\n" , + path); + goto out; + } +mutex_unlock (&nvme_subsystems_lock); + +list_for_each_entry (ictrl, &isubsys->ctrls, subsys_entry) { +spin_lock (&ictrl->lock); +nvme_get_ctrl (ictrl); +if (strcmp (cdev,kobject_name (&ictrl->device->kobj)) + ==0 ) { + *ctrl = ictrl; +if (try_module_get (ictrl->ops->module)) { +spin_unlock (&ictrl->lock); +mutex_lock (&nvme_subsystems_lock); + ret =0 ; + goto out; + } + }
drivers/nvme/host/core.c static void nvme_queue_scan(struct nvme_ctrl *ctrl) · line 174
Quoted 10 lines
drivers/nvme/host/core.c +10
-0+nvme_put_ctrl (ictrl); +spin_unlock (&ictrl->lock); + } +mutex_lock (&nvme_subsystems_lock); +nvme_put_subsystem (isubsys); + } +out: +mutex_unlock (&nvme_subsystems_lock); +return ret; +}
Some difficult to follow locking and reference counting here.
The 'nvme_get_subsystem' is a kref_get_unless_zero, so if the subsystem happens to be at the final 'put' during this iteration, why bail out instead of going to the next subsystem? Seems like a 'continue' is the right thing to do there.
The end part of the loop is even more confusing. If the nvme_put_subsystem happens to be the final 'put', it will deadlock because the nvme_destroy_subsystem will want to take the same lock you're already holding. It looks like getting rid of the reference counting on the subsystem, and hold nvme_subsystems_lock for the entire loop gets the desired result.
And while iterating the inner loop subsys_entry, it looks like you need to hold isubsys->lock. And if you're holding that lock, you won't need a reference on nvme_ctrl until you find the controller you're searching for.
Finally I cant even tell why ictrl->lock is being taken. That will only prevent a state transition, which doesn't seem to matter here.
PATCH v4 1/9 nvme-core: add new interfaces to access nvme-ctrl
Thanks Keith for the comments, I'll simplify the code for next version.
From: Keith Busch <[email protected]>
Sent: Wednesday, August 15, 2018 2:21 PM
To: Chaitanya Kulkarni
Cc: linux-nvme at lists.infradead.org; keith.busch at intel.com; hch at lst.de; sagi at grimberg.me
Subject: Re: [PATCH V4 1/9] nvme-core: add new interfaces to access nvme-ctrl
?
Quoted 35 lines · Chaitanya Kulkarni
(quoted diff) +35
-0+int nvme_get_ctrl_by_path(char *path, struct nvme_ctrl **ctrl) +{ +???? int ret = -ENODEV; +???? char *cdev = strrchr(path, '/'); +???? struct nvme_ctrl *ictrl = NULL; +???? struct nvme_subsystem *isubsys = NULL; + +???? if (!cdev) { +???????????? *ctrl = NULL; +???????????? return ret; +???? } +???? cdev++; + +???? mutex_lock(&nvme_subsystems_lock); +???? list_for_each_entry(isubsys, &nvme_subsystems, entry) { +???????????? if (!nvme_get_subsystem(isubsys)) { +???????????????????? pr_info("failed to get the subsystem for ctrl %s\n", +???????????????????????????????????? path); +???????????????????? goto out; +???????????? } +???????????? mutex_unlock(&nvme_subsystems_lock); + +???????????? list_for_each_entry(ictrl, &isubsys->ctrls, subsys_entry) { +???????????????????? spin_lock(&ictrl->lock); +???????????????????? nvme_get_ctrl(ictrl); +???????????????????? if (strcmp(cdev, kobject_name(&ictrl->device->kobj)) +???????????????????????????????????? == 0) { +???????????????????????????? *ctrl = ictrl; +???????????????????????????? if (try_module_get(ictrl->ops->module)) { +???????????????????????????????????? spin_unlock(&ictrl->lock); +???????????????????????????????????? mutex_lock(&nvme_subsystems_lock); +???????????????????????????????????? ret = 0; +???????????????????????????????????? goto out; +???????????????????????????? } +???????????????????? }
Review
Quoted 10 lines
(quoted diff) +10
-0+???????????????????? nvme_put_ctrl(ictrl); +???????????????????? spin_unlock(&ictrl->lock); +???????????? } +???????????? mutex_lock(&nvme_subsystems_lock); +???????????? nvme_put_subsystem(isubsys); +???? } +out: +???? mutex_unlock(&nvme_subsystems_lock); +???? return ret; +}
Some difficult to follow locking and reference counting here.
The 'nvme_get_subsystem' is a kref_get_unless_zero, so if the subsystem happens to be at the final 'put' during this iteration, why bail out instead of going to the next subsystem? Seems like a 'continue' is the right thing to do there.
The end part of the loop is even more confusing. If the nvme_put_subsystem happens to be the final 'put', it will deadlock because the nvme_destroy_subsystem will want to take the same lock you're already holding. It looks like getting rid of the reference counting on the subsystem, and hold nvme_subsystems_lock for the entire loop gets the desired result.
And while iterating the inner loop subsys_entry, it looks like you need to hold isubsys->lock. And if you're holding that lock, you won't need a reference on nvme_ctrl until you find the controller you're searching for.
Finally I cant even tell why ictrl->lock is being taken. That will only prevent a state transition, which doesn't seem to matter here.
PATCH v4 2/9 nvme-core: export existing ctrl and ns interfaces
In reply to Chaitanya KulkarniWe export existing nvme ctrl and ns management APIs so that target passthru code can manage the nvme ctrl.
This is a preparation patch for implementing NVMe Over Fabric target passthru feature.
--- drivers/nvme/host/core.c | 18 +++++++++++------- drivers/nvme/host/nvme.h | 8 ++++++++ 2 files changed, 19 insertions(+), 7 deletions(-)
drivers/nvme/host/core.c +11 -7
@@ -453,10 +453,11 @@ static void nvme_free_ns(struct kref *kref)kfree (ns); } -static void nvme_put_ns (struct nvme_ns *ns) +void nvme_put_ns (struct nvme_ns *ns) {kref_put (&ns->kref, nvme_free_ns); } +EXPORT_SYMBOL_GPL (nvme_put_ns);static inline void nvme_clear_nvme_request (struct request *req) {
@@ -1050,8 +1051,7 @@ static int nvme_identify_ns_list(struct nvme_ctrl *dev, unsigned nsid, __le32 *n NVME_IDENTIFY_DATA_SIZE); } -static struct nvme_id_ns *nvme_identify_ns (struct nvme_ctrl *ctrl, -unsigned nsid) +struct nvme_id_ns *nvme_identify_ns (struct nvme_ctrl *ctrl,unsigned nsid) {struct nvme_id_ns *id;struct nvme_command c = { };
@@ -1075,6 +1075,7 @@ static struct nvme_id_ns *nvme_identify_ns(struct nvme_ctrl *ctrl,return id; } +EXPORT_SYMBOL_GPL (nvme_identify_ns);static int nvme_set_features (struct nvme_ctrl *dev,unsigned fid,unsigned dword11,void *buffer,size_t buflen,u32 *result)
@@ -1205,8 +1206,8 @@ static u32 nvme_known_admin_effects(u8 opcode)return 0 ; } -static u32 nvme_passthru_start (struct nvme_ctrl *ctrl,struct nvme_ns *ns, -u8 opcode) +u32 nvme_passthru_start (struct nvme_ctrl *ctrl,struct nvme_ns *ns, +u8 opcode) {u32 effects =0 ;
@@ -1235,6 +1236,7 @@ static u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns, }return effects; } +EXPORT_SYMBOL_GPL (nvme_passthru_start);static void nvme_update_formats (struct nvme_ctrl *ctrl) {
@@ -1249,7 +1251,7 @@ static void nvme_update_formats(struct nvme_ctrl *ctrl)nvme_remove_invalid_namespaces (ctrl,NVME_NSID_ALL ); } -static void nvme_passthru_end (struct nvme_ctrl *ctrl,u32 effects) +void nvme_passthru_end (struct nvme_ctrl *ctrl,u32 effects) {/* * Revalidate LBA changes prior to unfreezing. This is necessary to
@@ -1265,6 +1267,7 @@ static void nvme_passthru_end(struct nvme_ctrl *ctrl, u32 effects)if (effects & (NVME_CMD_EFFECTS_NIC |NVME_CMD_EFFECTS_NCC ))nvme_queue_scan (ctrl); } +EXPORT_SYMBOL_GPL (nvme_passthru_end);static int nvme_user_cmd (struct nvme_ctrl *ctrl,struct nvme_ns *ns,struct nvme_passthru_cmd __user *ucmd)
@@ -3054,7 +3057,7 @@ static int ns_cmp(void *priv, struct list_head *a, struct list_head *b)return nsa->head->ns_id - nsb->head->ns_id; } -static struct nvme_ns *nvme_find_get_ns (struct nvme_ctrl *ctrl,unsigned nsid) +struct nvme_ns *nvme_find_get_ns (struct nvme_ctrl *ctrl,unsigned nsid) {struct nvme_ns *ns, *ret =NULL ;
@@ -3072,6 +3075,7 @@ static struct nvme_ns *nvme_find_get_ns(struct nvme_ctrl *ctrl, unsigned nsid)up_read (&ctrl->namespaces_rwsem);return ret; } +EXPORT_SYMBOL_GPL (nvme_find_get_ns);static int nvme_setup_streams_ns (struct nvme_ctrl *ctrl,struct nvme_ns *ns) {
drivers/nvme/host/nvme.h +8 -0
@@ -420,6 +420,8 @@ void nvme_start_ctrl(struct nvme_ctrl *ctrl);void nvme_stop_ctrl (struct nvme_ctrl *ctrl);void nvme_put_ctrl (struct nvme_ctrl *ctrl);int nvme_init_identify (struct nvme_ctrl *ctrl); +struct nvme_ns *nvme_find_get_ns (struct nvme_ctrl *ctrl,unsigned nsid); +void nvme_put_ns (struct nvme_ns *ns);void nvme_remove_namespaces (struct nvme_ctrl *ctrl);
@@ -449,8 +451,14 @@ int __nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, union nvme_result *result, void *buffer, unsigned bufflen, unsigned timeout, int qid, int at_head, blk_mq_req_flags_t flags); + +u32 nvme_passthru_start (struct nvme_ctrl *ctrl,struct nvme_ns *ns, +u8 opcode); +void nvme_passthru_end (struct nvme_ctrl *ctrl,u32 effects);int nvme_set_queue_count (struct nvme_ctrl *ctrl,int *count);void nvme_stop_keep_alive (struct nvme_ctrl *ctrl); +struct nvme_id_ns *nvme_identify_ns (struct nvme_ctrl *ctrl, +unsigned nsid);int nvme_reset_ctrl (struct nvme_ctrl *ctrl);int nvme_reset_ctrl_sync (struct nvme_ctrl *ctrl);int nvme_delete_ctrl (struct nvme_ctrl *ctrl);
Signature · 1 line
2.14.1
PATCH v4 3/9 nvmet: export nvmet_add_async_event api
In reply to Chaitanya KulkarniThis patch changes the return value for nvmet_add_async_event() and exports the same API.
This change is needed for the target passthru code to generate async events.
--- drivers/nvme/target/core.c | 5 +++-- drivers/nvme/target/nvmet.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-)
drivers/nvme/target/core.c +3 -2
@@ -129,14 +129,14 @@ static void nvmet_async_event_work(struct work_struct *work) } } -static void nvmet_add_async_event (struct nvmet_ctrl *ctrl,u8 event_type, +bool nvmet_add_async_event (struct nvmet_ctrl *ctrl,u8 event_type,u8 event_info,u8 log_page) {struct nvmet_async_event *aen; aen =kmalloc (sizeof (*aen),GFP_KERNEL );if (!aen) -return ; +return false; aen->event_type = event_type; aen->event_info = event_info;
@@ -147,6 +147,7 @@ static void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type,mutex_unlock (&ctrl->lock);schedule_work (&ctrl->async_event_work); +return true; }static bool nvmet_aen_disabled (struct nvmet_ctrl *ctrl,u32 aen)
drivers/nvme/target/nvmet.h +2 -0
@@ -370,6 +370,8 @@ void nvmet_send_ana_event(struct nvmet_subsys *subsys, struct nvmet_port *port);void nvmet_port_send_ana_event (struct nvmet_port *port); +bool nvmet_add_async_event (struct nvmet_ctrl *ctrl,u8 event_type, +u8 event_info,u8 log_page);int nvmet_register_transport (const struct nvmet_fabrics_ops *ops);void nvmet_unregister_transport (const struct nvmet_fabrics_ops *ops);
Signature · 1 line
2.14.1
PATCH v4 4/9 nvmet: add global subsystem list
In reply to Chaitanya KulkarniThis adds a new structure member for nvmet_subsys and global list of the subsystems.
We use this global list to maintain currently connected, not connected, linked and not linked subsystem on the lists.
This is a preparation patch for implementing the NVMeOF target passthru code.
--- drivers/nvme/target/core.c | 12 ++++++++++++ drivers/nvme/target/nvmet.h | 2 ++ 2 files changed, 14 insertions(+)
drivers/nvme/target/core.c +12 -0
@@ -22,6 +22,9 @@ struct workqueue_struct *buffered_io_wq;static const struct nvmet_fabrics_ops *nvmet_transports[NVMF_TRTYPE_MAX ];static DEFINE_IDA (cntlid_ida); +static LIST_HEAD (nvmet_subsystems); +DEFINE_MUTEX (nvmet_subsystems_lock); +/* * This read/write semaphore is used to synchronize access to configuration * information on a target system that will result in discovery log page
@@ -1166,6 +1169,11 @@ struct nvmet_subsys *nvmet_subsys_alloc(const char *subsysnqn,INIT_LIST_HEAD (&subsys->namespaces);INIT_LIST_HEAD (&subsys->ctrls);INIT_LIST_HEAD (&subsys->hosts); +INIT_LIST_HEAD (&subsys->entry); + +mutex_lock (&nvmet_subsystems_lock); +list_add_tail (&subsys->entry, &nvmet_subsystems); +mutex_unlock (&nvmet_subsystems_lock);return subsys; }
@@ -1177,6 +1185,10 @@ static void nvmet_subsys_free(struct kref *ref)WARN_ON_ONCE (!list_empty (&subsys->namespaces)); +mutex_lock (&nvmet_subsystems_lock); +list_del (&subsys->entry); +mutex_unlock (&nvmet_subsystems_lock); +kfree (subsys->subsysnqn);kfree (subsys); }
drivers/nvme/target/nvmet.h +2 -0
@@ -211,6 +211,8 @@ struct nvmet_subsys {struct config_group namespaces_group;struct config_group allowed_hosts_group; + +struct list_head entry; };static inline struct nvmet_subsys *to_subsys (struct config_item *item)
Signature · 1 line
2.14.1
PATCH v4 5/9 nvmet: add passthru cmd handlers
In reply to Chaitanya KulkarniThis patch adds passthru command handling capability for the NVMeOF target and exports passthru APIs which are used to integrate passthru code with nvmet-core. We add passthru ns member to the target request to hold the ns reference for respective commands.
The new file pt-cmd.c handles passthru cmd parsing and execution. In the passthru mode, we create a block layer request from the nvmet request and map the data on to the block layer request. For handling the side effects we add two functions similar to the nvme_passthru[start|end]() functions present in the nvme-core. We explicitly blacklist the commands at the time of parsing, which allows us to route the fabric commands through default code path.
--- drivers/nvme/target/Makefile | 2 +- drivers/nvme/target/nvmet.h | 9 + drivers/nvme/target/pt-cmd.c | 396 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 drivers/nvme/target/pt-cmd.c
drivers/nvme/target/Makefile +1 -1
@@ -7,7 +7,7 @@ obj-$(CONFIG_NVME_TARGET_FC) += nvmet-fc.o obj-$(CONFIG_NVME_TARGET_FCLOOP) += nvme-fcloop.o nvmet-y += core.o configfs.o admin-cmd.o fabrics-cmd.o \ - discovery.o io-cmd-file.o io-cmd-bdev.o + discovery.o io-cmd-file.o io-cmd-bdev.o pt-cmd.o nvme-loop-y += loop.o nvmet-rdma-y += rdma.o nvmet-fc-y += fc.o
drivers/nvme/target/nvmet.h +9 -0
@@ -27,6 +27,8 @@#include <linux/rcupdate.h> #include <linux/blkdev.h> +#include "../host/nvme.h" +#define NVMET_ASYNC_EVENTS 4#define NVMET_ERROR_LOG_SLOTS 128
@@ -213,6 +215,8 @@ struct nvmet_subsys {struct config_group allowed_hosts_group;struct list_head entry; + +struct nvme_ctrl *pt_ctrl; };static inline struct nvmet_subsys *to_subsys (struct config_item *item)
@@ -285,6 +289,9 @@ struct nvmet_req {struct bio_vec *bvec;struct work_struct work; } f; +struct { +struct nvme_ns *pt_ns; + } p; };int sg_cnt;/* data length as parsed from the command: */
@@ -327,6 +334,7 @@ struct nvmet_async_event { };u16 nvmet_parse_connect_cmd (struct nvmet_req *req); +u16 nvmet_pt_parse_pt_cmd (struct nvmet_req *req);u16 nvmet_bdev_parse_io_cmd (struct nvmet_req *req);u16 nvmet_file_parse_io_cmd (struct nvmet_req *req);u16 nvmet_parse_admin_cmd (struct nvmet_req *req);
@@ -437,6 +445,7 @@ void nvmet_file_ns_disable(struct nvmet_ns *ns);u16 nvmet_bdev_flush (struct nvmet_req *req);u16 nvmet_file_flush (struct nvmet_req *req);void nvmet_ns_changed (struct nvmet_subsys *subsys,u32 nsid); +bool nvmet_pt_cmd_allow (struct nvmet_req *req);static inline u32 nvmet_rw_len (struct nvmet_req *req) {
drivers/nvme/target/pt-cmd.c new file +396 -0
396 lines not on this page · raw message
Signature · 1 line
2.14.1
PATCH v4 6/9 nvmet: integrate passthru code with core
In reply to Chaitanya KulkarniThis patch integrates the passthru command code with NVMeOF target core. As we integrate the passthru code with target core, it exports APIs to enable/disable passthru ctrl via configfs.
Here we also make sure none of the target-ns belongs to the passthru-ctrl and none of the existing subsystems is configured with this passthru-ctrl.
--- drivers/nvme/target/core.c | 180 +++++++++++++++++++++++++++++++++++++++++--- drivers/nvme/target/nvmet.h | 4 + 2 files changed, 175 insertions(+), 9 deletions(-)
drivers/nvme/target/core.c +171 -9
@@ -369,11 +369,66 @@ static void nvmet_ns_dev_disable(struct nvmet_ns *ns)nvmet_file_ns_disable (ns); } +static int nvmet_pt_ns (struct nvme_ctrl *ctrl,char *disk_name) +{ +struct nvme_ns *ns; +int ret =0 ; + +if (!disk_name) +return -EINVAL ; + +down_read (&ctrl->namespaces_rwsem); +list_for_each_entry (ns, &ctrl->namespaces, list) { +if (!strcmp (ns->disk->disk_name, disk_name)) { + ret = -EINVAL ; +break ; + } + } +up_read (&ctrl->namespaces_rwsem); +return ret; +} + +static int nvmet_ns_device_path_allow (struct nvmet_ns *ns) +{ +struct nvmet_subsys *s = ns->subsys; +char *dev_file; +int ret =0 ; + +mutex_lock (&nvmet_subsystems_lock); +list_for_each_entry (s, &nvmet_subsystems, entry) { +kref_get (&s->ref); +mutex_unlock (&nvmet_subsystems_lock); +mutex_lock (&s->lock); +if (s->pt_ctrl) { + dev_file =strrchr (ns->device_path,'/' ); + dev_file++; +if (nvmet_pt_ns (s->pt_ctrl, dev_file)) { +pr_err ("%s ns belongs to passthru ctrl\n" , + ns->device_path); + ret = -EINVAL ; +mutex_unlock (&s->lock); +mutex_lock (&nvmet_subsystems_lock); +nvmet_subsys_put (s); +break ; + } + } +mutex_unlock (&s->lock); +mutex_lock (&nvmet_subsystems_lock); +nvmet_subsys_put (s); + } +mutex_unlock (&nvmet_subsystems_lock); +return ret; +} +int nvmet_ns_enable (struct nvmet_ns *ns) {struct nvmet_subsys *subsys = ns->subsys;int ret; + ret =nvmet_ns_device_path_allow (ns); +if (ret) + goto out; +mutex_lock (&subsys->lock); ret = -EMFILE ;if (subsys->nr_namespaces ==NVMET_MAX_NAMESPACES )
@@ -420,6 +475,7 @@ int nvmet_ns_enable(struct nvmet_ns *ns) ret =0 ; out_unlock:mutex_unlock (&subsys->lock); +out:return ret; out_dev_put:nvmet_ns_dev_disable (ns);
@@ -498,6 +554,77 @@ struct nvmet_ns *nvmet_ns_alloc(struct nvmet_subsys *subsys, u32 nsid)return ns; } +int nvmet_pt_ctrl_enable (struct nvmet_subsys *subsys) +{ +struct nvmet_subsys *s; +struct nvmet_ns *tns; +char *pt_ctrl_path; +char *dev_file; +int ret =0 ; + +if (!subsys) +return -ENODEV ; + +if (subsys->pt_ctrl) +return -EINVAL ; + + pt_ctrl_path = subsys->pt_ctrl_path; +if (nvme_get_ctrl_by_path (pt_ctrl_path, &subsys->pt_ctrl)) { +pr_err ("unable to find passthru ctrl '%s'\n" , pt_ctrl_path); +return -ENODEV ; + } + +mutex_lock (&nvmet_subsystems_lock); +list_for_each_entry (s, &nvmet_subsystems, entry) { +/* skip current subsys */ +if (!strcmp (s->subsysnqn, subsys->subsysnqn)) +continue ; + +kref_get (&s->ref); +mutex_unlock (&nvmet_subsystems_lock); +/* check if this pt ctrl is alredy belongs to other subsys */ +if (s->pt_ctrl && !strcmp (s->pt_ctrl_path, pt_ctrl_path)) { + ret = -EINVAL ; +pr_err ("pt ctrl %s is associated with subsys %s\n" , + subsys->pt_ctrl_path, subsys->subsysnqn); + goto out; + } +/* check if any target ns is present on pt ctrl's ns list */ +mutex_lock (&s->lock); +list_for_each_entry (tns, &s->namespaces, dev_link) { + dev_file =strrchr (tns->device_path,'/' ); + dev_file++; +if (nvmet_pt_ns (subsys->pt_ctrl, dev_file)) { + ret = -EINVAL ; +pr_err ("ns conflict passthru enable failed\n" ); +break ; + } + } +mutex_unlock (&s->lock); +out: +mutex_lock (&nvmet_subsystems_lock); +nvmet_subsys_put (s); +if (ret) +break ; + + } +mutex_unlock (&nvmet_subsystems_lock); +if (ret) { +nvmet_pt_ctrl_disable (subsys); +kfree (subsys->pt_ctrl_path); + subsys->pt_ctrl_path =NULL ; + } +return ret; +} + +void nvmet_pt_ctrl_disable (struct nvmet_subsys *subsys) +{ +if (!subsys || !subsys->pt_ctrl) +return ; + +nvme_put_ctrl_by_path (subsys->pt_ctrl); + subsys->pt_ctrl =NULL ; +}static void __nvmet_req_complete (struct nvmet_req *req,u16 status) {u32 old_sqhd, new_sqhd;
@@ -687,6 +814,8 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq,if (unlikely (!req->sq->ctrl))/* will return an error for any Non-connect command: */ status =nvmet_parse_connect_cmd (req); +else if (nvmet_pt_cmd_allow (req)) + status =nvmet_pt_parse_pt_cmd (req);else if (likely (req->sq->qid !=0 )) status =nvmet_parse_io_cmd (req);else if (req->cmd->common.opcode == nvme_fabrics_command)
@@ -722,7 +851,16 @@ EXPORT_SYMBOL_GPL(nvmet_req_uninit);void nvmet_req_execute (struct nvmet_req *req) { -if (unlikely (req->data_len != req->transfer_len)) +/* +* Right now data_len is calculated before the transfer len +* after we parse the command, With passthru interface +* we allow VUC's. In order to make the code simple and compact, +* instead of assinging the the dala len for each VUC in the command +* parse function just use the transfer len as it is. This may +* result is error if expected data_len != transfer_len. +*/ +if (!(req->sq->ctrl && req->sq->ctrl->subsys->pt_ctrl) && +unlikely (req->data_len != req->transfer_len))nvmet_req_complete (req,NVME_SC_SGL_INVALID_DATA |NVME_SC_DNR );else req->execute (req);
@@ -943,6 +1081,19 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn, } status =NVME_SC_CONNECT_INVALID_PARAM |NVME_SC_DNR ; +/* +* Check here if this subsystem is already connected to the +* passthru ctrl. We allow only one target ctrl for one passthru +* subsystem. +*/ +mutex_lock (&subsys->lock); +if (subsys->pt_ctrl) { +if (subsys->pt_connected == true) { +mutex_unlock (&subsys->lock); + goto out_put_subsystem; + } + } +mutex_unlock (&subsys->lock);down_read (&nvmet_config_sem);if (!nvmet_host_allowed (req, subsys, hostnqn)) { pr_info("connect by host %s for subsystem %s not allowed\n",
@@ -991,12 +1142,16 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,if (!ctrl->sqs) goto out_free_cqs; - ret =ida_simple_get (&cntlid_ida, -NVME_CNTLID_MIN ,NVME_CNTLID_MAX , -GFP_KERNEL ); -if (ret <0 ) { - status =NVME_SC_CONNECT_CTRL_BUSY |NVME_SC_DNR ; - goto out_free_sqs; +if (subsys->pt_ctrl) + ret = subsys->pt_ctrl->cntlid; +else { + ret =ida_simple_get (&cntlid_ida, +NVME_CNTLID_MIN ,NVME_CNTLID_MAX , +GFP_KERNEL ); +if (ret <0 ) { + status =NVME_SC_CONNECT_CTRL_BUSY |NVME_SC_DNR ; + goto out_free_sqs; + } } ctrl->cntlid = ret;
@@ -1030,13 +1185,16 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,mutex_lock (&subsys->lock);list_add_tail (&ctrl->subsys_entry, &subsys->ctrls); +if (subsys->pt_ctrl && subsys->pt_connected == false) + subsys->pt_connected = true;mutex_unlock (&subsys->lock); *ctrlp = ctrl;return 0 ; out_remove_ida: -ida_simple_remove (&cntlid_ida, ctrl->cntlid); +if (!subsys->pt_ctrl) +ida_simple_remove (&cntlid_ida, ctrl->cntlid); out_free_sqs:kfree (ctrl->sqs); out_free_cqs:
@@ -1057,6 +1215,8 @@ static void nvmet_ctrl_free(struct kref *ref)struct nvmet_subsys *subsys = ctrl->subsys;mutex_lock (&subsys->lock); +if (subsys->pt_ctrl && subsys->pt_connected == true) + subsys->pt_connected = false;list_del (&ctrl->subsys_entry);mutex_unlock (&subsys->lock);
@@ -1065,7 +1225,8 @@ static void nvmet_ctrl_free(struct kref *ref)flush_work (&ctrl->async_event_work);cancel_work_sync (&ctrl->fatal_err_work); -ida_simple_remove (&cntlid_ida, ctrl->cntlid); +if (!subsys->pt_ctrl) +ida_simple_remove (&cntlid_ida, ctrl->cntlid);kfree (ctrl->sqs);kfree (ctrl->cqs);
@@ -1189,6 +1350,7 @@ static void nvmet_subsys_free(struct kref *ref)list_del (&subsys->entry);mutex_unlock (&nvmet_subsystems_lock); +kfree (subsys->pt_ctrl_path);kfree (subsys->subsysnqn);kfree (subsys); }
drivers/nvme/target/nvmet.h +4 -0
@@ -216,6 +216,8 @@ struct nvmet_subsys {struct list_head entry; +bool pt_connected; +char *pt_ctrl_path;struct nvme_ctrl *pt_ctrl; };
@@ -446,6 +448,8 @@ u16 nvmet_bdev_flush(struct nvmet_req *req);u16 nvmet_file_flush (struct nvmet_req *req);void nvmet_ns_changed (struct nvmet_subsys *subsys,u32 nsid);bool nvmet_pt_cmd_allow (struct nvmet_req *req); +int nvmet_pt_ctrl_enable (struct nvmet_subsys *subsys); +void nvmet_pt_ctrl_disable (struct nvmet_subsys *subsys);static inline u32 nvmet_rw_len (struct nvmet_req *req) {
Signature · 1 line
2.14.1
PATCH v4 7/9 nvmet: add configfs interface for target passthru
In reply to Chaitanya KulkarniThis patch adds configfs interface for target passthru ctrl management.
The new directory "pt" under nvmet configfs is added to configure passthru ctrl (pt-ctrl). Once all the fields for the pt-ctrl are set user can enable the pt-ctrl through configfs.
The new directory pt is parallel to the subsystem and has similar attributes as default subsystem, each passthru ctrl is represented as one subsystem. Since we allow ctrl passthru we don't export or allow user to access the passthru namespace(s) in the configfs.
The new attribute "attr_ctrl_path" expects nvme ctrl path, e.g. "/dev/nvmeX".
--- drivers/nvme/target/configfs.c | 145 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-)
drivers/nvme/target/configfs.c +144 -1
@@ -21,6 +21,7 @@#include "nvmet.h" static const struct config_item_type nvmet_host_type; +static const struct config_item_type nvmet_pt_type;static const struct config_item_type nvmet_subsys_type; static const struct nvmet_transport_name {
@@ -570,7 +571,8 @@ static int nvmet_port_subsys_allow_link(struct config_item *parent,struct nvmet_subsys_link *link, *p;int ret; -if (target->ci_type != &nvmet_subsys_type) { +if (!(target->ci_type == &nvmet_subsys_type || + target->ci_type == &nvmet_pt_type)) {pr_err ("can only link subsystems into the subsystems dir.!\n" );return -EINVAL ; }
@@ -865,6 +867,141 @@ static const struct config_item_type nvmet_subsystems_type = { .ct_owner = THIS_MODULE, }; + +/* +* Passthru attributes and operations. +*/ +static ssize_t nvmet_pt_attr_ctrl_path_show (struct config_item *item, +char *page) +{ +struct nvmet_subsys *subsys =to_subsys (item); + +return snprintf (page,PAGE_SIZE ,"%s\n" , subsys->pt_ctrl_path); +} + +static ssize_t nvmet_pt_attr_ctrl_path_store (struct config_item *item, +const char *page,size_t count) +{ +struct nvmet_subsys *subsys =to_subsys (item); +int ret = -ENOMEM ; + +mutex_lock (&subsys->lock); +kfree (subsys->pt_ctrl_path); + + subsys->pt_ctrl_path =kstrdup (page,GFP_KERNEL ); +if (!subsys->pt_ctrl_path) + goto out_unlock; + +mutex_unlock (&subsys->lock); + +return count; +out_unlock: +mutex_unlock (&subsys->lock); +return ret; + +} +CONFIGFS_ATTR (nvmet_pt_, attr_ctrl_path); + +static ssize_t nvmet_pt_attr_enable_show (struct config_item *item,char *page) +{ +return sprintf (page,"%d\n" ,to_subsys (item)->pt_ctrl ?1 :0 ); +} + +static ssize_t nvmet_pt_attr_enable_store (struct config_item *item, +const char *page,size_t count) +{ +struct nvmet_subsys *subsys =to_subsys (item); +bool enable; +int ret =0 ; + +if (subsys->pt_ctrl_path ==NULL ) { +pr_err ("passthru ctrl path is not initialized.\n" ); +return -EINVAL ; + } + +if (strtobool (page, &enable)) +return -EINVAL ; + +if (enable) + ret =nvmet_pt_ctrl_enable (subsys); +else +nvmet_pt_ctrl_disable (subsys); + +return ret ? ret : count; +} +CONFIGFS_ATTR (nvmet_pt_, attr_enable); + +static struct configfs_attribute *nvmet_pt_attrs[] = { + &nvmet_pt_attr_attr_ctrl_path, + &nvmet_pt_attr_attr_enable, + &nvmet_subsys_attr_attr_allow_any_host, + &nvmet_subsys_attr_attr_version, + &nvmet_subsys_attr_attr_serial, +NULL , +}; + +/* +* Passthru structures & folder operation functions below +*/ +static void nvmet_pt_release (struct config_item *item) +{ +struct nvmet_subsys *subsys =to_subsys (item); + +nvmet_subsys_del_ctrls (subsys); +/* +* Since we are the only one to manage the pt ctrl, +* disable the pt ctrl in case user did not disable +* the ctrl attribute prior to removing the passthru subsystem. +*/ + +nvmet_pt_ctrl_disable (subsys); + +nvmet_subsys_put (subsys); +} + +static struct configfs_item_operations nvmet_pt_item_ops = { + .release = nvmet_pt_release, +}; + +static const struct config_item_type nvmet_pt_type = { + .ct_item_ops = &nvmet_pt_item_ops, + .ct_attrs = nvmet_pt_attrs, + .ct_owner =THIS_MODULE , +}; + +static struct config_group *nvmet_pt_make (struct config_group *group, +const char *name) +{ +struct nvmet_subsys *subsys; + +if (sysfs_streq (name,NVME_DISC_SUBSYS_NAME )) { +pr_err ("can't create discovery subsystem through configfs\n" ); +return ERR_PTR (-EINVAL ); + } + + subsys =nvmet_subsys_alloc (name,NVME_NQN_NVME ); +if (!subsys) +return ERR_PTR (-ENOMEM ); + +config_group_init_type_name (&subsys->group, name, &nvmet_pt_type); + +config_group_init_type_name (&subsys->allowed_hosts_group, +"allowed_hosts" , &nvmet_allowed_hosts_type); +configfs_add_default_group (&subsys->allowed_hosts_group, + &subsys->group); + +return &subsys->group; +} + +static struct configfs_group_operations nvmet_pts_group_ops = { + .make_group = nvmet_pt_make, +}; + +static struct config_item_type nvmet_pts_type = { + .ct_group_ops = &nvmet_pts_group_ops, + .ct_owner =THIS_MODULE , +}; +static ssize_t nvmet_referral_enable_show (struct config_item *item,char *page) {
@@ -1178,6 +1315,7 @@ static const struct config_item_type nvmet_ports_type = { };static struct config_group nvmet_subsystems_group; +static struct config_group nvmet_pts_group;static struct config_group nvmet_ports_group;static void nvmet_host_release (struct config_item *item)
@@ -1241,6 +1379,11 @@ int __init nvmet_init_configfs(void)config_group_init (&nvmet_configfs_subsystem.su_group);mutex_init (&nvmet_configfs_subsystem.su_mutex); +config_group_init_type_name (&nvmet_pts_group, +"passthru" , &nvmet_pts_type); +configfs_add_default_group (&nvmet_pts_group, + &nvmet_configfs_subsystem.su_group); +config_group_init_type_name (&nvmet_subsystems_group,"subsystems" , &nvmet_subsystems_type); configfs_add_default_group(&nvmet_subsystems_group,
Signature · 1 line
2.14.1
PATCH v4 8/9 block: add helper to get the request based cookie
In reply to Chaitanya KulkarniThis patch adds a helper function to get calculate the cookie based on the request. This is needed for the drivers which are issuing request based I/Os such as NVMe Over Fabrics target passthru driver an using polling.
--- block/blk-mq.c | 20 ++++++++++++++++++++ include/linux/blk-mq.h | 1 + 2 files changed, 21 insertions(+)
block/blk-mq.c +20 -0
@@ -1669,6 +1669,26 @@ static blk_qc_t request_to_qc_t(struct blk_mq_hw_ctx *hctx, struct request *rq)return blk_tag_to_qc_t (rq->internal_tag, hctx->queue_num, true); } +blk_qc_t blk_mq_req_cookie (struct request *rq) +{ +struct blk_mq_ctx *ctx; +struct blk_mq_hw_ctx *hctx; + +if (!rq || !rq->q) +return BLK_QC_T_NONE ; + + ctx =blk_mq_get_ctx (rq->q); +if (!ctx) +return BLK_QC_T_NONE ; + + hctx =blk_mq_map_queue (rq->q, ctx->cpu); +if (!hctx) +return BLK_QC_T_NONE ; + +return request_to_qc_t (hctx, rq); +} +EXPORT_SYMBOL (blk_mq_req_cookie); +static blk_status_t __blk_mq_issue_directly (struct blk_mq_hw_ctx *hctx,struct request *rq,blk_qc_t *cookie)
include/linux/blk-mq.h +1 -0
@@ -270,6 +270,7 @@ void blk_mq_stop_hw_queues(struct request_queue *q);void blk_mq_start_hw_queues (struct request_queue *q);void blk_mq_start_stopped_hw_queue (struct blk_mq_hw_ctx *hctx,bool async);void blk_mq_start_stopped_hw_queues (struct request_queue *q,bool async); +blk_qc_t blk_mq_req_cookie (struct request *rq);void blk_mq_quiesce_queue (struct request_queue *q);void blk_mq_unquiesce_queue (struct request_queue *q);void blk_mq_delay_run_hw_queue (struct blk_mq_hw_ctx *hctx,unsigned long msecs);
Signature · 1 line
2.14.1
PATCH v4 9/9 nvmet: add request polling support
In reply to Chaitanya KulkarniUse request based polling for NVMeOF target passthru I/O submission.
--- drivers/nvme/target/pt-cmd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/nvme/target/pt-cmd.c +3 -1
@@ -306,8 +306,10 @@ static void nvmet_pt_execute_pt(struct nvmet_req *req) goto fail_free_bio; rq->end_io_data = req; -if (likely (req->sq->qid !=0 )) +if (likely (req->sq->qid !=0 )) {blk_execute_rq_nowait (rq->q,NULL , rq,0 , nvmet_pt_req_done); +blk_poll (rq->q,blk_mq_req_cookie (rq)); + }else nvmet_pt_execute_admin_cmd (req, rq);return ;
Signature · 1 line
2.14.1