mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	PM / sleep: Asynchronous threads for suspend_noirq
In analogy with commits5af84b8270and97df8c1299, using asynchronous threads can improve the overall suspend_noirq time significantly. This patch is for suspend_noirq phase. Signed-off-by: Chuansheng Liu <chuansheng.liu@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
		
							parent
							
								
									9e5e7910df
								
							
						
					
					
						commit
						28b6fd6e37
					
				
					 1 changed files with 57 additions and 11 deletions
				
			
		| 
						 | 
					@ -990,14 +990,24 @@ static pm_message_t resume_event(pm_message_t sleep_state)
 | 
				
			||||||
 * The driver of @dev will not receive interrupts while this function is being
 | 
					 * The driver of @dev will not receive interrupts while this function is being
 | 
				
			||||||
 * executed.
 | 
					 * executed.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int device_suspend_noirq(struct device *dev, pm_message_t state)
 | 
					static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	pm_callback_t callback = NULL;
 | 
						pm_callback_t callback = NULL;
 | 
				
			||||||
	char *info = NULL;
 | 
						char *info = NULL;
 | 
				
			||||||
	int error;
 | 
						int error = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (async_error)
 | 
				
			||||||
 | 
							goto Complete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pm_wakeup_pending()) {
 | 
				
			||||||
 | 
							async_error = -EBUSY;
 | 
				
			||||||
 | 
							goto Complete;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dev->power.syscore)
 | 
						if (dev->power.syscore)
 | 
				
			||||||
		return 0;
 | 
							goto Complete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dpm_wait_for_children(dev, async);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dev->pm_domain) {
 | 
						if (dev->pm_domain) {
 | 
				
			||||||
		info = "noirq power domain ";
 | 
							info = "noirq power domain ";
 | 
				
			||||||
| 
						 | 
					@ -1021,10 +1031,40 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
 | 
				
			||||||
	error = dpm_run_callback(callback, dev, state, info);
 | 
						error = dpm_run_callback(callback, dev, state, info);
 | 
				
			||||||
	if (!error)
 | 
						if (!error)
 | 
				
			||||||
		dev->power.is_noirq_suspended = true;
 | 
							dev->power.is_noirq_suspended = true;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							async_error = error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Complete:
 | 
				
			||||||
 | 
						complete_all(&dev->power.completion);
 | 
				
			||||||
	return error;
 | 
						return error;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void async_suspend_noirq(void *data, async_cookie_t cookie)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct device *dev = (struct device *)data;
 | 
				
			||||||
 | 
						int error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						error = __device_suspend_noirq(dev, pm_transition, true);
 | 
				
			||||||
 | 
						if (error) {
 | 
				
			||||||
 | 
							dpm_save_failed_dev(dev_name(dev));
 | 
				
			||||||
 | 
							pm_dev_err(dev, pm_transition, " async", error);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						put_device(dev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int device_suspend_noirq(struct device *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						reinit_completion(&dev->power.completion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pm_async_enabled && dev->power.async_suspend) {
 | 
				
			||||||
 | 
							get_device(dev);
 | 
				
			||||||
 | 
							async_schedule(async_suspend_noirq, dev);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return __device_suspend_noirq(dev, pm_transition, false);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices.
 | 
					 * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices.
 | 
				
			||||||
 * @state: PM transition of the system being carried out.
 | 
					 * @state: PM transition of the system being carried out.
 | 
				
			||||||
| 
						 | 
					@ -1040,19 +1080,20 @@ static int dpm_suspend_noirq(pm_message_t state)
 | 
				
			||||||
	cpuidle_pause();
 | 
						cpuidle_pause();
 | 
				
			||||||
	suspend_device_irqs();
 | 
						suspend_device_irqs();
 | 
				
			||||||
	mutex_lock(&dpm_list_mtx);
 | 
						mutex_lock(&dpm_list_mtx);
 | 
				
			||||||
 | 
						pm_transition = state;
 | 
				
			||||||
 | 
						async_error = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (!list_empty(&dpm_late_early_list)) {
 | 
						while (!list_empty(&dpm_late_early_list)) {
 | 
				
			||||||
		struct device *dev = to_device(dpm_late_early_list.prev);
 | 
							struct device *dev = to_device(dpm_late_early_list.prev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		get_device(dev);
 | 
							get_device(dev);
 | 
				
			||||||
		mutex_unlock(&dpm_list_mtx);
 | 
							mutex_unlock(&dpm_list_mtx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		error = device_suspend_noirq(dev, state);
 | 
							error = device_suspend_noirq(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mutex_lock(&dpm_list_mtx);
 | 
							mutex_lock(&dpm_list_mtx);
 | 
				
			||||||
		if (error) {
 | 
							if (error) {
 | 
				
			||||||
			pm_dev_err(dev, state, " noirq", error);
 | 
								pm_dev_err(dev, state, " noirq", error);
 | 
				
			||||||
			suspend_stats.failed_suspend_noirq++;
 | 
					 | 
				
			||||||
			dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
 | 
					 | 
				
			||||||
			dpm_save_failed_dev(dev_name(dev));
 | 
								dpm_save_failed_dev(dev_name(dev));
 | 
				
			||||||
			put_device(dev);
 | 
								put_device(dev);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
| 
						 | 
					@ -1061,16 +1102,21 @@ static int dpm_suspend_noirq(pm_message_t state)
 | 
				
			||||||
			list_move(&dev->power.entry, &dpm_noirq_list);
 | 
								list_move(&dev->power.entry, &dpm_noirq_list);
 | 
				
			||||||
		put_device(dev);
 | 
							put_device(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (pm_wakeup_pending()) {
 | 
							if (async_error)
 | 
				
			||||||
			error = -EBUSY;
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	mutex_unlock(&dpm_list_mtx);
 | 
						mutex_unlock(&dpm_list_mtx);
 | 
				
			||||||
	if (error)
 | 
						async_synchronize_full();
 | 
				
			||||||
 | 
						if (!error)
 | 
				
			||||||
 | 
							error = async_error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (error) {
 | 
				
			||||||
 | 
							suspend_stats.failed_suspend_noirq++;
 | 
				
			||||||
 | 
							dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
 | 
				
			||||||
		dpm_resume_noirq(resume_event(state));
 | 
							dpm_resume_noirq(resume_event(state));
 | 
				
			||||||
	else
 | 
						} else {
 | 
				
			||||||
		dpm_show_time(starttime, state, "noirq");
 | 
							dpm_show_time(starttime, state, "noirq");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return error;
 | 
						return error;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue