Skip to content

Commit 9d16947

Browse files
rafaeljwbjorn-helgaas
authored andcommitted
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 <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]>
1 parent a870614 commit 9d16947

File tree

4 files changed

+36
-12
lines changed

4 files changed

+36
-12
lines changed

drivers/pci/pci-sysfs.c

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,6 @@ msi_bus_store(struct device *dev, struct device_attribute *attr,
297297
}
298298
static DEVICE_ATTR_RW(msi_bus);
299299

300-
static DEFINE_MUTEX(pci_remove_rescan_mutex);
301300
static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
302301
size_t count)
303302
{
@@ -308,10 +307,10 @@ static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
308307
return -EINVAL;
309308

310309
if (val) {
311-
mutex_lock(&pci_remove_rescan_mutex);
310+
pci_lock_rescan_remove();
312311
while ((b = pci_find_next_bus(b)) != NULL)
313312
pci_rescan_bus(b);
314-
mutex_unlock(&pci_remove_rescan_mutex);
313+
pci_unlock_rescan_remove();
315314
}
316315
return count;
317316
}
@@ -342,9 +341,9 @@ dev_rescan_store(struct device *dev, struct device_attribute *attr,
342341
return -EINVAL;
343342

344343
if (val) {
345-
mutex_lock(&pci_remove_rescan_mutex);
344+
pci_lock_rescan_remove();
346345
pci_rescan_bus(pdev->bus);
347-
mutex_unlock(&pci_remove_rescan_mutex);
346+
pci_unlock_rescan_remove();
348347
}
349348
return count;
350349
}
@@ -354,11 +353,7 @@ static struct device_attribute dev_rescan_attr = __ATTR(rescan,
354353

355354
static void remove_callback(struct device *dev)
356355
{
357-
struct pci_dev *pdev = 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);
356+
pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
362357
}
363358

364359
static ssize_t
@@ -395,12 +390,12 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr,
395390
return -EINVAL;
396391

397392
if (val) {
398-
mutex_lock(&pci_remove_rescan_mutex);
393+
pci_lock_rescan_remove();
399394
if (!pci_is_root_bus(bus) && list_empty(&bus->devices))
400395
pci_rescan_bus_bridge_resize(bus->self);
401396
else
402397
pci_rescan_bus(bus);
403-
mutex_unlock(&pci_remove_rescan_mutex);
398+
pci_unlock_rescan_remove();
404399
}
405400
return count;
406401
}

drivers/pci/probe.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,6 +2024,24 @@ EXPORT_SYMBOL(pci_scan_slot);
20242024
EXPORT_SYMBOL(pci_scan_bridge);
20252025
EXPORT_SYMBOL_GPL(pci_scan_child_bus);
20262026

2027+
/*
2028+
* pci_rescan_bus(), pci_rescan_bus_bridge_resize() and PCI device removal
2029+
* routines should always be executed under this mutex.
2030+
*/
2031+
static DEFINE_MUTEX(pci_rescan_remove_lock);
2032+
2033+
void pci_lock_rescan_remove(void)
2034+
{
2035+
mutex_lock(&pci_rescan_remove_lock);
2036+
}
2037+
EXPORT_SYMBOL_GPL(pci_lock_rescan_remove);
2038+
2039+
void pci_unlock_rescan_remove(void)
2040+
{
2041+
mutex_unlock(&pci_rescan_remove_lock);
2042+
}
2043+
EXPORT_SYMBOL_GPL(pci_unlock_rescan_remove);
2044+
20272045
static int __init pci_sort_bf_cmp(const struct device *d_a, const struct device *d_b)
20282046
{
20292047
const struct pci_dev *a = to_pci_dev(d_a);

drivers/pci/remove.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ void pci_stop_and_remove_bus_device(struct pci_dev *dev)
9595
}
9696
EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
9797

98+
void 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+
}
104+
EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);
105+
98106
void pci_stop_root_bus(struct pci_bus *bus)
99107
{
100108
struct pci_dev *child, *tmp;

include/linux/pci.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ struct pci_dev *pci_dev_get(struct pci_dev *dev);
775775
void pci_dev_put(struct pci_dev *dev);
776776
void pci_remove_bus(struct pci_bus *b);
777777
void pci_stop_and_remove_bus_device(struct pci_dev *dev);
778+
void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev);
778779
void pci_stop_root_bus(struct pci_bus *bus);
779780
void pci_remove_root_bus(struct pci_bus *bus);
780781
void pci_setup_cardbus(struct pci_bus *bus);
@@ -1011,6 +1012,8 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev);
10111012
int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);
10121013
unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge);
10131014
unsigned int pci_rescan_bus(struct pci_bus *bus);
1015+
void pci_lock_rescan_remove(void);
1016+
void pci_unlock_rescan_remove(void);
10141017

10151018
/* Vital product data routines */
10161019
ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf);

0 commit comments

Comments
 (0)