Commit 5302dd7d authored by Saravana Kannan's avatar Saravana Kannan Committed by Greg Kroah-Hartman

driver core: Add support for linking devices during device addition

When devices are added, the bus might want to create device links to track
functional dependencies between supplier and consumer devices. This
tracking of supplier-consumer relationship allows optimizing device probe
order and tracking whether all consumers of a supplier are active. The
add_links bus callback is added to support this.

However, when consumer devices are added, they might not have a supplier
device to link to despite needing mandatory resources/functionality from
one or more suppliers. A waiting_for_suppliers list is created to track
such consumers and retry linking them when new devices get added.
Signed-off-by: default avatarSaravana Kannan <saravanak@google.com>
Link: https://lore.kernel.org/r/20190731221721.187713-2-saravanak@google.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 313b46d8
...@@ -44,6 +44,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); ...@@ -44,6 +44,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup);
#endif #endif
/* Device links support. */ /* Device links support. */
static LIST_HEAD(wait_for_suppliers);
static DEFINE_MUTEX(wfs_lock);
#ifdef CONFIG_SRCU #ifdef CONFIG_SRCU
static DEFINE_MUTEX(device_links_lock); static DEFINE_MUTEX(device_links_lock);
...@@ -418,6 +420,51 @@ struct device_link *device_link_add(struct device *consumer, ...@@ -418,6 +420,51 @@ struct device_link *device_link_add(struct device *consumer,
} }
EXPORT_SYMBOL_GPL(device_link_add); EXPORT_SYMBOL_GPL(device_link_add);
/**
* device_link_wait_for_supplier - Mark device as waiting for supplier
* @consumer: Consumer device
*
* Marks the consumer device as waiting for suppliers to become available. The
* consumer device will never be probed until it's unmarked as waiting for
* suppliers. The caller is responsible for adding the link to the supplier
* once the supplier device is present.
*
* This function is NOT meant to be called from the probe function of the
* consumer but rather from code that creates/adds the consumer device.
*/
static void device_link_wait_for_supplier(struct device *consumer)
{
mutex_lock(&wfs_lock);
list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers);
mutex_unlock(&wfs_lock);
}
/**
* device_link_check_waiting_consumers - Try to unmark waiting consumers
*
* Loops through all consumers waiting on suppliers and tries to add all their
* supplier links. If that succeeds, the consumer device is unmarked as waiting
* for suppliers. Otherwise, they are left marked as waiting on suppliers,
*
* The add_links bus callback is expected to return 0 if it has found and added
* all the supplier links for the consumer device. It should return an error if
* it isn't able to do so.
*
* The caller of device_link_wait_for_supplier() is expected to call this once
* it's aware of potential suppliers becoming available.
*/
static void device_link_check_waiting_consumers(void)
{
struct device *dev, *tmp;
mutex_lock(&wfs_lock);
list_for_each_entry_safe(dev, tmp, &wait_for_suppliers,
links.needs_suppliers)
if (!dev->bus->add_links(dev))
list_del_init(&dev->links.needs_suppliers);
mutex_unlock(&wfs_lock);
}
static void device_link_free(struct device_link *link) static void device_link_free(struct device_link *link)
{ {
while (refcount_dec_not_one(&link->rpm_active)) while (refcount_dec_not_one(&link->rpm_active))
...@@ -552,6 +599,19 @@ int device_links_check_suppliers(struct device *dev) ...@@ -552,6 +599,19 @@ int device_links_check_suppliers(struct device *dev)
struct device_link *link; struct device_link *link;
int ret = 0; int ret = 0;
/*
* If a device is waiting for one or more suppliers (in
* wait_for_suppliers list), it is not ready to probe yet. So just
* return -EPROBE_DEFER without having to check the links with existing
* suppliers.
*/
mutex_lock(&wfs_lock);
if (!list_empty(&dev->links.needs_suppliers)) {
mutex_unlock(&wfs_lock);
return -EPROBE_DEFER;
}
mutex_unlock(&wfs_lock);
device_links_write_lock(); device_links_write_lock();
list_for_each_entry(link, &dev->links.suppliers, c_node) { list_for_each_entry(link, &dev->links.suppliers, c_node) {
...@@ -836,6 +896,10 @@ static void device_links_purge(struct device *dev) ...@@ -836,6 +896,10 @@ static void device_links_purge(struct device *dev)
{ {
struct device_link *link, *ln; struct device_link *link, *ln;
mutex_lock(&wfs_lock);
list_del(&dev->links.needs_suppliers);
mutex_unlock(&wfs_lock);
/* /*
* Delete all of the remaining links from this device to any other * Delete all of the remaining links from this device to any other
* devices (either consumers or suppliers). * devices (either consumers or suppliers).
...@@ -1697,6 +1761,7 @@ void device_initialize(struct device *dev) ...@@ -1697,6 +1761,7 @@ void device_initialize(struct device *dev)
#endif #endif
INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.consumers);
INIT_LIST_HEAD(&dev->links.suppliers); INIT_LIST_HEAD(&dev->links.suppliers);
INIT_LIST_HEAD(&dev->links.needs_suppliers);
dev->links.status = DL_DEV_NO_DRIVER; dev->links.status = DL_DEV_NO_DRIVER;
} }
EXPORT_SYMBOL_GPL(device_initialize); EXPORT_SYMBOL_GPL(device_initialize);
...@@ -2132,6 +2197,24 @@ int device_add(struct device *dev) ...@@ -2132,6 +2197,24 @@ int device_add(struct device *dev)
BUS_NOTIFY_ADD_DEVICE, dev); BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD); kobject_uevent(&dev->kobj, KOBJ_ADD);
/*
* Check if any of the other devices (consumers) have been waiting for
* this device (supplier) to be added so that they can create a device
* link to it.
*
* This needs to happen after device_pm_add() because device_link_add()
* requires the supplier be registered before it's called.
*
* But this also needs to happe before bus_probe_device() to make sure
* waiting consumers can link to it before the driver is bound to the
* device and the driver sync_state callback is called for this device.
*/
device_link_check_waiting_consumers();
if (dev->bus && dev->bus->add_links && dev->bus->add_links(dev))
device_link_wait_for_supplier(dev);
bus_probe_device(dev); bus_probe_device(dev);
if (parent) if (parent)
klist_add_tail(&dev->p->knode_parent, klist_add_tail(&dev->p->knode_parent,
......
...@@ -78,6 +78,17 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); ...@@ -78,6 +78,17 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
* -EPROBE_DEFER it will queue the device for deferred probing. * -EPROBE_DEFER it will queue the device for deferred probing.
* @uevent: Called when a device is added, removed, or a few other things * @uevent: Called when a device is added, removed, or a few other things
* that generate uevents to add the environment variables. * that generate uevents to add the environment variables.
* @add_links: Called, perhaps multiple times per device, after a device is
* added to this bus. The function is expected to create device
* links to all the suppliers of the input device that are
* available at the time this function is called. As in, the
* function should NOT stop at the first failed device link if
* other unlinked supplier devices are present in the system.
*
* Return 0 if device links have been successfully created to all
* the suppliers of this device. Return an error if some of the
* suppliers are not yet available and this function needs to be
* reattempted in the future.
* @probe: Called when a new device or driver add to this bus, and callback * @probe: Called when a new device or driver add to this bus, and callback
* the specific driver's probe to initial the matched device. * the specific driver's probe to initial the matched device.
* @remove: Called when a device removed from this bus. * @remove: Called when a device removed from this bus.
...@@ -122,6 +133,7 @@ struct bus_type { ...@@ -122,6 +133,7 @@ struct bus_type {
int (*match)(struct device *dev, struct device_driver *drv); int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*add_links)(struct device *dev);
int (*probe)(struct device *dev); int (*probe)(struct device *dev);
int (*remove)(struct device *dev); int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev); void (*shutdown)(struct device *dev);
...@@ -1128,11 +1140,13 @@ enum dl_dev_state { ...@@ -1128,11 +1140,13 @@ enum dl_dev_state {
* struct dev_links_info - Device data related to device links. * struct dev_links_info - Device data related to device links.
* @suppliers: List of links to supplier devices. * @suppliers: List of links to supplier devices.
* @consumers: List of links to consumer devices. * @consumers: List of links to consumer devices.
* @needs_suppliers: Hook to global list of devices waiting for suppliers.
* @status: Driver status information. * @status: Driver status information.
*/ */
struct dev_links_info { struct dev_links_info {
struct list_head suppliers; struct list_head suppliers;
struct list_head consumers; struct list_head consumers;
struct list_head needs_suppliers;
enum dl_dev_state status; enum dl_dev_state status;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment