aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhang Yanmin <yanmin.zhang@intel.com>2006-06-02 00:35:43 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-06-21 15:00:01 -0400
commitd71374dafbba7ec3f67371d3b7e9f6310a588808 (patch)
tree116dcd65fde3701d10fea954cdbd5bb063182b2c
parent733a7fe12248072e1bca729c88a26298666f1956 (diff)
[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>
-rw-r--r--drivers/pci/bus.c21
-rw-r--r--drivers/pci/pci.h2
-rw-r--r--drivers/pci/probe.c17
-rw-r--r--drivers/pci/remove.c12
-rw-r--r--drivers/pci/search.c32
5 files changed, 41 insertions, 43 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index eed67d9e73bc..723092682023 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -81,9 +81,9 @@ void __devinit pci_bus_add_device(struct pci_dev *dev)
81{ 81{
82 device_add(&dev->dev); 82 device_add(&dev->dev);
83 83
84 spin_lock(&pci_bus_lock); 84 down_write(&pci_bus_sem);
85 list_add_tail(&dev->global_list, &pci_devices); 85 list_add_tail(&dev->global_list, &pci_devices);
86 spin_unlock(&pci_bus_lock); 86 up_write(&pci_bus_sem);
87 87
88 pci_proc_attach_device(dev); 88 pci_proc_attach_device(dev);
89 pci_create_sysfs_dev_files(dev); 89 pci_create_sysfs_dev_files(dev);
@@ -125,10 +125,10 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus)
125 */ 125 */
126 if (dev->subordinate) { 126 if (dev->subordinate) {
127 if (list_empty(&dev->subordinate->node)) { 127 if (list_empty(&dev->subordinate->node)) {
128 spin_lock(&pci_bus_lock); 128 down_write(&pci_bus_sem);
129 list_add_tail(&dev->subordinate->node, 129 list_add_tail(&dev->subordinate->node,
130 &dev->bus->children); 130 &dev->bus->children);
131 spin_unlock(&pci_bus_lock); 131 up_write(&pci_bus_sem);
132 } 132 }
133 pci_bus_add_devices(dev->subordinate); 133 pci_bus_add_devices(dev->subordinate);
134 134
@@ -168,7 +168,7 @@ void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *),
168 struct list_head *next; 168 struct list_head *next;
169 169
170 bus = top; 170 bus = top;
171 spin_lock(&pci_bus_lock); 171 down_read(&pci_bus_sem);
172 next = top->devices.next; 172 next = top->devices.next;
173 for (;;) { 173 for (;;) {
174 if (next == &bus->devices) { 174 if (next == &bus->devices) {
@@ -180,22 +180,19 @@ void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *),
180 continue; 180 continue;
181 } 181 }
182 dev = list_entry(next, struct pci_dev, bus_list); 182 dev = list_entry(next, struct pci_dev, bus_list);
183 pci_dev_get(dev);
184 if (dev->subordinate) { 183 if (dev->subordinate) {
185 /* this is a pci-pci bridge, do its devices next */ 184 /* this is a pci-pci bridge, do its devices next */
186 next = dev->subordinate->devices.next; 185 next = dev->subordinate->devices.next;
187 bus = dev->subordinate; 186 bus = dev->subordinate;
188 } else 187 } else
189 next = dev->bus_list.next; 188 next = dev->bus_list.next;
190 spin_unlock(&pci_bus_lock);
191 189
192 /* Run device routines with the bus unlocked */ 190 /* Run device routines with the device locked */
191 down(&dev->dev.sem);
193 cb(dev, userdata); 192 cb(dev, userdata);
194 193 up(&dev->dev.sem);
195 spin_lock(&pci_bus_lock);
196 pci_dev_put(dev);
197 } 194 }
198 spin_unlock(&pci_bus_lock); 195 up_read(&pci_bus_sem);
199} 196}
200EXPORT_SYMBOL_GPL(pci_walk_bus); 197EXPORT_SYMBOL_GPL(pci_walk_bus);
201 198
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 30630cbe2fe3..29bdeca031a8 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -40,7 +40,7 @@ extern int pci_bus_find_capability (struct pci_bus *bus, unsigned int devfn, int
40extern void pci_remove_legacy_files(struct pci_bus *bus); 40extern void pci_remove_legacy_files(struct pci_bus *bus);
41 41
42/* Lock for read/write access to pci device and bus lists */ 42/* Lock for read/write access to pci device and bus lists */
43extern spinlock_t pci_bus_lock; 43extern struct rw_semaphore pci_bus_sem;
44 44
45#ifdef CONFIG_X86_IO_APIC 45#ifdef CONFIG_X86_IO_APIC
46extern int pci_msi_quirk; 46extern int pci_msi_quirk;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 27148db06ba0..f89dbc3738b7 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -383,9 +383,9 @@ struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_de
383 383
384 child = pci_alloc_child_bus(parent, dev, busnr); 384 child = pci_alloc_child_bus(parent, dev, busnr);
385 if (child) { 385 if (child) {
386 spin_lock(&pci_bus_lock); 386 down_write(&pci_bus_sem);
387 list_add_tail(&child->node, &parent->children); 387 list_add_tail(&child->node, &parent->children);
388 spin_unlock(&pci_bus_lock); 388 up_write(&pci_bus_sem);
389 } 389 }
390 return child; 390 return child;
391} 391}
@@ -844,9 +844,9 @@ void __devinit pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
844 * and the bus list for fixup functions, etc. 844 * and the bus list for fixup functions, etc.
845 */ 845 */
846 INIT_LIST_HEAD(&dev->global_list); 846 INIT_LIST_HEAD(&dev->global_list);
847 spin_lock(&pci_bus_lock); 847 down_write(&pci_bus_sem);
848 list_add_tail(&dev->bus_list, &bus->devices); 848 list_add_tail(&dev->bus_list, &bus->devices);
849 spin_unlock(&pci_bus_lock); 849 up_write(&pci_bus_sem);
850} 850}
851 851
852struct pci_dev * __devinit 852struct pci_dev * __devinit
@@ -981,9 +981,10 @@ struct pci_bus * __devinit pci_create_bus(struct device *parent,
981 pr_debug("PCI: Bus %04x:%02x already known\n", pci_domain_nr(b), bus); 981 pr_debug("PCI: Bus %04x:%02x already known\n", pci_domain_nr(b), bus);
982 goto err_out; 982 goto err_out;
983 } 983 }
984 spin_lock(&pci_bus_lock); 984
985 down_write(&pci_bus_sem);
985 list_add_tail(&b->node, &pci_root_buses); 986 list_add_tail(&b->node, &pci_root_buses);
986 spin_unlock(&pci_bus_lock); 987 up_write(&pci_bus_sem);
987 988
988 memset(dev, 0, sizeof(*dev)); 989 memset(dev, 0, sizeof(*dev));
989 dev->parent = parent; 990 dev->parent = parent;
@@ -1023,9 +1024,9 @@ class_dev_create_file_err:
1023class_dev_reg_err: 1024class_dev_reg_err:
1024 device_unregister(dev); 1025 device_unregister(dev);
1025dev_reg_err: 1026dev_reg_err:
1026 spin_lock(&pci_bus_lock); 1027 down_write(&pci_bus_sem);
1027 list_del(&b->node); 1028 list_del(&b->node);
1028 spin_unlock(&pci_bus_lock); 1029 up_write(&pci_bus_sem);
1029err_out: 1030err_out:
1030 kfree(dev); 1031 kfree(dev);
1031 kfree(b); 1032 kfree(b);
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 1a6bf9de166f..99ffbd478b29 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -22,18 +22,18 @@ static void pci_destroy_dev(struct pci_dev *dev)
22 pci_proc_detach_device(dev); 22 pci_proc_detach_device(dev);
23 pci_remove_sysfs_dev_files(dev); 23 pci_remove_sysfs_dev_files(dev);
24 device_unregister(&dev->dev); 24 device_unregister(&dev->dev);
25 spin_lock(&pci_bus_lock); 25 down_write(&pci_bus_sem);
26 list_del(&dev->global_list); 26 list_del(&dev->global_list);
27 dev->global_list.next = dev->global_list.prev = NULL; 27 dev->global_list.next = dev->global_list.prev = NULL;
28 spin_unlock(&pci_bus_lock); 28 up_write(&pci_bus_sem);
29 } 29 }
30 30
31 /* Remove the device from the device lists, and prevent any further 31 /* Remove the device from the device lists, and prevent any further
32 * list accesses from this device */ 32 * list accesses from this device */
33 spin_lock(&pci_bus_lock); 33 down_write(&pci_bus_sem);
34 list_del(&dev->bus_list); 34 list_del(&dev->bus_list);
35 dev->bus_list.next = dev->bus_list.prev = NULL; 35 dev->bus_list.next = dev->bus_list.prev = NULL;
36 spin_unlock(&pci_bus_lock); 36 up_write(&pci_bus_sem);
37 37
38 pci_free_resources(dev); 38 pci_free_resources(dev);
39 pci_dev_put(dev); 39 pci_dev_put(dev);
@@ -62,9 +62,9 @@ void pci_remove_bus(struct pci_bus *pci_bus)
62{ 62{
63 pci_proc_detach_bus(pci_bus); 63 pci_proc_detach_bus(pci_bus);
64 64
65 spin_lock(&pci_bus_lock); 65 down_write(&pci_bus_sem);
66 list_del(&pci_bus->node); 66 list_del(&pci_bus->node);
67 spin_unlock(&pci_bus_lock); 67 up_write(&pci_bus_sem);
68 pci_remove_legacy_files(pci_bus); 68 pci_remove_legacy_files(pci_bus);
69 class_device_remove_file(&pci_bus->class_dev, 69 class_device_remove_file(&pci_bus->class_dev,
70 &class_device_attr_cpuaffinity); 70 &class_device_attr_cpuaffinity);
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index ce7dd6e7be60..622b3f8ba820 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -13,7 +13,7 @@
13#include <linux/interrupt.h> 13#include <linux/interrupt.h>
14#include "pci.h" 14#include "pci.h"
15 15
16DEFINE_SPINLOCK(pci_bus_lock); 16DECLARE_RWSEM(pci_bus_sem);
17 17
18static struct pci_bus * __devinit 18static struct pci_bus * __devinit
19pci_do_find_bus(struct pci_bus* bus, unsigned char busnr) 19pci_do_find_bus(struct pci_bus* bus, unsigned char busnr)
@@ -72,11 +72,11 @@ pci_find_next_bus(const struct pci_bus *from)
72 struct pci_bus *b = NULL; 72 struct pci_bus *b = NULL;
73 73
74 WARN_ON(in_interrupt()); 74 WARN_ON(in_interrupt());
75 spin_lock(&pci_bus_lock); 75 down_read(&pci_bus_sem);
76 n = from ? from->node.next : pci_root_buses.next; 76 n = from ? from->node.next : pci_root_buses.next;
77 if (n != &pci_root_buses) 77 if (n != &pci_root_buses)
78 b = pci_bus_b(n); 78 b = pci_bus_b(n);
79 spin_unlock(&pci_bus_lock); 79 up_read(&pci_bus_sem);
80 return b; 80 return b;
81} 81}
82 82
@@ -124,7 +124,7 @@ struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)
124 struct pci_dev *dev; 124 struct pci_dev *dev;
125 125
126 WARN_ON(in_interrupt()); 126 WARN_ON(in_interrupt());
127 spin_lock(&pci_bus_lock); 127 down_read(&pci_bus_sem);
128 128
129 list_for_each(tmp, &bus->devices) { 129 list_for_each(tmp, &bus->devices) {
130 dev = pci_dev_b(tmp); 130 dev = pci_dev_b(tmp);
@@ -135,7 +135,7 @@ struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)
135 dev = NULL; 135 dev = NULL;
136 out: 136 out:
137 pci_dev_get(dev); 137 pci_dev_get(dev);
138 spin_unlock(&pci_bus_lock); 138 up_read(&pci_bus_sem);
139 return dev; 139 return dev;
140} 140}
141 141
@@ -167,7 +167,7 @@ static struct pci_dev * pci_find_subsys(unsigned int vendor,
167 struct pci_dev *dev; 167 struct pci_dev *dev;
168 168
169 WARN_ON(in_interrupt()); 169 WARN_ON(in_interrupt());
170 spin_lock(&pci_bus_lock); 170 down_read(&pci_bus_sem);
171 n = from ? from->global_list.next : pci_devices.next; 171 n = from ? from->global_list.next : pci_devices.next;
172 172
173 while (n && (n != &pci_devices)) { 173 while (n && (n != &pci_devices)) {
@@ -181,7 +181,7 @@ static struct pci_dev * pci_find_subsys(unsigned int vendor,
181 } 181 }
182 dev = NULL; 182 dev = NULL;
183exit: 183exit:
184 spin_unlock(&pci_bus_lock); 184 up_read(&pci_bus_sem);
185 return dev; 185 return dev;
186} 186}
187 187
@@ -232,7 +232,7 @@ pci_get_subsys(unsigned int vendor, unsigned int device,
232 struct pci_dev *dev; 232 struct pci_dev *dev;
233 233
234 WARN_ON(in_interrupt()); 234 WARN_ON(in_interrupt());
235 spin_lock(&pci_bus_lock); 235 down_read(&pci_bus_sem);
236 n = from ? from->global_list.next : pci_devices.next; 236 n = from ? from->global_list.next : pci_devices.next;
237 237
238 while (n && (n != &pci_devices)) { 238 while (n && (n != &pci_devices)) {
@@ -247,7 +247,7 @@ pci_get_subsys(unsigned int vendor, unsigned int device,
247 dev = NULL; 247 dev = NULL;
248exit: 248exit:
249 dev = pci_dev_get(dev); 249 dev = pci_dev_get(dev);
250 spin_unlock(&pci_bus_lock); 250 up_read(&pci_bus_sem);
251 pci_dev_put(from); 251 pci_dev_put(from);
252 return dev; 252 return dev;
253} 253}
@@ -292,7 +292,7 @@ pci_find_device_reverse(unsigned int vendor, unsigned int device, const struct p
292 struct pci_dev *dev; 292 struct pci_dev *dev;
293 293
294 WARN_ON(in_interrupt()); 294 WARN_ON(in_interrupt());
295 spin_lock(&pci_bus_lock); 295 down_read(&pci_bus_sem);
296 n = from ? from->global_list.prev : pci_devices.prev; 296 n = from ? from->global_list.prev : pci_devices.prev;
297 297
298 while (n && (n != &pci_devices)) { 298 while (n && (n != &pci_devices)) {
@@ -304,7 +304,7 @@ pci_find_device_reverse(unsigned int vendor, unsigned int device, const struct p
304 } 304 }
305 dev = NULL; 305 dev = NULL;
306exit: 306exit:
307 spin_unlock(&pci_bus_lock); 307 up_read(&pci_bus_sem);
308 return dev; 308 return dev;
309} 309}
310 310
@@ -328,7 +328,7 @@ struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from)
328 struct pci_dev *dev; 328 struct pci_dev *dev;
329 329
330 WARN_ON(in_interrupt()); 330 WARN_ON(in_interrupt());
331 spin_lock(&pci_bus_lock); 331 down_read(&pci_bus_sem);
332 n = from ? from->global_list.next : pci_devices.next; 332 n = from ? from->global_list.next : pci_devices.next;
333 333
334 while (n && (n != &pci_devices)) { 334 while (n && (n != &pci_devices)) {
@@ -340,7 +340,7 @@ struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from)
340 dev = NULL; 340 dev = NULL;
341exit: 341exit:
342 dev = pci_dev_get(dev); 342 dev = pci_dev_get(dev);
343 spin_unlock(&pci_bus_lock); 343 up_read(&pci_bus_sem);
344 pci_dev_put(from); 344 pci_dev_put(from);
345 return dev; 345 return dev;
346} 346}
@@ -362,7 +362,7 @@ int pci_dev_present(const struct pci_device_id *ids)
362 int found = 0; 362 int found = 0;
363 363
364 WARN_ON(in_interrupt()); 364 WARN_ON(in_interrupt());
365 spin_lock(&pci_bus_lock); 365 down_read(&pci_bus_sem);
366 while (ids->vendor || ids->subvendor || ids->class_mask) { 366 while (ids->vendor || ids->subvendor || ids->class_mask) {
367 list_for_each_entry(dev, &pci_devices, global_list) { 367 list_for_each_entry(dev, &pci_devices, global_list) {
368 if (pci_match_one_device(ids, dev)) { 368 if (pci_match_one_device(ids, dev)) {
@@ -372,8 +372,8 @@ int pci_dev_present(const struct pci_device_id *ids)
372 } 372 }
373 ids++; 373 ids++;
374 } 374 }
375exit: 375exit:
376 spin_unlock(&pci_bus_lock); 376 up_read(&pci_bus_sem);
377 return found; 377 return found;
378} 378}
379EXPORT_SYMBOL(pci_dev_present); 379EXPORT_SYMBOL(pci_dev_present);