]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PCI: Make pci_stop_dev() concurrent safe
authorKeith Busch <kbusch@kernel.org>
Tue, 22 Oct 2024 22:48:47 +0000 (15:48 -0700)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 11 Nov 2024 19:05:50 +0000 (13:05 -0600)
Use the atomic ADDED flag to ensure concurrent callers can't attempt to
stop the device multiple times. Callers should currently all be holding the
pci_rescan_remove_lock, so there shouldn't be an existing race. But that
global lock can cause lock dependency issues, so this is preparing to
reduce reliance on that lock by using the existing existing atomic bit ops.

Link: https://lore.kernel.org/r/20241022224851.340648-2-kbusch@meta.com
Signed-off-by: Keith Busch <kbusch@kernel.org>
[bhelgaas: squash https://lore.kernel.org/r/20241111180659.3321671-1-kbusch@meta.com]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/pci/bus.c
drivers/pci/pci.h
drivers/pci/remove.c

index 55c85368605187586d5d10908bf12005025a656e..e41dfece0d9697e35e660230c5a2ca5d00e53834 100644 (file)
@@ -348,7 +348,7 @@ void pci_bus_add_device(struct pci_dev *dev)
        if (retval < 0 && retval != -EPROBE_DEFER)
                pci_warn(dev, "device attach failed (%d)\n", retval);
 
-       pci_dev_assign_added(dev, true);
+       pci_dev_assign_added(dev);
 
        if (dev_of_node(&dev->dev) && pci_is_bridge(dev)) {
                retval = of_platform_populate(dev_of_node(&dev->dev), NULL, NULL,
index 14d00ce45bfa954daea1a297c00c2c08d98636d3..0d9169e0e7d2a7638532c227f323e4e864fc2d24 100644 (file)
@@ -470,9 +470,16 @@ static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused)
 #define PCI_DPC_RECOVERED 1
 #define PCI_DPC_RECOVERING 2
 
-static inline void pci_dev_assign_added(struct pci_dev *dev, bool added)
+static inline void pci_dev_assign_added(struct pci_dev *dev)
 {
-       assign_bit(PCI_DEV_ADDED, &dev->priv_flags, added);
+       smp_mb__before_atomic();
+       set_bit(PCI_DEV_ADDED, &dev->priv_flags);
+       smp_mb__after_atomic();
+}
+
+static inline bool pci_dev_test_and_clear_added(struct pci_dev *dev)
+{
+       return test_and_clear_bit(PCI_DEV_ADDED, &dev->priv_flags);
 }
 
 static inline bool pci_dev_is_added(const struct pci_dev *dev)
index e4ce1145aa3ee0d05f6e38319dc0a1941d12b3e8..78863f241a886ebb35772772e6c1afc74f6aa918 100644 (file)
@@ -33,16 +33,15 @@ static void pci_stop_dev(struct pci_dev *dev)
 {
        pci_pme_active(dev, false);
 
-       if (pci_dev_is_added(dev)) {
-               device_for_each_child(dev->dev.parent, dev_of_node(&dev->dev),
-                                     pci_pwrctl_unregister);
-               device_release_driver(&dev->dev);
-               pci_proc_detach_device(dev);
-               pci_remove_sysfs_dev_files(dev);
-               of_pci_remove_node(dev);
-
-               pci_dev_assign_added(dev, false);
-       }
+       if (!pci_dev_test_and_clear_added(dev))
+               return;
+
+       device_for_each_child(dev->dev.parent, dev_of_node(&dev->dev),
+                             pci_pwrctl_unregister);
+       device_release_driver(&dev->dev);
+       pci_proc_detach_device(dev);
+       pci_remove_sysfs_dev_files(dev);
+       of_pci_remove_node(dev);
 }
 
 static void pci_destroy_dev(struct pci_dev *dev)