summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeith Busch <kbusch@kernel.org>2024-10-23 00:48:47 +0200
committerBjorn Helgaas <bhelgaas@google.com>2024-11-11 20:05:50 +0100
commit93093ea1f05928b123dae38b710631362bef1601 (patch)
treed4878a521c9687541a9b9c69da0e4ed8c70434b7
parentLinux 6.12-rc1 (diff)
downloadlinux-93093ea1f05928b123dae38b710631362bef1601.tar.xz
linux-93093ea1f05928b123dae38b710631362bef1601.zip
PCI: Make pci_stop_dev() concurrent safe
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>
-rw-r--r--drivers/pci/bus.c2
-rw-r--r--drivers/pci/pci.h11
-rw-r--r--drivers/pci/remove.c19
3 files changed, 19 insertions, 13 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 55c853686051..e41dfece0d96 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -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,
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 14d00ce45bfa..0d9169e0e7d2 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 e4ce1145aa3e..78863f241a88 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -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)