diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-10-27 20:28:07 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-10-27 20:28:07 -0400 |
commit | 6e0ca95aa3c83c47d13f9f400bfaaa853d0b224b (patch) | |
tree | dcdf4bf395ed43d287d3e0a2aff649f38d63b1cb /drivers/base | |
parent | 400fc45273d53837390bf1a24f5930c1ad9de61e (diff) | |
parent | 7bc9b1cffc95675a957e870d258e95d43dcbba0b (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.c | 73 |
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 | ||
396 | struct 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 | */ | ||
413 | static 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 | */ | ||
428 | static 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 | */ | ||
447 | static 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); |