[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>
This commit is contained in:
parent
1d2450a4a6
commit
cecf4864cf
|
@ -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(pci_bus_alloc_resource);
|
||||||
EXPORT_SYMBOL_GPL(pci_bus_add_device);
|
EXPORT_SYMBOL_GPL(pci_bus_add_device);
|
||||||
EXPORT_SYMBOL(pci_bus_add_devices);
|
EXPORT_SYMBOL(pci_bus_add_devices);
|
||||||
|
|
|
@ -434,6 +434,9 @@ const struct pci_device_id *pci_match_device(struct pci_driver *drv, struct pci_
|
||||||
const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, struct pci_dev *dev);
|
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);
|
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() */
|
/* kmem_cache style wrapper around pci_alloc_consistent() */
|
||||||
|
|
||||||
#include <linux/dmapool.h>
|
#include <linux/dmapool.h>
|
||||||
|
|
Loading…
Reference in New Issue