diff options
| author | Rafael J. Wysocki <rjw@sisk.pl> | 2007-10-18 06:04:55 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-18 17:37:20 -0400 |
| commit | c7e0831d385d620a58d95b25e4afa9b643f9a411 (patch) | |
| tree | d3dde7c47276aeb6fad0a1f25fa6b74cc5dacfc1 /kernel/power | |
| parent | efa4d2fb047b25a6be67fe92178a2a78da6b3f6a (diff) | |
Hibernation: Check if ACPI is enabled during restore in the right place
The following scenario leads to total confusion of the platform firmware on
some boxes (eg. HPC nx6325):
* Hibernate with ACPI enabled
* Resume passing "acpi=off" to the boot kernel
To prevent this from happening it's necessary to check if ACPI is enabled (and
enable it if that's not the case) _right_ _after_ control has been transfered
from the boot kernel to the image kernel, before device_power_up() is called
(ie. with interrupts disabled). Enabling ACPI after calling
device_power_up() turns out to be insufficient.
For this reason, introduce new hibernation callback ->leave() that will be
executed before device_power_up() by the restored image kernel. To make it
work, it also is necessary to move swsusp_suspend() from swsusp.c to disk.c
(it's name is changed to "create_image", which is more up to the point).
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>
Diffstat (limited to 'kernel/power')
| -rw-r--r-- | kernel/power/disk.c | 58 | ||||
| -rw-r--r-- | kernel/power/power.h | 1 | ||||
| -rw-r--r-- | kernel/power/swsusp.c | 33 |
3 files changed, 57 insertions, 35 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 555c0f0b2f7e..e50f4da18fd5 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
| @@ -93,6 +93,17 @@ static int platform_pre_snapshot(int platform_mode) | |||
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | /** | 95 | /** |
| 96 | * platform_leave - prepare the machine for switching to the normal mode | ||
| 97 | * of operation using the platform driver (called with interrupts disabled) | ||
| 98 | */ | ||
| 99 | |||
| 100 | static void platform_leave(int platform_mode) | ||
| 101 | { | ||
| 102 | if (platform_mode && hibernation_ops) | ||
| 103 | hibernation_ops->leave(); | ||
| 104 | } | ||
| 105 | |||
| 106 | /** | ||
| 96 | * platform_finish - switch the machine to the normal mode of operation | 107 | * platform_finish - switch the machine to the normal mode of operation |
| 97 | * using the platform driver (must be called after platform_prepare()) | 108 | * using the platform driver (must be called after platform_prepare()) |
| 98 | */ | 109 | */ |
| @@ -129,6 +140,51 @@ static void platform_restore_cleanup(int platform_mode) | |||
| 129 | } | 140 | } |
| 130 | 141 | ||
| 131 | /** | 142 | /** |
| 143 | * create_image - freeze devices that need to be frozen with interrupts | ||
| 144 | * off, create the hibernation image and thaw those devices. Control | ||
| 145 | * reappears in this routine after a restore. | ||
| 146 | */ | ||
| 147 | |||
| 148 | int create_image(int platform_mode) | ||
| 149 | { | ||
| 150 | int error; | ||
| 151 | |||
| 152 | error = arch_prepare_suspend(); | ||
| 153 | if (error) | ||
| 154 | return error; | ||
| 155 | |||
| 156 | local_irq_disable(); | ||
| 157 | /* At this point, device_suspend() has been called, but *not* | ||
| 158 | * device_power_down(). We *must* call device_power_down() now. | ||
| 159 | * Otherwise, drivers for some devices (e.g. interrupt controllers) | ||
| 160 | * become desynchronized with the actual state of the hardware | ||
| 161 | * at resume time, and evil weirdness ensues. | ||
| 162 | */ | ||
| 163 | error = device_power_down(PMSG_FREEZE); | ||
| 164 | if (error) { | ||
| 165 | printk(KERN_ERR "Some devices failed to power down, " | ||
| 166 | KERN_ERR "aborting suspend\n"); | ||
| 167 | goto Enable_irqs; | ||
| 168 | } | ||
| 169 | |||
| 170 | save_processor_state(); | ||
| 171 | error = swsusp_arch_suspend(); | ||
| 172 | if (error) | ||
| 173 | printk(KERN_ERR "Error %d while creating the image\n", error); | ||
| 174 | /* Restore control flow magically appears here */ | ||
| 175 | restore_processor_state(); | ||
| 176 | if (!in_suspend) | ||
| 177 | platform_leave(platform_mode); | ||
| 178 | /* NOTE: device_power_up() is just a resume() for devices | ||
| 179 | * that suspended with irqs off ... no overall powerup. | ||
| 180 | */ | ||
| 181 | device_power_up(); | ||
| 182 | Enable_irqs: | ||
| 183 | local_irq_enable(); | ||
| 184 | return error; | ||
| 185 | } | ||
| 186 | |||
| 187 | /** | ||
| 132 | * hibernation_snapshot - quiesce devices and create the hibernation | 188 | * hibernation_snapshot - quiesce devices and create the hibernation |
| 133 | * snapshot image. | 189 | * snapshot image. |
| 134 | * @platform_mode - if set, use the platform driver, if available, to | 190 | * @platform_mode - if set, use the platform driver, if available, to |
| @@ -163,7 +219,7 @@ int hibernation_snapshot(int platform_mode) | |||
| 163 | if (!error) { | 219 | if (!error) { |
| 164 | if (hibernation_mode != HIBERNATION_TEST) { | 220 | if (hibernation_mode != HIBERNATION_TEST) { |
| 165 | in_suspend = 1; | 221 | in_suspend = 1; |
| 166 | error = swsusp_suspend(); | 222 | error = create_image(platform_mode); |
| 167 | /* Control returns here after successful restore */ | 223 | /* Control returns here after successful restore */ |
| 168 | } else { | 224 | } else { |
| 169 | printk("swsusp debug: Waiting for 5 seconds.\n"); | 225 | printk("swsusp debug: Waiting for 5 seconds.\n"); |
diff --git a/kernel/power/power.h b/kernel/power/power.h index a0204dfc6c4c..195dc4611764 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
| @@ -183,7 +183,6 @@ extern int swsusp_swap_in_use(void); | |||
| 183 | extern int swsusp_check(void); | 183 | extern int swsusp_check(void); |
| 184 | extern int swsusp_shrink_memory(void); | 184 | extern int swsusp_shrink_memory(void); |
| 185 | extern void swsusp_free(void); | 185 | extern void swsusp_free(void); |
| 186 | extern int swsusp_suspend(void); | ||
| 187 | extern int swsusp_resume(void); | 186 | extern int swsusp_resume(void); |
| 188 | extern int swsusp_read(unsigned int *flags_p); | 187 | extern int swsusp_read(unsigned int *flags_p); |
| 189 | extern int swsusp_write(unsigned int flags); | 188 | extern int swsusp_write(unsigned int flags); |
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 5da304c8f1f6..e1722d3155f1 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c | |||
| @@ -270,39 +270,6 @@ int swsusp_shrink_memory(void) | |||
| 270 | return 0; | 270 | return 0; |
| 271 | } | 271 | } |
| 272 | 272 | ||
| 273 | int swsusp_suspend(void) | ||
| 274 | { | ||
| 275 | int error; | ||
| 276 | |||
| 277 | if ((error = arch_prepare_suspend())) | ||
| 278 | return error; | ||
| 279 | |||
| 280 | local_irq_disable(); | ||
| 281 | /* At this point, device_suspend() has been called, but *not* | ||
| 282 | * device_power_down(). We *must* device_power_down() now. | ||
| 283 | * Otherwise, drivers for some devices (e.g. interrupt controllers) | ||
| 284 | * become desynchronized with the actual state of the hardware | ||
| 285 | * at resume time, and evil weirdness ensues. | ||
| 286 | */ | ||
| 287 | if ((error = device_power_down(PMSG_FREEZE))) { | ||
| 288 | printk(KERN_ERR "Some devices failed to power down, aborting suspend\n"); | ||
| 289 | goto Enable_irqs; | ||
| 290 | } | ||
| 291 | |||
| 292 | save_processor_state(); | ||
| 293 | if ((error = swsusp_arch_suspend())) | ||
| 294 | printk(KERN_ERR "Error %d suspending\n", error); | ||
| 295 | /* Restore control flow magically appears here */ | ||
| 296 | restore_processor_state(); | ||
| 297 | /* NOTE: device_power_up() is just a resume() for devices | ||
| 298 | * that suspended with irqs off ... no overall powerup. | ||
| 299 | */ | ||
| 300 | device_power_up(); | ||
| 301 | Enable_irqs: | ||
| 302 | local_irq_enable(); | ||
| 303 | return error; | ||
| 304 | } | ||
| 305 | |||
| 306 | int swsusp_resume(void) | 273 | int swsusp_resume(void) |
| 307 | { | 274 | { |
| 308 | int error; | 275 | int error; |
