aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-01-10 09:22:18 -0500
committerBjorn Helgaas <bhelgaas@google.com>2014-01-13 19:49:49 -0500
commit9d16947b75831acd317ab9a53e0e94d160731d33 (patch)
tree6eeee1ae3a6864ceaae7ba60e7abc846f1acb0a9
parenta870614a5371f8e36676e9bb2e089f4121976135 (diff)
PCI: Add global pci_lock_rescan_remove()
There are multiple PCI device addition and removal code paths that may be run concurrently with the generic PCI bus rescan and device removal that can be triggered via sysfs. If that happens, it may lead to multiple different, potentially dangerous race conditions. The most straightforward way to address those problems is to run the code in question under the same lock that is used by the generic rescan/remove code in pci-sysfs.c. To prepare for those changes, move the definition of the global PCI remove/rescan lock to probe.c and provide global wrappers, pci_lock_rescan_remove() and pci_unlock_rescan_remove(), allowing drivers to manipulate that lock. Also provide pci_stop_and_remove_bus_device_locked() for the callers of pci_stop_and_remove_bus_device() who only need to hold the rescan/remove lock around it. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r--drivers/pci/pci-sysfs.c19
-rw-r--r--drivers/pci/probe.c18
-rw-r--r--drivers/pci/remove.c8
-rw-r--r--include/linux/pci.h3
4 files changed, 36 insertions, 12 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index c91e6c18debc..276ef9c18802 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -297,7 +297,6 @@ msi_bus_store(struct device *dev, struct device_attribute *attr,
297} 297}
298static DEVICE_ATTR_RW(msi_bus); 298static DEVICE_ATTR_RW(msi_bus);
299 299
300static DEFINE_MUTEX(pci_remove_rescan_mutex);
301static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, 300static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
302 size_t count) 301 size_t count)
303{ 302{
@@ -308,10 +307,10 @@ static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
308 return -EINVAL; 307 return -EINVAL;
309 308
310 if (val) { 309 if (val) {
311 mutex_lock(&pci_remove_rescan_mutex); 310 pci_lock_rescan_remove();
312 while ((b = pci_find_next_bus(b)) != NULL) 311 while ((b = pci_find_next_bus(b)) != NULL)
313 pci_rescan_bus(b); 312 pci_rescan_bus(b);
314 mutex_unlock(&pci_remove_rescan_mutex); 313 pci_unlock_rescan_remove();
315 } 314 }
316 return count; 315 return count;
317} 316}
@@ -342,9 +341,9 @@ dev_rescan_store(struct device *dev, struct device_attribute *attr,
342 return -EINVAL; 341 return -EINVAL;
343 342
344 if (val) { 343 if (val) {
345 mutex_lock(&pci_remove_rescan_mutex); 344 pci_lock_rescan_remove();
346 pci_rescan_bus(pdev->bus); 345 pci_rescan_bus(pdev->bus);
347 mutex_unlock(&pci_remove_rescan_mutex); 346 pci_unlock_rescan_remove();
348 } 347 }
349 return count; 348 return count;
350} 349}
@@ -354,11 +353,7 @@ static struct device_attribute dev_rescan_attr = __ATTR(rescan,
354 353
355static void remove_callback(struct device *dev) 354static void remove_callback(struct device *dev)
356{ 355{
357 struct pci_dev *pdev = to_pci_dev(dev); 356 pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
358
359 mutex_lock(&pci_remove_rescan_mutex);
360 pci_stop_and_remove_bus_device(pdev);
361 mutex_unlock(&pci_remove_rescan_mutex);
362} 357}
363 358
364static ssize_t 359static ssize_t
@@ -395,12 +390,12 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr,
395 return -EINVAL; 390 return -EINVAL;
396 391
397 if (val) { 392 if (val) {
398 mutex_lock(&pci_remove_rescan_mutex); 393 pci_lock_rescan_remove();
399 if (!pci_is_root_bus(bus) && list_empty(&bus->devices)) 394 if (!pci_is_root_bus(bus) && list_empty(&bus->devices))
400 pci_rescan_bus_bridge_resize(bus->self); 395 pci_rescan_bus_bridge_resize(bus->self);
401 else 396 else
402 pci_rescan_bus(bus); 397 pci_rescan_bus(bus);
403 mutex_unlock(&pci_remove_rescan_mutex); 398 pci_unlock_rescan_remove();
404 } 399 }
405 return count; 400 return count;
406} 401}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index c86c8638d3c4..04796c056d12 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2024,6 +2024,24 @@ EXPORT_SYMBOL(pci_scan_slot);
2024EXPORT_SYMBOL(pci_scan_bridge); 2024EXPORT_SYMBOL(pci_scan_bridge);
2025EXPORT_SYMBOL_GPL(pci_scan_child_bus); 2025EXPORT_SYMBOL_GPL(pci_scan_child_bus);
2026 2026
2027/*
2028 * pci_rescan_bus(), pci_rescan_bus_bridge_resize() and PCI device removal
2029 * routines should always be executed under this mutex.
2030 */
2031static DEFINE_MUTEX(pci_rescan_remove_lock);
2032
2033void pci_lock_rescan_remove(void)
2034{
2035 mutex_lock(&pci_rescan_remove_lock);
2036}
2037EXPORT_SYMBOL_GPL(pci_lock_rescan_remove);
2038
2039void pci_unlock_rescan_remove(void)
2040{
2041 mutex_unlock(&pci_rescan_remove_lock);
2042}
2043EXPORT_SYMBOL_GPL(pci_unlock_rescan_remove);
2044
2027static int __init pci_sort_bf_cmp(const struct device *d_a, const struct device *d_b) 2045static int __init pci_sort_bf_cmp(const struct device *d_a, const struct device *d_b)
2028{ 2046{
2029 const struct pci_dev *a = to_pci_dev(d_a); 2047 const struct pci_dev *a = to_pci_dev(d_a);
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index f452148e6d55..10fa13f9e309 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -95,6 +95,14 @@ void pci_stop_and_remove_bus_device(struct pci_dev *dev)
95} 95}
96EXPORT_SYMBOL(pci_stop_and_remove_bus_device); 96EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
97 97
98void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev)
99{
100 pci_lock_rescan_remove();
101 pci_stop_and_remove_bus_device(dev);
102 pci_unlock_rescan_remove();
103}
104EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);
105
98void pci_stop_root_bus(struct pci_bus *bus) 106void pci_stop_root_bus(struct pci_bus *bus)
99{ 107{
100 struct pci_dev *child, *tmp; 108 struct pci_dev *child, *tmp;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 9e3ec8b951b7..2087e6b35545 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -775,6 +775,7 @@ struct pci_dev *pci_dev_get(struct pci_dev *dev);
775void pci_dev_put(struct pci_dev *dev); 775void pci_dev_put(struct pci_dev *dev);
776void pci_remove_bus(struct pci_bus *b); 776void pci_remove_bus(struct pci_bus *b);
777void pci_stop_and_remove_bus_device(struct pci_dev *dev); 777void pci_stop_and_remove_bus_device(struct pci_dev *dev);
778void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev);
778void pci_stop_root_bus(struct pci_bus *bus); 779void pci_stop_root_bus(struct pci_bus *bus);
779void pci_remove_root_bus(struct pci_bus *bus); 780void pci_remove_root_bus(struct pci_bus *bus);
780void pci_setup_cardbus(struct pci_bus *bus); 781void pci_setup_cardbus(struct pci_bus *bus);
@@ -1011,6 +1012,8 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev);
1011int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); 1012int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);
1012unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge); 1013unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge);
1013unsigned int pci_rescan_bus(struct pci_bus *bus); 1014unsigned int pci_rescan_bus(struct pci_bus *bus);
1015void pci_lock_rescan_remove(void);
1016void pci_unlock_rescan_remove(void);
1014 1017
1015/* Vital product data routines */ 1018/* Vital product data routines */
1016ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf); 1019ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf);