On 06/18/2013 04:06 AM, Bjorn Helgaas wrote:
> On Sun, May 26, 2013 at 11:52:58PM +0800, Jiang Liu wrote:
>> Introduce hotplug-safe PCI bus iterators as below, which hold a
>> reference on the returned PCI bus object.
>> bool pci_bus_exists(int domain, int busnr);
>> struct pci_bus *pci_get_bus(int domain, int busnr);
>> struct pci_bus *pci_get_next_bus(struct pci_bus *from);
>> struct pci_bus *pci_get_next_root_bus(struct pci_bus *from);
>> #define for_each_pci_bus(b) for (b = NULL; (b = pci_get_next_bus(b)); )
>> #define for_each_pci_root_bus(b)  \
>>              for (b = NULL; (b = pci_get_next_root_bus(b)); )
>>
>> The long-term goal is to remove hotplug-unsafe pci_find_bus(),
>> pci_find_next_bus() and the global pci_root_buses list.
> 
> I think you should mark the unsafe interfaces as __deprecated so
> users will get compile-time warnings.
> 
> I don't think pci_bus_exists() is a safe interface, because the value
> it returns may be incorrect before the caller can look at it.  The
> only safe thing would be to make it so we try to create the bus
> and return failure if it already exists.  Then the mutex can be in
> the code that creates the bus.
> 
> I don't see any uses of for_each_pci_bus(), so please remove that.
> 
> It sounds like we don't have a consensus on how to iterate over
> PCI root buses.  If you separate that from the pci_get_bus()
> changes, maybe we can at least move forward on the pci_get_bus()
> stuff.
Hi Bjorn and Yinghai,
    I have thought about the way to implement pci_for_each_root_bus()
again. And there are several possible ways here:
1) Yinghai has a patch set implementing an iterator for PCI host
bridges, but we can't safely refer the PCI root bus associated with a
host bridge device because the host bridge doesn't hold a reference to
associated PCI root bus. So we need to find a safe way to refer the PCI
root bus associated with a PCI host bridge.
2) Unexport pci_root_buses and convert it to klist, then we could walk
all root buses effectively. This solution is straight-forward, but it
may break out of tree drivers.
3) Keep current implementation, which does waste some computation cycles:(

So what's your thoughts about above solutions? Or any other suggestions?
Regards!
Gerry

> 
> Bjorn
> 
>> These new interfaces may be a littler slower than existing interfaces,
>> but it should be acceptable because they are not used on hot paths.
>>
>> Signed-off-by: Jiang Liu <jiang....@huawei.com>
>> Acked-by: Yinghai Lu <ying...@kernel.org>
>> Cc: linux-...@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org
>> ---
>>  drivers/pci/pci.h    |   1 +
>>  drivers/pci/probe.c  |   2 +-
>>  drivers/pci/search.c | 159 
>> +++++++++++++++++++++++++++++++++++++++++----------
>>  include/linux/pci.h  |  23 +++++++-
>>  4 files changed, 153 insertions(+), 32 deletions(-)
>>
>> diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
>> index 68678ed..8fe15f6 100644
>> --- a/drivers/pci/pci.h
>> +++ b/drivers/pci/pci.h
>> @@ -126,6 +126,7 @@ static inline void pci_remove_legacy_files(struct 
>> pci_bus *bus) { return; }
>>  
>>  /* Lock for read/write access to pci device and bus lists */
>>  extern struct rw_semaphore pci_bus_sem;
>> +extern struct class pcibus_class;
>>  
>>  extern raw_spinlock_t pci_lock;
>>  
>> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
>> index 2830070..1004a05 100644
>> --- a/drivers/pci/probe.c
>> +++ b/drivers/pci/probe.c
>> @@ -93,7 +93,7 @@ static void release_pcibus_dev(struct device *dev)
>>      kfree(pci_bus);
>>  }
>>  
>> -static struct class pcibus_class = {
>> +struct class pcibus_class = {
>>      .name           = "pci_bus",
>>      .dev_release    = &release_pcibus_dev,
>>      .dev_attrs      = pcibus_dev_attrs,
>> diff --git a/drivers/pci/search.c b/drivers/pci/search.c
>> index d0627fa..16ccaf8 100644
>> --- a/drivers/pci/search.c
>> +++ b/drivers/pci/search.c
>> @@ -52,20 +52,27 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
>>      return tmp;
>>  }
>>  
>> -static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char 
>> busnr)
>> +struct pci_bus_match_arg {
>> +    int domain;
>> +    int bus;
>> +};
>> +
>> +static int pci_match_bus(struct device *dev, const void *data)
>>  {
>> -    struct pci_bus* child;
>> -    struct list_head *tmp;
>> +    struct pci_bus *bus = to_pci_bus(dev);
>> +    const struct pci_bus_match_arg *arg = data;
>>  
>> -    if(bus->number == busnr)
>> -            return bus;
>> +    return (pci_domain_nr(bus) == arg->domain && bus->number == arg->bus);
>> +}
>>  
>> -    list_for_each(tmp, &bus->children) {
>> -            child = pci_do_find_bus(pci_bus_b(tmp), busnr);
>> -            if(child)
>> -                    return child;
>> -    }
>> -    return NULL;
>> +static int pci_match_next_bus(struct device *dev, const void *data)
>> +{
>> +    return 1;
>> +}
>> +
>> +static int pci_match_next_root_bus(struct device *dev, const void *data)
>> +{
>> +    return pci_is_root_bus(to_pci_bus(dev));
>>  }
>>  
>>  /**
>> @@ -76,20 +83,19 @@ static struct pci_bus *pci_do_find_bus(struct pci_bus 
>> *bus, unsigned char busnr)
>>   * Given a PCI bus number and domain number, the desired PCI bus is located
>>   * 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.
>> + *
>> + * Note: it's not hotplug safe, the returned bus may be destroyed at any 
>> time.
>> + * Please use pci_get_bus() instead which holds a reference on the returned
>> + * PCI bus.
>>   */
>> -struct pci_bus * 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;
>> +    struct pci_bus *bus;
>>  
>> -    while ((bus = pci_find_next_bus(bus)) != NULL)  {
>> -            if (pci_domain_nr(bus) != domain)
>> -                    continue;
>> -            tmp_bus = pci_do_find_bus(bus, busnr);
>> -            if (tmp_bus)
>> -                    return tmp_bus;
>> -    }
>> -    return NULL;
>> +    bus = pci_get_bus(domain, busnr);
>> +    pci_bus_put(bus);
>> +
>> +    return bus;
>>  }
>>  
>>  /**
>> @@ -100,21 +106,114 @@ struct pci_bus * pci_find_bus(int domain, int busnr)
>>   * initiated by passing %NULL as the @from argument.  Otherwise if
>>   * @from is not %NULL, searches continue from next device on the
>>   * global list.
>> + *
>> + * Note: it's not hotplug safe, the returned bus may be destroyed at any 
>> time.
>> + * Please use pci_get_next_root_bus() instead which holds a reference
>> + * on the returned PCI root bus.
>>   */
>>  struct pci_bus * 
>>  pci_find_next_bus(const struct pci_bus *from)
>>  {
>> -    struct list_head *n;
>> -    struct pci_bus *b = NULL;
>> +    struct device *dev = from ? (struct device *)&from->dev : NULL;
>> +
>> +    dev = class_find_device(&pcibus_class, dev, NULL,
>> +                            &pci_match_next_root_bus);
>> +    if (dev) {
>> +            put_device(dev);
>> +            return to_pci_bus(dev);
>> +    }
>> +
>> +    return NULL;
>> +}
>> +
>> +bool pci_bus_exists(int domain, int busnr)
>> +{
>> +    struct device *dev;
>> +    struct pci_bus_match_arg arg = { domain, busnr };
>>  
>>      WARN_ON(in_interrupt());
>> -    down_read(&pci_bus_sem);
>> -    n = from ? from->node.next : pci_root_buses.next;
>> -    if (n != &pci_root_buses)
>> -            b = pci_bus_b(n);
>> -    up_read(&pci_bus_sem);
>> -    return b;
>> +    dev = class_find_device(&pcibus_class, NULL, &arg, &pci_match_bus);
>> +    if (dev)
>> +            put_device(dev);
>> +
>> +    return dev != NULL;
>> +}
>> +EXPORT_SYMBOL(pci_bus_exists);
>> +
>> +/**
>> + * pci_get_bus - locate PCI bus from a given domain and bus number
>> + * @domain: number of PCI domain to search
>> + * @busnr: number of desired PCI bus
>> + *
>> + * Given a PCI bus number and domain number, the desired PCI bus is located.
>> + * If the bus is found, a pointer to its data structure is returned.
>> + * If no bus is found, %NULL is returned.
>> + * Caller needs to release the returned bus by calling pci_bus_put().
>> + */
>> +struct pci_bus *pci_get_bus(int domain, int busnr)
>> +{
>> +    struct device *dev;
>> +    struct pci_bus_match_arg arg = { domain, busnr };
>> +
>> +    WARN_ON(in_interrupt());
>> +    dev = class_find_device(&pcibus_class, NULL, &arg, &pci_match_bus);
>> +    if (dev)
>> +            return to_pci_bus(dev);
>> +
>> +    return NULL;
>> +}
>> +EXPORT_SYMBOL(pci_get_bus);
>> +
>> +/**
>> + * pci_get_next_bus - begin or continue searching for a PCI bus
>> + * @from: Previous PCI bus found, or %NULL for new search.
>> + *
>> + * Iterates through the list of known PCI busses. If a PCI bus is found,
>> + * the reference count to the bus is incremented and a pointer to it 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 bus on the global list. The reference count
>> + * for @from is always decremented if it is not %NULL.
>> + */
>> +struct pci_bus *pci_get_next_bus(struct pci_bus *from)
>> +{
>> +    struct device *dev = from ? &from->dev : NULL;
>> +
>> +    WARN_ON(in_interrupt());
>> +    dev = class_find_device(&pcibus_class, dev, NULL, &pci_match_next_bus);
>> +    pci_bus_put(from);
>> +    if (dev)
>> +            return to_pci_bus(dev);
>> +
>> +    return NULL;
>> +}
>> +EXPORT_SYMBOL(pci_get_next_bus);
>> +
>> +/**
>> + * pci_find_next_root_bus - begin or continue searching for a PCI root bus
>> + * @from: Previous PCI bus found, or %NULL for new search.
>> + *
>> + * Iterates through the list of known PCI root busses. If a PCI bus is 
>> found,
>> + * the reference count to the root bus is incremented and a pointer to it 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 root bus on the global list. The reference
>> + * count for @from is always decremented if it is not %NULL.
>> + */
>> +struct pci_bus *pci_get_next_root_bus(struct pci_bus *from)
>> +{
>> +    struct device *dev = from ? &from->dev : NULL;
>> +
>> +    WARN_ON(in_interrupt());
>> +    dev = class_find_device(&pcibus_class, dev, NULL,
>> +                            &pci_match_next_root_bus);
>> +    pci_bus_put(from);
>> +    if (dev)
>> +            return to_pci_bus(dev);
>> +
>> +    return NULL;
>>  }
>> +EXPORT_SYMBOL(pci_get_next_root_bus);
>>  
>>  /**
>>   * pci_get_slot - locate PCI device for a given PCI slot
>> diff --git a/include/linux/pci.h b/include/linux/pci.h
>> index 7b23fa0..1e43423 100644
>> --- a/include/linux/pci.h
>> +++ b/include/linux/pci.h
>> @@ -454,6 +454,9 @@ struct pci_bus {
>>  
>>  #define pci_bus_b(n)        list_entry(n, struct pci_bus, node)
>>  #define to_pci_bus(n)       container_of(n, struct pci_bus, dev)
>> +#define for_each_pci_bus(b) for (b = NULL; (b = pci_get_next_bus(b)); )
>> +#define for_each_pci_root_bus(b) \
>> +                    for (b = NULL; (b = pci_get_next_root_bus(b)); )
>>  
>>  /*
>>   * Returns true if the pci bus is root (behind host-pci bridge),
>> @@ -718,7 +721,6 @@ void pcibios_resource_to_bus(struct pci_dev *dev, struct 
>> pci_bus_region *region,
>>  void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res,
>>                           struct pci_bus_region *region);
>>  void pcibios_scan_specific_bus(int busn);
>> -struct pci_bus *pci_find_bus(int domain, int busnr);
>>  void pci_bus_add_devices(const struct pci_bus *bus);
>>  struct pci_bus * __deprecated pci_scan_bus_parented(struct device *parent,
>>                      int bus, struct pci_ops *ops, void *sysdata);
>> @@ -778,8 +780,15 @@ int pci_find_ext_capability(struct pci_dev *dev, int 
>> cap);
>>  int pci_find_next_ext_capability(struct pci_dev *dev, int pos, int cap);
>>  int pci_find_ht_capability(struct pci_dev *dev, int ht_cap);
>>  int pci_find_next_ht_capability(struct pci_dev *dev, int pos, int ht_cap);
>> +
>> +struct pci_bus *pci_find_bus(int domain, int busnr);
>>  struct pci_bus *pci_find_next_bus(const struct pci_bus *from);
>>  
>> +bool pci_bus_exists(int domain, int busnr);
>> +struct pci_bus *pci_get_bus(int domain, int busnr);
>> +struct pci_bus *pci_get_next_bus(struct pci_bus *from);
>> +struct pci_bus *pci_get_next_root_bus(struct pci_bus *from);
>> +
>>  struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device,
>>                              struct pci_dev *from);
>>  struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
>> @@ -1430,6 +1439,18 @@ static inline void pci_unblock_cfg_access(struct 
>> pci_dev *dev)
>>  static inline struct pci_bus *pci_find_next_bus(const struct pci_bus *from)
>>  { return NULL; }
>>  
>> +static inline bool pci_bus_exists(int domain, int busnr)
>> +{ return false; }
>> +
>> +static inline struct pci_bus *pci_get_bus(int domain, int busnr)
>> +{ return NULL; }
>> +
>> +static inline struct pci_bus *pci_get_next_bus(struct pci_bus *from)
>> +{ return NULL; }
>> +
>> +static inline struct pci_bus *pci_get_next_root_bus(struct pci_bus *from)
>> +{ return NULL; }
>> +
>>  static inline struct pci_dev *pci_get_slot(struct pci_bus *bus,
>>                                              unsigned int devfn)
>>  { return NULL; }
>> -- 
>> 1.8.1.2
>>

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to