Commit cecf4864cf52a4a243a62b2856a6a155edbb55e8

Authored by Paul Mackerras
Committed by Greg Kroah-Hartman
1 parent 1d2450a4a6

[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

... ... @@ -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);
... ... @@ -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>