Commit 9528e0d9 authored by Saravana Kannan's avatar Saravana Kannan Committed by Greg Kroah-Hartman

driver core: fw_devlink: Detect supplier devices that will never be added

During the initial parsing of firmware by fw_devlink, fw_devlink might
infer that some supplier firmware nodes would get populated as devices.
But the inference is not always correct. This patch tries to logically
detect and fix such mistakes as boot progresses or more devices probe.

fw_devlink makes a fundamental assumption that once a device binds to a
driver, it will populate (i.e: add as struct devices) all the child
firmware nodes that could be populated as devices (if they aren't
populated already).

So, whenever a device probes, we check all its child firmware nodes. If
a child firmware node has a corresponding device populated, we don't
modify the child node or its descendants. However, if a child firmware
node has not been populated as a device, we delete all the fwnode links
where the child node or its descendants are suppliers. This ensures that
no other device is blocked on a firmware node that will never be
populated as a device. We also mark such fwnodes as NOT_DEVICE, so that
no new fwnode links are created with these nodes as suppliers.

Fixes: e5904747 ("driver core: Set fw_devlink=on by default")
Tested-by: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Acked-by: default avatarRafael J. Wysocki <rafael@kernel.org>
Signed-off-by: default avatarSaravana Kannan <saravanak@google.com>
Link: https://lore.kernel.org/r/20210205222644.2357303-2-saravanak@google.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e5e1c209
...@@ -148,6 +148,21 @@ void fwnode_links_purge(struct fwnode_handle *fwnode) ...@@ -148,6 +148,21 @@ void fwnode_links_purge(struct fwnode_handle *fwnode)
fwnode_links_purge_consumers(fwnode); fwnode_links_purge_consumers(fwnode);
} }
static void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode)
{
struct fwnode_handle *child;
/* Don't purge consumer links of an added child */
if (fwnode->dev)
return;
fwnode->flags |= FWNODE_FLAG_NOT_DEVICE;
fwnode_links_purge_consumers(fwnode);
fwnode_for_each_available_child_node(fwnode, child)
fw_devlink_purge_absent_suppliers(child);
}
#ifdef CONFIG_SRCU #ifdef CONFIG_SRCU
static DEFINE_MUTEX(device_links_lock); static DEFINE_MUTEX(device_links_lock);
DEFINE_STATIC_SRCU(device_links_srcu); DEFINE_STATIC_SRCU(device_links_srcu);
...@@ -1154,12 +1169,22 @@ void device_links_driver_bound(struct device *dev) ...@@ -1154,12 +1169,22 @@ void device_links_driver_bound(struct device *dev)
LIST_HEAD(sync_list); LIST_HEAD(sync_list);
/* /*
* If a device probes successfully, it's expected to have created all * If a device binds successfully, it's expected to have created all
* the device links it needs to or make new device links as it needs * the device links it needs to or make new device links as it needs
* them. So, it no longer needs to wait on any suppliers. * them. So, fw_devlink no longer needs to create device links to any
* of the device's suppliers.
*
* Also, if a child firmware node of this bound device is not added as
* a device by now, assume it is never going to be added and make sure
* other devices don't defer probe indefinitely by waiting for such a
* child device.
*/ */
if (dev->fwnode && dev->fwnode->dev == dev) if (dev->fwnode && dev->fwnode->dev == dev) {
struct fwnode_handle *child;
fwnode_links_purge_suppliers(dev->fwnode); fwnode_links_purge_suppliers(dev->fwnode);
fwnode_for_each_available_child_node(dev->fwnode, child)
fw_devlink_purge_absent_suppliers(child);
}
device_remove_file(dev, &dev_attr_waiting_for_supplier); device_remove_file(dev, &dev_attr_waiting_for_supplier);
device_links_write_lock(); device_links_write_lock();
......
...@@ -19,8 +19,10 @@ struct device; ...@@ -19,8 +19,10 @@ struct device;
* fwnode link flags * fwnode link flags
* *
* LINKS_ADDED: The fwnode has already be parsed to add fwnode links. * LINKS_ADDED: The fwnode has already be parsed to add fwnode links.
* NOT_DEVICE: The fwnode will never be populated as a struct device.
*/ */
#define FWNODE_FLAG_LINKS_ADDED BIT(0) #define FWNODE_FLAG_LINKS_ADDED BIT(0)
#define FWNODE_FLAG_NOT_DEVICE BIT(1)
struct fwnode_handle { struct fwnode_handle {
struct fwnode_handle *secondary; struct fwnode_handle *secondary;
......
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