diff options
author | Paul Mackerras <paulus@samba.org> | 2005-08-18 00:33:01 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-09-08 18:04:30 -0400 |
commit | cecf4864cf52a4a243a62b2856a6a155edbb55e8 (patch) | |
tree | ffbf170ed0208e81e9561dca3ad7786c362a7024 | |
parent | 1d2450a4a6eb656798c6282b5ffc8e5f9f52ac14 (diff) |
[PATCH] PCI: Add pci_walk_bus function to PCI core (nonrecursive)
The PCI error recovery infrastructure needs to be able to contact all
the drivers affected by a PCI error event, which may mean traversing
all the devices under a given PCI-PCI bridge. This patch adds a
function to the PCI core that traverses all the PCI devices on a PCI
bus and under any PCI-PCI bridges on that bus (and so on), calling a
given function for each device. This provides a way for the error
recovery code to iterate through all devices that are affected by an
error event.
This version is not implemented as a recursive function. Instead,
when we reach a PCI-PCI bridge, we set the pointers to start doing the
devices on the bus under the bridge, and when we reach the end of a
bus's devices, we use the bus->self pointer to go back up to the next
higher bus and continue doing its devices.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/pci/bus.c | 48 | ||||
-rw-r--r-- | include/linux/pci.h | 3 |
2 files changed, 51 insertions, 0 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index a83ee0b85394..eed67d9e73bc 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c | |||
@@ -151,6 +151,54 @@ void pci_enable_bridges(struct pci_bus *bus) | |||
151 | } | 151 | } |
152 | } | 152 | } |
153 | 153 | ||
154 | /** pci_walk_bus - walk devices on/under bus, calling callback. | ||
155 | * @top bus whose devices should be walked | ||
156 | * @cb callback to be called for each device found | ||
157 | * @userdata arbitrary pointer to be passed to callback. | ||
158 | * | ||
159 | * Walk the given bus, including any bridged devices | ||
160 | * on buses under this bus. Call the provided callback | ||
161 | * on each device found. | ||
162 | */ | ||
163 | void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *), | ||
164 | void *userdata) | ||
165 | { | ||
166 | struct pci_dev *dev; | ||
167 | struct pci_bus *bus; | ||
168 | struct list_head *next; | ||
169 | |||
170 | bus = top; | ||
171 | spin_lock(&pci_bus_lock); | ||
172 | next = top->devices.next; | ||
173 | for (;;) { | ||
174 | if (next == &bus->devices) { | ||
175 | /* end of this bus, go up or finish */ | ||
176 | if (bus == top) | ||
177 | break; | ||
178 | next = bus->self->bus_list.next; | ||
179 | bus = bus->self->bus; | ||
180 | continue; | ||
181 | } | ||
182 | dev = list_entry(next, struct pci_dev, bus_list); | ||
183 | pci_dev_get(dev); | ||
184 | if (dev->subordinate) { | ||
185 | /* this is a pci-pci bridge, do its devices next */ | ||
186 | next = dev->subordinate->devices.next; | ||
187 | bus = dev->subordinate; | ||
188 | } else | ||
189 | next = dev->bus_list.next; | ||
190 | spin_unlock(&pci_bus_lock); | ||
191 | |||
192 | /* Run device routines with the bus unlocked */ | ||
193 | cb(dev, userdata); | ||
194 | |||
195 | spin_lock(&pci_bus_lock); | ||
196 | pci_dev_put(dev); | ||
197 | } | ||
198 | spin_unlock(&pci_bus_lock); | ||
199 | } | ||
200 | EXPORT_SYMBOL_GPL(pci_walk_bus); | ||
201 | |||
154 | EXPORT_SYMBOL(pci_bus_alloc_resource); | 202 | EXPORT_SYMBOL(pci_bus_alloc_resource); |
155 | EXPORT_SYMBOL_GPL(pci_bus_add_device); | 203 | EXPORT_SYMBOL_GPL(pci_bus_add_device); |
156 | EXPORT_SYMBOL(pci_bus_add_devices); | 204 | EXPORT_SYMBOL(pci_bus_add_devices); |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 8878ccff9e3c..b0e244713281 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -434,6 +434,9 @@ const struct pci_device_id *pci_match_device(struct pci_driver *drv, struct pci_ | |||
434 | const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, struct pci_dev *dev); | 434 | const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, struct pci_dev *dev); |
435 | int pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass); | 435 | int pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass); |
436 | 436 | ||
437 | void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *), | ||
438 | void *userdata); | ||
439 | |||
437 | /* kmem_cache style wrapper around pci_alloc_consistent() */ | 440 | /* kmem_cache style wrapper around pci_alloc_consistent() */ |
438 | 441 | ||
439 | #include <linux/dmapool.h> | 442 | #include <linux/dmapool.h> |