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:
Rafael J. Wysocki 2013-10-28 01:28:07 +01:00
commit 6e0ca95aa3
3 changed files with 99 additions and 10 deletions

View File

@ -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);

View File

@ -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

View File

@ -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: