summaryrefslogtreecommitdiff
path: root/drivers/pci/remove.c
diff options
context:
space:
mode:
authorHari Vyas <hari.vyas@broadcom.com>2018-07-03 12:05:41 +0300
committerBjorn Helgaas <bhelgaas@google.com>2018-07-31 19:27:54 +0300
commit44bda4b7d26e9fffed6d7152d98a2e9edaeb2a76 (patch)
tree46ed284871234e825bc3211dd5bf0d960b7c3cda /drivers/pci/remove.c
parenta54e43f993f8ec2f063b616a0e4d2b09e08d78a5 (diff)
downloadlinux-44bda4b7d26e9fffed6d7152d98a2e9edaeb2a76.tar.xz
PCI: Fix is_added/is_busmaster race condition
When a PCI device is detected, pdev->is_added is set to 1 and proc and sysfs entries are created. When the device is removed, pdev->is_added is checked for one and then device is detached with clearing of proc and sys entries and at end, pdev->is_added is set to 0. is_added and is_busmaster are bit fields in pci_dev structure sharing same memory location. A strange issue was observed with multiple removal and rescan of a PCIe NVMe device using sysfs commands where is_added flag was observed as zero instead of one while removing device and proc,sys entries are not cleared. This causes issue in later device addition with warning message "proc_dir_entry" already registered. Debugging revealed a race condition between the PCI core setting the is_added bit in pci_bus_add_device() and the NVMe driver reset work-queue setting the is_busmaster bit in pci_set_master(). As these fields are not handled atomically, that clears the is_added bit. Move the is_added bit to a separate private flag variable and use atomic functions to set and retrieve the device addition state. This avoids the race because is_added no longer shares a memory location with is_busmaster. Link: https://bugzilla.kernel.org/show_bug.cgi?id=200283 Signed-off-by: Hari Vyas <hari.vyas@broadcom.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Lukas Wunner <lukas@wunner.de> Acked-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'drivers/pci/remove.c')
-rw-r--r--drivers/pci/remove.c5
1 files changed, 3 insertions, 2 deletions
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 6f072eae4f7a..5e3d0dced2b8 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -19,11 +19,12 @@ static void pci_stop_dev(struct pci_dev *dev)
{
pci_pme_active(dev, false);
- if (dev->is_added) {
+ if (pci_dev_is_added(dev)) {
device_release_driver(&dev->dev);
pci_proc_detach_device(dev);
pci_remove_sysfs_dev_files(dev);
- dev->is_added = 0;
+
+ pci_dev_assign_added(dev, false);
}
if (dev->bus->self)