Merge branch 'pm-sleep'
* pm-sleep: PM / Hibernate: Use bool for boolean fields of struct snapshot_data PM / Sleep: Detect device suspend/resume lockup and log event
This commit is contained in:
		
				commit
				
					
						6e0ca95aa3
					
				
			
		
					 3 changed files with 99 additions and 10 deletions
				
			
		| 
						 | 
					@ -30,6 +30,8 @@
 | 
				
			||||||
#include <linux/suspend.h>
 | 
					#include <linux/suspend.h>
 | 
				
			||||||
#include <trace/events/power.h>
 | 
					#include <trace/events/power.h>
 | 
				
			||||||
#include <linux/cpuidle.h>
 | 
					#include <linux/cpuidle.h>
 | 
				
			||||||
 | 
					#include <linux/timer.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "../base.h"
 | 
					#include "../base.h"
 | 
				
			||||||
#include "power.h"
 | 
					#include "power.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -390,6 +392,71 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev,
 | 
				
			||||||
	return error;
 | 
						return error;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_DPM_WATCHDOG
 | 
				
			||||||
 | 
					struct dpm_watchdog {
 | 
				
			||||||
 | 
						struct device		*dev;
 | 
				
			||||||
 | 
						struct task_struct	*tsk;
 | 
				
			||||||
 | 
						struct timer_list	timer;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \
 | 
				
			||||||
 | 
						struct dpm_watchdog wd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * dpm_watchdog_handler - Driver suspend / resume watchdog handler.
 | 
				
			||||||
 | 
					 * @data: Watchdog object address.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Called when a driver has timed out suspending or resuming.
 | 
				
			||||||
 | 
					 * There's not much we can do here to recover so panic() to
 | 
				
			||||||
 | 
					 * capture a crash-dump in pstore.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void dpm_watchdog_handler(unsigned long data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dpm_watchdog *wd = (void *)data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev_emerg(wd->dev, "**** DPM device timeout ****\n");
 | 
				
			||||||
 | 
						show_stack(wd->tsk, NULL);
 | 
				
			||||||
 | 
						panic("%s %s: unrecoverable failure\n",
 | 
				
			||||||
 | 
							dev_driver_string(wd->dev), dev_name(wd->dev));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * dpm_watchdog_set - Enable pm watchdog for given device.
 | 
				
			||||||
 | 
					 * @wd: Watchdog. Must be allocated on the stack.
 | 
				
			||||||
 | 
					 * @dev: Device to handle.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct timer_list *timer = &wd->timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wd->dev = dev;
 | 
				
			||||||
 | 
						wd->tsk = current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						init_timer_on_stack(timer);
 | 
				
			||||||
 | 
						/* use same timeout value for both suspend and resume */
 | 
				
			||||||
 | 
						timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_TIMEOUT;
 | 
				
			||||||
 | 
						timer->function = dpm_watchdog_handler;
 | 
				
			||||||
 | 
						timer->data = (unsigned long)wd;
 | 
				
			||||||
 | 
						add_timer(timer);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * dpm_watchdog_clear - Disable suspend/resume watchdog.
 | 
				
			||||||
 | 
					 * @wd: Watchdog to disable.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void dpm_watchdog_clear(struct dpm_watchdog *wd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct timer_list *timer = &wd->timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						del_timer_sync(timer);
 | 
				
			||||||
 | 
						destroy_timer_on_stack(timer);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define DECLARE_DPM_WATCHDOG_ON_STACK(wd)
 | 
				
			||||||
 | 
					#define dpm_watchdog_set(x, y)
 | 
				
			||||||
 | 
					#define dpm_watchdog_clear(x)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*------------------------- Resume routines -------------------------*/
 | 
					/*------------------------- Resume routines -------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -576,6 +643,7 @@ static int device_resume(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 = 0;
 | 
						int error = 0;
 | 
				
			||||||
 | 
						DECLARE_DPM_WATCHDOG_ON_STACK(wd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TRACE_DEVICE(dev);
 | 
						TRACE_DEVICE(dev);
 | 
				
			||||||
	TRACE_RESUME(0);
 | 
						TRACE_RESUME(0);
 | 
				
			||||||
| 
						 | 
					@ -584,6 +652,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
 | 
				
			||||||
		goto Complete;
 | 
							goto Complete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dpm_wait(dev->parent, async);
 | 
						dpm_wait(dev->parent, async);
 | 
				
			||||||
 | 
						dpm_watchdog_set(&wd, dev);
 | 
				
			||||||
	device_lock(dev);
 | 
						device_lock(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
| 
						 | 
					@ -642,6 +711,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 Unlock:
 | 
					 Unlock:
 | 
				
			||||||
	device_unlock(dev);
 | 
						device_unlock(dev);
 | 
				
			||||||
 | 
						dpm_watchdog_clear(&wd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 Complete:
 | 
					 Complete:
 | 
				
			||||||
	complete_all(&dev->power.completion);
 | 
						complete_all(&dev->power.completion);
 | 
				
			||||||
| 
						 | 
					@ -1060,6 +1130,7 @@ static int __device_suspend(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 = 0;
 | 
						int error = 0;
 | 
				
			||||||
 | 
						DECLARE_DPM_WATCHDOG_ON_STACK(wd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dpm_wait_for_children(dev, async);
 | 
						dpm_wait_for_children(dev, async);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1083,6 +1154,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
 | 
				
			||||||
	if (dev->power.syscore)
 | 
						if (dev->power.syscore)
 | 
				
			||||||
		goto Complete;
 | 
							goto Complete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dpm_watchdog_set(&wd, dev);
 | 
				
			||||||
	device_lock(dev);
 | 
						device_lock(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dev->pm_domain) {
 | 
						if (dev->pm_domain) {
 | 
				
			||||||
| 
						 | 
					@ -1139,6 +1211,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	device_unlock(dev);
 | 
						device_unlock(dev);
 | 
				
			||||||
 | 
						dpm_watchdog_clear(&wd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 Complete:
 | 
					 Complete:
 | 
				
			||||||
	complete_all(&dev->power.completion);
 | 
						complete_all(&dev->power.completion);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -178,6 +178,22 @@ config PM_SLEEP_DEBUG
 | 
				
			||||||
	def_bool y
 | 
						def_bool y
 | 
				
			||||||
	depends on PM_DEBUG && PM_SLEEP
 | 
						depends on PM_DEBUG && PM_SLEEP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config DPM_WATCHDOG
 | 
				
			||||||
 | 
						bool "Device suspend/resume watchdog"
 | 
				
			||||||
 | 
						depends on PM_DEBUG && PSTORE
 | 
				
			||||||
 | 
						---help---
 | 
				
			||||||
 | 
						  Sets up a watchdog timer to capture drivers that are
 | 
				
			||||||
 | 
						  locked up attempting to suspend/resume a device.
 | 
				
			||||||
 | 
						  A detected lockup causes system panic with message
 | 
				
			||||||
 | 
						  captured in pstore device for inspection in subsequent
 | 
				
			||||||
 | 
						  boot session.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config DPM_WATCHDOG_TIMEOUT
 | 
				
			||||||
 | 
						int "Watchdog timeout in seconds"
 | 
				
			||||||
 | 
						range 1 120
 | 
				
			||||||
 | 
						default 12
 | 
				
			||||||
 | 
						depends on DPM_WATCHDOG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config PM_TRACE
 | 
					config PM_TRACE
 | 
				
			||||||
	bool
 | 
						bool
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,9 +36,9 @@ static struct snapshot_data {
 | 
				
			||||||
	struct snapshot_handle handle;
 | 
						struct snapshot_handle handle;
 | 
				
			||||||
	int swap;
 | 
						int swap;
 | 
				
			||||||
	int mode;
 | 
						int mode;
 | 
				
			||||||
	char frozen;
 | 
						bool frozen;
 | 
				
			||||||
	char ready;
 | 
						bool ready;
 | 
				
			||||||
	char platform_support;
 | 
						bool platform_support;
 | 
				
			||||||
	bool free_bitmaps;
 | 
						bool free_bitmaps;
 | 
				
			||||||
} snapshot_state;
 | 
					} snapshot_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -93,9 +93,9 @@ static int snapshot_open(struct inode *inode, struct file *filp)
 | 
				
			||||||
	if (error)
 | 
						if (error)
 | 
				
			||||||
		atomic_inc(&snapshot_device_available);
 | 
							atomic_inc(&snapshot_device_available);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data->frozen = 0;
 | 
						data->frozen = false;
 | 
				
			||||||
	data->ready = 0;
 | 
						data->ready = false;
 | 
				
			||||||
	data->platform_support = 0;
 | 
						data->platform_support = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 Unlock:
 | 
					 Unlock:
 | 
				
			||||||
	unlock_system_sleep();
 | 
						unlock_system_sleep();
 | 
				
			||||||
| 
						 | 
					@ -229,7 +229,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 | 
				
			||||||
		if (error)
 | 
							if (error)
 | 
				
			||||||
			thaw_processes();
 | 
								thaw_processes();
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			data->frozen = 1;
 | 
								data->frozen = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -240,7 +240,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 | 
				
			||||||
		free_basic_memory_bitmaps();
 | 
							free_basic_memory_bitmaps();
 | 
				
			||||||
		data->free_bitmaps = false;
 | 
							data->free_bitmaps = false;
 | 
				
			||||||
		thaw_processes();
 | 
							thaw_processes();
 | 
				
			||||||
		data->frozen = 0;
 | 
							data->frozen = false;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case SNAPSHOT_CREATE_IMAGE:
 | 
						case SNAPSHOT_CREATE_IMAGE:
 | 
				
			||||||
| 
						 | 
					@ -270,7 +270,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 | 
				
			||||||
	case SNAPSHOT_FREE:
 | 
						case SNAPSHOT_FREE:
 | 
				
			||||||
		swsusp_free();
 | 
							swsusp_free();
 | 
				
			||||||
		memset(&data->handle, 0, sizeof(struct snapshot_handle));
 | 
							memset(&data->handle, 0, sizeof(struct snapshot_handle));
 | 
				
			||||||
		data->ready = 0;
 | 
							data->ready = false;
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * It is necessary to thaw kernel threads here, because
 | 
							 * It is necessary to thaw kernel threads here, because
 | 
				
			||||||
		 * SNAPSHOT_CREATE_IMAGE may be invoked directly after
 | 
							 * SNAPSHOT_CREATE_IMAGE may be invoked directly after
 | 
				
			||||||
| 
						 | 
					@ -334,7 +334,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 | 
				
			||||||
		 * PM_HIBERNATION_PREPARE
 | 
							 * PM_HIBERNATION_PREPARE
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		error = suspend_devices_and_enter(PM_SUSPEND_MEM);
 | 
							error = suspend_devices_and_enter(PM_SUSPEND_MEM);
 | 
				
			||||||
		data->ready = 0;
 | 
							data->ready = false;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case SNAPSHOT_PLATFORM_SUPPORT:
 | 
						case SNAPSHOT_PLATFORM_SUPPORT:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue