aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
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 /drivers/base
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
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/power/main.c73
1 files changed, 73 insertions, 0 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);