Skip to contentSkip to conversation

nvme: Request cancelling helpers

PATCH New Keith Busch 3 participants 13 messages Jan 22, 2018 – Jan 23, 2018

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/5 nvme: Request cancelling helpers

This patch provides an API for cancelling IO requests, replacing each driver's use of blk_mq_busy_tag_iter with a more convenient API for nvme controllers.

The nvme_cancel_request is used only in the core now, so this patch makes that function private.

Signed-off-by Keith Busch <keith.busch at intel.com>
---
 drivers/nvme/host/core.c   | 31 +++++++++++++++++++++++++++++--
 drivers/nvme/host/nvme.h   |  8 +++++++-
 drivers/nvme/host/pci.c    |  4 +---
 drivers/nvme/host/rdma.c   | 12 ++++--------
 drivers/nvme/target/loop.c |  6 ++----
 5 files changed, 43 insertions(+), 18 deletions(-)
drivers/nvme/host/core.c +29 -2
index fde6fd2e7eef..b9cf2bce2132 100644
@@ -233,7 +233,7 @@ void nvme_complete_rq(struct request *req)
 }
 EXPORT_SYMBOL_GPL(nvme_complete_rq);
 
-void nvme_cancel_request(struct request *req, void *data, bool reserved)
+static void nvme_cancel_request(struct request *req, void *data, bool reserved)
 {
 	if (!blk_mq_request_started(req))
 		return;
@@ -245,7 +245,34 @@ void nvme_cancel_request(struct request *req, void *data, bool reserved)
 	blk_mq_complete_request(req);
 
 }
-EXPORT_SYMBOL_GPL(nvme_cancel_request);
+
+void nvme_set_iter(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set,
+		   busy_tag_iter_fn *fn)
+{
+	if (!set)
+		return;
+	blk_mq_tagset_busy_iter(set, fn, ctrl);
+}
+EXPORT_SYMBOL_GPL(nvme_set_iter);
+
+void nvme_cancel_io_requests(struct nvme_ctrl *ctrl)
+{
+	nvme_set_iter(ctrl, ctrl->tagset, nvme_cancel_request);
+}
+EXPORT_SYMBOL_GPL(nvme_cancel_io_requests);
+
+void nvme_cancel_admin_requests(struct nvme_ctrl *ctrl)
+{
+	nvme_set_iter(ctrl, ctrl->admin_tagset, nvme_cancel_request);
+}
+EXPORT_SYMBOL_GPL(nvme_cancel_admin_requests);
+
+void nvme_cancel_requests(struct nvme_ctrl *ctrl)
+{
+	nvme_cancel_io_requests(ctrl);
+	nvme_cancel_admin_requests(ctrl);
+}
+EXPORT_SYMBOL_GPL(nvme_cancel_requests);
 
 bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl,
 		enum nvme_ctrl_state new_state)
drivers/nvme/host/nvme.h +7 -1
index 8e7fc1b041b7..5fb9d600f9c0 100644
@@ -349,7 +349,6 @@ static inline void nvme_put_ctrl(struct nvme_ctrl *ctrl)
 }
 
 void nvme_complete_rq(struct request *req);
-void nvme_cancel_request(struct request *req, void *data, bool reserved);
 bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl,
 		enum nvme_ctrl_state new_state);
 int nvme_disable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
@@ -372,6 +371,13 @@ int nvme_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, size_t len,
 void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status,
 		union nvme_result *res);
 
+void nvme_cancel_requests(struct nvme_ctrl *ctrl);
+void nvme_cancel_io_requests(struct nvme_ctrl *ctrl);
+void nvme_cancel_admin_requests(struct nvme_ctrl *ctrl);
+
+void nvme_set_iter(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set,
+		   busy_tag_iter_fn *fn);
+
 void nvme_stop_queues(struct nvme_ctrl *ctrl);
 void nvme_start_queues(struct nvme_ctrl *ctrl);
 void nvme_kill_queues(struct nvme_ctrl *ctrl);
