nvme-loop: add support for multiple ports
This is useful at least for multipath testing. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
This commit is contained in:
@@ -45,6 +45,7 @@ struct nvme_loop_ctrl {
|
|||||||
struct nvme_ctrl ctrl;
|
struct nvme_ctrl ctrl;
|
||||||
|
|
||||||
struct nvmet_ctrl *target_ctrl;
|
struct nvmet_ctrl *target_ctrl;
|
||||||
|
struct nvmet_port *port;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct nvme_loop_ctrl *to_loop_ctrl(struct nvme_ctrl *ctrl)
|
static inline struct nvme_loop_ctrl *to_loop_ctrl(struct nvme_ctrl *ctrl)
|
||||||
@@ -63,7 +64,8 @@ struct nvme_loop_queue {
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct nvmet_port *nvmet_loop_port;
|
static LIST_HEAD(nvme_loop_ports);
|
||||||
|
static DEFINE_MUTEX(nvme_loop_ports_mutex);
|
||||||
|
|
||||||
static LIST_HEAD(nvme_loop_ctrl_list);
|
static LIST_HEAD(nvme_loop_ctrl_list);
|
||||||
static DEFINE_MUTEX(nvme_loop_ctrl_mutex);
|
static DEFINE_MUTEX(nvme_loop_ctrl_mutex);
|
||||||
@@ -169,7 +171,7 @@ static blk_status_t nvme_loop_queue_rq(struct blk_mq_hw_ctx *hctx,
|
|||||||
|
|
||||||
blk_mq_start_request(req);
|
blk_mq_start_request(req);
|
||||||
iod->cmd.common.flags |= NVME_CMD_SGL_METABUF;
|
iod->cmd.common.flags |= NVME_CMD_SGL_METABUF;
|
||||||
iod->req.port = nvmet_loop_port;
|
iod->req.port = queue->ctrl->port;
|
||||||
if (!nvmet_req_init(&iod->req, &queue->nvme_cq,
|
if (!nvmet_req_init(&iod->req, &queue->nvme_cq,
|
||||||
&queue->nvme_sq, &nvme_loop_ops))
|
&queue->nvme_sq, &nvme_loop_ops))
|
||||||
return BLK_STS_OK;
|
return BLK_STS_OK;
|
||||||
@@ -517,6 +519,7 @@ static const struct nvme_ctrl_ops nvme_loop_ctrl_ops = {
|
|||||||
.free_ctrl = nvme_loop_free_ctrl,
|
.free_ctrl = nvme_loop_free_ctrl,
|
||||||
.submit_async_event = nvme_loop_submit_async_event,
|
.submit_async_event = nvme_loop_submit_async_event,
|
||||||
.delete_ctrl = nvme_loop_delete_ctrl_host,
|
.delete_ctrl = nvme_loop_delete_ctrl_host,
|
||||||
|
.get_address = nvmf_get_address,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int nvme_loop_create_io_queues(struct nvme_loop_ctrl *ctrl)
|
static int nvme_loop_create_io_queues(struct nvme_loop_ctrl *ctrl)
|
||||||
@@ -565,6 +568,23 @@ out_destroy_queues:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct nvmet_port *nvme_loop_find_port(struct nvme_ctrl *ctrl)
|
||||||
|
{
|
||||||
|
struct nvmet_port *p, *found = NULL;
|
||||||
|
|
||||||
|
mutex_lock(&nvme_loop_ports_mutex);
|
||||||
|
list_for_each_entry(p, &nvme_loop_ports, entry) {
|
||||||
|
/* if no transport address is specified use the first port */
|
||||||
|
if ((ctrl->opts->mask & NVMF_OPT_TRADDR) &&
|
||||||
|
strcmp(ctrl->opts->traddr, p->disc_addr.traddr))
|
||||||
|
continue;
|
||||||
|
found = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mutex_unlock(&nvme_loop_ports_mutex);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
static struct nvme_ctrl *nvme_loop_create_ctrl(struct device *dev,
|
static struct nvme_ctrl *nvme_loop_create_ctrl(struct device *dev,
|
||||||
struct nvmf_ctrl_options *opts)
|
struct nvmf_ctrl_options *opts)
|
||||||
{
|
{
|
||||||
@@ -589,6 +609,7 @@ static struct nvme_ctrl *nvme_loop_create_ctrl(struct device *dev,
|
|||||||
|
|
||||||
ctrl->ctrl.sqsize = opts->queue_size - 1;
|
ctrl->ctrl.sqsize = opts->queue_size - 1;
|
||||||
ctrl->ctrl.kato = opts->kato;
|
ctrl->ctrl.kato = opts->kato;
|
||||||
|
ctrl->port = nvme_loop_find_port(&ctrl->ctrl);
|
||||||
|
|
||||||
ctrl->queues = kcalloc(opts->nr_io_queues + 1, sizeof(*ctrl->queues),
|
ctrl->queues = kcalloc(opts->nr_io_queues + 1, sizeof(*ctrl->queues),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
@@ -646,27 +667,17 @@ out_put_ctrl:
|
|||||||
|
|
||||||
static int nvme_loop_add_port(struct nvmet_port *port)
|
static int nvme_loop_add_port(struct nvmet_port *port)
|
||||||
{
|
{
|
||||||
/*
|
mutex_lock(&nvme_loop_ports_mutex);
|
||||||
* XXX: disalow adding more than one port so
|
list_add_tail(&port->entry, &nvme_loop_ports);
|
||||||
* there is no connection rejections when a
|
mutex_unlock(&nvme_loop_ports_mutex);
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (nvmet_loop_port)
|
|
||||||
return -EPERM;
|
|
||||||
|
|
||||||
nvmet_loop_port = port;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_loop_remove_port(struct nvmet_port *port)
|
static void nvme_loop_remove_port(struct nvmet_port *port)
|
||||||
{
|
{
|
||||||
if (port == nvmet_loop_port)
|
mutex_lock(&nvme_loop_ports_mutex);
|
||||||
nvmet_loop_port = NULL;
|
list_del_init(&port->entry);
|
||||||
|
mutex_unlock(&nvme_loop_ports_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct nvmet_fabrics_ops nvme_loop_ops = {
|
static const struct nvmet_fabrics_ops nvme_loop_ops = {
|
||||||
@@ -682,6 +693,7 @@ static struct nvmf_transport_ops nvme_loop_transport = {
|
|||||||
.name = "loop",
|
.name = "loop",
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.create_ctrl = nvme_loop_create_ctrl,
|
.create_ctrl = nvme_loop_create_ctrl,
|
||||||
|
.allowed_opts = NVMF_OPT_TRADDR,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init nvme_loop_init_module(void)
|
static int __init nvme_loop_init_module(void)
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ struct nvmet_sq {
|
|||||||
/**
|
/**
|
||||||
* struct nvmet_port - Common structure to keep port
|
* struct nvmet_port - Common structure to keep port
|
||||||
* information for the target.
|
* information for the target.
|
||||||
* @entry: List head for holding a list of these elements.
|
* @entry: Entry into referrals or transport list.
|
||||||
* @disc_addr: Address information is stored in a format defined
|
* @disc_addr: Address information is stored in a format defined
|
||||||
* for a discovery log page entry.
|
* for a discovery log page entry.
|
||||||
* @group: ConfigFS group for this element's folder.
|
* @group: ConfigFS group for this element's folder.
|
||||||
|
|||||||
Reference in New Issue
Block a user