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 | |
| 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
| -rw-r--r-- | drivers/base/power/main.c | 73 | ||||
| -rw-r--r-- | kernel/power/Kconfig | 16 | ||||
| -rw-r--r-- | kernel/power/user.c | 20 |
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 | ||
| 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); |
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 | ||
| 181 | config 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 | |||
| 191 | config DPM_WATCHDOG_TIMEOUT | ||
| 192 | int "Watchdog timeout in seconds" | ||
| 193 | range 1 120 | ||
| 194 | default 12 | ||
| 195 | depends on DPM_WATCHDOG | ||
| 196 | |||
| 181 | config PM_TRACE | 197 | config 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: |
