diff options
Diffstat (limited to 'kernel/power/disk.c')
-rw-r--r-- | kernel/power/disk.c | 204 |
1 files changed, 155 insertions, 49 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index b138b431e271..d09da0895174 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -54,8 +54,8 @@ static struct platform_hibernation_ops *hibernation_ops; | |||
54 | 54 | ||
55 | void hibernation_set_ops(struct platform_hibernation_ops *ops) | 55 | void hibernation_set_ops(struct platform_hibernation_ops *ops) |
56 | { | 56 | { |
57 | if (ops && !(ops->start && ops->pre_snapshot && ops->finish | 57 | if (ops && !(ops->begin && ops->end && ops->pre_snapshot |
58 | && ops->prepare && ops->enter && ops->pre_restore | 58 | && ops->prepare && ops->finish && ops->enter && ops->pre_restore |
59 | && ops->restore_cleanup)) { | 59 | && ops->restore_cleanup)) { |
60 | WARN_ON(1); | 60 | WARN_ON(1); |
61 | return; | 61 | return; |
@@ -70,15 +70,55 @@ void hibernation_set_ops(struct platform_hibernation_ops *ops) | |||
70 | mutex_unlock(&pm_mutex); | 70 | mutex_unlock(&pm_mutex); |
71 | } | 71 | } |
72 | 72 | ||
73 | #ifdef CONFIG_PM_DEBUG | ||
74 | static void hibernation_debug_sleep(void) | ||
75 | { | ||
76 | printk(KERN_INFO "hibernation debug: Waiting for 5 seconds.\n"); | ||
77 | mdelay(5000); | ||
78 | } | ||
79 | |||
80 | static int hibernation_testmode(int mode) | ||
81 | { | ||
82 | if (hibernation_mode == mode) { | ||
83 | hibernation_debug_sleep(); | ||
84 | return 1; | ||
85 | } | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static int hibernation_test(int level) | ||
90 | { | ||
91 | if (pm_test_level == level) { | ||
92 | hibernation_debug_sleep(); | ||
93 | return 1; | ||
94 | } | ||
95 | return 0; | ||
96 | } | ||
97 | #else /* !CONFIG_PM_DEBUG */ | ||
98 | static int hibernation_testmode(int mode) { return 0; } | ||
99 | static int hibernation_test(int level) { return 0; } | ||
100 | #endif /* !CONFIG_PM_DEBUG */ | ||
101 | |||
73 | /** | 102 | /** |
74 | * platform_start - tell the platform driver that we're starting | 103 | * platform_begin - tell the platform driver that we're starting |
75 | * hibernation | 104 | * hibernation |
76 | */ | 105 | */ |
77 | 106 | ||
78 | static int platform_start(int platform_mode) | 107 | static int platform_begin(int platform_mode) |
79 | { | 108 | { |
80 | return (platform_mode && hibernation_ops) ? | 109 | return (platform_mode && hibernation_ops) ? |
81 | hibernation_ops->start() : 0; | 110 | hibernation_ops->begin() : 0; |
111 | } | ||
112 | |||
113 | /** | ||
114 | * platform_end - tell the platform driver that we've entered the | ||
115 | * working state | ||
116 | */ | ||
117 | |||
118 | static void platform_end(int platform_mode) | ||
119 | { | ||
120 | if (platform_mode && hibernation_ops) | ||
121 | hibernation_ops->end(); | ||
82 | } | 122 | } |
83 | 123 | ||
84 | /** | 124 | /** |
@@ -162,19 +202,25 @@ int create_image(int platform_mode) | |||
162 | */ | 202 | */ |
163 | error = device_power_down(PMSG_FREEZE); | 203 | error = device_power_down(PMSG_FREEZE); |
164 | if (error) { | 204 | if (error) { |
165 | printk(KERN_ERR "Some devices failed to power down, " | 205 | printk(KERN_ERR "PM: Some devices failed to power down, " |
166 | KERN_ERR "aborting suspend\n"); | 206 | "aborting hibernation\n"); |
167 | goto Enable_irqs; | 207 | goto Enable_irqs; |
168 | } | 208 | } |
169 | 209 | ||
210 | if (hibernation_test(TEST_CORE)) | ||
211 | goto Power_up; | ||
212 | |||
213 | in_suspend = 1; | ||
170 | save_processor_state(); | 214 | save_processor_state(); |
171 | error = swsusp_arch_suspend(); | 215 | error = swsusp_arch_suspend(); |
172 | if (error) | 216 | if (error) |
173 | printk(KERN_ERR "Error %d while creating the image\n", error); | 217 | printk(KERN_ERR "PM: Error %d creating hibernation image\n", |
218 | error); | ||
174 | /* Restore control flow magically appears here */ | 219 | /* Restore control flow magically appears here */ |
175 | restore_processor_state(); | 220 | restore_processor_state(); |
176 | if (!in_suspend) | 221 | if (!in_suspend) |
177 | platform_leave(platform_mode); | 222 | platform_leave(platform_mode); |
223 | Power_up: | ||
178 | /* NOTE: device_power_up() is just a resume() for devices | 224 | /* NOTE: device_power_up() is just a resume() for devices |
179 | * that suspended with irqs off ... no overall powerup. | 225 | * that suspended with irqs off ... no overall powerup. |
180 | */ | 226 | */ |
@@ -202,36 +248,90 @@ int hibernation_snapshot(int platform_mode) | |||
202 | if (error) | 248 | if (error) |
203 | return error; | 249 | return error; |
204 | 250 | ||
205 | error = platform_start(platform_mode); | 251 | error = platform_begin(platform_mode); |
206 | if (error) | 252 | if (error) |
207 | return error; | 253 | goto Close; |
208 | 254 | ||
209 | suspend_console(); | 255 | suspend_console(); |
210 | error = device_suspend(PMSG_FREEZE); | 256 | error = device_suspend(PMSG_FREEZE); |
211 | if (error) | 257 | if (error) |
212 | goto Resume_console; | 258 | goto Resume_console; |
213 | 259 | ||
214 | error = platform_pre_snapshot(platform_mode); | 260 | if (hibernation_test(TEST_DEVICES)) |
215 | if (error) | ||
216 | goto Resume_devices; | 261 | goto Resume_devices; |
217 | 262 | ||
263 | error = platform_pre_snapshot(platform_mode); | ||
264 | if (error || hibernation_test(TEST_PLATFORM)) | ||
265 | goto Finish; | ||
266 | |||
218 | error = disable_nonboot_cpus(); | 267 | error = disable_nonboot_cpus(); |
219 | if (!error) { | 268 | if (!error) { |
220 | if (hibernation_mode != HIBERNATION_TEST) { | 269 | if (hibernation_test(TEST_CPUS)) |
221 | in_suspend = 1; | 270 | goto Enable_cpus; |
222 | error = create_image(platform_mode); | 271 | |
223 | /* Control returns here after successful restore */ | 272 | if (hibernation_testmode(HIBERNATION_TEST)) |
224 | } else { | 273 | goto Enable_cpus; |
225 | printk("swsusp debug: Waiting for 5 seconds.\n"); | 274 | |
226 | mdelay(5000); | 275 | error = create_image(platform_mode); |
227 | } | 276 | /* Control returns here after successful restore */ |
228 | } | 277 | } |
278 | Enable_cpus: | ||
229 | enable_nonboot_cpus(); | 279 | enable_nonboot_cpus(); |
230 | Resume_devices: | 280 | Finish: |
231 | platform_finish(platform_mode); | 281 | platform_finish(platform_mode); |
282 | Resume_devices: | ||
232 | device_resume(); | 283 | device_resume(); |
233 | Resume_console: | 284 | Resume_console: |
234 | resume_console(); | 285 | resume_console(); |
286 | Close: | ||
287 | platform_end(platform_mode); | ||
288 | return error; | ||
289 | } | ||
290 | |||
291 | /** | ||
292 | * resume_target_kernel - prepare devices that need to be suspended with | ||
293 | * interrupts off, restore the contents of highmem that have not been | ||
294 | * restored yet from the image and run the low level code that will restore | ||
295 | * the remaining contents of memory and switch to the just restored target | ||
296 | * kernel. | ||
297 | */ | ||
298 | |||
299 | static int resume_target_kernel(void) | ||
300 | { | ||
301 | int error; | ||
302 | |||
303 | local_irq_disable(); | ||
304 | error = device_power_down(PMSG_PRETHAW); | ||
305 | if (error) { | ||
306 | printk(KERN_ERR "PM: Some devices failed to power down, " | ||
307 | "aborting resume\n"); | ||
308 | goto Enable_irqs; | ||
309 | } | ||
310 | /* We'll ignore saved state, but this gets preempt count (etc) right */ | ||
311 | save_processor_state(); | ||
312 | error = restore_highmem(); | ||
313 | if (!error) { | ||
314 | error = swsusp_arch_resume(); | ||
315 | /* | ||
316 | * The code below is only ever reached in case of a failure. | ||
317 | * Otherwise execution continues at place where | ||
318 | * swsusp_arch_suspend() was called | ||
319 | */ | ||
320 | BUG_ON(!error); | ||
321 | /* This call to restore_highmem() undos the previous one */ | ||
322 | restore_highmem(); | ||
323 | } | ||
324 | /* | ||
325 | * The only reason why swsusp_arch_resume() can fail is memory being | ||
326 | * very tight, so we have to free it as soon as we can to avoid | ||
327 | * subsequent failures | ||
328 | */ | ||
329 | swsusp_free(); | ||
330 | restore_processor_state(); | ||
331 | touch_softlockup_watchdog(); | ||
332 | device_power_up(); | ||
333 | Enable_irqs: | ||
334 | local_irq_enable(); | ||
235 | return error; | 335 | return error; |
236 | } | 336 | } |
237 | 337 | ||
@@ -258,7 +358,7 @@ int hibernation_restore(int platform_mode) | |||
258 | if (!error) { | 358 | if (!error) { |
259 | error = disable_nonboot_cpus(); | 359 | error = disable_nonboot_cpus(); |
260 | if (!error) | 360 | if (!error) |
261 | error = swsusp_resume(); | 361 | error = resume_target_kernel(); |
262 | enable_nonboot_cpus(); | 362 | enable_nonboot_cpus(); |
263 | } | 363 | } |
264 | platform_restore_cleanup(platform_mode); | 364 | platform_restore_cleanup(platform_mode); |
@@ -286,9 +386,9 @@ int hibernation_platform_enter(void) | |||
286 | * hibernation_ops->finish() before saving the image, so we should let | 386 | * hibernation_ops->finish() before saving the image, so we should let |
287 | * the firmware know that we're going to enter the sleep state after all | 387 | * the firmware know that we're going to enter the sleep state after all |
288 | */ | 388 | */ |
289 | error = hibernation_ops->start(); | 389 | error = hibernation_ops->begin(); |
290 | if (error) | 390 | if (error) |
291 | return error; | 391 | goto Close; |
292 | 392 | ||
293 | suspend_console(); | 393 | suspend_console(); |
294 | error = device_suspend(PMSG_SUSPEND); | 394 | error = device_suspend(PMSG_SUSPEND); |
@@ -322,6 +422,8 @@ int hibernation_platform_enter(void) | |||
322 | device_resume(); | 422 | device_resume(); |
323 | Resume_console: | 423 | Resume_console: |
324 | resume_console(); | 424 | resume_console(); |
425 | Close: | ||
426 | hibernation_ops->end(); | ||
325 | return error; | 427 | return error; |
326 | } | 428 | } |
327 | 429 | ||
@@ -352,24 +454,17 @@ static void power_down(void) | |||
352 | * Valid image is on the disk, if we continue we risk serious data | 454 | * Valid image is on the disk, if we continue we risk serious data |
353 | * corruption after resume. | 455 | * corruption after resume. |
354 | */ | 456 | */ |
355 | printk(KERN_CRIT "Please power me down manually\n"); | 457 | printk(KERN_CRIT "PM: Please power down manually\n"); |
356 | while(1); | 458 | while(1); |
357 | } | 459 | } |
358 | 460 | ||
359 | static void unprepare_processes(void) | ||
360 | { | ||
361 | thaw_processes(); | ||
362 | pm_restore_console(); | ||
363 | } | ||
364 | |||
365 | static int prepare_processes(void) | 461 | static int prepare_processes(void) |
366 | { | 462 | { |
367 | int error = 0; | 463 | int error = 0; |
368 | 464 | ||
369 | pm_prepare_console(); | ||
370 | if (freeze_processes()) { | 465 | if (freeze_processes()) { |
371 | error = -EBUSY; | 466 | error = -EBUSY; |
372 | unprepare_processes(); | 467 | thaw_processes(); |
373 | } | 468 | } |
374 | return error; | 469 | return error; |
375 | } | 470 | } |
@@ -389,6 +484,7 @@ int hibernate(void) | |||
389 | goto Unlock; | 484 | goto Unlock; |
390 | } | 485 | } |
391 | 486 | ||
487 | pm_prepare_console(); | ||
392 | error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); | 488 | error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); |
393 | if (error) | 489 | if (error) |
394 | goto Exit; | 490 | goto Exit; |
@@ -398,7 +494,7 @@ int hibernate(void) | |||
398 | if (error) | 494 | if (error) |
399 | goto Exit; | 495 | goto Exit; |
400 | 496 | ||
401 | printk("Syncing filesystems ... "); | 497 | printk(KERN_INFO "PM: Syncing filesystems ... "); |
402 | sys_sync(); | 498 | sys_sync(); |
403 | printk("done.\n"); | 499 | printk("done.\n"); |
404 | 500 | ||
@@ -406,11 +502,12 @@ int hibernate(void) | |||
406 | if (error) | 502 | if (error) |
407 | goto Finish; | 503 | goto Finish; |
408 | 504 | ||
409 | if (hibernation_mode == HIBERNATION_TESTPROC) { | 505 | if (hibernation_test(TEST_FREEZER)) |
410 | printk("swsusp debug: Waiting for 5 seconds.\n"); | ||
411 | mdelay(5000); | ||
412 | goto Thaw; | 506 | goto Thaw; |
413 | } | 507 | |
508 | if (hibernation_testmode(HIBERNATION_TESTPROC)) | ||
509 | goto Thaw; | ||
510 | |||
414 | error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); | 511 | error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); |
415 | if (in_suspend && !error) { | 512 | if (in_suspend && !error) { |
416 | unsigned int flags = 0; | 513 | unsigned int flags = 0; |
@@ -427,11 +524,12 @@ int hibernate(void) | |||
427 | swsusp_free(); | 524 | swsusp_free(); |
428 | } | 525 | } |
429 | Thaw: | 526 | Thaw: |
430 | unprepare_processes(); | 527 | thaw_processes(); |
431 | Finish: | 528 | Finish: |
432 | free_basic_memory_bitmaps(); | 529 | free_basic_memory_bitmaps(); |
433 | Exit: | 530 | Exit: |
434 | pm_notifier_call_chain(PM_POST_HIBERNATION); | 531 | pm_notifier_call_chain(PM_POST_HIBERNATION); |
532 | pm_restore_console(); | ||
435 | atomic_inc(&snapshot_device_available); | 533 | atomic_inc(&snapshot_device_available); |
436 | Unlock: | 534 | Unlock: |
437 | mutex_unlock(&pm_mutex); | 535 | mutex_unlock(&pm_mutex); |
@@ -473,22 +571,23 @@ static int software_resume(void) | |||
473 | return -ENOENT; | 571 | return -ENOENT; |
474 | } | 572 | } |
475 | swsusp_resume_device = name_to_dev_t(resume_file); | 573 | swsusp_resume_device = name_to_dev_t(resume_file); |
476 | pr_debug("swsusp: Resume From Partition %s\n", resume_file); | 574 | pr_debug("PM: Resume from partition %s\n", resume_file); |
477 | } else { | 575 | } else { |
478 | pr_debug("swsusp: Resume From Partition %d:%d\n", | 576 | pr_debug("PM: Resume from partition %d:%d\n", |
479 | MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device)); | 577 | MAJOR(swsusp_resume_device), |
578 | MINOR(swsusp_resume_device)); | ||
480 | } | 579 | } |
481 | 580 | ||
482 | if (noresume) { | 581 | if (noresume) { |
483 | /** | 582 | /** |
484 | * FIXME: If noresume is specified, we need to find the partition | 583 | * FIXME: If noresume is specified, we need to find the |
485 | * and reset it back to normal swap space. | 584 | * partition and reset it back to normal swap space. |
486 | */ | 585 | */ |
487 | mutex_unlock(&pm_mutex); | 586 | mutex_unlock(&pm_mutex); |
488 | return 0; | 587 | return 0; |
489 | } | 588 | } |
490 | 589 | ||
491 | pr_debug("PM: Checking swsusp image.\n"); | 590 | pr_debug("PM: Checking hibernation image.\n"); |
492 | error = swsusp_check(); | 591 | error = swsusp_check(); |
493 | if (error) | 592 | if (error) |
494 | goto Unlock; | 593 | goto Unlock; |
@@ -499,6 +598,11 @@ static int software_resume(void) | |||
499 | goto Unlock; | 598 | goto Unlock; |
500 | } | 599 | } |
501 | 600 | ||
601 | pm_prepare_console(); | ||
602 | error = pm_notifier_call_chain(PM_RESTORE_PREPARE); | ||
603 | if (error) | ||
604 | goto Finish; | ||
605 | |||
502 | error = create_basic_memory_bitmaps(); | 606 | error = create_basic_memory_bitmaps(); |
503 | if (error) | 607 | if (error) |
504 | goto Finish; | 608 | goto Finish; |
@@ -510,7 +614,7 @@ static int software_resume(void) | |||
510 | goto Done; | 614 | goto Done; |
511 | } | 615 | } |
512 | 616 | ||
513 | pr_debug("PM: Reading swsusp image.\n"); | 617 | pr_debug("PM: Reading hibernation image.\n"); |
514 | 618 | ||
515 | error = swsusp_read(&flags); | 619 | error = swsusp_read(&flags); |
516 | if (!error) | 620 | if (!error) |
@@ -518,10 +622,12 @@ static int software_resume(void) | |||
518 | 622 | ||
519 | printk(KERN_ERR "PM: Restore failed, recovering.\n"); | 623 | printk(KERN_ERR "PM: Restore failed, recovering.\n"); |
520 | swsusp_free(); | 624 | swsusp_free(); |
521 | unprepare_processes(); | 625 | thaw_processes(); |
522 | Done: | 626 | Done: |
523 | free_basic_memory_bitmaps(); | 627 | free_basic_memory_bitmaps(); |
524 | Finish: | 628 | Finish: |
629 | pm_notifier_call_chain(PM_POST_RESTORE); | ||
630 | pm_restore_console(); | ||
525 | atomic_inc(&snapshot_device_available); | 631 | atomic_inc(&snapshot_device_available); |
526 | /* For success case, the suspend path will release the lock */ | 632 | /* For success case, the suspend path will release the lock */ |
527 | Unlock: | 633 | Unlock: |
@@ -636,7 +742,7 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, | |||
636 | error = -EINVAL; | 742 | error = -EINVAL; |
637 | 743 | ||
638 | if (!error) | 744 | if (!error) |
639 | pr_debug("PM: suspend-to-disk mode set to '%s'\n", | 745 | pr_debug("PM: Hibernation mode set to '%s'\n", |
640 | hibernation_modes[mode]); | 746 | hibernation_modes[mode]); |
641 | mutex_unlock(&pm_mutex); | 747 | mutex_unlock(&pm_mutex); |
642 | return error ? error : n; | 748 | return error ? error : n; |
@@ -668,7 +774,7 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr, | |||
668 | mutex_lock(&pm_mutex); | 774 | mutex_lock(&pm_mutex); |
669 | swsusp_resume_device = res; | 775 | swsusp_resume_device = res; |
670 | mutex_unlock(&pm_mutex); | 776 | mutex_unlock(&pm_mutex); |
671 | printk("Attempting manual resume\n"); | 777 | printk(KERN_INFO "PM: Starting manual resume from disk\n"); |
672 | noresume = 0; | 778 | noresume = 0; |
673 | software_resume(); | 779 | software_resume(); |
674 | ret = n; | 780 | ret = n; |