]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
nvme: ensure forward progress during Admin passthru
authorScott Bauer <scott.bauer@intel.com>
Fri, 29 Jun 2018 19:03:28 +0000 (13:03 -0600)
committerChristoph Hellwig <hch@lst.de>
Tue, 17 Jul 2018 12:46:19 +0000 (05:46 -0700)
If the controller supports effects and goes down during the passthru admin
command we will deadlock during namespace revalidation.

[  363.488275] INFO: task kworker/u16:5:231 blocked for more than 120 seconds.
[  363.488290]       Not tainted 4.17.0+ #2
[  363.488296] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[  363.488303] kworker/u16:5   D    0   231      2 0x80000000
[  363.488331] Workqueue: nvme-reset-wq nvme_reset_work [nvme]
[  363.488338] Call Trace:
[  363.488385]  schedule+0x75/0x190
[  363.488396]  rwsem_down_read_failed+0x1c3/0x2f0
[  363.488481]  call_rwsem_down_read_failed+0x14/0x30
[  363.488504]  down_read+0x1d/0x80
[  363.488523]  nvme_stop_queues+0x1e/0xa0 [nvme_core]
[  363.488536]  nvme_dev_disable+0xae4/0x1620 [nvme]
[  363.488614]  nvme_reset_work+0xd1e/0x49d9 [nvme]
[  363.488911]  process_one_work+0x81a/0x1400
[  363.488934]  worker_thread+0x87/0xe80
[  363.488955]  kthread+0x2db/0x390
[  363.488977]  ret_from_fork+0x35/0x40

Fixes: 84fef62d135b6 ("nvme: check admin passthru command effects")
Signed-off-by: Scott Bauer <scott.bauer@intel.com>
Reviewed-by: Keith Busch <keith.busch@linux.intel.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/nvme/host/core.c

index 46df030b2c3f74f33621dc7d4512b17fe40fd079..e7668c4bb4dddcadd514cc9e62a387fb22a979b1 100644 (file)
@@ -100,6 +100,22 @@ static struct class *nvme_subsys_class;
 static void nvme_ns_remove(struct nvme_ns *ns);
 static int nvme_revalidate_disk(struct gendisk *disk);
 static void nvme_put_subsystem(struct nvme_subsystem *subsys);
+static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl,
+                                          unsigned nsid);
+
+static void nvme_set_queue_dying(struct nvme_ns *ns)
+{
+       /*
+        * Revalidating a dead namespace sets capacity to 0. This will end
+        * buffered writers dirtying pages that can't be synced.
+        */
+       if (!ns->disk || test_and_set_bit(NVME_NS_DEAD, &ns->flags))
+               return;
+       revalidate_disk(ns->disk);
+       blk_set_queue_dying(ns->queue);
+       /* Forcibly unquiesce queues to avoid blocking dispatch */
+       blk_mq_unquiesce_queue(ns->queue);
+}
 
 static void nvme_queue_scan(struct nvme_ctrl *ctrl)
 {
@@ -1151,19 +1167,15 @@ static u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
 
 static void nvme_update_formats(struct nvme_ctrl *ctrl)
 {
-       struct nvme_ns *ns, *next;
-       LIST_HEAD(rm_list);
+       struct nvme_ns *ns;
 
-       down_write(&ctrl->namespaces_rwsem);
-       list_for_each_entry(ns, &ctrl->namespaces, list) {
-               if (ns->disk && nvme_revalidate_disk(ns->disk)) {
-                       list_move_tail(&ns->list, &rm_list);
-               }
-       }
-       up_write(&ctrl->namespaces_rwsem);
+       down_read(&ctrl->namespaces_rwsem);
+       list_for_each_entry(ns, &ctrl->namespaces, list)
+               if (ns->disk && nvme_revalidate_disk(ns->disk))
+                       nvme_set_queue_dying(ns);
+       up_read(&ctrl->namespaces_rwsem);
 
-       list_for_each_entry_safe(ns, next, &rm_list, list)
-               nvme_ns_remove(ns);
+       nvme_remove_invalid_namespaces(ctrl, NVME_NSID_ALL);
 }
 
 static void nvme_passthru_end(struct nvme_ctrl *ctrl, u32 effects)
@@ -3138,7 +3150,7 @@ static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl,
 
        down_write(&ctrl->namespaces_rwsem);
        list_for_each_entry_safe(ns, next, &ctrl->namespaces, list) {
-               if (ns->head->ns_id > nsid)
+               if (ns->head->ns_id > nsid || test_bit(NVME_NS_DEAD, &ns->flags))
                        list_move_tail(&ns->list, &rm_list);
        }
        up_write(&ctrl->namespaces_rwsem);
@@ -3542,19 +3554,9 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
        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
-                * end buffered writers dirtying pages that can't be synced.
-                */
-               if (!ns->disk || test_and_set_bit(NVME_NS_DEAD, &ns->flags))
-                       continue;
-               revalidate_disk(ns->disk);
-               blk_set_queue_dying(ns->queue);
+       list_for_each_entry(ns, &ctrl->namespaces, list)
+               nvme_set_queue_dying(ns);
 
-               /* Forcibly unquiesce queues to avoid blocking dispatch */
-               blk_mq_unquiesce_queue(ns->queue);
-       }
        up_read(&ctrl->namespaces_rwsem);
 }
 EXPORT_SYMBOL_GPL(nvme_kill_queues);