drivers/nvme/host/pci.c +1 -3
index a2ffb557b616..4d2477c3c86c 100644
@@ -2213,9 +2213,7 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown)
 	}
 	nvme_pci_disable(dev);
 
-	blk_mq_tagset_busy_iter(&dev->tagset, nvme_cancel_request, &dev->ctrl);
-	blk_mq_tagset_busy_iter(&dev->admin_tagset, nvme_cancel_request, &dev->ctrl);
-
+	nvme_cancel_requests(&dev->ctrl);
 	/*
 	 * The driver will not be starting up queues again if shutting down so
 	 * must flush all entered requests to their failed completion to avoid
drivers/nvme/host/rdma.c +4 -8
index 38e183461d9d..71070eedb773 100644
@@ -957,14 +957,12 @@ static void nvme_rdma_error_recovery_work(struct work_struct *work)
 
 	if (ctrl->ctrl.queue_count > 1) {
 		nvme_stop_queues(&ctrl->ctrl);
-		blk_mq_tagset_busy_iter(&ctrl->tag_set,
-					nvme_cancel_request, &ctrl->ctrl);
+		nvme_cancel_io_requests(&ctrl->ctrl);
 		nvme_rdma_destroy_io_queues(ctrl, false);
 	}
 
 	blk_mq_quiesce_queue(ctrl->ctrl.admin_q);
-	blk_mq_tagset_busy_iter(&ctrl->admin_tag_set,
-				nvme_cancel_request, &ctrl->ctrl);
+	nvme_cancel_admin_requests(&ctrl->ctrl);
 	nvme_rdma_destroy_admin_queue(ctrl, false);
 
 	/*
@@ -1721,8 +1719,7 @@ static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown)
 
 	if (ctrl->ctrl.queue_count > 1) {
 		nvme_stop_queues(&ctrl->ctrl);
-		blk_mq_tagset_busy_iter(&ctrl->tag_set,
-					nvme_cancel_request, &ctrl->ctrl);
+		nvme_cancel_io_requests(&ctrl->ctrl);
 		nvme_rdma_destroy_io_queues(ctrl, shutdown);
 	}
 
@@ -1732,8 +1729,7 @@ static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown)
 		nvme_disable_ctrl(&ctrl->ctrl, ctrl->ctrl.cap);
 
 	blk_mq_quiesce_queue(ctrl->ctrl.admin_q);
-	blk_mq_tagset_busy_iter(&ctrl->admin_tag_set,
-				nvme_cancel_request, &ctrl->ctrl);
+	nvme_cancel_admin_requests(&ctrl->ctrl);
 	blk_mq_unquiesce_queue(ctrl->ctrl.admin_q);
 	nvme_rdma_destroy_admin_queue(ctrl, shutdown);
 }
drivers/nvme/target/loop.c +2 -4
index 7991ec3a17db..5dd7834a35da 100644
@@ -439,8 +439,7 @@ static void nvme_loop_shutdown_ctrl(struct nvme_loop_ctrl *ctrl)
 {
 	if (ctrl->ctrl.queue_count > 1) {
 		nvme_stop_queues(&ctrl->ctrl);
-		blk_mq_tagset_busy_iter(&ctrl->tag_set,
-					nvme_cancel_request, &ctrl->ctrl);
+		nvme_cancel_io_requests(&ctrl->ctrl);
 		nvme_loop_destroy_io_queues(ctrl);
 	}
 
@@ -448,8 +447,7 @@ static void nvme_loop_shutdown_ctrl(struct nvme_loop_ctrl *ctrl)
 		nvme_shutdown_ctrl(&ctrl->ctrl);
 
 	blk_mq_quiesce_queue(ctrl->ctrl.admin_q);
-	blk_mq_tagset_busy_iter(&ctrl->admin_tag_set,
-				nvme_cancel_request, &ctrl->ctrl);
+	nvme_cancel_admin_requests(&ctrl->ctrl);
 	blk_mq_unquiesce_queue(ctrl->ctrl.admin_q);
 	nvme_loop_destroy_admin_queue(ctrl);
 }
Signature · 1 line
2.14.3
#1 lore raw

PATCH 2/5 nvme: Ending failed unstarted requests

This patch provides new nvme driver APIs for directly ending unstarted requests when they need to be failed. Previously, drivers needed to temporarily quiesce request queues while setting up the IO path to take on the responsibilty of error handling, then restart those queues. Handling these errors should be done directly in the error handling path, freeing up the IO path to not concern itself with such failure cases.

Signed-off-by Keith Busch <keith.busch at intel.com>
---
 drivers/nvme/host/core.c | 44 +++++++++++++++++++++++++++++++++++---------
 drivers/nvme/host/nvme.h |  4 ++++
 drivers/nvme/host/pci.c  |  4 ++--
 drivers/nvme/host/rdma.c |  8 +++-----
 4 files changed, 44 insertions(+), 16 deletions(-)
drivers/nvme/host/core.c +35 -9
index b9cf2bce2132..ae4349a4f3a8 100644
@@ -246,6 +246,17 @@ static void nvme_cancel_request(struct request *req, void *data, bool reserved)
 
 }
 
+static void nvme_end_unstarted_request(struct request *req, void *data,
+				       bool reserved)
+{
+	if (blk_mq_request_started(req))
+		return;
+
+	dev_dbg_ratelimited(((struct nvme_ctrl *) data)->device,
+				"Ending I/O %d", req->tag);
+	blk_mq_end_request(req, BLK_STS_IOERR);
+}
+
 void nvme_set_iter(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set,
 		   busy_tag_iter_fn *fn)
 {
@@ -255,6 +266,25 @@ void nvme_set_iter(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set,
 }
 EXPORT_SYMBOL_GPL(nvme_set_iter);
 
+void nvme_end_io_requests(struct nvme_ctrl *ctrl)
+{
+	nvme_set_iter(ctrl, ctrl->tagset, nvme_end_unstarted_request);
+}
+EXPORT_SYMBOL_GPL(nvme_end_io_requests);
+
+void nvme_end_admin_requests(struct nvme_ctrl *ctrl)
+{
+	nvme_set_iter(ctrl, ctrl->admin_tagset, nvme_end_unstarted_request);
+}
+EXPORT_SYMBOL_GPL(nvme_end_admin_requests);
+
+void nvme_end_requests(struct nvme_ctrl *ctrl)
+{
+	nvme_end_io_requests(ctrl);
+	nvme_end_admin_requests(ctrl);
+}
+EXPORT_SYMBOL_GPL(nvme_end_requests);
+
 void nvme_cancel_io_requests(struct nvme_ctrl *ctrl)
 {
 	nvme_set_iter(ctrl, ctrl->tagset, nvme_cancel_request);
@@ -3215,8 +3245,10 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
 	 * removing the namespaces' disks; fail all the queues now to avoid
 	 * potentially having to clean up the failed sync later.
 	 */
