diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2007-10-18 06:04:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-18 17:37:20 -0400 |
commit | 9cd9a0058dd35268b24fa16795a92c800f4086d4 (patch) | |
tree | 2525a95542eaa16a9aea865e8078cf66f3274e18 | |
parent | c7e0831d385d620a58d95b25e4afa9b643f9a411 (diff) |
Hibernation: Enter platform hibernation state in a consistent way
Make hibernation_platform_enter() execute the enter-a-sleep-state sequence
instead of the mixed shutdown-with-entering-S4 thing.
Replace the shutting down of devices done by kernel_shutdown_prepare(), before
entering the ACPI S4 sleep state, with suspending them and the shutting down
of sysdevs with calling device_power_down(PMSG_SUSPEND) (just like before
entering S1 or S3, but the target state is now S4). Also, disable the
nonboot CPUs before entering the sleep state (S4), which generally always is a
good idea.
This is known to fix the "double disk spin down during hibernation" on some
machines, eg. HPC nx6325 (ref. http://lkml.org/lkml/2007/8/7/316 and the
following thread). Moreover, it has been reported to make
/sys/class/rtc/rtc0/wakealarm work correctly with hibernation for some users.
It also generally causes the hibernation state (ACPI S4) to be entered faster.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | kernel/power/disk.c | 63 |
1 files changed, 46 insertions, 17 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index e50f4da18fd5..8b15f777010a 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -278,21 +278,50 @@ int hibernation_platform_enter(void) | |||
278 | { | 278 | { |
279 | int error; | 279 | int error; |
280 | 280 | ||
281 | if (hibernation_ops) { | 281 | if (!hibernation_ops) |
282 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); | 282 | return -ENOSYS; |
283 | /* | 283 | |
284 | * We have cancelled the power transition by running | 284 | /* |
285 | * hibernation_ops->finish() before saving the image, so we | 285 | * We have cancelled the power transition by running |
286 | * should let the firmware know that we're going to enter the | 286 | * hibernation_ops->finish() before saving the image, so we should let |
287 | * sleep state after all | 287 | * the firmware know that we're going to enter the sleep state after all |
288 | */ | 288 | */ |
289 | error = hibernation_ops->prepare(); | 289 | error = hibernation_ops->start(); |
290 | sysdev_shutdown(); | 290 | if (error) |
291 | if (!error) | 291 | return error; |
292 | error = hibernation_ops->enter(); | 292 | |
293 | } else { | 293 | suspend_console(); |
294 | error = -ENOSYS; | 294 | error = device_suspend(PMSG_SUSPEND); |
295 | if (error) | ||
296 | goto Resume_console; | ||
297 | |||
298 | error = hibernation_ops->prepare(); | ||
299 | if (error) | ||
300 | goto Resume_devices; | ||
301 | |||
302 | error = disable_nonboot_cpus(); | ||
303 | if (error) | ||
304 | goto Finish; | ||
305 | |||
306 | local_irq_disable(); | ||
307 | error = device_power_down(PMSG_SUSPEND); | ||
308 | if (!error) { | ||
309 | hibernation_ops->enter(); | ||
310 | /* We should never get here */ | ||
311 | while (1); | ||
295 | } | 312 | } |
313 | local_irq_enable(); | ||
314 | |||
315 | /* | ||
316 | * We don't need to reenable the nonboot CPUs or resume consoles, since | ||
317 | * the system is going to be halted anyway. | ||
318 | */ | ||
319 | Finish: | ||
320 | hibernation_ops->finish(); | ||
321 | Resume_devices: | ||
322 | device_resume(); | ||
323 | Resume_console: | ||
324 | resume_console(); | ||
296 | return error; | 325 | return error; |
297 | } | 326 | } |
298 | 327 | ||
@@ -309,14 +338,14 @@ static void power_down(void) | |||
309 | case HIBERNATION_TEST: | 338 | case HIBERNATION_TEST: |
310 | case HIBERNATION_TESTPROC: | 339 | case HIBERNATION_TESTPROC: |
311 | break; | 340 | break; |
312 | case HIBERNATION_SHUTDOWN: | ||
313 | kernel_power_off(); | ||
314 | break; | ||
315 | case HIBERNATION_REBOOT: | 341 | case HIBERNATION_REBOOT: |
316 | kernel_restart(NULL); | 342 | kernel_restart(NULL); |
317 | break; | 343 | break; |
318 | case HIBERNATION_PLATFORM: | 344 | case HIBERNATION_PLATFORM: |
319 | hibernation_platform_enter(); | 345 | hibernation_platform_enter(); |
346 | case HIBERNATION_SHUTDOWN: | ||
347 | kernel_power_off(); | ||
348 | break; | ||
320 | } | 349 | } |
321 | kernel_halt(); | 350 | kernel_halt(); |
322 | /* | 351 | /* |