When work with nvme multipathing, if one path failed, the requests will be canceled and retry on another path. Before cancel the requests, nvme_stop_queues quiesce queues for all namespaces, now quiesce one by one, if there is a large set of namespaces, it will take long time. Because every synchronize_rcu may need more than 10 milliseconds, the total waiting time will be long. Quiesce all queues in parallel, and it is safe to synchronize_rcu once. The quiesce time and I/O fail over time can be reduced to one synchronize_rcu time. Signed-off-by: Chao Leng <lengchao@xxxxxxxxxx> --- drivers/nvme/host/core.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index eabffbc708cd..fcfa27e1078a 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -4924,6 +4924,30 @@ static void nvme_stop_ns_queue(struct nvme_ns *ns) blk_mq_wait_quiesce_done(ns->queue); } +static void nvme_stop_ns_queue_nosync(struct nvme_ns *ns) +{ + if (!test_and_set_bit(NVME_NS_STOPPED, &ns->flags)) + blk_mq_quiesce_queue_nowait(ns->queue); +} + +static void nvme_stop_blocking_queues(struct nvme_ctrl *ctrl) +{ + struct nvme_ns *ns; + + list_for_each_entry(ns, &ctrl->namespaces, list) + nvme_stop_ns_queue(ns); +} + +static void nvme_stop_nonblocking_queues(struct nvme_ctrl *ctrl) +{ + struct nvme_ns *ns; + + list_for_each_entry(ns, &ctrl->namespaces, list) + nvme_stop_ns_queue_nosync(ns); + + synchronize_rcu(); +} + /* * Prepare a queue for teardown. * @@ -5017,11 +5041,13 @@ EXPORT_SYMBOL_GPL(nvme_start_freeze); void nvme_stop_queues(struct nvme_ctrl *ctrl) { - struct nvme_ns *ns; - down_read(&ctrl->namespaces_rwsem); - list_for_each_entry(ns, &ctrl->namespaces, list) - nvme_stop_ns_queue(ns); + + if (ctrl->tagset->flags & BLK_MQ_F_BLOCKING) + nvme_stop_blocking_queues(ctrl); + else + nvme_stop_nonblocking_queues(ctrl); + up_read(&ctrl->namespaces_rwsem); } EXPORT_SYMBOL_GPL(nvme_stop_queues); -- 2.16.4