diff options
Diffstat (limited to 'drivers/base/core.c')
| -rw-r--r-- | drivers/base/core.c | 123 | 
1 files changed, 116 insertions, 7 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 460d6f163e41..753e7cca0f40 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -54,6 +54,7 @@ static unsigned int defer_sync_state_count = 1;  static DEFINE_MUTEX(fwnode_link_lock);  static bool fw_devlink_is_permissive(void);  static bool fw_devlink_drv_reg_done; +static bool fw_devlink_best_effort;  /**   * fwnode_link_add - Create a link between two fwnode_handles. @@ -976,6 +977,12 @@ static void device_links_missing_supplier(struct device *dev)  	}  } +static bool dev_is_best_effort(struct device *dev) +{ +	return (fw_devlink_best_effort && dev->can_match) || +		(dev->fwnode && (dev->fwnode->flags & FWNODE_FLAG_BEST_EFFORT)); +} +  /**   * device_links_check_suppliers - Check presence of supplier drivers.   * @dev: Consumer device. @@ -995,7 +1002,7 @@ static void device_links_missing_supplier(struct device *dev)  int device_links_check_suppliers(struct device *dev)  {  	struct device_link *link; -	int ret = 0; +	int ret = 0, fwnode_ret = 0;  	struct fwnode_handle *sup_fw;  	/* @@ -1008,12 +1015,17 @@ int device_links_check_suppliers(struct device *dev)  		sup_fw = list_first_entry(&dev->fwnode->suppliers,  					  struct fwnode_link,  					  c_hook)->supplier; -		dev_err_probe(dev, -EPROBE_DEFER, "wait for supplier %pfwP\n", -			      sup_fw); -		mutex_unlock(&fwnode_link_lock); -		return -EPROBE_DEFER; +		if (!dev_is_best_effort(dev)) { +			fwnode_ret = -EPROBE_DEFER; +			dev_err_probe(dev, -EPROBE_DEFER, +				    "wait for supplier %pfwP\n", sup_fw); +		} else { +			fwnode_ret = -EAGAIN; +		}  	}  	mutex_unlock(&fwnode_link_lock); +	if (fwnode_ret == -EPROBE_DEFER) +		return fwnode_ret;  	device_links_write_lock(); @@ -1023,6 +1035,14 @@ int device_links_check_suppliers(struct device *dev)  		if (link->status != DL_STATE_AVAILABLE &&  		    !(link->flags & DL_FLAG_SYNC_STATE_ONLY)) { + +			if (dev_is_best_effort(dev) && +			    link->flags & DL_FLAG_INFERRED && +			    !link->supplier->can_match) { +				ret = -EAGAIN; +				continue; +			} +  			device_links_missing_supplier(dev);  			dev_err_probe(dev, -EPROBE_DEFER,  				      "supplier %s not ready\n", @@ -1035,7 +1055,8 @@ int device_links_check_suppliers(struct device *dev)  	dev->links.status = DL_DEV_PROBING;  	device_links_write_unlock(); -	return ret; + +	return ret ? ret : fwnode_ret;  }  /** @@ -1300,6 +1321,18 @@ void device_links_driver_bound(struct device *dev)  			 * save to drop the managed link completely.  			 */  			device_link_drop_managed(link); +		} else if (dev_is_best_effort(dev) && +			   link->flags & DL_FLAG_INFERRED && +			   link->status != DL_STATE_CONSUMER_PROBE && +			   !link->supplier->can_match) { +			/* +			 * When dev_is_best_effort() is true, we ignore device +			 * links to suppliers that don't have a driver.  If the +			 * consumer device still managed to probe, there's no +			 * point in maintaining a device link in a weird state +			 * (consumer probed before supplier). So delete it. +			 */ +			device_link_drop_managed(link);  		} else {  			WARN_ON(link->status != DL_STATE_CONSUMER_PROBE);  			WRITE_ONCE(link->status, DL_STATE_ACTIVE); @@ -1592,7 +1625,7 @@ static int __init fw_devlink_setup(char *arg)  }  early_param("fw_devlink", fw_devlink_setup); -static bool fw_devlink_strict; +static bool fw_devlink_strict = true;  static int __init fw_devlink_strict_setup(char *arg)  {  	return strtobool(arg, &fw_devlink_strict); @@ -1666,6 +1699,62 @@ void fw_devlink_drivers_done(void)  	device_links_write_unlock();  } +/** + * wait_for_init_devices_probe - Try to probe any device needed for init + * + * 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 temporarily try and probe all devices that have + * a driver even if some of their suppliers haven't been added or don't have + * drivers. + * + * The drivers can then decide which of the suppliers are optional vs mandatory + * and probe the device if possible. By the time this function returns, all such + * "best effort" probes are guaranteed to be completed. If a device successfully + * probes in this mode, we delete all fw_devlink discovered dependencies of that + * device where the supplier hasn't yet probed successfully because they have to + * be optional dependencies. + * + * Any devices that didn't successfully probe go back to being treated as if + * this function was never called. + * + * This also means that some devices that aren't needed for init and could have + * waited for their optional supplier to probe (when the supplier's module is + * loaded later on) would end up probing prematurely with limited functionality. + * So call this function only when boot would fail without it. + */ +void __init wait_for_init_devices_probe(void) +{ +	if (!fw_devlink_flags || fw_devlink_is_permissive()) +		return; + +	/* +	 * Wait for all ongoing probes to finish so that the "best effort" is +	 * only applied to devices that can't probe otherwise. +	 */ +	wait_for_device_probe(); + +	pr_info("Trying to probe devices needed for running init ...\n"); +	fw_devlink_best_effort = true; +	driver_deferred_probe_trigger(); + +	/* +	 * Wait for all "best effort" probes to finish before going back to +	 * normal enforcement. +	 */ +	wait_for_device_probe(); +	fw_devlink_best_effort = false; +} +  static void fw_devlink_unblock_consumers(struct device *dev)  {  	struct device_link *link; @@ -3843,6 +3932,26 @@ struct device *device_find_child_by_name(struct device *parent,  }  EXPORT_SYMBOL_GPL(device_find_child_by_name); +static int match_any(struct device *dev, void *unused) +{ +	return 1; +} + +/** + * device_find_any_child - device iterator for locating a child device, if any. + * @parent: parent struct device + * + * This is similar to the device_find_child() function above, but it + * returns a reference to a child device, if any. + * + * NOTE: you will need to drop the reference with put_device() after use. + */ +struct device *device_find_any_child(struct device *parent) +{ +	return device_find_child(parent, NULL, match_any); +} +EXPORT_SYMBOL_GPL(device_find_any_child); +  int __init devices_init(void)  {  	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);  | 
