This function can be used during the kernel boot sequence to forcefully override fw_devlink=on and unblock the probing of all devices that have a driver. It's mainly meant to be called from late_initcall() or late_initcall_sync() where a device needs to probe before the kernel can mount rootfs. Signed-off-by: Saravana Kannan <saravanak@xxxxxxxxxx> --- drivers/base/base.h | 1 + drivers/base/core.c | 58 ++++++++++++++++++++++++++++++++++++++++++ drivers/base/dd.c | 2 +- include/linux/fwnode.h | 2 ++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index ab71403d102f..b3a43a164dcd 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -160,6 +160,7 @@ extern int devres_release_all(struct device *dev); extern void device_block_probing(void); extern void device_unblock_probing(void); extern void deferred_probe_extend_timeout(void); +extern void driver_deferred_probe_trigger(void); /* /sys/devices directory */ extern struct kset *devices_kset; diff --git a/drivers/base/core.c b/drivers/base/core.c index 7672f23231c1..7ff7fbb00643 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1655,6 +1655,64 @@ void fw_devlink_drivers_done(void) device_links_write_unlock(); } +static int fw_devlink_may_probe(struct device *dev, void *data) +{ + struct device_link *link = to_devlink(dev); + + if (!link->supplier->can_match && link->consumer->can_match) + fw_devlink_relax_link(link); + + return 0; +} + +/** + * fw_devlink_unblock_may_probe - Force unblock any device that has a driver + * + * This function is more of a sledge hammer than a scalpel. Use this very + * sparingly. + * + * Some devices might need to be probed and bound successfully before the kernel + * boot sequence can finish and move on to init/userspace. For example, a + * network interface might need to be bound to be able to mount a NFS rootfs. + * + * With fw_devlink=on by default, some of these devices might be blocked from + * probing because they are waiting on a optional supplier that doesn't have a + * driver. While fw_devlink will eventually identify such devices and unblock + * the probing automatically, it might be too late by the time it unblocks the + * probing of devices. For example, the IP4 autoconfig might timeout before + * fw_devlink unblocks probing of the network interface. This function is + * available to unblock the probing of such devices. + * + * Since there's no easy way to know which unprobed device needs to probe for + * boot to succeed, this function makes sure fw_devlink doesn't block any device + * that has a driver at the point in time this function is called. + * + * It does this by relaxing (fw_devlink=permissive behavior) all the device + * links created by fw_devlink where the consumer has a driver and the supplier + * doesn't have a driver. + * + * It's extremely unlikely that a proper use of this function will be outside of + * an initcall. So, until a case is made for that, this function is + * intentionally marked with __init. + */ +void __init fw_devlink_unblock_may_probe(void) +{ + struct device_link *link, *ln; + + if (!fw_devlink_flags || fw_devlink_is_permissive()) + return; + + /* Wait for current probes to finish to limit impact. */ + wait_for_device_probe(); + + device_links_write_lock(); + class_for_each_device(&devlink_class, NULL, NULL, + fw_devlink_may_probe); + device_links_write_unlock(); + + driver_deferred_probe_trigger(); +} + static void fw_devlink_unblock_consumers(struct device *dev) { struct device_link *link; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index f963d9010d7f..af8138d44e6c 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -172,7 +172,7 @@ static bool driver_deferred_probe_enable; * changes in the midst of a probe, then deferred processing should be triggered * again. */ -static void driver_deferred_probe_trigger(void) +void driver_deferred_probe_trigger(void) { if (!driver_deferred_probe_enable) return; diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 9a81c4410b9f..0770edda7068 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -13,6 +13,7 @@ #include <linux/list.h> #include <linux/bits.h> #include <linux/err.h> +#include <linux/init.h> struct fwnode_operations; struct device; @@ -199,5 +200,6 @@ extern bool fw_devlink_is_strict(void); int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup); void fwnode_links_purge(struct fwnode_handle *fwnode); void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode); +void __init fw_devlink_unblock_may_probe(void); #endif -- 2.36.1.124.g0e6072fb45-goog