-	if (ctrl->state == NVME_CTRL_DEAD)
+	if (ctrl->state == NVME_CTRL_DEAD) {
 		nvme_kill_queues(ctrl);
+		nvme_end_admin_requests(ctrl);
+	}
 
 	list_for_each_entry_safe(ns, next, &ctrl->namespaces, list)
 		nvme_ns_remove(ns);
@@ -3467,11 +3499,6 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
 	struct nvme_ns *ns;
 
 	mutex_lock(&ctrl->namespaces_mutex);
-
-	/* Forcibly unquiesce queues to avoid blocking dispatch */
-	if (ctrl->admin_q)
-		blk_mq_unquiesce_queue(ctrl->admin_q);
-
 	list_for_each_entry(ns, &ctrl->namespaces, list) {
 		/*
 		 * Revalidating a dead namespace sets capacity to 0. This will
@@ -3481,11 +3508,10 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
 			continue;
 		revalidate_disk(ns->disk);
 		blk_set_queue_dying(ns->queue);
-
-		/* Forcibly unquiesce queues to avoid blocking dispatch */
-		blk_mq_unquiesce_queue(ns->queue);
 	}
 	mutex_unlock(&ctrl->namespaces_mutex);
+
+	nvme_end_io_requests(ctrl);
 }
 EXPORT_SYMBOL_GPL(nvme_kill_queues);
 
