aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-10-27 20:28:07 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-10-27 20:28:07 -0400
commit6e0ca95aa3c83c47d13f9f400bfaaa853d0b224b (patch)
treedcdf4bf395ed43d287d3e0a2aff649f38d63b1cb
parent400fc45273d53837390bf1a24f5930c1ad9de61e (diff)
parent7bc9b1cffc95675a957e870d258e95d43dcbba0b (diff)
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
-rw-r--r--drivers/base/power/main.c73
-rw-r--r--kernel/power/Kconfig16
-rw-r--r--kernel/power/user.c20
3 files changed, 99 insertions, 10 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 9f098a82cf04..ee039afe9078 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -30,6 +30,8 @@
30#include <linux/suspend.h> 30#include <linux/suspend.h>
31#include <trace/events/power.h> 31#include <trace/events/power.h>
32#include <linux/cpuidle.h> 32#include <linux/cpuidle.h>
33#include <linux/timer.h>
34
33#include "../base.h" 35#include "../base.h"
34#include "power.h" 36#include "power.h"
35 37
@@ -390,6 +392,71 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev,
390 return error; 392 return error;
391} 393}
392 394
395#ifdef CONFIG_DPM_WATCHDOG
396struct dpm_watchdog {
397 struct device *dev;
398 struct task_struct *tsk;
399 struct timer_list timer;
400};
401
402#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \
403 struct dpm_watchdog wd
404
405/**
406 * dpm_watchdog_handler - Driver suspend / resume watchdog handler.
407 * @data: Watchdog object address.
408 *
409 * Called when a driver has timed out suspending or resuming.
410 * There's not much we can do here to recover so panic() to
411 * capture a crash-dump in pstore.
412 */
413static void dpm_watchdog_handler(unsigned long data)
414{
415 struct dpm_watchdog *wd = (void *)data;
416
417 dev_emerg(wd->dev, "**** DPM device timeout ****\n");
418 show_stack(wd->tsk, NULL);
419 panic("%s %s: unrecoverable failure\n",
420 dev_driver_string(wd->dev), dev_name(wd->dev));
421}
422
423/**
424 * dpm_watchdog_set - Enable pm watchdog for given device.
425 * @wd: Watchdog. Must be allocated on the stack.
426 * @dev: Device to handle.
427 */
428static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev)
429{
430 struct timer_list *timer = &wd->timer;
431
432 wd->dev = dev;
433 wd->tsk = current;
434
435 init_timer_on_stack(timer);
436 /* use same timeout value for both suspend and resume */
437 timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_TIMEOUT;
438 timer->function = dpm_watchdog_handler;
439 timer->data = (unsigned long)wd;
440 add_timer(timer);
441}
442
443/**
444 * dpm_watchdog_clear - Disable suspend/resume watchdog.
445 * @wd: Watchdog to disable.
446 */
447static void dpm_watchdog_clear(struct dpm_watchdog *wd)
448{
449 struct timer_list *timer = &wd->timer;
450
451 del_timer_sync(timer);
452 destroy_timer_on_stack(timer);
453}
454#else
455#define DECLARE_DPM_WATCHDOG_ON_STACK(wd)
456#define dpm_watchdog_set(x, y)
457#define dpm_watchdog_clear(x)
458#endif
459
393/*------------------------- Resume routines -------------------------*/ 460/*------------------------- Resume routines -------------------------*/
394 461
395/** 462/**
@@ -576,6 +643,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
576 pm_callback_t callback = NULL; 643 pm_callback_t callback = NULL;
577 char *info = NULL; 644 char *info = NULL;
578 int error = 0; 645 int error = 0;
646 DECLARE_DPM_WATCHDOG_ON_STACK(wd);
579 647
580 TRACE_DEVICE(dev); 648 TRACE_DEVICE(dev);
581 TRACE_RESUME(0); 649 TRACE_RESUME(0);
@@ -584,6 +652,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
584 goto Complete; 652 goto Complete;
585 653
586 dpm_wait(dev->parent, async); 654 dpm_wait(dev->parent, async);
655 dpm_watchdog_set(&wd, dev);
587 device_lock(dev); 656 device_lock(dev);
588 657
589 /* 658 /*
@@ -642,6 +711,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
642 711
643 Unlock: 712 Unlock:
644 device_unlock(dev); 713 device_unlock(dev);
714 dpm_watchdog_clear(&wd);
645 715
646 Complete: 716 Complete:
647 complete_all(&dev->power.completion); 717 complete_all(&dev->power.completion);
@@ -1060,6 +1130,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
1060 pm_callback_t callback = NULL; 1130 pm_callback_t callback = NULL;
1061 char *info = NULL; 1131 char *info = NULL;
1062 int error = 0; 1132 int error = 0;
1133 DECLARE_DPM_WATCHDOG_ON_STACK(wd);
1063 1134
1064 dpm_wait_for_children(dev, async); 1135 dpm_wait_for_children(dev, async);
1065 1136
@@ -1083,6 +1154,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
1083 if (dev->power.syscore) 1154 if (dev->power.syscore)
1084 goto Complete; 1155 goto Complete;
1085 1156
1157 dpm_watchdog_set(&wd, dev);
1086 device_lock(dev); 1158 device_lock(dev);
1087 1159
1088 if (dev->pm_domain) { 1160 if (dev->pm_domain) {
@@ -1139,6 +1211,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
1139 } 1211 }
1140 1212
1141 device_unlock(dev); 1213 device_unlock(dev);
1214 dpm_watchdog_clear(&wd);
1142 1215
1143 Complete: 1216 Complete:
1144 complete_all(&dev->power.completion); 1217 complete_all(&dev->power.completion);
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index d444c4e834f4..2fac9cc79b3d 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -178,6 +178,22 @@ config PM_SLEEP_DEBUG
178 def_bool y 178 def_bool y
179 depends on PM_DEBUG && PM_SLEEP 179 depends on PM_DEBUG && PM_SLEEP
180 180
181config DPM_WATCHDOG
182 bool "Device suspend/resume watchdog"
183 depends on PM_DEBUG && PSTORE
184 ---help---
185 Sets up a watchdog timer to capture drivers that are
186 locked up attempting to suspend/resume a device.
187 A detected lockup causes system panic with message
188 captured in pstore device for inspection in subsequent
189 boot session.
190
191config DPM_WATCHDOG_TIMEOUT
192 int "Watchdog timeout in seconds"
193 range 1 120
194 default 12
195 depends on DPM_WATCHDOG
196
181config PM_TRACE 197config PM_TRACE
182 bool 198 bool
183 help 199 help
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 957f06164ad1..24850270c802 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -36,9 +36,9 @@ static struct snapshot_data {
36 struct snapshot_handle handle; 36 struct snapshot_handle handle;
37 int swap; 37 int swap;
38 int mode; 38 int mode;
39 char frozen; 39 bool frozen;
40 char ready; 40 bool ready;
41 char platform_support; 41 bool platform_support;
42 bool free_bitmaps; 42 bool free_bitmaps;
43} snapshot_state; 43} snapshot_state;
44 44
@@ -93,9 +93,9 @@ static int snapshot_open(struct inode *inode, struct file *filp)
93 if (error) 93 if (error)
94 atomic_inc(&snapshot_device_available); 94 atomic_inc(&snapshot_device_available);
95 95
96 data->frozen = 0; 96 data->frozen = false;
97 data->ready = 0; 97 data->ready = false;
98 data->platform_support = 0; 98 data->platform_support = false;
99 99
100 Unlock: 100 Unlock:
101 unlock_system_sleep(); 101 unlock_system_sleep();
@@ -229,7 +229,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
229 if (error) 229 if (error)
230 thaw_processes(); 230 thaw_processes();
231 else 231 else
232 data->frozen = 1; 232 data->frozen = true;
233 233
234 break; 234 break;
235 235
@@ -240,7 +240,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
240 free_basic_memory_bitmaps(); 240 free_basic_memory_bitmaps();
241 data->free_bitmaps = false; 241 data->free_bitmaps = false;
242 thaw_processes(); 242 thaw_processes();
243 data->frozen = 0; 243 data->frozen = false;
244 break; 244 break;
245 245
246 case SNAPSHOT_CREATE_IMAGE: 246 case SNAPSHOT_CREATE_IMAGE:
@@ -270,7 +270,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
270 case SNAPSHOT_FREE: 270 case SNAPSHOT_FREE:
271 swsusp_free(); 271 swsusp_free();
272 memset(&data->handle, 0, sizeof(struct snapshot_handle)); 272 memset(&data->handle, 0, sizeof(struct snapshot_handle));
273 data->ready = 0; 273 data->ready = false;
274 /* 274 /*
275 * It is necessary to thaw kernel threads here, because 275 * It is necessary to thaw kernel threads here, because
276 * SNAPSHOT_CREATE_IMAGE may be invoked directly after 276 * SNAPSHOT_CREATE_IMAGE may be invoked directly after
@@ -334,7 +334,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
334 * PM_HIBERNATION_PREPARE 334 * PM_HIBERNATION_PREPARE
335 */ 335 */
336 error = suspend_devices_and_enter(PM_SUSPEND_MEM); 336 error = suspend_devices_and_enter(PM_SUSPEND_MEM);
337 data->ready = 0; 337 data->ready = false;
338 break; 338 break;
339 339
340 case SNAPSHOT_PLATFORM_SUPPORT: 340 case SNAPSHOT_PLATFORM_SUPPORT: