summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2005-08-18 14:33:01 +1000
committerGreg Kroah-Hartman <gregkh@suse.de>2005-09-08 15:04:30 -0700
commitcecf4864cf52a4a243a62b2856a6a155edbb55e8 (patch)
treeffbf170ed0208e81e9561dca3ad7786c362a7024 /drivers
parent1d2450a4a6eb656798c6282b5ffc8e5f9f52ac14 (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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/bus.c48
1 files changed, 48 insertions, 0 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index a83ee0b8539..eed67d9e73b 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -151,6 +151,54 @@ void pci_enable_bridges(struct pci_bus *bus)
}
}
+/** pci_walk_bus - walk devices on/under bus, calling callback.
+ * @top bus whose devices should be walked
+ * @cb callback to be called for each device found
+ * @userdata arbitrary pointer to be passed to callback.
+ *
+ * Walk the given bus, including any bridged devices
+ * on buses under this bus. Call the provided callback
+ * on each device found.
+ */
+void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *),
+ void *userdata)
+{
+ struct pci_dev *dev;
+ struct pci_bus *bus;
+ struct list_head *next;
+
+ bus = top;
+ spin_lock(&pci_bus_lock);
+ next = top->devices.next;
+ for (;;) {
+ if (next == &bus->devices) {
+ /* end of this bus, go up or finish */
+ if (bus == top)
+ break;
+ next = bus->self->bus_list.next;
+ bus = bus->self->bus;
+ continue;
+ }
+ dev = list_entry(next, struct pci_dev, bus_list);
+ pci_dev_get(dev);
+ if (dev->subordinate) {
+ /* this is a pci-pci bridge, do its devices next */
+ next = dev->subordinate->devices.next;
+ bus = dev->subordinate;
+ } else
+ next = dev->bus_list.next;
+ spin_unlock(&pci_bus_lock);
+
+ /* Run device routines with the bus unlocked */
+ cb(dev, userdata);
+
+ spin_lock(&pci_bus_lock);
+ pci_dev_put(dev);
+ }
+ spin_unlock(&pci_bus_lock);
+}
+EXPORT_SYMBOL_GPL(pci_walk_bus);
+
EXPORT_SYMBOL(pci_bus_alloc_resource);
EXPORT_SYMBOL_GPL(pci_bus_add_device);
EXPORT_SYMBOL(pci_bus_add_devices);