drivers/nvme/host/nvme.h +4 -0
index 5fb9d600f9c0..96fbc233cbb2 100644
@@ -371,6 +371,10 @@ int nvme_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, size_t len,
 void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status,
 		union nvme_result *res);
 
+void nvme_end_requests(struct nvme_ctrl *ctrl);
+void nvme_end_io_requests(struct nvme_ctrl *ctrl);
+void nvme_end_admin_requests(struct nvme_ctrl *ctrl);
+
 void nvme_cancel_requests(struct nvme_ctrl *ctrl);
 void nvme_cancel_io_requests(struct nvme_ctrl *ctrl);
 void nvme_cancel_admin_requests(struct nvme_ctrl *ctrl);
drivers/nvme/host/pci.c +2 -2
index 4d2477c3c86c..63b477bfef37 100644
@@ -2216,11 +2216,11 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown)
 	nvme_cancel_requests(&dev->ctrl);
 	/*
 	 * The driver will not be starting up queues again if shutting down so
-	 * must flush all entered requests to their failed completion to avoid
+	 * must end all entered requests to their failed completion to avoid
 	 * deadlocking blk-mq hot-cpu notifier.
 	 */
 	if (shutdown)
-		nvme_start_queues(&dev->ctrl);
+		nvme_end_requests(&dev->ctrl);
 	mutex_unlock(&dev->shutdown_lock);
 }
 
drivers/nvme/host/rdma.c +3 -5
index 71070eedb773..e51a84d1d732 100644
@@ -966,11 +966,9 @@ static void nvme_rdma_error_recovery_work(struct work_struct *work)
 	nvme_rdma_destroy_admin_queue(ctrl, false);
 
 	/*
-	 * queues are not a live anymore, so restart the queues to fail fast
-	 * new IO
+	 * queues are not a live anymore, so end all unstarted requests.
 	 */
-	blk_mq_unquiesce_queue(ctrl->ctrl.admin_q);
-	nvme_start_queues(&ctrl->ctrl);
+	nvme_end_requests(&ctrl->ctrl);
 
 	nvme_rdma_reconnect_or_remove(ctrl);
 }
@@ -1730,7 +1728,7 @@ static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown)
 
 	blk_mq_quiesce_queue(ctrl->ctrl.admin_q);
 	nvme_cancel_admin_requests(&ctrl->ctrl);
-	blk_mq_unquiesce_queue(ctrl->ctrl.admin_q);
+	nvme_end_admin_requests(&ctrl->ctrl);
 	nvme_rdma_destroy_admin_queue(ctrl, shutdown);
 }
 
Signature · 1 line
2.14.3
#2 lore raw

PATCH 2/5 nvme: Ending failed unstarted requests

Hi Keith

Quoted 6 lines · Keith Busch

This patch provides new nvme driver APIs for directly ending unstarted requests when they need to be failed. Previously, drivers needed to temporarily quiesce request queues while setting up the IO path to take on the responsibilty of error handling, then restart those queues. Handling these errors should be done directly in the error handling path, freeing up the IO path to not concern itself with such failure cases.

Yes, we indeed need to ensure the entered requests to be drained after nvme_dev_disable returns for shutdown case.

Quoted 17 lines
Signed-off-by Keith Busch <keith.busch at intel.com>
---
 drivers/nvme/host/core.c | 44 +++++++++++++++++++++++++++++++++++---------
 drivers/nvme/host/nvme.h |  4 ++++
 drivers/nvme/host/pci.c  |  4 ++--
 drivers/nvme/host/rdma.c |  8 +++-----
 4 files changed, 44 insertions(+), 16 deletions(-)
drivers/nvme/host/core.c +1 -0
index b9cf2bce2132..ae4349a4f3a8 100644
@@ -246,6 +246,17 @@ static void nvme_cancel_request(struct request *req, void *data, bool reserved)
 
 }
 
