X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fpci%2Fsearch.c;h=b2653c4afe9e69212f8923df0574561cc049eaef;hb=refs%2Fheads%2Fvserver;hp=05fa91a31c620b0dd9de57a59066ed6adae71062;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 05fa91a31..b2653c4af 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -13,7 +13,7 @@ #include #include "pci.h" -DEFINE_SPINLOCK(pci_bus_lock); +DECLARE_RWSEM(pci_bus_sem); static struct pci_bus * __devinit pci_do_find_bus(struct pci_bus* bus, unsigned char busnr) @@ -41,7 +41,7 @@ pci_do_find_bus(struct pci_bus* bus, unsigned char busnr) * in the global list of PCI buses. If the bus is found, a pointer to its * data structure is returned. If no bus is found, %NULL is returned. */ -struct pci_bus * __devinit pci_find_bus(int domain, int busnr) +struct pci_bus * pci_find_bus(int domain, int busnr) { struct pci_bus *bus = NULL; struct pci_bus *tmp_bus; @@ -61,7 +61,7 @@ struct pci_bus * __devinit pci_find_bus(int domain, int busnr) * @from: Previous PCI bus found, or %NULL for new search. * * Iterates through the list of known PCI busses. A new search is - * initiated by passing %NULL to the @from argument. Otherwise if + * initiated by passing %NULL as the @from argument. Otherwise if * @from is not %NULL, searches continue from next device on the * global list. */ @@ -72,11 +72,11 @@ pci_find_next_bus(const struct pci_bus *from) struct pci_bus *b = NULL; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); n = from ? from->node.next : pci_root_buses.next; if (n != &pci_root_buses) b = pci_bus_b(n); - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); return b; } @@ -124,7 +124,7 @@ struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn) struct pci_dev *dev; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); list_for_each(tmp, &bus->devices) { dev = pci_dev_b(tmp); @@ -135,10 +135,35 @@ struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn) dev = NULL; out: pci_dev_get(dev); - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); return dev; } +/** + * pci_get_bus_and_slot - locate PCI device from a given PCI slot + * @bus: number of PCI bus on which desired PCI device resides + * @devfn: encodes number of PCI slot in which the desired PCI + * device resides and the logical device number within that slot + * in case of multi-function devices. + * + * Given a PCI bus and slot/function number, the desired PCI device + * is located in system global list of PCI devices. If the device + * is found, a pointer to its data structure is returned. If no + * device is found, %NULL is returned. The returned device has its + * reference count bumped by one. + */ + +struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn) +{ + struct pci_dev *dev = NULL; + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + if (dev->bus->number == bus && dev->devfn == devfn) + return dev; + } + return NULL; +} + /** * pci_find_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids @@ -148,13 +173,14 @@ struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn) * @from: Previous PCI device found in search, or %NULL for new search. * * Iterates through the list of known PCI devices. If a PCI device is - * found with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its - * device structure is returned. Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL to the @from argument. - * Otherwise if @from is not %NULL, searches continue from next device on the global list. + * found with a matching @vendor, @device, @ss_vendor and @ss_device, a + * pointer to its device structure is returned. Otherwise, %NULL is returned. + * A new search is initiated by passing %NULL as the @from argument. + * Otherwise if @from is not %NULL, searches continue from next device + * on the global list. * - * NOTE: Do not use this function anymore, use pci_get_subsys() instead, as - * the pci device returned by this function can disappear at any moment in + * NOTE: Do not use this function any more; use pci_get_subsys() instead, as + * the PCI device returned by this function can disappear at any moment in * time. */ static struct pci_dev * pci_find_subsys(unsigned int vendor, @@ -167,7 +193,16 @@ static struct pci_dev * pci_find_subsys(unsigned int vendor, struct pci_dev *dev; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + + /* + * pci_find_subsys() can be called on the ide_setup() path, super-early + * in boot. But the down_read() will enable local interrupts, which + * can cause some machines to crash. So here we detect and flag that + * situation and bail out early. + */ + if (unlikely(list_empty(&pci_devices))) + return NULL; + down_read(&pci_bus_sem); n = from ? from->global_list.next : pci_devices.next; while (n && (n != &pci_devices)) { @@ -181,7 +216,7 @@ static struct pci_dev * pci_find_subsys(unsigned int vendor, } dev = NULL; exit: - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); return dev; } @@ -191,14 +226,15 @@ exit: * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids * @from: Previous PCI device found in search, or %NULL for new search. * - * Iterates through the list of known PCI devices. If a PCI device is - * found with a matching @vendor and @device, a pointer to its device structure is + * Iterates through the list of known PCI devices. If a PCI device is found + * with a matching @vendor and @device, a pointer to its device structure is * returned. Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL to the @from argument. - * Otherwise if @from is not %NULL, searches continue from next device on the global list. + * A new search is initiated by passing %NULL as the @from argument. + * Otherwise if @from is not %NULL, searches continue from next device + * on the global list. * - * NOTE: Do not use this function anymore, use pci_get_device() instead, as - * the pci device returned by this function can disappear at any moment in + * NOTE: Do not use this function any more; use pci_get_device() instead, as + * the PCI device returned by this function can disappear at any moment in * time. */ struct pci_dev * @@ -215,11 +251,11 @@ pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev * * @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids * @from: Previous PCI device found in search, or %NULL for new search. * - * Iterates through the list of known PCI devices. If a PCI device is - * found with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its + * Iterates through the list of known PCI devices. If a PCI device is found + * with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its * device structure is returned, and the reference count to the device is * incremented. Otherwise, %NULL is returned. A new search is initiated by - * passing %NULL to the @from argument. Otherwise if @from is not %NULL, + * passing %NULL as the @from argument. Otherwise if @from is not %NULL, * searches continue from next device on the global list. * The reference count for @from is always decremented if it is not %NULL. */ @@ -232,7 +268,16 @@ pci_get_subsys(unsigned int vendor, unsigned int device, struct pci_dev *dev; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + + /* + * pci_get_subsys() can potentially be called by drivers super-early + * in boot. But the down_read() will enable local interrupts, which + * can cause some machines to crash. So here we detect and flag that + * situation and bail out early. + */ + if (unlikely(list_empty(&pci_devices))) + return NULL; + down_read(&pci_bus_sem); n = from ? from->global_list.next : pci_devices.next; while (n && (n != &pci_devices)) { @@ -246,9 +291,9 @@ pci_get_subsys(unsigned int vendor, unsigned int device, } dev = NULL; exit: - pci_dev_put(from); dev = pci_dev_get(dev); - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); + pci_dev_put(from); return dev; } @@ -262,7 +307,7 @@ exit: * found with a matching @vendor and @device, the reference count to the * device is incremented and a pointer to its device structure is returned. * Otherwise, %NULL is returned. A new search is initiated by passing %NULL - * to the @from argument. Otherwise if @from is not %NULL, searches continue + * as the @from argument. Otherwise if @from is not %NULL, searches continue * from next device on the global list. The reference count for @from is * always decremented if it is not %NULL. */ @@ -272,6 +317,45 @@ pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from) return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from); } +/** + * pci_get_device_reverse - begin or continue searching for a PCI device by vendor/device id + * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids + * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids + * @from: Previous PCI device found in search, or %NULL for new search. + * + * Iterates through the list of known PCI devices in the reverse order of + * pci_get_device. + * If a PCI device is found with a matching @vendor and @device, the reference + * count to the device is incremented and a pointer to its device structure + * is returned Otherwise, %NULL is returned. A new search is initiated by + * passing %NULL as the @from argument. Otherwise if @from is not %NULL, + * searches continue from next device on the global list. The reference + * count for @from is always decremented if it is not %NULL. + */ +struct pci_dev * +pci_get_device_reverse(unsigned int vendor, unsigned int device, struct pci_dev *from) +{ + struct list_head *n; + struct pci_dev *dev; + + WARN_ON(in_interrupt()); + down_read(&pci_bus_sem); + n = from ? from->global_list.prev : pci_devices.prev; + + while (n && (n != &pci_devices)) { + dev = pci_dev_g(n); + if ((vendor == PCI_ANY_ID || dev->vendor == vendor) && + (device == PCI_ANY_ID || dev->device == device)) + goto exit; + n = n->prev; + } + dev = NULL; +exit: + dev = pci_dev_get(dev); + up_read(&pci_bus_sem); + pci_dev_put(from); + return dev; +} /** * pci_find_device_reverse - begin or continue searching for a PCI device by vendor/device id @@ -279,11 +363,13 @@ pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from) * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids * @from: Previous PCI device found in search, or %NULL for new search. * - * Iterates through the list of known PCI devices in the reverse order of pci_find_device(). + * Iterates through the list of known PCI devices in the reverse order of + * pci_find_device(). * If a PCI device is found with a matching @vendor and @device, a pointer to * its device structure is returned. Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL to the @from argument. - * Otherwise if @from is not %NULL, searches continue from previous device on the global list. + * A new search is initiated by passing %NULL as the @from argument. + * Otherwise if @from is not %NULL, searches continue from previous device + * on the global list. */ struct pci_dev * pci_find_device_reverse(unsigned int vendor, unsigned int device, const struct pci_dev *from) @@ -292,7 +378,7 @@ pci_find_device_reverse(unsigned int vendor, unsigned int device, const struct p struct pci_dev *dev; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); n = from ? from->global_list.prev : pci_devices.prev; while (n && (n != &pci_devices)) { @@ -304,7 +390,7 @@ pci_find_device_reverse(unsigned int vendor, unsigned int device, const struct p } dev = NULL; exit: - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); return dev; } @@ -317,7 +403,7 @@ exit: * found with a matching @class, the reference count to the device is * incremented and a pointer to its device structure is returned. * Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL to the @from argument. + * A new search is initiated by passing %NULL as the @from argument. * Otherwise if @from is not %NULL, searches continue from next device * on the global list. The reference count for @from is always decremented * if it is not %NULL. @@ -328,7 +414,7 @@ struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from) struct pci_dev *dev; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); n = from ? from->global_list.next : pci_devices.next; while (n && (n != &pci_devices)) { @@ -339,12 +425,30 @@ struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from) } dev = NULL; exit: - pci_dev_put(from); dev = pci_dev_get(dev); - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); + pci_dev_put(from); return dev; } +const struct pci_device_id *pci_find_present(const struct pci_device_id *ids) +{ + struct pci_dev *dev; + const struct pci_device_id *found = NULL; + + WARN_ON(in_interrupt()); + down_read(&pci_bus_sem); + while (ids->vendor || ids->subvendor || ids->class_mask) { + list_for_each_entry(dev, &pci_devices, global_list) { + if ((found = pci_match_one_device(ids, dev)) != NULL) + break; + } + ids++; + } + up_read(&pci_bus_sem); + return found; +} + /** * pci_dev_present - Returns 1 if device matching the device list is present, 0 if not. * @ids: A pointer to a null terminated list of struct pci_device_id structures @@ -358,32 +462,22 @@ exit: */ int pci_dev_present(const struct pci_device_id *ids) { - struct pci_dev *dev; - int found = 0; - - WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); - while (ids->vendor || ids->subvendor || ids->class_mask) { - list_for_each_entry(dev, &pci_devices, global_list) { - if (pci_match_one_device(ids, dev)) { - found = 1; - goto exit; - } - } - ids++; - } -exit: - spin_unlock(&pci_bus_lock); - return found; + return pci_find_present(ids) == NULL ? 0 : 1; } + EXPORT_SYMBOL(pci_dev_present); +EXPORT_SYMBOL(pci_find_present); -EXPORT_SYMBOL(pci_find_bus); -EXPORT_SYMBOL(pci_find_next_bus); EXPORT_SYMBOL(pci_find_device); EXPORT_SYMBOL(pci_find_device_reverse); EXPORT_SYMBOL(pci_find_slot); +/* For boot time work */ +EXPORT_SYMBOL(pci_find_bus); +EXPORT_SYMBOL(pci_find_next_bus); +/* For everyone */ EXPORT_SYMBOL(pci_get_device); +EXPORT_SYMBOL(pci_get_device_reverse); EXPORT_SYMBOL(pci_get_subsys); EXPORT_SYMBOL(pci_get_slot); +EXPORT_SYMBOL(pci_get_bus_and_slot); EXPORT_SYMBOL(pci_get_class);