mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
nvme-rdma: parallelize I/O queue allocation and startup
Refactor nvme rdma I/O queue setup to use async API, combining allocation and startup into a single parallel operation per queue. This reduces connection and reconnection setup time when there are delays in establishing connections, which is especially important for high-core-count hosts. Key changes: - Use async API to facilitate parallel calls for io queue setup. - Add nvme_rdma_setup_ctx for propagating errors from async workers. - Remove nvme_rdma_alloc_io_queues() and nvme_rdma_start_io_queues(); their logic is folded into nvme_rdma_setup_io_queues() and nvme_rdma_configure_io_queues(). - Move queue count negotiation (nvme_set_queue_count, nvmf_set_io_queues) from the removed nvme_rdma_alloc_io_queues() into nvme_rdma_configure_io_queues(). Testing on a 64-core host with 64 IO-queues shows nvme-rdma connection time reduced from ~1.4s to 416ms. Signed-off-by: Surabhi Gogte <sgogte@purestorage.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Keith Busch <kbusch@kernel.org>
This commit is contained in:
parent
f4254b18d4
commit
2a8513091d
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/async.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/nvme.h>
|
#include <linux/nvme.h>
|
||||||
#include <linux/unaligned.h>
|
#include <linux/unaligned.h>
|
||||||
|
|
@ -100,6 +101,11 @@ struct nvme_rdma_queue {
|
||||||
struct mutex queue_lock;
|
struct mutex queue_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct nvme_rdma_setup_ctx {
|
||||||
|
struct nvme_rdma_queue *queue;
|
||||||
|
int *err;
|
||||||
|
};
|
||||||
|
|
||||||
struct nvme_rdma_ctrl {
|
struct nvme_rdma_ctrl {
|
||||||
/* read only in the hot path */
|
/* read only in the hot path */
|
||||||
struct nvme_rdma_queue *queues;
|
struct nvme_rdma_queue *queues;
|
||||||
|
|
@ -690,60 +696,68 @@ static int nvme_rdma_start_queue(struct nvme_rdma_ctrl *ctrl, int idx)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nvme_rdma_start_io_queues(struct nvme_rdma_ctrl *ctrl,
|
static void nvme_rdma_setup_queue_async(void *data, async_cookie_t cookie)
|
||||||
int first, int last)
|
|
||||||
{
|
{
|
||||||
int i, ret = 0;
|
struct nvme_rdma_setup_ctx *ctx = data;
|
||||||
|
struct nvme_rdma_queue *queue;
|
||||||
|
int ret;
|
||||||
|
|
||||||
for (i = first; i < last; i++) {
|
queue = ctx->queue;
|
||||||
ret = nvme_rdma_start_queue(ctrl, i);
|
ret = nvme_rdma_alloc_queue(queue);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_stop_queues;
|
goto out_err;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
ret = nvme_rdma_start_queue(queue->ctrl, nvme_rdma_queue_idx(queue));
|
||||||
|
if (ret)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
out_stop_queues:
|
return;
|
||||||
for (i--; i >= first; i--)
|
out_err:
|
||||||
nvme_rdma_stop_queue(&ctrl->queues[i]);
|
WRITE_ONCE(*ctx->err, ret);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nvme_rdma_alloc_io_queues(struct nvme_rdma_ctrl *ctrl)
|
static int nvme_rdma_setup_io_queues(struct nvme_rdma_ctrl *ctrl,
|
||||||
|
unsigned int first, unsigned int last, size_t queue_size)
|
||||||
{
|
{
|
||||||
struct nvmf_ctrl_options *opts = ctrl->ctrl.opts;
|
ASYNC_DOMAIN_EXCLUSIVE(queue_domain);
|
||||||
unsigned int nr_io_queues;
|
struct nvme_rdma_setup_ctx *ctxs;
|
||||||
int i, ret;
|
int nr_queues = last - first;
|
||||||
|
int err = 0, i, ret;
|
||||||
|
|
||||||
nr_io_queues = nvmf_nr_io_queues(opts);
|
ctxs = kmalloc_objs(*ctxs, nr_queues);
|
||||||
ret = nvme_set_queue_count(&ctrl->ctrl, &nr_io_queues);
|
if (!ctxs)
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (nr_io_queues == 0) {
|
|
||||||
dev_err(ctrl->ctrl.device,
|
|
||||||
"unable to set any I/O queues\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < nr_queues; i++) {
|
||||||
|
struct nvme_rdma_queue *queue = &ctrl->queues[first + i];
|
||||||
|
|
||||||
|
queue->ctrl = ctrl;
|
||||||
|
queue->queue_size = queue_size;
|
||||||
|
|
||||||
|
ctxs[i].queue = queue;
|
||||||
|
ctxs[i].err = &err;
|
||||||
|
async_schedule_domain(nvme_rdma_setup_queue_async, &ctxs[i],
|
||||||
|
&queue_domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl->ctrl.queue_count = nr_io_queues + 1;
|
async_synchronize_full_domain(&queue_domain);
|
||||||
dev_info(ctrl->ctrl.device,
|
kfree(ctxs);
|
||||||
"creating %d I/O queues.\n", nr_io_queues);
|
|
||||||
|
|
||||||
nvmf_set_io_queues(opts, nr_io_queues, ctrl->io_queues);
|
ret = READ_ONCE(err);
|
||||||
for (i = 1; i < ctrl->ctrl.queue_count; i++) {
|
if (ret)
|
||||||
ctrl->queues[i].ctrl = ctrl;
|
goto out_free_queues;
|
||||||
ctrl->queues[i].queue_size = ctrl->ctrl.sqsize + 1;
|
|
||||||
ret = nvme_rdma_alloc_queue(&ctrl->queues[i]);
|
|
||||||
if (ret)
|
|
||||||
goto out_free_queues;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free_queues:
|
out_free_queues:
|
||||||
for (i--; i >= 1; i--)
|
for (i = 0; i < nr_queues; i++) {
|
||||||
nvme_rdma_free_queue(&ctrl->queues[i]);
|
struct nvme_rdma_queue *queue =
|
||||||
|
&ctrl->queues[first + i];
|
||||||
|
|
||||||
|
if (test_bit(NVME_RDMA_Q_LIVE, &queue->flags))
|
||||||
|
nvme_rdma_stop_queue(queue);
|
||||||
|
if (test_bit(NVME_RDMA_Q_ALLOCATED, &queue->flags))
|
||||||
|
nvme_rdma_free_queue(queue);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -862,12 +876,23 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl,
|
||||||
|
|
||||||
static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new)
|
static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new)
|
||||||
{
|
{
|
||||||
|
unsigned int nr_io_queues;
|
||||||
int ret, nr_queues;
|
int ret, nr_queues;
|
||||||
|
|
||||||
ret = nvme_rdma_alloc_io_queues(ctrl);
|
nr_io_queues = nvmf_nr_io_queues(ctrl->ctrl.opts);
|
||||||
|
ret = nvme_set_queue_count(&ctrl->ctrl, &nr_io_queues);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (nr_io_queues == 0) {
|
||||||
|
dev_err(ctrl->ctrl.device, "unable to set any I/O queues\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl->ctrl.queue_count = nr_io_queues + 1;
|
||||||
|
dev_info(ctrl->ctrl.device, "creating %d I/O queues.\n", nr_io_queues);
|
||||||
|
nvmf_set_io_queues(ctrl->ctrl.opts, nr_io_queues, ctrl->io_queues);
|
||||||
|
|
||||||
if (new) {
|
if (new) {
|
||||||
ret = nvme_rdma_alloc_tag_set(&ctrl->ctrl);
|
ret = nvme_rdma_alloc_tag_set(&ctrl->ctrl);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
@ -880,7 +905,9 @@ static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new)
|
||||||
* queue number might have changed.
|
* queue number might have changed.
|
||||||
*/
|
*/
|
||||||
nr_queues = min(ctrl->tag_set.nr_hw_queues + 1, ctrl->ctrl.queue_count);
|
nr_queues = min(ctrl->tag_set.nr_hw_queues + 1, ctrl->ctrl.queue_count);
|
||||||
ret = nvme_rdma_start_io_queues(ctrl, 1, nr_queues);
|
ret = nvme_rdma_setup_io_queues(ctrl, 1, nr_queues,
|
||||||
|
ctrl->ctrl.sqsize + 1);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_cleanup_tagset;
|
goto out_cleanup_tagset;
|
||||||
|
|
||||||
|
|
@ -904,12 +931,15 @@ static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the number of queues has increased (reconnect case)
|
* If the number of queues has increased (reconnect case)
|
||||||
* start all new queues now.
|
* setup all new queues now.
|
||||||
*/
|
*/
|
||||||
ret = nvme_rdma_start_io_queues(ctrl, nr_queues,
|
if (ctrl->tag_set.nr_hw_queues + 1 > nr_queues) {
|
||||||
ctrl->tag_set.nr_hw_queues + 1);
|
ret = nvme_rdma_setup_io_queues(ctrl, nr_queues,
|
||||||
if (ret)
|
ctrl->tag_set.nr_hw_queues + 1,
|
||||||
goto out_wait_freeze_timed_out;
|
ctrl->ctrl.sqsize + 1);
|
||||||
|
if (ret)
|
||||||
|
goto out_wait_freeze_timed_out;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user