+static void nvme_end_unstarted_request(struct request *req, void *data,

drivers/nvme/host/core.c static void nvme_cancel_request(struct request *req, void *data, bool reserved) · line 259

Quoted 10 lines
drivers/nvme/host/core.c +10 -0
+				       bool reserved)
+{
+	if (blk_mq_request_started(req))
+		return;
+
+	dev_dbg_ratelimited(((struct nvme_ctrl *) data)->device,
+				"Ending I/O %d", req->tag);
+	blk_mq_end_request(req, BLK_STS_IOERR);
+}
+

This unstarted requests have not been dequeued from the blk-mq queue. We cannot end them directly.

Thanks
Jianchao

maintainer
#3 lore raw

PATCH 2/5 nvme: Ending failed unstarted requests

Quoted 15 lines

drivers/nvme/host/core.c static void nvme_cancel_request(struct request *req, void *data, bool reserved) · line 259

Quoted 12 lines
drivers/nvme/host/core.c +11 -0
+static void nvme_end_unstarted_request(struct request *req, void *data,
+				       bool reserved)
+{
+	if (blk_mq_request_started(req))
+		return;
+
+	dev_dbg_ratelimited(((struct nvme_ctrl *) data)->device,
+				"Ending I/O %d", req->tag);
+	blk_mq_end_request(req, BLK_STS_IOERR);
+}
+

This unstarted requests have not been dequeued from the blk-mq queue. We cannot end them directly.

I think we cannot complete them. I think its fine to end them.

#4 lore raw

PATCH 2/5 nvme: Ending failed unstarted requests

Hi Sagi

Quoted 16 lines · Sagi Grimberg
Quoted 15 lines

Review

Quoted 12 lines
(quoted diff) +11 -0
+static void nvme_end_unstarted_request(struct request *req, void *data,
+?????????????????????? bool reserved)
+{
+??? if (blk_mq_request_started(req))
+??????? return;
+
+??? dev_dbg_ratelimited(((struct nvme_ctrl *) data)->device,
+??????????????? "Ending I/O %d", req->tag);
+??? blk_mq_end_request(req, BLK_STS_IOERR);
+}
+

This unstarted requests have not been dequeued from the blk-mq queue. We cannot end them directly.

Quoted 2 lines

I think we cannot complete them. I think its fine to end them.

The end here which is done by blk_mq_end_request is to free it,

blk_mq_end_request()
-> __blk_mq_end_request()

    -> blk_mq_free_request()

The requests are still on the blk-mq queues.
How can we free them ?

Thanks
Jianchao

#5 lore raw

PATCH 2/5 nvme: Ending failed unstarted requests

In reply to Keith Busch

Hi Keith

drivers/nvme/host/pci.c static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown) · line 2223

Quoted 10 lines · Keith Busch
drivers/nvme/host/pci.c +2 -2
 	nvme_cancel_requests(&dev->ctrl);
 	/*
 	 * The driver will not be starting up queues again if shutting down so
-	 * must flush all entered requests to their failed completion to avoid
+	 * must end all entered requests to their failed completion to avoid
 	 * deadlocking blk-mq hot-cpu notifier.
 	 */
 	if (shutdown)
-		nvme_start_queues(&dev->ctrl);
+		nvme_end_requests(&dev->ctrl);

The blk-mq cpuhp notifier has been changed by Christoph's 4b855ad (blk-mq: Create hctx for each present CPU), the deadlocking here should have been fixed here.
So the comment here should be modified at least to avoid confusion.
On the other hand, we needn't freeze the queue for reset case, the device will be setup soon later, just quiesce the blk-mq queue should be ok. And regarding to the shutdown case, frozen and drained queue will be safer. To drain the queue, we have to unquiesce it.
Finally, we get a quiesced queue for reset case, and frozen and drained queue for shutdown case.

Thanks
Jianchao

maintainer
#6 lore raw

