nvmet: Add support for multi-tenant configfs
Subsystems (3)
v2 cover nvmet: Add support for multi-tenant configfs
From: Nicholas Bellinger <[email protected]>
Hi folks,
Here's the second pass of a nvmet multi-tenant configfs layout, following what we've learned in target_core_fabric_configfs.c wrt to independent operation of storage endpoints.
Namely, it allows existing /sys/kernel/config/target/core/ backends to be configfs symlinked into ../nvmet/subsystems/$SUBSYS_NQN/ as nvme namespaces.
Here is how the running RFC-v2 code currently looks:
/sys/kernel/config/nvmet/subsystems/
??? nqn.2003-01.org.linux-iscsi.NVMf.skylake-ep
??? hosts
??? namespaces
? ??? 1
? ??? ramdisk0 -> ../../../../../target/core/rd_mcp_1/ramdisk0
??? ports
??? loop
??? addr_adrfam
??? addr_portid
??? addr_traddr
??? addr_treq
??? addr_trsvcid
??? addr_trtype
??? enableThe series exposes T10-PI from /sys/kernel/config/target/core/ as ID_NS.ms + ID_NS.dps feature bits, and enables block integrity support with nvmet/loop driver.
Note this series depends upon the following prerequisites of target-core:
http://marc.info/?l=linux-scsi&m=146527281416606&w=2
and of course, last week's earlier release of nvmet + friends:
http://lists.infradead.org/pipermail/linux-nvme/2016-June/004754.html
Note the full set of patches is available from:
https://git.kernel.org/cgit/linux/kernel/git/nab/target-pending.git/log/?h=nvmet-configfs-ng
v2 changes:
- Introduce struct nvmet_port_binding in configfs-ng.c, in order to support 1:N mappings.
- Convert nvmet_find_get_subsys() + discovery.c logic to use
nvmet_port->port_binding_list.
- Convert nvmet/loop to use nvmet_port_binding. (1:1 mapping)
- Convert nvmet/rdma to use nvmet_port_binding + nvmet_rdma_ports.
(1:N mapping) - Export nvmf_host_add + generate hostnqn if necessary. (HCH)
- Make nvmet/loop multi-controller allocate it's own nvmf_host per controller. (HCH)
- Change nvmet_fabric_ops get/put to use nvmf_disc_rsp_page_entry.
- Convert nvmet_genctr to atomic_long_t.
- Enable ../nvmet/subsystems/$NQN/hosts/$HOSTNQN group usage in configfs-ng.c.
Comments..?
--nab
Nicholas Bellinger (11):
nvme-fabrics: Export nvmf_host_add + generate hostnqn if necessary
nvmet: Add nvmet_fabric_ops get/put transport helpers
nvmet: Add support for configfs-ng multi-tenant logic
nvmet: Hookup nvmet_ns->dev to nvmet_ns_enable nvmet/loop: Add support for controller-per-port model +
nvmet_port_binding
nvmet/rdma: Convert to struct nvmet_port_binding nvmet/io-cmd: Hookup sbc_ops->execute_rw backend ops nvmet/io-cmd: Hookup sbc_ops->execute_sync_cache backend ops nvmet/io-cmd: Hookup sbc_ops->execute_unmap backend ops nvmet/admin-cmd: Hookup T10-PI to ID_NS.ms + ID_NS.dps feature bits nvmet/loop: Add support for bio integrity handling
drivers/nvme/host/fabrics.c | 18 +- drivers/nvme/host/fabrics.h | 1 + drivers/nvme/target/Makefile | 2 +- drivers/nvme/target/admin-cmd.c | 17 + drivers/nvme/target/configfs-ng.c | 661 ++++++++++++++++++++++++++++++++++++++ drivers/nvme/target/configfs.c | 12 +- drivers/nvme/target/core.c | 153 ++++++--- drivers/nvme/target/discovery.c | 31 +- drivers/nvme/target/io-cmd.c | 169 ++++++---- drivers/nvme/target/loop.c | 223 +++++++++++-- drivers/nvme/target/nvmet.h | 67 +++- drivers/nvme/target/rdma.c | 127 +++++++- 12 files changed, 1317 insertions(+), 164 deletions(-) create mode 100644 drivers/nvme/target/configfs-ng.c
Signature · 1 line
1.9.1
RFC v2 1/11 nvme-fabrics: Export nvmf_host_add + generate hostnqn if necessary
From: Nicholas Bellinger <[email protected]>
This patch allows nvmf_host_add() to be used externally, and optionally if no hostnqn is passed will generate a hostnqn based on host->id, following nvmf_host_default().
Note it's required for nvme-loop multi-controller support, in order to drive nvmet_port creation directly via configfs attribute write from in ../nvmet/subsystems/$NQN/ports/$PORT/ group context.
--- drivers/nvme/host/fabrics.c | 18 +++++++++++++----- drivers/nvme/host/fabrics.h | 1 + 2 files changed, 14 insertions(+), 5 deletions(-)
drivers/nvme/host/fabrics.c +13 -5
@@ -41,28 +41,36 @@ static struct nvmf_host *__nvmf_host_find(const char *hostnqn)return NULL ; } -static struct nvmf_host *nvmf_host_add (const char *hostnqn) +struct nvmf_host *nvmf_host_add (const char *hostnqn) {struct nvmf_host *host;mutex_lock (&nvmf_hosts_mutex); - host =__nvmf_host_find (hostnqn); -if (host) - goto out_unlock; +if (hostnqn) { + host =__nvmf_host_find (hostnqn); +if (host) + goto out_unlock; + } host =kmalloc (sizeof (*host),GFP_KERNEL );if (!host) goto out_unlock;kref_init (&host->ref); -memcpy (host->nqn, hostnqn,NVMF_NQN_SIZE );uuid_le_gen (&host->id); +if (hostnqn) +memcpy (host->nqn, hostnqn,NVMF_NQN_SIZE ); +else +snprintf (host->nqn,NVMF_NQN_SIZE , +"nqn.2014-08.org.nvmexpress:NVMf:uuid:%pUl" , &host->id); +list_add_tail (&host->list, &nvmf_hosts); out_unlock:mutex_unlock (&nvmf_hosts_mutex);return host; } +EXPORT_SYMBOL_GPL (nvmf_host_add);static struct nvmf_host *nvmf_host_default (void ) {
drivers/nvme/host/fabrics.h +1 -0
@@ -128,6 +128,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl);int nvmf_connect_io_queue (struct nvme_ctrl *ctrl,u16 qid);void nvmf_register_transport (struct nvmf_transport_ops *ops);void nvmf_unregister_transport (struct nvmf_transport_ops *ops); +struct nvmf_host *nvmf_host_add (const char *hostnqn);void nvmf_free_options (struct nvmf_ctrl_options *opts);const char *nvmf_get_subsysnqn (struct nvme_ctrl *ctrl);int nvmf_get_address (struct nvme_ctrl *ctrl,char *buf,int size);
Signature · 1 line
1.9.1
RFC v2 2/11 nvmet: Add nvmet_fabric_ops get/put transport helpers
In reply to Nicholas A. BellingerFrom: Nicholas Bellinger <[email protected]>
This patch introduces two helpers for obtaining + releasing nvmet_fabric_ops for nvmet_port usage, and the associated struct module ops->owner reference.
This is required in order to support nvmet/configfs-ng and multiple nvmet_port configfs groups living under /sys/kernel/config/nvmet/subsystems/$SUBSYS_NQN/ports/
--- drivers/nvme/target/core.c | 32 ++++++++++++++++++++++++++++++++ drivers/nvme/target/nvmet.h | 4 ++++ 2 files changed, 36 insertions(+)
drivers/nvme/target/core.c +32 -0
@@ -191,6 +191,38 @@ void nvmet_disable_port(struct nvmet_port *port)module_put (ops->owner); } +struct nvmet_fabrics_ops * +nvmet_get_transport (struct nvmf_disc_rsp_page_entry *disc_addr) +{ +struct nvmet_fabrics_ops *ops; + +down_write (&nvmet_config_sem); + ops = nvmet_transports[disc_addr->trtype]; +if (!ops) { +pr_err ("transport type %d not supported\n" , + disc_addr->trtype); +return ERR_PTR (-EINVAL ); + } + +if (!try_module_get (ops->owner)) { +up_write (&nvmet_config_sem); +return ERR_PTR (-EINVAL ); + } +up_write (&nvmet_config_sem); + +return ops; +} + +void nvmet_put_transport (struct nvmf_disc_rsp_page_entry *disc_addr) +{ +struct nvmet_fabrics_ops *ops; + +down_write (&nvmet_config_sem); + ops = nvmet_transports[disc_addr->trtype]; +module_put (ops->owner); +up_write (&nvmet_config_sem); +} +static void nvmet_keep_alive_timer (struct work_struct *work) { struct nvmet_ctrl *ctrl = container_of(to_delayed_work(work),
drivers/nvme/target/nvmet.h +4 -0
@@ -299,6 +299,10 @@ void nvmet_unregister_transport(struct nvmet_fabrics_ops *ops);int nvmet_enable_port (struct nvmet_port *port);void nvmet_disable_port (struct nvmet_port *port); +struct nvmet_fabrics_ops *nvmet_get_transport ( +struct nvmf_disc_rsp_page_entry *disc_addr); +void nvmet_put_transport (struct nvmf_disc_rsp_page_entry *disc_addr);; +void nvmet_referral_enable (struct nvmet_port *parent,struct nvmet_port *port);void nvmet_referral_disable (struct nvmet_port *port);
Signature · 1 line
1.9.1
RFC v2 3/11 nvmet: Add support for configfs-ng multi-tenant logic
In reply to Nicholas A. BellingerFrom: Nicholas Bellinger <[email protected]>
This patch introduces support for configfs-ng, that allows for multi-tenant /sys/kernel/config/nvmet/subsystems/$SUBSYS_NQN/ operation, using existing /sys/kernel/config/target/core/ backends from target-core to be configfs symlinked as per nvme-target subsystem NQN namespaces.
Here's how the layout looks:
/sys/kernel/config/nvmet/
??? subsystems
??? nqn.2003-01.org.linux-iscsi.NVMf.skylake-ep
??? namespaces
??? ??? 1
??? ??? ramdisk0 -> ../../../../../target/core/rd_mcp_1/ramdisk0
??? ports
??? loop
??? addr_adrfam
??? addr_portid
??? addr_traddr
??? addr_treq
??? addr_trsvcid
??? addr_trtype
??? enableIt convert nvmet_find_get_subsys to port_binding_list, and do the same for nvmet_host_discovery_allowed.
Also convert nvmet_genctr to atomic_long_t, so it can be used outside of nvmet_config_sem.
--- drivers/nvme/target/Makefile | 2 +- drivers/nvme/target/configfs-ng.c | 662 ++++++++++++++++++++++++++++++++++++++ drivers/nvme/target/configfs.c | 12 +- drivers/nvme/target/core.c | 91 ++++-- drivers/nvme/target/discovery.c | 31 +- drivers/nvme/target/nvmet.h | 50 ++- 6 files changed, 812 insertions(+), 36 deletions(-) create mode 100644 drivers/nvme/target/configfs-ng.c
drivers/nvme/target/Makefile +1 -1
@@ -3,7 +3,7 @@ obj-$(CONFIG_NVME_TARGET) += nvmet.o obj-$(CONFIG_NVME_TARGET_LOOP) += nvme-loop.o obj-$(CONFIG_NVME_TARGET_RDMA) += nvmet-rdma.o -nvmet-y += core.o configfs.o admin-cmd.o io-cmd.o fabrics-cmd.o \ +nvmet-y += core.o configfs-ng.o admin-cmd.o io-cmd.o fabrics-cmd.o \ discovery.o nvme-loop-y += loop.o nvmet-rdma-y += rdma.o
drivers/nvme/target/configfs-ng.c new file +662 -0
662 lines not on this page · raw message
drivers/nvme/target/configfs.c +11 -1
@@ -441,7 +441,9 @@ static int nvmet_port_subsys_allow_link(struct config_item *parent,if (!link)return -ENOMEM ; link->subsys = subsys; - +#if 1 +BUG_ON (1 ); +#else down_write (&nvmet_config_sem); ret = -EEXIST ;list_for_each_entry (p, &port->subsystems, entry) {
@@ -458,6 +460,7 @@ static int nvmet_port_subsys_allow_link(struct config_item *parent,list_add_tail (&link->entry, &port->subsystems); nvmet_genctr++;up_write (&nvmet_config_sem); +#endif return 0 ; out_free_link:
@@ -469,6 +472,7 @@ out_free_link:static int nvmet_port_subsys_drop_link (struct config_item *parent,struct config_item *target) { +#if 0 struct nvmet_port *port =to_nvmet_port (parent->ci_parent);struct nvmet_subsys *subsys =to_subsys (target);struct nvmet_subsys_link *p;
@@ -487,7 +491,9 @@ found:if (list_empty (&port->subsystems))nvmet_disable_port (port);up_write (&nvmet_config_sem); +kfree (p); +#endif return 0 ; }
@@ -504,6 +510,7 @@ static struct config_item_type nvmet_port_subsys_type = {static int nvmet_allowed_hosts_allow_link (struct config_item *parent,struct config_item *target) { +#if 0 struct nvmet_subsys *subsys =to_subsys (parent->ci_parent);struct nvmet_host *host;struct nvmet_host_link *link, *p;
@@ -540,11 +547,13 @@ out_free_link:up_write (&nvmet_config_sem);kfree (link);return ret; +#endif }static int nvmet_allowed_hosts_drop_link (struct config_item *parent,struct config_item *target) { +#if 0 struct nvmet_subsys *subsys =to_subsys (parent->ci_parent);struct nvmet_host *host =to_host (target);struct nvmet_host_link *p;
@@ -563,6 +572,7 @@ found:up_write (&nvmet_config_sem);kfree (p);return 0 ; +#endif }static struct configfs_item_operations nvmet_allowed_hosts_item_ops = {
drivers/nvme/target/core.c +67 -24
191 lines not on this page · raw message
drivers/nvme/target/discovery.c +25 -6
@@ -18,7 +18,7 @@struct nvmet_subsys *nvmet_disc_subsys; -u64 nvmet_genctr; +atomic_long_t nvmet_genctr;void nvmet_referral_enable (struct nvmet_port *parent,struct nvmet_port *port) {
@@ -26,7 +26,7 @@ void nvmet_referral_enable(struct nvmet_port *parent, struct nvmet_port *port)if (list_empty (&port->entry)) {list_add_tail (&port->entry, &parent->referrals); port->enabled = true; - nvmet_genctr++; +atomic64_inc (&nvmet_genctr); }up_write (&nvmet_config_sem); }
@@ -37,7 +37,7 @@ void nvmet_referral_disable(struct nvmet_port *port)if (!list_empty (&port->entry)) { port->enabled = false;list_del_init (&port->entry); - nvmet_genctr++; +atomic64_inc (&nvmet_genctr); }up_write (&nvmet_config_sem); }
@@ -69,8 +69,8 @@ static void nvmet_execute_get_disc_log_page(struct nvmet_req *req)size_t data_len =nvmet_get_log_page_len (req->cmd);size_t alloc_len =max (data_len,sizeof (*hdr));int residual_len = data_len -sizeof (*hdr); -struct nvmet_subsys_link *p; -struct nvmet_port *r; +struct nvmet_port *port = req->port; +struct nvmet_port_binding *pb;u32 numrec =0 ;u16 status =0 ;
@@ -84,7 +84,7 @@ static void nvmet_execute_get_disc_log_page(struct nvmet_req *req) status =NVME_SC_INTERNAL ; goto out; } - +#if 0 down_read (&nvmet_config_sem);list_for_each_entry (p, &req->port->subsystems, entry) {if (!nvmet_host_allowed (req, p->subsys, ctrl->hostnqn))
@@ -113,7 +113,26 @@ static void nvmet_execute_get_disc_log_page(struct nvmet_req *req) hdr->recfmt =cpu_to_le16 (0 );up_read (&nvmet_config_sem); +#else +mutex_lock (&port->port_binding_mutex); +list_for_each_entry (pb, &port->port_binding_list, node) { +if (!nvmet_host_allowed (req, pb->nf_subsys, ctrl->hostnqn)) +continue ; + +if (residual_len >= entry_size) { +nvmet_format_discovery_entry (hdr, port, + pb->nf_subsys->subsysnqn, +NVME_NQN_NVME , numrec); + residual_len -= entry_size; + } + numrec++; + } + hdr->genctr =cpu_to_le64 (atomic64_read (&nvmet_genctr)); + hdr->numrec =cpu_to_le64 (numrec); + hdr->recfmt =cpu_to_le16 (0 ); +mutex_unlock (&port->port_binding_mutex); +#endif status =nvmet_copy_to_sgl (req,0 , hdr, data_len);kfree (hdr); out:
drivers/nvme/target/nvmet.h +46 -4
105 lines not on this page · raw message
Signature · 1 line
1.9.1
RFC v2 4/11 nvmet: Hookup nvmet_ns->dev to nvmet_ns_enable
In reply to Nicholas A. BellingerFrom: Nicholas Bellinger <[email protected]>
This patch hooks up nvmet_ns_enable() to accept the RCU protected struct se_device provided as a configfs symlink from existing /sys/kernel/config/target/core/ driver backends.
Also, drop the now unused internal ns->bdev + ns->device_path usage, and add WIP stubs for nvmet/io-cmd sbc_ops backend conversion to be added in subsequent patches.
--- drivers/nvme/target/configfs-ng.c | 3 +-- drivers/nvme/target/core.c | 30 ++++++++---------------------- drivers/nvme/target/io-cmd.c | 17 +++++++++++++++-- drivers/nvme/target/nvmet.h | 6 ++---- 4 files changed, 26 insertions(+), 30 deletions(-)
drivers/nvme/target/configfs-ng.c +1 -2
@@ -470,8 +470,7 @@ static int nvmet_ns_link(struct config_item *ns_ci, struct config_item *dev_ci)return -ENOSYS ; } -// XXX: Pass in struct se_device into nvmet_ns_enable -return nvmet_ns_enable (ns); +return nvmet_ns_enable (ns, dev); }static int nvmet_ns_unlink (struct config_item *ns_ci,struct config_item *dev_ci)
drivers/nvme/target/core.c +8 -22
@@ -13,6 +13,8 @@*/ #define pr_fmt (fmt) KBUILD_MODNAME ": " fmt#include <linux/module.h> +#include <target/target_core_base.h> +#include <target/target_core_backend.h> #include "nvmet.h" static struct nvmet_fabrics_ops *nvmet_transports[NVMF_TRTYPE_MAX ];
@@ -292,7 +294,7 @@ void nvmet_put_namespace(struct nvmet_ns *ns)percpu_ref_put (&ns->ref); } -int nvmet_ns_enable (struct nvmet_ns *ns) +int nvmet_ns_enable (struct nvmet_ns *ns,struct se_device *dev) {struct nvmet_subsys *subsys = ns->subsys;struct nvmet_ctrl *ctrl;
@@ -302,23 +304,14 @@ int nvmet_ns_enable(struct nvmet_ns *ns)if (!list_empty (&ns->dev_link)) goto out_unlock; - ns->bdev =blkdev_get_by_path (ns->device_path,FMODE_READ |FMODE_WRITE , -NULL ); -if (IS_ERR (ns->bdev)) { -pr_err ("nvmet: failed to open block device %s: (%ld)\n" , - ns->device_path,PTR_ERR (ns->bdev)); - ret =PTR_ERR (ns->bdev); - ns->bdev =NULL ; - goto out_unlock; - } - - ns->size =i_size_read (ns->bdev->bd_inode); - ns->blksize_shift =blksize_bits (bdev_logical_block_size (ns->bdev)); +rcu_assign_pointer (ns->dev, dev); + ns->size = dev->transport->get_blocks (dev) * dev->dev_attrib.hw_block_size; + ns->blksize_shift =blksize_bits (dev->dev_attrib.hw_block_size); ret =percpu_ref_init (&ns->ref, nvmet_destroy_namespace,0 ,GFP_KERNEL );if (ret) - goto out_blkdev_put; + goto out_unlock;if (ns->nsid > subsys->max_nsid) subsys->max_nsid = ns->nsid;
@@ -348,10 +341,6 @@ int nvmet_ns_enable(struct nvmet_ns *ns) out_unlock:mutex_unlock (&subsys->lock);return ret; -out_blkdev_put: -blkdev_put (ns->bdev,FMODE_WRITE |FMODE_READ ); - ns->bdev =NULL ; - goto out_unlock; }void nvmet_ns_disable (struct nvmet_ns *ns)
@@ -384,16 +373,13 @@ void nvmet_ns_disable(struct nvmet_ns *ns)list_for_each_entry (ctrl, &subsys->ctrls, subsys_entry)nvmet_add_async_event (ctrl,NVME_AER_TYPE_NOTICE ,0 ,0 ); -if (ns->bdev) -blkdev_put (ns->bdev,FMODE_WRITE |FMODE_READ ); +rcu_assign_pointer (ns->dev,NULL );mutex_unlock (&subsys->lock); }void nvmet_ns_free (struct nvmet_ns *ns) {nvmet_ns_disable (ns); - -kfree (ns->device_path);kfree (ns); }
drivers/nvme/target/io-cmd.c +15 -2
@@ -16,6 +16,7 @@#include <linux/module.h> #include "nvmet.h" +#if 0 static void nvmet_bio_done (struct bio *bio) {struct nvmet_req *req = bio->bi_private;
@@ -26,6 +27,7 @@ static void nvmet_bio_done(struct bio *bio)if (bio != &req->inline_bio) bio_put(bio); } +#endif static inline u32 nvmet_rw_len (struct nvmet_req *req) {
@@ -33,6 +35,7 @@ static inline u32 nvmet_rw_len(struct nvmet_req *req) req->ns->blksize_shift; } +#if 0 static void nvmet_inline_bio_init (struct nvmet_req *req) {struct bio *bio = &req->inline_bio;
@@ -41,21 +44,23 @@ static void nvmet_inline_bio_init(struct nvmet_req *req) bio->bi_max_vecs =NVMET_MAX_INLINE_BIOVEC ; bio->bi_io_vec = req->inline_bvec; } +#endif static void nvmet_execute_rw (struct nvmet_req *req) { +#if 0 int sg_cnt = req->sg_cnt;struct scatterlist *sg;struct bio *bio;sector_t sector;blk_qc_t cookie;int rw, i; - +#endif if (!req->sg_cnt) {nvmet_req_complete (req,0 );return ; } - +#if 0 if (req->cmd->rw.opcode == nvme_cmd_write) {if (req->cmd->rw.control &cpu_to_le16 (NVME_RW_FUA )) rw =WRITE_FUA ;
@@ -95,10 +100,12 @@ static void nvmet_execute_rw(struct nvmet_req *req) cookie =submit_bio (rw, bio);blk_poll (bdev_get_queue (req->ns->bdev), cookie); +#endif }static void nvmet_execute_flush (struct nvmet_req *req) { +#if 0 struct bio *bio;nvmet_inline_bio_init (req);
@@ -109,8 +116,10 @@ static void nvmet_execute_flush(struct nvmet_req *req) bio->bi_end_io = nvmet_bio_done;submit_bio (WRITE_FLUSH , bio); +#endif } +#if 0 static u16 nvmet_discard_range (struct nvmet_ns *ns,struct nvme_dsm_range *range,int type,struct bio **bio) {
@@ -119,11 +128,14 @@ static u16 nvmet_discard_range(struct nvmet_ns *ns, le32_to_cpu(range->nlb) << (ns->blksize_shift - 9), GFP_KERNEL, type, bio))return NVME_SC_INTERNAL |NVME_SC_DNR ; + return 0; } +#endif static void nvmet_execute_discard (struct nvmet_req *req) { +#if 0 struct nvme_dsm_range range;struct bio *bio =NULL ;int type =REQ_WRITE |REQ_DISCARD , i;
@@ -152,6 +164,7 @@ static void nvmet_execute_discard(struct nvmet_req *req) } else {nvmet_req_complete (req, status); } +#endif }static void nvmet_execute_dsm (struct nvmet_req *req)
drivers/nvme/target/nvmet.h +2 -4
@@ -41,15 +41,13 @@struct nvmet_ns {struct list_head dev_link;struct percpu_ref ref; -struct block_device *bdev; +struct se_device __rcu *dev;u32 nsid;u32 blksize_shift;loff_t size;u8 nguid[16 ];struct nvmet_subsys *subsys; -const char *device_path; -struct config_group device_group;struct config_group group;
@@ -330,7 +328,7 @@ void nvmet_subsys_put(struct nvmet_subsys *subsys);struct nvmet_ns *nvmet_find_namespace (struct nvmet_ctrl *ctrl,__le32 nsid);void nvmet_put_namespace (struct nvmet_ns *ns); -int nvmet_ns_enable (struct nvmet_ns *ns); +int nvmet_ns_enable (struct nvmet_ns *ns,struct se_device *dev);void nvmet_ns_disable (struct nvmet_ns *ns);struct nvmet_ns *nvmet_ns_alloc (struct nvmet_subsys *subsys,u32 nsid);void nvmet_ns_free (struct nvmet_ns *ns);
Signature · 1 line
1.9.1
RFC v2 5/11 nvmet/loop: Add support for controller-per-port model + nvmet_port_binding
In reply to Nicholas A. BellingerFrom: Nicholas Bellinger <[email protected]>
This patch introduces loopback support for a nvme host controller per nvmet_port instance model, following what we've done in drivers/target/loopback/ for allowing multiple host LLDs to co-exist.
It changes nvme_loop_add_port() to use struct nvme_loop_port and take the nvmf_host_add() reference, and invokes device_register() to nvme_loop_driver_probe() to kick off controller creation within nvme_loop_create_ctrl().
This allows nvme_loop_queue_rq to setup iod->req.port to the per nvmet_port pointer, instead of a single hardcoded global nvmet_loop_port.
Subsequently, it also adds nvme_loop_remove_port() to call device_unregister() and call nvme_loop_del_ctrl() and nvmf_free_options() to drop nvmet_port's struct nvmf_host rereference, when the nvmet_port_binding is being removed from the associated nvmet_subsys.
--- drivers/nvme/target/loop.c | 205 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 185 insertions(+), 20 deletions(-)
drivers/nvme/target/loop.c +185 -20
@@ -45,6 +45,13 @@ struct nvme_loop_iod {struct scatterlist first_sgl[]; }; +struct nvme_loop_port { +struct device dev; +struct nvmf_ctrl_options *opts; +struct nvme_ctrl *ctrl; +struct nvmet_port port; +}; +struct nvme_loop_ctrl {spinlock_t lock;struct nvme_loop_queue *queues;
@@ -61,6 +68,8 @@ struct nvme_loop_ctrl {struct nvmet_ctrl *target_ctrl;struct work_struct delete_work;struct work_struct reset_work; + +struct nvme_loop_port *port; };static inline struct nvme_loop_ctrl *to_loop_ctrl (struct nvme_ctrl *ctrl)
@@ -74,8 +83,6 @@ struct nvme_loop_queue {struct nvme_loop_ctrl *ctrl; }; -static struct nvmet_port *nvmet_loop_port; -static LIST_HEAD (nvme_loop_ctrl_list);static DEFINE_MUTEX (nvme_loop_ctrl_mutex);
@@ -172,7 +179,8 @@ static int nvme_loop_queue_rq(struct blk_mq_hw_ctx *hctx,return ret; iod->cmd.common.flags |=NVME_CMD_SGL_METABUF ; - iod->req.port = nvmet_loop_port; + iod->req.port = &queue->ctrl->port->port; +if (!nvmet_req_init (&iod->req, &queue->nvme_cq, &queue->nvme_sq, &nvme_loop_ops)) {nvme_cleanup_cmd (req);
@@ -599,6 +607,8 @@ out_destroy_queues:static struct nvme_ctrl *nvme_loop_create_ctrl (struct device *dev,struct nvmf_ctrl_options *opts) { +struct nvme_loop_port *loop_port =container_of (dev, + struct nvme_loop_port, dev);struct nvme_loop_ctrl *ctrl;bool changed;int ret;
@@ -607,6 +617,7 @@ static struct nvme_ctrl *nvme_loop_create_ctrl(struct device *dev,if (!ctrl)return ERR_PTR (-ENOMEM ); ctrl->ctrl.opts = opts; + ctrl->port = loop_port;INIT_LIST_HEAD (&ctrl->list);INIT_WORK (&ctrl->delete_work, nvme_loop_del_ctrl_work);
@@ -681,29 +692,135 @@ out_put_ctrl:return ERR_PTR (ret); } -static int nvme_loop_add_port (struct nvmet_port *port) +static int nvme_loop_driver_probe (struct device *dev) { -/* -* XXX: disalow adding more than one port so -* there is no connection rejections when a -* a subsystem is assigned to a port for which -* loop doesn't have a pointer. -* This scenario would be possible if we allowed -* more than one port to be added and a subsystem -* was assigned to a port other than nvmet_loop_port. -*/ +struct nvme_loop_port *loop_port =container_of (dev, + struct nvme_loop_port, dev); +struct nvme_ctrl *ctrl; -if (nvmet_loop_port) -return -EPERM ; + ctrl =nvme_loop_create_ctrl (dev, loop_port->opts); +if (IS_ERR (ctrl)) +return PTR_ERR (ctrl); - nvmet_loop_port = port; + loop_port->ctrl = ctrl;return 0 ; } -static void nvme_loop_remove_port (struct nvmet_port *port) +static int nvme_loop_driver_remove (struct device *dev) +{ +struct nvme_loop_port *loop_port =container_of (dev, + struct nvme_loop_port, dev); +struct nvme_ctrl *ctrl = loop_port->ctrl; +struct nvmf_ctrl_options *opts = loop_port->opts; + +nvme_loop_del_ctrl (ctrl); +nvmf_free_options (opts); +return 0 ; +} + +static int pseudo_bus_match (struct device *dev, +struct device_driver *dev_driver) +{ +return 1 ; +} + +static struct bus_type nvme_loop_bus = { + .name ="nvme_loop_bus" , + .match = pseudo_bus_match, + .probe = nvme_loop_driver_probe, + .remove = nvme_loop_driver_remove, +}; + +static struct device_driver nvme_loop_driverfs = { + .name ="nvme_loop" , + .bus = &nvme_loop_bus, +}; + +static void nvme_loop_release_adapter (struct device *dev) { -if (port == nvmet_loop_port) - nvmet_loop_port =NULL ; +struct nvme_loop_port *loop_port =container_of (dev, + struct nvme_loop_port, dev); + +kfree (loop_port); +} + +static struct device *nvme_loop_primary; + +static int nvme_loop_add_port (struct nvmet_port_binding *pb) +{ +struct nvmet_subsys *subsys = pb->nf_subsys; +struct nvme_loop_port *loop_port; +struct nvmf_ctrl_options *opts; +struct device *dev; +int ret; + + loop_port =kzalloc (sizeof (*loop_port),GFP_KERNEL ); +if (!loop_port) +return -ENOMEM ; + +mutex_init (&loop_port->port.port_binding_mutex); +INIT_LIST_HEAD (&loop_port->port.port_binding_list); + loop_port->port.priv = loop_port; + loop_port->port.nf_subsys = pb->nf_subsys; + loop_port->port.nf_ops = pb->nf_ops; + pb->port = &loop_port->port; + + opts =kzalloc (sizeof (*opts),GFP_KERNEL ); +if (!opts) { +kfree (loop_port); +return -ENOMEM ; + } + loop_port->opts = opts; + +/* Set defaults */ + opts->queue_size =NVMF_DEF_QUEUE_SIZE ; + opts->nr_io_queues =num_online_cpus (); + opts->tl_retry_count =2 ; + opts->reconnect_delay =NVMF_DEF_RECONNECT_DELAY ; + opts->kato =NVME_DEFAULT_KATO ; + + opts->host =nvmf_host_add (NULL ); +if (!opts->host) { +kfree (opts); +kfree (loop_port); +return -ENOMEM ; + } + + opts->transport =kstrdup ("loop" ,GFP_KERNEL ); + opts->subsysnqn =kstrdup (subsys->subsysnqn,GFP_KERNEL ); + + dev = &loop_port->dev; + dev->bus = &nvme_loop_bus; + dev->parent = nvme_loop_primary; + dev->release = &nvme_loop_release_adapter; +dev_set_name (dev,"nvme_loop_ctrl:%s" , subsys->subsysnqn); + +nvmet_port_binding_enable (pb, &loop_port->port); + + ret =device_register (dev); +if (ret) { +pr_err ("device_register() failed: %d\n" , ret); +nvmet_port_binding_disable (pb, &loop_port->port); +nvmf_free_options (opts); +kfree (loop_port); +return ret; + } + +return 0 ; +} + +static void nvme_loop_remove_port (struct nvmet_port_binding *pb) +{ +struct nvmet_port *port = pb->port; +struct nvme_loop_port *loop_port; + +if (!port) +return ; + + loop_port = container_of(port, struct nvme_loop_port, port); +nvmet_port_binding_disable (pb, &loop_port->port); + +device_unregister (&loop_port->dev); }static struct nvmet_fabrics_ops nvme_loop_ops = {
@@ -720,13 +837,59 @@ static struct nvmf_transport_ops nvme_loop_transport = { .create_ctrl = nvme_loop_create_ctrl, }; +static int nvme_loop_alloc_core_bus (void ) +{ +int ret; + + nvme_loop_primary =root_device_register ("nvme_loop_0" ); +if (IS_ERR (nvme_loop_primary)) { +pr_err ("Unable to allocate nvme_loop_primary\n" ); +return PTR_ERR (nvme_loop_primary); + } + + ret =bus_register (&nvme_loop_bus); +if (ret) { +pr_err ("bus_register() failed for nvme_loop_bus\n" ); + goto dev_unreg; + } + + ret =driver_register (&nvme_loop_driverfs); +if (ret) { +pr_err ("driver_register() failed for" +" nvme_loop_driverfs\n" ); + goto bus_unreg; + } + +return ret; + +bus_unreg: +bus_unregister (&nvme_loop_bus); +dev_unreg: +root_device_unregister (nvme_loop_primary); +return ret; +} + +static void nvme_loop_release_core_bus (void ) +{ +driver_unregister (&nvme_loop_driverfs); +bus_unregister (&nvme_loop_bus); +root_device_unregister (nvme_loop_primary); +} +static int __init nvme_loop_init_module (void ) {int ret; - ret =nvmet_register_transport (&nvme_loop_ops); + ret =nvme_loop_alloc_core_bus ();if (ret)return ret; + + ret =nvmet_register_transport (&nvme_loop_ops); +if (ret) { +nvme_loop_release_core_bus (); +return ret; + } +nvmf_register_transport (&nvme_loop_transport);return 0 ; }
@@ -744,6 +907,8 @@ static void __exit nvme_loop_cleanup_module(void)mutex_unlock (&nvme_loop_ctrl_mutex);flush_scheduled_work (); + +nvme_loop_release_core_bus (); }module_init (nvme_loop_init_module);
Signature · 1 line
1.9.1
RFC v2 6/11 nvmet/rdma: Convert to struct nvmet_port_binding
In reply to Nicholas A. BellingerFrom: Nicholas Bellinger <[email protected]>
This patch converts nvmet/rdma to nvmet_port_binding in configfs-ng, and introduces a nvmet_rdma_port that allows for multiple nvmet_subsys nvmet_port_bindings to be mapped to a single nvmet_rdma_port rdma_cm_id listener.
It moves rdma_cm_id setup into nvmet_rdma_listen_cmid(), and rdma_cm_id destroy into nvmet_rmda_destroy_cmid() using nvmet_rdma_port->ref.
It also updates nvmet_rdma_add_port() to do internal port lookup matching traddr and trsvcid, and grabs nvmet_rdma_port->ref if a matching port already exists.
--- drivers/nvme/target/rdma.c | 127 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 114 insertions(+), 13 deletions(-)
drivers/nvme/target/rdma.c +114 -13
@@ -118,6 +118,17 @@ struct nvmet_rdma_device {struct list_head entry; }; +struct nvmet_rdma_port { +atomic_t enabled; + +struct rdma_cm_id *cm_id; +struct nvmf_disc_rsp_page_entry port_addr; + +struct list_head node; +struct kref ref; +struct nvmet_port port; +}; +static bool nvmet_rdma_use_srq;module_param_named (use_srq, nvmet_rdma_use_srq, bool,0444 );MODULE_PARM_DESC (use_srq,"Use shared receive queue." );
@@ -129,6 +140,9 @@ static DEFINE_MUTEX(nvmet_rdma_queue_mutex);static LIST_HEAD (device_list);static DEFINE_MUTEX (device_list_mutex); +static LIST_HEAD (nvmet_rdma_ports); +static DEFINE_MUTEX (nvmet_rdma_ports_mutex); +static bool nvmet_rdma_execute_command (struct nvmet_rdma_rsp *rsp);static void nvmet_rdma_send_done (struct ib_cq *cq,struct ib_wc *wc);static void nvmet_rdma_recv_done (struct ib_cq *cq,struct ib_wc *wc);
@@ -1127,6 +1141,7 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id, {struct nvmet_rdma_device *ndev;struct nvmet_rdma_queue *queue; +struct nvmet_rdma_port *rdma_port;int ret = -EINVAL ; ndev =nvmet_rdma_find_get_device (cm_id);
@@ -1141,7 +1156,8 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id, ret = -ENOMEM ; goto put_device; } - queue->port = cm_id->context; + rdma_port = cm_id->context; + queue->port = &rdma_port->port; ret =nvmet_rdma_cm_accept (cm_id, queue, &event->param.conn);if (ret)
@@ -1306,26 +1322,50 @@ static void nvmet_rdma_delete_ctrl(struct nvmet_ctrl *ctrl)nvmet_rdma_queue_disconnect (queue); } -static int nvmet_rdma_add_port (struct nvmet_port *port) +static struct nvmet_rdma_port *nvmet_rdma_listen_cmid (struct nvmet_port_binding *pb) { +struct nvmet_rdma_port *rdma_port;struct rdma_cm_id *cm_id;struct sockaddr_in addr_in;u16 port_in;int ret; - ret =kstrtou16 (port->disc_addr.trsvcid,0 , &port_in); + rdma_port =kzalloc (sizeof (*rdma_port),GFP_KERNEL ); +if (!rdma_port) +return ERR_PTR (-ENOMEM ); + +INIT_LIST_HEAD (&rdma_port->node); +kref_init (&rdma_port->ref); +mutex_init (&rdma_port->port.port_binding_mutex); +INIT_LIST_HEAD (&rdma_port->port.port_binding_list); + rdma_port->port.priv = rdma_port; + rdma_port->port.nf_subsys = pb->nf_subsys; + rdma_port->port.nf_ops = pb->nf_ops; + pb->port = &rdma_port->port; + +memcpy (&rdma_port->port_addr, &pb->disc_addr, +sizeof (struct nvmf_disc_rsp_page_entry )); + +nvmet_port_binding_enable (pb, &rdma_port->port); + +mutex_lock (&nvmet_rdma_ports_mutex); +list_add_tail (&rdma_port->node, &nvmet_rdma_ports); +mutex_unlock (&nvmet_rdma_ports_mutex); + + ret =kstrtou16 (pb->disc_addr.trsvcid,0 , &port_in);if (ret) -return ret; + goto out_port_disable; addr_in.sin_family =AF_INET ; - addr_in.sin_addr.s_addr =in_aton (port->disc_addr.traddr); + addr_in.sin_addr.s_addr =in_aton (pb->disc_addr.traddr); addr_in.sin_port =htons (port_in); - cm_id =rdma_create_id (&init_net, nvmet_rdma_cm_handler, port, + cm_id =rdma_create_id (&init_net, nvmet_rdma_cm_handler, rdma_port,RDMA_PS_TCP ,IB_QPT_RC );if (IS_ERR (cm_id)) {pr_err ("CM ID creation failed\n" ); -return PTR_ERR (cm_id); + ret =PTR_ERR (cm_id); + goto out_port_disable; } ret =rdma_bind_addr (cm_id, (struct sockaddr *)&addr_in);
@@ -1340,21 +1380,82 @@ static int nvmet_rdma_add_port(struct nvmet_port *port) goto out_destroy_id; } +atomic_set (&rdma_port->enabled,1 );pr_info ("enabling port %d (%pISpc)\n" , -le16_to_cpu (port->disc_addr.portid), &addr_in); - port->priv = cm_id; -return 0 ; +le16_to_cpu (pb->disc_addr.portid), &addr_in); + +return rdma_port; out_destroy_id:rdma_destroy_id (cm_id); -return ret; +out_port_disable: +mutex_lock (&nvmet_rdma_ports_mutex); +list_del_init (&rdma_port->node); +mutex_unlock (&nvmet_rdma_ports_mutex); + +nvmet_port_binding_disable (pb, &rdma_port->port); +kfree (rdma_port); +return ERR_PTR (ret); } -static void nvmet_rdma_remove_port (struct nvmet_port *port) +static void nvmet_rmda_destroy_cmid (struct kref *ref) { -struct rdma_cm_id *cm_id = port->priv; +struct nvmet_rdma_port *rdma_port =container_of (ref, + struct nvmet_rdma_port, ref); +struct rdma_cm_id *cm_id = rdma_port->cm_id; + +mutex_lock (&nvmet_rdma_ports_mutex); +atomic_set (&rdma_port->enabled,0 ); +list_del_init (&rdma_port->node); +mutex_unlock (&nvmet_rdma_ports_mutex);rdma_destroy_id (cm_id); +kfree (rdma_port); +} + +static int nvmet_rdma_add_port (struct nvmet_port_binding *pb) +{ +struct nvmet_rdma_port *rdma_port; +struct nvmf_disc_rsp_page_entry *pb_addr = &pb->disc_addr; + +mutex_lock (&nvmet_rdma_ports_mutex); +list_for_each_entry (rdma_port, &nvmet_rdma_ports, node) { +struct nvmf_disc_rsp_page_entry *port_addr = &rdma_port->port_addr; + +if (!strcmp (port_addr->traddr, pb_addr->traddr) && + !strcmp (port_addr->trsvcid, pb_addr->trsvcid)) { +if (!atomic_read (&rdma_port->enabled)) { +mutex_unlock (&nvmet_rdma_ports_mutex); +return -ENODEV ; + } +kref_get (&rdma_port->ref); +mutex_unlock (&nvmet_rdma_ports_mutex); + +nvmet_port_binding_enable (pb, &rdma_port->port); +return 0 ; + } + } +mutex_unlock (&nvmet_rdma_ports_mutex); + + rdma_port =nvmet_rdma_listen_cmid (pb); +if (IS_ERR (rdma_port)) +return PTR_ERR (rdma_port); + +return 0 ; +} + +static void nvmet_rdma_remove_port (struct nvmet_port_binding *pb) +{ +struct nvmet_port *port = pb->port; +struct nvmet_rdma_port *rdma_port; + +if (!port) +return ; + + rdma_port = container_of(port, struct nvmet_rdma_port, port); +nvmet_port_binding_disable (pb, &rdma_port->port); + +kref_put (&rdma_port->ref, nvmet_rmda_destroy_cmid); }static struct nvmet_fabrics_ops nvmet_rdma_ops = {
Signature · 1 line
1.9.1
RFC v2 7/11 nvmet/io-cmd: Hookup sbc_ops->execute_rw backend ops
In reply to Nicholas A. BellingerFrom: Nicholas Bellinger <[email protected]>
This patch converts nvmet_execute_rw() to utilize sbc_ops->execute_rw() for target_iostate + target_iomem based I/O submission into existing backends drivers via configfs in /sys/kernel/config/target/core/.
This includes support for passing T10-PI scatterlists via target_iomem into existing sbc_ops->execute_rw() logic, and is functioning with IBLOCK, FILEIO, and RAMDISK.
Note the preceeding target/iblock patch absorbs inline bio + bvecs and blk_poll() optimizations from Ming + Sagi in nvmet/io-cmd into target_core_iblock.c code.
--- drivers/nvme/target/io-cmd.c | 116 ++++++++++++++++++++++--------------------- drivers/nvme/target/nvmet.h | 7 +++ 2 files changed, 67 insertions(+), 56 deletions(-)
drivers/nvme/target/io-cmd.c +60 -56
@@ -14,20 +14,16 @@#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt#include <linux/blkdev.h> #include <linux/module.h> +#include <target/target_core_base.h> +#include <target/target_core_backend.h> #include "nvmet.h" -#if 0 -static void nvmet_bio_done (struct bio *bio) +static void nvmet_complete_ios (struct target_iostate *ios,u16 status) { -struct nvmet_req *req = bio->bi_private; - -nvmet_req_complete (req, - bio->bi_error ?NVME_SC_INTERNAL |NVME_SC_DNR :0 ); + struct nvmet_req *req = container_of(ios, struct nvmet_req, t_iostate); -if (bio != &req->inline_bio) -bio_put (bio); +nvmet_req_complete (req, status ?NVME_SC_INTERNAL |NVME_SC_DNR :0 ); } -#endif static inline u32 nvmet_rw_len (struct nvmet_req *req) {
@@ -35,72 +31,80 @@ static inline u32 nvmet_rw_len(struct nvmet_req *req) req->ns->blksize_shift; } -#if 0 -static void nvmet_inline_bio_init (struct nvmet_req *req) -{ -struct bio *bio = &req->inline_bio; - -bio_init (bio); - bio->bi_max_vecs =NVMET_MAX_INLINE_BIOVEC ; - bio->bi_io_vec = req->inline_bvec; -} -#endif -static void nvmet_execute_rw (struct nvmet_req *req) { -#if 0 -int sg_cnt = req->sg_cnt; -struct scatterlist *sg; -struct bio *bio; +struct target_iostate *ios = &req->t_iostate; +struct target_iomem *iomem = &req->t_iomem; +struct se_device *dev =rcu_dereference_raw (req->ns->dev); +struct sbc_ops *sbc_ops = dev->transport->sbc_ops;sector_t sector; -blk_qc_t cookie; -int rw, i; -#endif +enum dma_data_direction data_direction; +sense_reason_t rc; +bool fua_write = false, prot_enabled = false; + +if (!sbc_ops || !sbc_ops->execute_rw) { +nvmet_req_complete (req,NVME_SC_INTERNAL |NVME_SC_DNR ); +return ; + } +if (!req->sg_cnt) {nvmet_req_complete (req,0 );return ; } -#if 0 +if (req->cmd->rw.opcode == nvme_cmd_write) {if (req->cmd->rw.control &cpu_to_le16 (NVME_RW_FUA )) - rw =WRITE_FUA ; -else - rw =WRITE ; + fua_write = true; + + data_direction =DMA_TO_DEVICE ; }else { - rw =READ ; + data_direction =DMA_FROM_DEVICE ; } sector =le64_to_cpu (req->cmd->rw.slba); sector <<= (req->ns->blksize_shift -9 ); -nvmet_inline_bio_init (req); - bio = &req->inline_bio; - bio->bi_bdev = req->ns->bdev; - bio->bi_iter.bi_sector = sector; - bio->bi_private = req; - bio->bi_end_io = nvmet_bio_done; - -for_each_sg (req->sg, sg, req->sg_cnt, i) { -while (bio_add_page (bio,sg_page (sg), sg->length, sg->offset) - != sg->length) { -struct bio *prev = bio; - - bio =bio_alloc (GFP_KERNEL ,min (sg_cnt,BIO_MAX_PAGES )); - bio->bi_bdev = req->ns->bdev; - bio->bi_iter.bi_sector = sector; - -bio_chain (bio, prev); - cookie =submit_bio (rw, prev); - } + ios->t_task_lba = sector; + ios->data_length =nvmet_rw_len (req); + ios->data_direction = data_direction; + iomem->t_data_sg = req->sg; + iomem->t_data_nents = req->sg_cnt; + iomem->t_prot_sg = req->prot_sg; + iomem->t_prot_nents = req->prot_sg_cnt; + +// XXX: Make common between sbc_check_prot and nvme-target +switch (dev->dev_attrib.pi_prot_type) { +case TARGET_DIF_TYPE3_PROT : + ios->reftag_seed =0xffffffff ; + prot_enabled = true; +break ; +case TARGET_DIF_TYPE1_PROT : + ios->reftag_seed = ios->t_task_lba; + prot_enabled = true; +break ; +default : +break ; + } - sector += sg->length >>9 ; - sg_cnt--; +if (prot_enabled) { + ios->prot_type = dev->dev_attrib.pi_prot_type; + ios->prot_length = dev->prot_length * + (le16_to_cpu (req->cmd->rw.length) +1 ); +#if 0 +printk ("req->cmd->rw.length: %u\n" ,le16_to_cpu (req->cmd->rw.length)); +printk ("nvmet_rw_len: %u\n" ,nvmet_rw_len (req)); +printk ("req->se_cmd.prot_type: %d\n" , req->se_cmd.prot_type); +printk ("req->se_cmd.prot_length: %u\n" , req->se_cmd.prot_length); +#endif } - cookie =submit_bio (rw, bio); + ios->se_dev = dev; + ios->iomem = iomem; + ios->t_comp_func = &nvmet_complete_ios; -blk_poll (bdev_get_queue (req->ns->bdev), cookie); -#endif + rc = sbc_ops->execute_rw (ios, iomem->t_data_sg, iomem->t_data_nents, + ios->data_direction, fua_write, + &nvmet_complete_ios); }static void nvmet_execute_flush (struct nvmet_req *req)
drivers/nvme/target/nvmet.h +7 -0
@@ -25,6 +25,7 @@#include <linux/configfs.h> #include <linux/rcupdate.h> #include <linux/blkdev.h> +#include <target/target_core_base.h> #define NVMET_ASYNC_EVENTS 4#define NVMET_ERROR_LOG_SLOTS 128
@@ -262,6 +263,12 @@ struct nvmet_req {int sg_cnt;size_t data_len; +struct scatterlist *prot_sg; +int prot_sg_cnt; + +struct target_iostate t_iostate; +struct target_iomem t_iomem; +struct nvmet_port *port;void (*execute)(struct nvmet_req *req);
Signature · 1 line
1.9.1
RFC v2 8/11 nvmet/io-cmd: Hookup sbc_ops->execute_sync_cache backend ops
In reply to Nicholas A. BellingerFrom: Nicholas Bellinger <[email protected]>
This patch converts nvmet_execute_flush() to utilize sbc_ops->execute_sync_cache() for target_iostate submission into existing backends drivers via configfs in /sys/kernel/config/target/core/.
--- drivers/nvme/target/io-cmd.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-)
drivers/nvme/target/io-cmd.c +12 -9
@@ -109,18 +109,21 @@ static void nvmet_execute_rw(struct nvmet_req *req)static void nvmet_execute_flush (struct nvmet_req *req) { -#if 0 -struct bio *bio; +struct target_iostate *ios = &req->t_iostate; +struct se_device *dev =rcu_dereference_raw (req->ns->dev); +struct sbc_ops *sbc_ops = dev->transport->sbc_ops; +sense_reason_t rc; -nvmet_inline_bio_init (req); - bio = &req->inline_bio; +if (!sbc_ops || !sbc_ops->execute_sync_cache) { +nvmet_req_complete (req,NVME_SC_INTERNAL |NVME_SC_DNR ); +return ; + } - bio->bi_bdev = req->ns->bdev; - bio->bi_private = req; - bio->bi_end_io = nvmet_bio_done; + ios->se_dev = dev; + ios->iomem =NULL ; + ios->t_comp_func = &nvmet_complete_ios; -submit_bio (WRITE_FLUSH , bio); -#endif + rc = sbc_ops->execute_sync_cache (ios, false); }#if 0
Signature · 1 line
1.9.1
RFC v2 9/11 nvmet/io-cmd: Hookup sbc_ops->execute_unmap backend ops
In reply to Nicholas A. BellingerFrom: Nicholas Bellinger <[email protected]>
This patch converts nvmet_execute_discard() to utilize sbc_ops->execute_unmap() for target_iostate submission into existing backends drivers via configfs in /sys/kernel/config/target/core/.
--- drivers/nvme/target/io-cmd.c | 47 ++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 13 deletions(-)
drivers/nvme/target/io-cmd.c +34 -13
@@ -126,52 +126,73 @@ static void nvmet_execute_flush(struct nvmet_req *req) rc = sbc_ops->execute_sync_cache (ios, false); } -#if 0 -static u16 nvmet_discard_range (struct nvmet_ns *ns, -struct nvme_dsm_range *range,int type,struct bio **bio) +static u16 nvmet_discard_range (struct nvmet_req *req,struct sbc_ops *sbc_ops, +struct nvme_dsm_range *range,struct bio **bio) { -if (__blkdev_issue_discard (ns->bdev, +struct nvmet_ns *ns = req->ns; +sense_reason_t rc; + + rc = sbc_ops->execute_unmap (&req->t_iostate,le64_to_cpu (range->slba) << (ns->blksize_shift -9 ),le32_to_cpu (range->nlb) << (ns->blksize_shift -9 ), -GFP_KERNEL , type, bio)) + bio); +if (rc)return NVME_SC_INTERNAL |NVME_SC_DNR ;return 0 ; } -#endif + +static void nvmet_discard_bio_done (struct bio *bio) +{ +struct nvmet_req *req = bio->bi_private; +int err = bio->bi_error; + +bio_put (bio); +nvmet_req_complete (req, err ?NVME_SC_INTERNAL |NVME_SC_DNR :0 ); +}static void nvmet_execute_discard (struct nvmet_req *req) { -#if 0 -struct nvme_dsm_range range; +struct target_iostate *ios = &req->t_iostate; +struct se_device *dev =rcu_dereference_raw (req->ns->dev); +struct sbc_ops *sbc_ops = dev->transport->sbc_ops;struct bio *bio =NULL ; -int type =REQ_WRITE |REQ_DISCARD , i; +struct nvme_dsm_range range; +int i;u16 status; +if (!sbc_ops || !sbc_ops->execute_unmap) { +nvmet_req_complete (req,NVME_SC_INTERNAL |NVME_SC_DNR ); +return ; + } + + ios->se_dev = dev; + ios->iomem =NULL ; + ios->t_comp_func =NULL ; +for (i =0 ; i <=le32_to_cpu (req->cmd->dsm.nr); i++) { status =nvmet_copy_from_sgl (req, i *sizeof (range), &range,sizeof (range));if (status)break ; - status =nvmet_discard_range (req->ns, &range, type, &bio); + status =nvmet_discard_range (req, sbc_ops, &range, &bio);if (status)break ; }if (bio) { bio->bi_private = req; - bio->bi_end_io = nvmet_bio_done; + bio->bi_end_io = nvmet_discard_bio_done;if (status) { bio->bi_error = -EIO ;bio_endio (bio); }else { -submit_bio (type, bio); +submit_bio (REQ_WRITE |REQ_DISCARD , bio); } }else {nvmet_req_complete (req, status); } -#endif }static void nvmet_execute_dsm (struct nvmet_req *req)
Signature · 1 line
1.9.1
RFC v2 10/11 nvmet/admin-cmd: Hookup T10-PI to ID_NS.ms + ID_NS.dps feature bits
In reply to Nicholas A. BellingerFrom: Nicholas Bellinger <[email protected]>
This patch updates nvmet_execute_identify_ns() to report target-core backend T10-PI related feature bits to the NVMe host controller.
Note this assumes support for NVME_NS_DPC_PI_TYPE1 and NVME_NS_DPC_PI_TYPE3 as reported by backend drivers via /sys/kernel/config/target/core/*/*/attrib/pi_prot_type.
--- drivers/nvme/target/admin-cmd.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
drivers/nvme/target/admin-cmd.c +17 -0
@@ -200,6 +200,7 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req) {struct nvmet_ns *ns;struct nvme_id_ns *id; +struct se_device *dev;u16 status =0 ; ns =nvmet_find_namespace (req->sq->ctrl, req->cmd->identify.nsid);
@@ -228,6 +229,22 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req) id->nlbaf =0 ; id->flbas =0 ; +/* Populate bits for T10-PI from se_device backend */ +rcu_read_lock (); + dev =rcu_dereference (ns->dev); +if (dev && dev->dev_attrib.pi_prot_type) { +int pi_prot_type = dev->dev_attrib.pi_prot_type; + + id->lbaf[0 ].ms =cpu_to_le16 (sizeof (struct t10_pi_tuple )); +printk ("nvmet_set_id_ns: ms: %u\n" , id->lbaf[0 ].ms); + +if (pi_prot_type ==1 ) + id->dps =NVME_NS_DPC_PI_TYPE1 ; +else if (pi_prot_type ==3 ) + id->dps =NVME_NS_DPC_PI_TYPE3 ; + } +rcu_read_unlock (); +/* * Our namespace might always be shared. Not just with other * controllers, but also with any other user of the block device.
Signature · 1 line
1.9.1
RFC v2 11/11 nvmet/loop: Add support for bio integrity handling
In reply to Nicholas A. BellingerFrom: Nicholas Bellinger <[email protected]>
This patch adds support for nvme/loop block integrity, based upon the reported ID_NS.ms + ID_NS.dps feature bits in nvmet_execute_identify_ns().
--- drivers/nvme/target/loop.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
drivers/nvme/target/loop.c +18 -0
@@ -42,6 +42,7 @@ struct nvme_loop_iod {struct nvme_loop_queue *queue;struct work_struct work;struct sg_table sg_table; +struct scatterlist meta_sg;struct scatterlist first_sgl[]; };
@@ -201,6 +202,23 @@ static int nvme_loop_queue_rq(struct blk_mq_hw_ctx *hctx,BUG_ON (iod->req.sg_cnt > req->nr_phys_segments); } +if (blk_integrity_rq (req)) { +int count; + +if (blk_rq_count_integrity_sg (hctx->queue, req->bio) !=1 ) +BUG_ON (1 ); + +sg_init_table (&iod->meta_sg,1 ); + count =blk_rq_map_integrity_sg (hctx->queue, req->bio, + &iod->meta_sg); + + iod->req.prot_sg = &iod->meta_sg; + iod->req.prot_sg_cnt =1 ; + +pr_debug ("nvme/loop: Set prot_sg %p and prot_sg_cnt: %d\n" , + iod->req.prot_sg, iod->req.prot_sg_cnt); + } + iod->cmd.common.command_id = req->tag;blk_mq_start_request (req);
Signature · 1 line
1.9.1
Christoph Hellwig
maintainerv2 cover nvmet: Add support for multi-tenant configfs
In reply to Nicholas A. BellingerQuoted 1 line · Nicholas A. Bellinger
Comments..?
Still no good reason for doing anything like this.
On a conceptual level:
The NVMe target is front end implementing a simple protocol to export block devices to a remove host. The SCSI target is larger front end to expose a more complex protocol to remote hosts. None of them should actually implement any real protocol independent behavior, and except for persistent reservations in the SCSI neither of them does.
On a practical level it means we drag in over 25.000 lines of code as a dependency, without actually dropping any code in the nvmet module, and vastly more complicated object hierarchies that don't make any sense for the tight-knit NVMe standard. We'd also get dragged into into the nightmare of diverging an incompatible user space tooling, and we'd lose all the test coverage we've built up. We'd also have to deal with tons of tunables that neither fit the protocol we implement nor the philosophy of the project.