nvme: protect against simultaneous shutdown invocations
authorKeith Busch <keith.busch@intel.com>
Thu, 26 Nov 2015 11:21:29 +0000 (12:21 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 12 Oct 2017 09:27:35 +0000 (11:27 +0200)
commit 77bf25ea70200cddf083f74b7f617e5f07fac8bd upstream.

[Back-ported to 4.4. The difference is the file location of the struct
definition that's adding the mutex.

This fixes reported kernel panics in 4.4-stable from simultaneous
controller resets that was never supposed to be allowed to happen.]

Signed-off-by: Keith Busch <keith.busch@intel.com>
[hch: split from a larger patch]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@fb.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/nvme/host/nvme.h
drivers/nvme/host/pci.c

index b8a5a8e8f57d13680291637be1328ad132d96f76..88cf4f5025b0e5d041536a5093d2432ae95eada9 100644 (file)
@@ -14,6 +14,7 @@
 #ifndef _NVME_H
 #define _NVME_H
 
+#include <linux/mutex.h>
 #include <linux/nvme.h>
 #include <linux/pci.h>
 #include <linux/kref.h>
@@ -62,6 +63,7 @@ struct nvme_dev {
        struct work_struct reset_work;
        struct work_struct probe_work;
        struct work_struct scan_work;
+       struct mutex shutdown_lock;
        char name[12];
        char serial[20];
        char model[40];
index 4c673d45f1bd7895fa1b4a84c9b8a5d01c17da37..669edbd476020e4f6b1f7e8c15e3f3ba6a0f13f0 100644 (file)
@@ -2954,6 +2954,7 @@ static void nvme_dev_shutdown(struct nvme_dev *dev)
 
        nvme_dev_list_remove(dev);
 
+       mutex_lock(&dev->shutdown_lock);
        if (pci_is_enabled(to_pci_dev(dev->dev))) {
                nvme_freeze_queues(dev);
                csts = readl(&dev->bar->csts);
@@ -2972,6 +2973,7 @@ static void nvme_dev_shutdown(struct nvme_dev *dev)
 
        for (i = dev->queue_count - 1; i >= 0; i--)
                nvme_clear_queue(dev->queues[i]);
+       mutex_unlock(&dev->shutdown_lock);
 }
 
 static void nvme_dev_remove(struct nvme_dev *dev)
@@ -3328,6 +3330,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        INIT_LIST_HEAD(&dev->namespaces);
        INIT_WORK(&dev->reset_work, nvme_reset_work);
+       mutex_init(&dev->shutdown_lock);
        dev->dev = get_device(&pdev->dev);
        pci_set_drvdata(pdev, dev);