PATCH 2/5 nvme: Ending failed unstarted requests

Quoted 4 lines

Hi Keith

On 01/23/2018 05:56 AM, Keith Busch wrote:

Quoted 16 lines

drivers/nvme/host/pci.c static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown) · line 2223

Quoted 10 lines

nvme_cancel_requests(&dev->ctrl);
/*
* The driver will not be starting up queues again if shutting down so

drivers/nvme/host/pci.c +2 -2
-	 * must flush all entered requests to their failed completion to avoid
+	 * must end all entered requests to their failed completion to avoid
  	 * deadlocking blk-mq hot-cpu notifier.
  	 */
  	if (shutdown)
-		nvme_start_queues(&dev->ctrl);
+		nvme_end_requests(&dev->ctrl);

The blk-mq cpuhp notifier has been changed by Christoph's 4b855ad (blk-mq: Create hctx for each present CPU), the deadlocking here should have been fixed here.
So the comment here should be modified at least to avoid confusion.
On the other hand, we needn't freeze the queue for reset case, the device will be setup soon later, just quiesce the blk-mq queue should be ok. And regarding to the shutdown case, frozen and drained queue will be safer. To drain the queue, we have to unquiesce it.

I think that this is exactly the point of the patch, not to unquiesce and rely on .queue_rq to fail the requests.

maintainer
#7 lore raw

PATCH 2/5 nvme: Ending failed unstarted requests

In reply to Keith Busch
Quoted 20 lines
drivers/nvme/host/rdma.c +2 -4
index 71070eedb773..e51a84d1d732 100644
@@ -966,11 +966,9 @@ static void nvme_rdma_error_recovery_work(struct work_struct *work)
  	nvme_rdma_destroy_admin_queue(ctrl, false);
 
  	/*
-	 * queues are not a live anymore, so restart the queues to fail fast
-	 * new IO
+	 * queues are not a live anymore, so end all unstarted requests.
  	 */
-	blk_mq_unquiesce_queue(ctrl->ctrl.admin_q);
-	nvme_start_queues(&ctrl->ctrl);
+	nvme_end_requests(&ctrl->ctrl);
 
  	nvme_rdma_reconnect_or_remove(ctrl);
  }

drivers/nvme/host/rdma.c static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown) · line 1733

Quoted 7 lines
drivers/nvme/host/rdma.c +1 -1
@@ -1730,7 +1728,7 @@ static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown)
 
  	blk_mq_quiesce_queue(ctrl->ctrl.admin_q);
  	nvme_cancel_admin_requests(&ctrl->ctrl);
-	blk_mq_unquiesce_queue(ctrl->ctrl.admin_q);
+	nvme_end_admin_requests(&ctrl->ctrl);
  	nvme_rdma_destroy_admin_queue(ctrl, shutdown);
  }

I think we need to end_io_requests a little above as well.

#8 lore raw

PATCH 3/5 nvme/pci: End stopped queue requests directly

In reply to Keith Busch

For requests that have entered a queue that is not able to be brought back online, this patch will end those requests directly rather than rely on the IO path to handle this error.

Signed-off-by Keith Busch <keith.busch at intel.com>
---
 drivers/nvme/host/pci.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
drivers/nvme/host/pci.c +15 -0
index 63b477bfef37..9e9e3a59bfb3 100644
@@ -2272,6 +2272,19 @@ static void nvme_remove_dead_ctrl(struct nvme_dev *dev, int status)
 		nvme_put_ctrl(&dev->ctrl);
 }
 
