diff options
Diffstat (limited to 'drivers/base/core.c')
| -rw-r--r-- | drivers/base/core.c | 44 | 
1 files changed, 32 insertions, 12 deletions
| diff --git a/drivers/base/core.c b/drivers/base/core.c index 67d39a90b45c..05d414e9e8a4 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -50,6 +50,7 @@ static DEFINE_MUTEX(wfs_lock);  static LIST_HEAD(deferred_sync);  static unsigned int defer_sync_state_count = 1;  static unsigned int defer_fw_devlink_count; +static LIST_HEAD(deferred_fw_devlink);  static DEFINE_MUTEX(defer_fw_devlink_lock);  static bool fw_devlink_is_permissive(void); @@ -754,11 +755,11 @@ static void __device_links_queue_sync_state(struct device *dev,  	 */  	dev->state_synced = true; -	if (WARN_ON(!list_empty(&dev->links.defer_sync))) +	if (WARN_ON(!list_empty(&dev->links.defer_hook)))  		return;  	get_device(dev); -	list_add_tail(&dev->links.defer_sync, list); +	list_add_tail(&dev->links.defer_hook, list);  }  /** @@ -776,8 +777,8 @@ static void device_links_flush_sync_list(struct list_head *list,  {  	struct device *dev, *tmp; -	list_for_each_entry_safe(dev, tmp, list, links.defer_sync) { -		list_del_init(&dev->links.defer_sync); +	list_for_each_entry_safe(dev, tmp, list, links.defer_hook) { +		list_del_init(&dev->links.defer_hook);  		if (dev != dont_lock_dev)  			device_lock(dev); @@ -815,12 +816,12 @@ void device_links_supplier_sync_state_resume(void)  	if (defer_sync_state_count)  		goto out; -	list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_sync) { +	list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_hook) {  		/*  		 * Delete from deferred_sync list before queuing it to -		 * sync_list because defer_sync is used for both lists. +		 * sync_list because defer_hook is used for both lists.  		 */ -		list_del_init(&dev->links.defer_sync); +		list_del_init(&dev->links.defer_hook);  		__device_links_queue_sync_state(dev, &sync_list);  	}  out: @@ -838,8 +839,8 @@ late_initcall(sync_state_resume_initcall);  static void __device_links_supplier_defer_sync(struct device *sup)  { -	if (list_empty(&sup->links.defer_sync) && dev_has_sync_state(sup)) -		list_add_tail(&sup->links.defer_sync, &deferred_sync); +	if (list_empty(&sup->links.defer_hook) && dev_has_sync_state(sup)) +		list_add_tail(&sup->links.defer_hook, &deferred_sync);  }  static void device_link_drop_managed(struct device_link *link) @@ -1052,7 +1053,7 @@ void device_links_driver_cleanup(struct device *dev)  		WRITE_ONCE(link->status, DL_STATE_DORMANT);  	} -	list_del_init(&dev->links.defer_sync); +	list_del_init(&dev->links.defer_hook);  	__device_links_no_driver(dev);  	device_links_write_unlock(); @@ -1244,6 +1245,12 @@ static void fw_devlink_link_device(struct device *dev)  			fw_ret = -EAGAIN;  	} else {  		fw_ret = -ENODEV; +		/* +		 * defer_hook is not used to add device to deferred_sync list +		 * until device is bound. Since deferred fw devlink also blocks +		 * probing, same list hook can be used for deferred_fw_devlink. +		 */ +		list_add_tail(&dev->links.defer_hook, &deferred_fw_devlink);  	}  	if (fw_ret == -ENODEV) @@ -1312,6 +1319,9 @@ void fw_devlink_pause(void)   */  void fw_devlink_resume(void)  { +	struct device *dev, *tmp; +	LIST_HEAD(probe_list); +  	mutex_lock(&defer_fw_devlink_lock);  	if (!defer_fw_devlink_count) {  		WARN(true, "Unmatched fw_devlink pause/resume!"); @@ -1323,9 +1333,19 @@ void fw_devlink_resume(void)  		goto out;  	device_link_add_missing_supplier_links(); -	driver_deferred_probe_force_trigger(); +	list_splice_tail_init(&deferred_fw_devlink, &probe_list);  out:  	mutex_unlock(&defer_fw_devlink_lock); + +	/* +	 * bus_probe_device() can cause new devices to get added and they'll +	 * try to grab defer_fw_devlink_lock. So, this needs to be done outside +	 * the defer_fw_devlink_lock. +	 */ +	list_for_each_entry_safe(dev, tmp, &probe_list, links.defer_hook) { +		list_del_init(&dev->links.defer_hook); +		bus_probe_device(dev); +	}  }  /* Device links support end. */ @@ -2172,7 +2192,7 @@ void device_initialize(struct device *dev)  	INIT_LIST_HEAD(&dev->links.consumers);  	INIT_LIST_HEAD(&dev->links.suppliers);  	INIT_LIST_HEAD(&dev->links.needs_suppliers); -	INIT_LIST_HEAD(&dev->links.defer_sync); +	INIT_LIST_HEAD(&dev->links.defer_hook);  	dev->links.status = DL_DEV_NO_DRIVER;  }  EXPORT_SYMBOL_GPL(device_initialize); |