diff options
author | Zhang Yanmin <yanmin.zhang@intel.com> | 2006-06-02 00:35:43 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-06-21 15:00:01 -0400 |
commit | d71374dafbba7ec3f67371d3b7e9f6310a588808 (patch) | |
tree | 116dcd65fde3701d10fea954cdbd5bb063182b2c /drivers/pci/bus.c | |
parent | 733a7fe12248072e1bca729c88a26298666f1956 (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>
Diffstat (limited to 'drivers/pci/bus.c')
-rw-r--r-- | drivers/pci/bus.c | 21 |
1 files changed, 9 insertions, 12 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 | } |
200 | EXPORT_SYMBOL_GPL(pci_walk_bus); | 197 | EXPORT_SYMBOL_GPL(pci_walk_bus); |
201 | 198 | ||