From paulus@ozlabs.org Wed Aug 17 21:36:47 2005 Message-ID: <17156.3965.483826.692623@cargo.ozlabs.ibm.com> Date: Thu, 18 Aug 2005 14:33:01 +1000 From: Paul Mackerras To: Greg KH CC: Subject: 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 Signed-off-by: Greg Kroah-Hartman --- drivers/pci/bus.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 3 +++ 2 files changed, 51 insertions(+) --- gregkh-2.6.orig/drivers/pci/bus.c 2005-08-18 01:36:33.000000000 -0700 +++ gregkh-2.6/drivers/pci/bus.c 2005-08-18 23:51:53.000000000 -0700 @@ -151,6 +151,54 @@ void pci_enable_bridges(struct pci_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); --- gregkh-2.6.orig/include/linux/pci.h 2005-08-18 23:51:51.000000000 -0700 +++ gregkh-2.6/include/linux/pci.h 2005-08-18 23:51:53.000000000 -0700 @@ -436,6 +436,9 @@ const struct pci_device_id *pci_match_de const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, struct pci_dev *dev); int pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass); +void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *), + void *userdata); + /* kmem_cache style wrapper around pci_alloc_consistent() */ #include