+static void nvme_end_stopped_queue_request(struct request *req, void *data,
+				   bool reserved)
+{
+	struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
+	struct nvme_queue *nvmeq = iod->nvmeq;
+
+	if (nvmeq->cq_vector != -1)
+		return;
+	dev_dbg_ratelimited(((struct nvme_ctrl *) data)->device,
+			    "Ending I/O %d QID:%d", req->tag, nvmeq->qid);
+	blk_mq_end_request(req, BLK_STS_IOERR);
+}
+
 static void nvme_reset_work(struct work_struct *work)
 {
 	struct nvme_dev *dev =
@@ -2344,6 +2357,8 @@ static void nvme_reset_work(struct work_struct *work)
 		nvme_remove_namespaces(&dev->ctrl);
 		new_state = NVME_CTRL_ADMIN_ONLY;
 	} else {
+		nvme_set_iter(&dev->ctrl, dev->ctrl.tagset,
+			      nvme_end_stopped_queue_request);
 		nvme_start_queues(&dev->ctrl);
 		nvme_wait_freeze(&dev->ctrl);
 		/* hit this only when allocate tagset fails */
Signature · 1 line
2.14.3
maintainer
#9 lore raw

PATCH 3/5 nvme/pci: End stopped queue requests directly

drivers/nvme/host/pci.c static void nvme_remove_dead_ctrl(struct nvme_dev *dev, int status) · line 2276

Quoted 3 lines
drivers/nvme/host/pci.c +2 -0
+static void nvme_end_stopped_queue_request(struct request *req, void *data,
+				   bool reserved)

nvme_pci_end_stopped_queue_request

Quoted 15 lines
drivers/nvme/host/pci.c +11 -0
+{
+	struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
+	struct nvme_queue *nvmeq = iod->nvmeq;
+
+	if (nvmeq->cq_vector != -1)
+		return;
+	dev_dbg_ratelimited(((struct nvme_ctrl *) data)->device,
+			    "Ending I/O %d QID:%d", req->tag, nvmeq->qid);
+	blk_mq_end_request(req, BLK_STS_IOERR);
+}
+
  static void nvme_reset_work(struct work_struct *work)
  {
  	struct nvme_dev *dev =

drivers/nvme/host/pci.c static void nvme_reset_work(struct work_struct *work) · line 2361

Quoted 5 lines
drivers/nvme/host/pci.c +2 -0
@@ -2344,6 +2357,8 @@ static void nvme_reset_work(struct work_struct *work)
  		nvme_remove_namespaces(&dev->ctrl);
  		new_state = NVME_CTRL_ADMIN_ONLY;
  	} else {
+		nvme_set_iter(&dev->ctrl, dev->ctrl.tagset,
+			      nvme_end_stopped_queue_request);

It would have been good if we could reuse that in other transports where we maintain state flags (NVME_*_Q_LIVE).

#10 lore raw

PATCH 4/5 nvme/pci: Remove cq_vector checks in io path

In reply to Keith Busch

We don't need to check for if the queue is suspended in the submission or completion path. Requests to suspended queues are no longer submitted, and we don't unmap the doorbell while requests are active.

Signed-off-by Keith Busch <keith.busch at intel.com>
---
 drivers/nvme/host/pci.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)
drivers/nvme/host/pci.c +3 -10
index 9e9e3a59bfb3..99ac0de43206 100644
@@ -889,11 +889,6 @@ static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
 	blk_mq_start_request(req);
 
 	spin_lock_irq(&nvmeq->q_lock);
-	if (unlikely(nvmeq->cq_vector < 0)) {
-		ret = BLK_STS_IOERR;
-		spin_unlock_irq(&nvmeq->q_lock);
-		goto out_cleanup_iod;
-	}
 	__nvme_submit_cmd(nvmeq, &cmnd);
 	nvme_process_cq(nvmeq);
 	spin_unlock_irq(&nvmeq->q_lock);
@@ -924,11 +919,9 @@ static inline void nvme_ring_cq_doorbell(struct nvme_queue *nvmeq)
 {
 	u16 head = nvmeq->cq_head;
 
-	if (likely(nvmeq->cq_vector >= 0)) {
-		if (nvme_dbbuf_update_and_check_event(head, nvmeq->dbbuf_cq_db,
-						      nvmeq->dbbuf_cq_ei))
-			writel(head, nvmeq->q_db + nvmeq->dev->db_stride);
-	}
+	if (nvme_dbbuf_update_and_check_event(head, nvmeq->dbbuf_cq_db,
+					      nvmeq->dbbuf_cq_ei))
+		writel(head, nvmeq->q_db + nvmeq->dev->db_stride);
 }
 
 static inline void nvme_handle_cqe(struct nvme_queue *nvmeq,
Signature · 1 line
2.14.3
#11 lore raw

PATCH 5/5 nvme: Sync queues on controller resets

In reply to Keith Busch

This patch has the nvme pci driver synchronize request queues to ensure that starting up the controller is not racing with a previously running timeout handler.

Reported-by Jianchao Wang <jianchao.w.wang at oracle.com>
Signed-off-by Keith Busch <keith.busch at intel.com>
---
 drivers/nvme/host/core.c | 15 ++++++++++++++-
 drivers/nvme/host/nvme.h |  1 +
 drivers/nvme/host/pci.c  |  4 +++-
 3 files changed, 18 insertions(+), 2 deletions(-)
drivers/nvme/host/core.c +14 -1
index ae4349a4f3a8..e8826fab54b8 100644
@@ -3578,12 +3578,25 @@ void nvme_start_queues(struct nvme_ctrl *ctrl)
 	struct nvme_ns *ns;
 
 	mutex_lock(&ctrl->namespaces_mutex);
-	list_for_each_entry(ns, &ctrl->namespaces, list)
+	list_for_each_entry(ns, &ctrl->namespaces, list) {
 		blk_mq_unquiesce_queue(ns->queue);
+		blk_mq_kick_requeue_list(ns->queue);
+	}
 	mutex_unlock(&ctrl->namespaces_mutex);
 }
 EXPORT_SYMBOL_GPL(nvme_start_queues);
 
+void nvme_sync_queues(struct nvme_ctrl *ctrl)
+{
+	struct nvme_ns *ns;
+
+	mutex_lock(&ctrl->namespaces_mutex);
+	list_for_each_entry(ns, &ctrl->namespaces, list)
+		blk_sync_queue(ns->queue);
+	mutex_unlock(&ctrl->namespaces_mutex);
+}
+EXPORT_SYMBOL_GPL(nvme_sync_queues);
+
 int nvme_reinit_tagset(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set)
 {
 	if (!ctrl->ops->reinit_request)
drivers/nvme/host/nvme.h +1 -0
index 96fbc233cbb2..6e80912fe707 100644
@@ -384,6 +384,7 @@ void nvme_set_iter(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set,
 
 void nvme_stop_queues(struct nvme_ctrl *ctrl);
 void nvme_start_queues(struct nvme_ctrl *ctrl);
+void nvme_sync_queues(struct nvme_ctrl *ctrl);
 void nvme_kill_queues(struct nvme_ctrl *ctrl);
 void nvme_unfreeze(struct nvme_ctrl *ctrl);
 void nvme_wait_freeze(struct nvme_ctrl *ctrl);
drivers/nvme/host/pci.c +3 -1
index 99ac0de43206..2de40d1780c1 100644
@@ -2293,8 +2293,10 @@ static void nvme_reset_work(struct work_struct *work)
 	 * If we're called to reset a live controller first shut it down before
 	 * moving on.
 	 */
-	if (dev->ctrl.ctrl_config & NVME_CC_ENABLE)
+	if (dev->ctrl.ctrl_config & NVME_CC_ENABLE) {
 		nvme_dev_disable(dev, false);
+		nvme_sync_queues(&dev->ctrl);
+	}
 
 	result = nvme_pci_enable(dev);
 	if (result)
Signature · 1 line
2.14.3
maintainer
#12 lore raw

PATCH 1/5 nvme: Request cancelling helpers

In reply to Keith Busch

drivers/nvme/host/core.c void nvme_cancel_request(struct request *req, void *data, bool reserved) · line 250

Quoted 3 lines
drivers/nvme/host/core.c +2 -0
+void nvme_set_iter(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set,
+		   busy_tag_iter_fn *fn)

Nit: maybe nvme_tagset_iter?

Other than that looks good!

Reviewed-by Sagi Grimberg <sagi at grimberg.me>