]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
NVMe: Fix namespace removal deadlock
authorKeith Busch <keith.busch@intel.com>
Wed, 24 Feb 2016 16:15:54 +0000 (09:15 -0700)
committerChuck Anderson <chuck.anderson@oracle.com>
Thu, 1 Jun 2017 20:41:06 +0000 (13:41 -0700)
This patch makes nvme namespace removal lockless. It is up to the caller
to ensure no active namespace scanning is occuring. To ensure no scan
work occurs, the nvme pci driver adds a removing state to the controller
device to avoid queueing scan work during removal. The work is flushed
after setting the state, so no new scan work can be queued.

The lockless removal allows the driver to cleanup a namespace
request_queue if the controller fails during removal. Previously this
could deadlock trying to acquire the namespace mutex in order to handle
such events.

Signed-off-by: Keith Busch <keith.busch@intel.com>
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@fb.com>
(cherry picked from commit 646017a612e72f19bd9f991fe25287a149c5f627)

Orabug: 25130845

Signed-off-by: Ashok Vairavan <ashok.vairavan@oracle.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/nvme/host/core.c
drivers/nvme/host/nvme.h
drivers/nvme/host/pci.c

index 53ab78dc196d82e7d0a46b790d1217f0603fa20e..b1471bed3602c8bc1cb79a5e005de9077a8a92de 100644 (file)
@@ -1195,12 +1195,14 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
 
 static void nvme_ns_remove(struct nvme_ns *ns)
 {
-       bool kill = nvme_io_incapable(ns->ctrl) &&
-                       !blk_queue_dying(ns->queue);
+       bool kill;
 
-       lockdep_assert_held(&ns->ctrl->namespaces_mutex);
+       if (test_and_set_bit(NVME_NS_REMOVING, &ns->flags))
+               return;
 
-       if (kill)
+       kill = nvme_io_incapable(ns->ctrl) &&
+                       !blk_queue_dying(ns->queue);
+       if (kill) {
                blk_set_queue_dying(ns->queue);
        if (ns->disk->flags & GENHD_FL_UP) {
                if (blk_get_integrity(ns->disk))
@@ -1213,7 +1215,9 @@ static void nvme_ns_remove(struct nvme_ns *ns)
                blk_mq_abort_requeue_list(ns->queue);
                blk_cleanup_queue(ns->queue);
        }
+       mutex_lock(&ns->ctrl->namespaces_mutex);
        list_del_init(&ns->list);
+       mutex_unlock(&ns->ctrl->namespaces_mutex);
        nvme_put_ns(ns);
 }
 
@@ -1258,10 +1262,8 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
 {
        struct nvme_ns *ns, *next;
 
-       mutex_lock(&ctrl->namespaces_mutex);
        list_for_each_entry_safe(ns, next, &ctrl->namespaces, list)
                nvme_ns_remove(ns);
-       mutex_unlock(&ctrl->namespaces_mutex);
 }
 EXPORT_SYMBOL_GPL(nvme_remove_namespaces);
 
index f257017c49784c6cad3f8c5035ff542b520ac949..0893902bf2ea0893344a910c600226408c34c898 100644 (file)
@@ -108,6 +108,10 @@ struct nvme_ns {
        u16 ms;
        bool ext;
        u8 pi_type;
+       unsigned long flags;
+
+#define NVME_NS_REMOVING 0
+
        u64 mode_select_num_blocks;
        u32 mode_select_block_len;
 };
index 0523e90470c1b253ad6fab4558484603b5111338..748a1c24d21482ec0bc1939c192e40b96fb3dc17 100644 (file)
@@ -119,6 +119,7 @@ struct nvme_dev {
        unsigned long flags;
 
 #define NVME_CTRL_RESETTING    0
+#define NVME_CTRL_REMOVING     1
 
        struct nvme_ctrl ctrl;
        struct completion ioq_wait;
@@ -285,6 +286,17 @@ static int nvme_init_request(void *data, struct request *req,
        return 0;
 }
 
+static void nvme_queue_scan(struct nvme_dev *dev)
+{
+       /*
+        * Do not queue new scan work when a controller is reset during
+        * removal.
+        */
+       if (test_bit(NVME_CTRL_REMOVING, &dev->flags))
+               return;
+       queue_work(nvme_workq, &dev->scan_work);
+}
+
 static void nvme_complete_async_event(struct nvme_dev *dev,
                struct nvme_completion *cqe)
 {
@@ -302,7 +314,7 @@ static void nvme_complete_async_event(struct nvme_dev *dev,
        switch (result & 0xff07) {
        case NVME_AER_NOTICE_NS_CHANGED:
                dev_info(dev->ctrl.device, "rescanning\n");
-               queue_work(nvme_workq, &dev->scan_work);
+               nvme_queue_scan(dev);
        default:
                dev_warn(dev->ctrl.device, "async event result %08x\n", result);
        }
@@ -1703,8 +1715,7 @@ static int nvme_dev_add(struct nvme_dev *dev)
                /* Free previously allocated queues that are no longer usable */
                nvme_free_queues(dev, dev->online_queues);
        }
-
-       queue_work(nvme_workq, &dev->scan_work);
+       nvme_queue_scan(dev);
        return 0;
 }
 
@@ -2097,7 +2108,7 @@ static void nvme_remove(struct pci_dev *pdev)
        struct nvme_dev *dev = pci_get_drvdata(pdev);
 
        del_timer_sync(&dev->watchdog_timer);
-
+       set_bit(NVME_CTRL_REMOVING, &dev->flags);
        pci_set_drvdata(pdev, NULL);
        flush_work(&dev->async_work);
        flush_work(&dev->reset_work);