[RFC PATCH v1 14/22] PCI/sysfs: use PCI bus lock to avoid race conditions
From: Jiang Liu <hidden>
Date: 2012-08-07 16:20:45
Also in:
lkml
Subsystem:
pci subsystem, the rest · Maintainers:
Bjorn Helgaas, Linus Torvalds
This patch uses PCI bus lock mechanism to avoid race conditions when doing PCI device/host bridge hotplug through PCI sysfs interfaces. Signed-off-by: Jiang Liu <redacted> --- drivers/pci/pci-sysfs.c | 26 +++++++++++++++++++++----- drivers/pci/probe.c | 17 +++++++++++------ 2 files changed, 32 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 99fefbe..11043b4 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c@@ -298,7 +298,10 @@ static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, pci_host_bridge_hotplug_lock(); mutex_lock(&pci_remove_rescan_mutex); while ((b = pci_find_next_bus(b)) != NULL) - pci_rescan_bus(b); + if (pci_bus_lock_states(b, PCI_BUS_STATE_WORKING) > 0) { + pci_rescan_bus(b); + pci_bus_unlock(b); + } mutex_unlock(&pci_remove_rescan_mutex); pci_host_bridge_hotplug_unlock(); }
@@ -321,8 +324,14 @@ dev_rescan_store(struct device *dev, struct device_attribute *attr, return -EINVAL; if (val) { + struct pci_bus *bus = pdev->bus; + mutex_lock(&pci_remove_rescan_mutex); - pci_rescan_bus(pdev->bus); + if (pci_bus_lock_states(bus, PCI_BUS_STATE_WORKING) > 0) { + if (pdev->is_added) + pci_rescan_bus(bus); + pci_bus_unlock(bus); + } mutex_unlock(&pci_remove_rescan_mutex); } return count;
@@ -331,9 +340,14 @@ dev_rescan_store(struct device *dev, struct device_attribute *attr, static void remove_callback(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); + struct pci_bus *bus = pdev->bus; mutex_lock(&pci_remove_rescan_mutex); - pci_stop_and_remove_bus_device(pdev); + if (pci_bus_lock_states(bus, PCI_BUS_STATE_WORKING) > 0) { + pci_bus_get(bus); + pci_stop_and_remove_bus_device(pdev); + pci_unlock_and_put_bus(bus); + } mutex_unlock(&pci_remove_rescan_mutex); }
@@ -369,10 +383,12 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, if (val) { mutex_lock(&pci_remove_rescan_mutex); - if (!pci_is_root_bus(bus) && list_empty(&bus->devices)) + if (!pci_is_root_bus(bus)) pci_rescan_bus_bridge_resize(bus->self); - else + else if (pci_bus_lock_states(bus, PCI_BUS_STATE_WORKING) > 0) { pci_rescan_bus(bus); + pci_bus_unlock(bus); + } mutex_unlock(&pci_remove_rescan_mutex); } return count;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 47bf071..da6f04c 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c@@ -1919,12 +1919,17 @@ EXPORT_SYMBOL(pci_scan_bus); unsigned int __ref pci_rescan_bus_bridge_resize(struct pci_dev *bridge) { unsigned int max = -1; - struct pci_bus *bus = bridge->subordinate; - - if (pci_bus_lock_states(bus, PCI_BUS_STATE_WORKING) > 0) { - max = pci_scan_child_bus(bus); - pci_assign_unassigned_bridge_resources(bridge); - pci_bus_add_devices(bus); + struct pci_bus *bus; + + bus = pci_lock_subordinate(bridge, PCI_BUS_STATE_WORKING); + if (bus) { + if (list_empty(&bus->devices)) { + max = pci_scan_child_bus(bus); + pci_assign_unassigned_bridge_resources(bridge); + pci_bus_add_devices(bus); + } else { + pci_rescan_bus(bus); + } pci_bus_unlock(bus); }
--
1.7.9.5