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 <liuj97@xxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html