Commit cecf4864cf52a4a243a62b2856a6a155edbb55e8
Committed by
Greg Kroah-Hartman
1 parent
1d2450a4a6
Exists in
master
and in
7 other branches
[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>
Showing 2 changed files with 51 additions and 0 deletions Side-by-side Diff
drivers/pci/bus.c
... | ... | @@ -151,6 +151,54 @@ |
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 | 202 | EXPORT_SYMBOL(pci_bus_alloc_resource); |
155 | 203 | EXPORT_SYMBOL_GPL(pci_bus_add_device); |
156 | 204 | EXPORT_SYMBOL(pci_bus_add_devices); |
include/linux/pci.h
... | ... | @@ -434,6 +434,9 @@ |
434 | 434 | const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, struct pci_dev *dev); |
435 | 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 | 440 | /* kmem_cache style wrapper around pci_alloc_consistent() */ |
438 | 441 | |
439 | 442 | #include <linux/dmapool.h> |