diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2007-10-18 03:04:56 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-18 14:37:20 -0700 |
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) { int error; - if (hibernation_ops) { - kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); - /* - * We have cancelled the power transition by running - * hibernation_ops->finish() before saving the image, so we - * should let the firmware know that we're going to enter the - * sleep state after all - */ - error = hibernation_ops->prepare(); - sysdev_shutdown(); - if (!error) - error = hibernation_ops->enter(); - } else { - error = -ENOSYS; + if (!hibernation_ops) + return -ENOSYS; + + /* + * We have cancelled the power transition by running + * hibernation_ops->finish() before saving the image, so we should let + * the firmware know that we're going to enter the sleep state after all + */ + error = hibernation_ops->start(); + if (error) + return error; + + suspend_console(); + error = device_suspend(PMSG_SUSPEND); + if (error) + goto Resume_console; + + error = hibernation_ops->prepare(); + if (error) + goto Resume_devices; + + error = disable_nonboot_cpus(); + if (error) + goto Finish; + + local_irq_disable(); + error = device_power_down(PMSG_SUSPEND); + if (!error) { + hibernation_ops->enter(); + /* We should never get here */ + while (1); } + local_irq_enable(); + + /* + * We don't need to reenable the nonboot CPUs or resume consoles, since + * the system is going to be halted anyway. + */ + Finish: + hibernation_ops->finish(); + Resume_devices: + device_resume(); + Resume_console: + resume_console(); return error; } @@ -309,14 +338,14 @@ static void power_down(void) case HIBERNATION_TEST: case HIBERNATION_TESTPROC: break; - case HIBERNATION_SHUTDOWN: - kernel_power_off(); - break; case HIBERNATION_REBOOT: kernel_restart(NULL); break; case HIBERNATION_PLATFORM: hibernation_platform_enter(); + case HIBERNATION_SHUTDOWN: + kernel_power_off(); + break; } kernel_halt(); /* |