[PATCHv3 1/5] pci: make pci_stop_dev concurrent safe

Keith Busch
2024-10-22T22:49:17Z

From: Keith Busch <kbusch@kernel.org>

Use the atomic ADDED flag to safely 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.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Keith Busch <kbusch@kernel.org>
---
drivers/pci/bus.c | 2 +-
drivers/pci/pci.h | 11 +++++++++--
drivers/pci/remove.c | 20 +++++++++-----------
3 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index e0a2441be6d32..aec08e81abff7 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -358,7 +358,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,
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index d89fdbf04f363..c4cceec006d0d 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -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)
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index e4ce1145aa3ee..e0d4402ec3391 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -31,18 +31,16 @@ static int pci_pwrctl_unregister(struct device *dev, void *data)

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);
+ if (!pci_dev_test_and_clear_added(dev))
+ return;

- pci_dev_assign_added(dev, false);
- }
+ pci_pme_active(dev, false);
+ 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)
--
2.43.5