From: Keith Busch Date: Fri, 8 Apr 2016 22:11:02 +0000 (-0600) Subject: NVMe: Fix reset/remove race X-Git-Tag: v4.1.12-102.0.20170601_1400~106 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=72ea8e4e050438dd45320bf03060b5d479e46b60;p=users%2Fjedix%2Flinux-maple.git NVMe: Fix reset/remove race This fixes a scenario where device is present and being reset, but a request to unbind the driver occurs. A previous patch series addressing a device failure removal scenario flushed reset_work after controller disable to unblock reset_work waiting on a completion that wouldn't occur. This isn't safe as-is. The broken scenario can potentially be induced with: modprobe nvme && modprobe -r nvme To fix, the reset work is flushed immediately after setting the controller removing flag, and any subsequent reset will not proceed with controller initialization if the flag is set. The controller status must be polled while active, so the watchdog timer is also left active until the controller is disabled to cleanup requests that may be stuck during namespace removal. [Fixes: ff23a2a15a2117245b4599c1352343c8b8fb4c43] Signed-off-by: Keith Busch Reviewed-by: Christoph Hellwig Reviewed-by: Johannes Thumshirn Reviewed-by: Sagi Grimberg Signed-off-by: Jens Axboe (cherry picked from commit 9bf2b972afeaffd173fe2ce211ebc555ea7e8a87) Orabug: 25130845 Signed-off-by: Ashok Vairavan Reviewed-by: Martin K. Petersen --- diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index f638f7603aa77..04a1f498fee26 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -1904,6 +1904,9 @@ static void nvme_reset_work(struct work_struct *work) if (dev->bar) nvme_dev_disable(dev, false); + if (test_bit(NVME_CTRL_REMOVING, &dev->flags)) + goto out; + set_bit(NVME_CTRL_RESETTING, &dev->flags); result = nvme_dev_map(dev); @@ -2100,7 +2103,6 @@ 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);