pci: Add pci_walk_slot() interface
PATCH 1/1 pci: Add pci_walk_slot() interface
We have a pci_walk_bus() interface, but it's exceptionally cumbersome for the callback function to figure out if the device is relevant if the caller is trying to walk all the devices in or below a slot. Add a variant to walk only slot devices.
---
This allows me to use pci_walk_bus() and pci_walk_slot() instead of rolling my own for iterating devices for bus/slot reset. Please consider for 3.12 to go with the rest of the bus/slot infrastructure.
drivers/pci/bus.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 58 insertions(+)
drivers/pci/bus.c +56 -0
@@ -283,6 +283,62 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), }EXPORT_SYMBOL_GPL (pci_walk_bus); +/** pci_walk_slot - walk devices in/under slot, calling callback. +* @slot slot 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 slot, including any bridged devices +* on buses under this slot. Call the provided callback +* on each device found. +* +* We check the return of @cb each time. If it returns anything +* other than 0, we break out. +* +*/ +void pci_walk_slot (struct pci_slot *slot,int (*cb)(struct pci_dev *,void *), +void *userdata) +{ +struct pci_dev *dev; +struct pci_bus *bus; +struct list_head *next; +int retval; + + bus = slot->bus; +down_read (&pci_bus_sem); + next = slot->bus->devices.next; +for (;;) { +if (next == &bus->devices) { +/* end of this bus, go up or finish */ +if (bus == slot->bus) +break ; + next = bus->self->bus_list.next; + bus = bus->self->bus; +continue ; + } + dev = list_entry(next, struct pci_dev, bus_list); + +/* skip anything on the top level bus not in our slot */ +if (dev->bus == slot->bus && dev->slot != slot) { + next = dev->bus_list.next; +continue ; + } + +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; + + retval =cb (dev, userdata); +if (retval) +break ; + } +up_read (&pci_bus_sem); +} +EXPORT_SYMBOL_GPL (pci_walk_slot); +struct pci_bus *pci_bus_get (struct pci_bus *bus) {if (bus)
include/linux/pci.h +2 -0
@@ -1087,6 +1087,8 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,void pci_walk_bus (struct pci_bus *top,int (*cb)(struct pci_dev *,void *),void *userdata); +void pci_walk_slot (struct pci_slot *slot,int (*cb)(struct pci_dev *,void *), +void *userdata);int pci_cfg_space_size_ext (struct pci_dev *dev);int pci_cfg_space_size (struct pci_dev *dev);unsigned char pci_bus_max_busnr (struct pci_bus *bus);
Bjorn Helgaas
maintainerQuoted 4 lines · Alex Williamson
We have a
pci_walk_bus()interface, but it's exceptionally cumbersome for the callback function to figure out if the device is relevant if the caller is trying to walk all the devices in or below a slot. Add a variant to walk only slot devices.
Is it really exceptionally cumbersome? You can supply the top-level slot in the userdata. Can you then use something like this in the callback?
bool dev_below_slot(struct pci_dev *dev, struct pci_slot *slot)
{
if (!dev)
return false;
if (dev->slot == slot)
return true;
return dev_below_slot(dev->bus->self, slot);
}Bjorn
Quoted 92 lines
Signed-off-by Alex Williamson <[email protected]>---
This allows me to use
pci_walk_bus()andpci_walk_slot()instead of rolling my own for iterating devices for bus/slot reset. Please consider for 3.12 to go with the rest of the bus/slot infrastructure.drivers/pci/bus.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 58 insertions(+)drivers/pci/bus.c +56
-0@@ -283,6 +283,62 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), }EXPORT_SYMBOL_GPL (pci_walk_bus); +/** pci_walk_slot - walk devices in/under slot, calling callback. +* @slot slot 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 slot, including any bridged devices +* on buses under this slot. Call the provided callback +* on each device found. +* +* We check the return of @cb each time. If it returns anything +* other than 0, we break out. +* +*/ +void pci_walk_slot (struct pci_slot *slot,int (*cb)(struct pci_dev *,void *), +void *userdata) +{ +struct pci_dev *dev; +struct pci_bus *bus; +struct list_head *next; +int retval; + + bus = slot->bus; +down_read (&pci_bus_sem); + next = slot->bus->devices.next; +for (;;) { +if (next == &bus->devices) { +/* end of this bus, go up or finish */ +if (bus == slot->bus) +break ; + next = bus->self->bus_list.next; + bus = bus->self->bus; +continue ; + } + dev = list_entry(next, struct pci_dev, bus_list); + +/* skip anything on the top level bus not in our slot */ +if (dev->bus == slot->bus && dev->slot != slot) { + next = dev->bus_list.next; +continue ; + } + +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; + + retval =cb (dev, userdata); +if (retval) +break ; + } +up_read (&pci_bus_sem); +} +EXPORT_SYMBOL_GPL (pci_walk_slot); +struct pci_bus *pci_bus_get (struct pci_bus *bus) {if (bus)include/linux/pci.h +2
-0@@ -1087,6 +1087,8 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,void pci_walk_bus (struct pci_bus *top,int (*cb)(struct pci_dev *,void *),void *userdata); +void pci_walk_slot (struct pci_slot *slot,int (*cb)(struct pci_dev *,void *), +void *userdata);int pci_cfg_space_size_ext (struct pci_dev *dev);int pci_cfg_space_size (struct pci_dev *dev);unsigned char pci_bus_max_busnr (struct pci_bus *bus);
Quoted 11 lines · Bjorn Helgaas
Quoted 4 lines · Alex Williamson
We have a
pci_walk_bus()interface, but it's exceptionally cumbersome for the callback function to figure out if the device is relevant if the caller is trying to walk all the devices in or below a slot. Add a variant to walk only slot devices.Is it really exceptionally cumbersome? You can supply the top-level slot in the userdata. Can you then use something like this in the callback?
Quoted 8 lines
bool dev_below_slot(struct pci_dev *dev, struct pci_slot *slot)
{
if (!dev)
return false;
if (dev->slot == slot)
return true;
return dev_below_slot(dev->bus->self, slot);
}Maybe I should have said it's ugly and inefficient rather than cumbersome. This feels like a backwards way to do it, but if you don't want a walk_slot interface, then it looks like this would work. Thanks,
Alex
Quoted 96 lines
Quoted 84 lines
Signed-off-by Alex Williamson <[email protected]>---
This allows me to use
pci_walk_bus()andpci_walk_slot()instead of rolling my own for iterating devices for bus/slot reset. Please consider for 3.12 to go with the rest of the bus/slot infrastructure.drivers/pci/bus.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 58 insertions(+)drivers/pci/bus.c +56
-0@@ -283,6 +283,62 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), }EXPORT_SYMBOL_GPL (pci_walk_bus); +/** pci_walk_slot - walk devices in/under slot, calling callback. +* @slot slot 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 slot, including any bridged devices +* on buses under this slot. Call the provided callback +* on each device found. +* +* We check the return of @cb each time. If it returns anything +* other than 0, we break out. +* +*/ +void pci_walk_slot (struct pci_slot *slot,int (*cb)(struct pci_dev *,void *), +void *userdata) +{ +struct pci_dev *dev; +struct pci_bus *bus; +struct list_head *next; +int retval; + + bus = slot->bus; +down_read (&pci_bus_sem); + next = slot->bus->devices.next; +for (;;) { +if (next == &bus->devices) { +/* end of this bus, go up or finish */ +if (bus == slot->bus) +break ; + next = bus->self->bus_list.next; + bus = bus->self->bus; +continue ; + } + dev = list_entry(next, struct pci_dev, bus_list); + +/* skip anything on the top level bus not in our slot */ +if (dev->bus == slot->bus && dev->slot != slot) { + next = dev->bus_list.next; +continue ; + } + +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; + + retval =cb (dev, userdata); +if (retval) +break ; + } +up_read (&pci_bus_sem); +} +EXPORT_SYMBOL_GPL (pci_walk_slot); +struct pci_bus *pci_bus_get (struct pci_bus *bus) {if (bus)include/linux/pci.h +0
-0include/linux/pci.h int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, · line 1094
Quoted 8 lines
include/linux/pci.h +2
-0@@ -1087,6 +1087,8 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,void pci_walk_bus (struct pci_bus *top,int (*cb)(struct pci_dev *,void *),void *userdata); +void pci_walk_slot (struct pci_slot *slot,int (*cb)(struct pci_dev *,void *), +void *userdata);int pci_cfg_space_size_ext (struct pci_dev *dev);int pci_cfg_space_size (struct pci_dev *dev);unsigned char pci_bus_max_busnr (struct pci_bus *bus);--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html