[PATCH] PCI: fix race with pci_walk_bus and pci_destroy_dev
pci_walk_bus has a race with pci_destroy_dev. When cb is called
in pci_walk_bus, pci_destroy_dev might unlink the dev pointed by next.
Later on in the next loop, pointer next becomes NULL and cause
kernel panic.
Below patch against 2.6.17-rc4 fixes it by changing pci_bus_lock (spin_lock)
to pci_bus_sem (rw_semaphore).
Signed-off-by: Zhang Yanmin <yanmin.zhang@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index ce7dd6e..622b3f8 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -13,7 +13,7 @@
#include <linux/interrupt.h>
#include "pci.h"
-DEFINE_SPINLOCK(pci_bus_lock);
+DECLARE_RWSEM(pci_bus_sem);
static struct pci_bus * __devinit
pci_do_find_bus(struct pci_bus* bus, unsigned char busnr)
@@ -72,11 +72,11 @@
struct pci_bus *b = NULL;
WARN_ON(in_interrupt());
- spin_lock(&pci_bus_lock);
+ down_read(&pci_bus_sem);
n = from ? from->node.next : pci_root_buses.next;
if (n != &pci_root_buses)
b = pci_bus_b(n);
- spin_unlock(&pci_bus_lock);
+ up_read(&pci_bus_sem);
return b;
}
@@ -124,7 +124,7 @@
struct pci_dev *dev;
WARN_ON(in_interrupt());
- spin_lock(&pci_bus_lock);
+ down_read(&pci_bus_sem);
list_for_each(tmp, &bus->devices) {
dev = pci_dev_b(tmp);
@@ -135,7 +135,7 @@
dev = NULL;
out:
pci_dev_get(dev);
- spin_unlock(&pci_bus_lock);
+ up_read(&pci_bus_sem);
return dev;
}
@@ -167,7 +167,7 @@
struct pci_dev *dev;
WARN_ON(in_interrupt());
- spin_lock(&pci_bus_lock);
+ down_read(&pci_bus_sem);
n = from ? from->global_list.next : pci_devices.next;
while (n && (n != &pci_devices)) {
@@ -181,7 +181,7 @@
}
dev = NULL;
exit:
- spin_unlock(&pci_bus_lock);
+ up_read(&pci_bus_sem);
return dev;
}
@@ -232,7 +232,7 @@
struct pci_dev *dev;
WARN_ON(in_interrupt());
- spin_lock(&pci_bus_lock);
+ down_read(&pci_bus_sem);
n = from ? from->global_list.next : pci_devices.next;
while (n && (n != &pci_devices)) {
@@ -247,7 +247,7 @@
dev = NULL;
exit:
dev = pci_dev_get(dev);
- spin_unlock(&pci_bus_lock);
+ up_read(&pci_bus_sem);
pci_dev_put(from);
return dev;
}
@@ -292,7 +292,7 @@
struct pci_dev *dev;
WARN_ON(in_interrupt());
- spin_lock(&pci_bus_lock);
+ down_read(&pci_bus_sem);
n = from ? from->global_list.prev : pci_devices.prev;
while (n && (n != &pci_devices)) {
@@ -304,7 +304,7 @@
}
dev = NULL;
exit:
- spin_unlock(&pci_bus_lock);
+ up_read(&pci_bus_sem);
return dev;
}
@@ -328,7 +328,7 @@
struct pci_dev *dev;
WARN_ON(in_interrupt());
- spin_lock(&pci_bus_lock);
+ down_read(&pci_bus_sem);
n = from ? from->global_list.next : pci_devices.next;
while (n && (n != &pci_devices)) {
@@ -340,7 +340,7 @@
dev = NULL;
exit:
dev = pci_dev_get(dev);
- spin_unlock(&pci_bus_lock);
+ up_read(&pci_bus_sem);
pci_dev_put(from);
return dev;
}
@@ -362,7 +362,7 @@
int found = 0;
WARN_ON(in_interrupt());
- spin_lock(&pci_bus_lock);
+ down_read(&pci_bus_sem);
while (ids->vendor || ids->subvendor || ids->class_mask) {
list_for_each_entry(dev, &pci_devices, global_list) {
if (pci_match_one_device(ids, dev)) {
@@ -372,8 +372,8 @@
}
ids++;
}
-exit:
- spin_unlock(&pci_bus_lock);
+exit:
+ up_read(&pci_bus_sem);
return found;
}
EXPORT_SYMBOL(pci_dev_present);