driver core: Add edit_links() callback for drivers

The driver core/bus adding supplier-consumer dependencies by default
enables functional dependencies to be tracked correctly even when the
consumer devices haven't had their drivers registered or loaded (if they
are modules).

However, when the bus incorrectly adds dependencies that it shouldn't
have added, the devices might never probe.

For example, if device-C is a consumer of device-S and they have
phandles to each other in DT, the following could happen:

1.  Device-S get added first.
2.  The bus add_links() callback will (incorrectly) try to link it as
    a consumer of device-C.
3.  Since device-C isn't present, device-S will be put in
    "waiting-for-supplier" list.
4.  Device-C gets added next.
5.  All devices in "waiting-for-supplier" list are retried for linking.
6.  Device-S gets linked as consumer to Device-C.
7.  The bus add_links() callback will (correctly) try to link it as
    a consumer of device-S.
8.  This isn't allowed because it would create a cyclic device links.

Neither devices will get probed since the supplier is marked as
dependent on the consumer. And the consumer will never probe because the
consumer can't get resources from the supplier.

Without this patch, things stay in this broken state. However, with this
patch, the execution will continue like this:

9.  Device-C's driver is loaded.
10. Device-C's driver removes Device-S as a consumer of Device-C.
11. Device-C's driver adds Device-C as a consumer of Device-S.
12. Device-S probes.
14. Device-C probes.

kbuild test robot reported missing documentation for device.has_edit_links
Reported-by: kbuild test robot <lkp@intel.com>
Signed-off-by: Saravana Kannan <saravanak@google.com>
Link: https://lore.kernel.org/r/20190731221721.187713-3-saravanak@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Saravana Kannan
2019-07-31 15:17:15 -07:00
committed by Greg Kroah-Hartman
parent 5302dd7dd0
commit 134b23eec9
3 changed files with 71 additions and 2 deletions

View File

@@ -439,6 +439,19 @@ static void device_link_wait_for_supplier(struct device *consumer)
mutex_unlock(&wfs_lock);
}
/**
* device_link_remove_from_wfs - Unmark device as waiting for supplier
* @consumer: Consumer device
*
* Unmark the consumer device as waiting for suppliers to become available.
*/
void device_link_remove_from_wfs(struct device *consumer)
{
mutex_lock(&wfs_lock);
list_del_init(&consumer->links.needs_suppliers);
mutex_unlock(&wfs_lock);
}
/**
* device_link_check_waiting_consumers - Try to unmark waiting consumers
*
@@ -456,12 +469,19 @@ static void device_link_wait_for_supplier(struct device *consumer)
static void device_link_check_waiting_consumers(void)
{
struct device *dev, *tmp;
int ret;
mutex_lock(&wfs_lock);
list_for_each_entry_safe(dev, tmp, &wait_for_suppliers,
links.needs_suppliers)
if (!dev->bus->add_links(dev))
links.needs_suppliers) {
ret = 0;
if (dev->has_edit_links)
ret = driver_edit_links(dev);
else if (dev->bus->add_links)
ret = dev->bus->add_links(dev);
if (!ret)
list_del_init(&dev->links.needs_suppliers);
}
mutex_unlock(&wfs_lock);
}