Commit 62c552ccc3eda1198632a4f344aa32623d226bab

Authored by Bojan Smojver
Committed by Rafael J. Wysocki
1 parent 6887a4131d

PM / Hibernate: Enable suspend to both for in-kernel hibernation.

It is often useful to suspend to memory after hibernation image has been
written to disk. If the battery runs out or power is otherwise lost, the
computer will resume from the hibernated image. If not, it will resume
from memory and hibernation image will be discarded.

Signed-off-by: Bojan Smojver <bojan@rexursive.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Showing 4 changed files with 72 additions and 0 deletions Side-by-side Diff

Documentation/power/swsusp.txt
... ... @@ -33,6 +33,11 @@
33 33  
34 34 echo platform > /sys/power/disk; echo disk > /sys/power/state
35 35  
  36 +. If you would like to write hibernation image to swap and then suspend
  37 +to RAM (provided your platform supports it), you can try
  38 +
  39 +echo suspend > /sys/power/disk; echo disk > /sys/power/state
  40 +
36 41 . If you have SATA disks, you'll need recent kernels with SATA suspend
37 42 support. For suspend and resume to work, make sure your disk drivers
38 43 are built into kernel -- not modules. [There's way to make
kernel/power/hibernate.c
... ... @@ -5,6 +5,7 @@
5 5 * Copyright (c) 2003 Open Source Development Lab
6 6 * Copyright (c) 2004 Pavel Machek <pavel@ucw.cz>
7 7 * Copyright (c) 2009 Rafael J. Wysocki, Novell Inc.
  8 + * Copyright (C) 2012 Bojan Smojver <bojan@rexursive.com>
8 9 *
9 10 * This file is released under the GPLv2.
10 11 */
... ... @@ -46,6 +47,9 @@
46 47 HIBERNATION_PLATFORM,
47 48 HIBERNATION_SHUTDOWN,
48 49 HIBERNATION_REBOOT,
  50 +#ifdef CONFIG_SUSPEND
  51 + HIBERNATION_SUSPEND,
  52 +#endif
49 53 /* keep last */
50 54 __HIBERNATION_AFTER_LAST
51 55 };
... ... @@ -574,6 +578,10 @@
574 578 */
575 579 static void power_down(void)
576 580 {
  581 +#ifdef CONFIG_SUSPEND
  582 + int error;
  583 +#endif
  584 +
577 585 switch (hibernation_mode) {
578 586 case HIBERNATION_REBOOT:
579 587 kernel_restart(NULL);
... ... @@ -583,6 +591,25 @@
583 591 case HIBERNATION_SHUTDOWN:
584 592 kernel_power_off();
585 593 break;
  594 +#ifdef CONFIG_SUSPEND
  595 + case HIBERNATION_SUSPEND:
  596 + error = suspend_devices_and_enter(PM_SUSPEND_MEM);
  597 + if (error) {
  598 + if (hibernation_ops)
  599 + hibernation_mode = HIBERNATION_PLATFORM;
  600 + else
  601 + hibernation_mode = HIBERNATION_SHUTDOWN;
  602 + power_down();
  603 + }
  604 + /*
  605 + * Restore swap signature.
  606 + */
  607 + error = swsusp_unmark();
  608 + if (error)
  609 + printk(KERN_ERR "PM: Swap will be unusable! "
  610 + "Try swapon -a.\n");
  611 + return;
  612 +#endif
586 613 }
587 614 kernel_halt();
588 615 /*
... ... @@ -827,6 +854,9 @@
827 854 [HIBERNATION_PLATFORM] = "platform",
828 855 [HIBERNATION_SHUTDOWN] = "shutdown",
829 856 [HIBERNATION_REBOOT] = "reboot",
  857 +#ifdef CONFIG_SUSPEND
  858 + [HIBERNATION_SUSPEND] = "suspend",
  859 +#endif
830 860 };
831 861  
832 862 /*
... ... @@ -867,6 +897,9 @@
867 897 switch (i) {
868 898 case HIBERNATION_SHUTDOWN:
869 899 case HIBERNATION_REBOOT:
  900 +#ifdef CONFIG_SUSPEND
  901 + case HIBERNATION_SUSPEND:
  902 +#endif
870 903 break;
871 904 case HIBERNATION_PLATFORM:
872 905 if (hibernation_ops)
... ... @@ -907,6 +940,9 @@
907 940 switch (mode) {
908 941 case HIBERNATION_SHUTDOWN:
909 942 case HIBERNATION_REBOOT:
  943 +#ifdef CONFIG_SUSPEND
  944 + case HIBERNATION_SUSPEND:
  945 +#endif
910 946 hibernation_mode = mode;
911 947 break;
912 948 case HIBERNATION_PLATFORM:
kernel/power/power.h
... ... @@ -156,6 +156,9 @@
156 156 extern int swsusp_read(unsigned int *flags_p);
157 157 extern int swsusp_write(unsigned int flags);
158 158 extern void swsusp_close(fmode_t);
  159 +#ifdef CONFIG_SUSPEND
  160 +extern int swsusp_unmark(void);
  161 +#endif
159 162  
160 163 /* kernel/power/block_io.c */
161 164 extern struct block_device *hib_resume_bdev;
... ... @@ -1472,6 +1472,34 @@
1472 1472 blkdev_put(hib_resume_bdev, mode);
1473 1473 }
1474 1474  
  1475 +/**
  1476 + * swsusp_unmark - Unmark swsusp signature in the resume device
  1477 + */
  1478 +
  1479 +#ifdef CONFIG_SUSPEND
  1480 +int swsusp_unmark(void)
  1481 +{
  1482 + int error;
  1483 +
  1484 + hib_bio_read_page(swsusp_resume_block, swsusp_header, NULL);
  1485 + if (!memcmp(HIBERNATE_SIG,swsusp_header->sig, 10)) {
  1486 + memcpy(swsusp_header->sig,swsusp_header->orig_sig, 10);
  1487 + error = hib_bio_write_page(swsusp_resume_block,
  1488 + swsusp_header, NULL);
  1489 + } else {
  1490 + printk(KERN_ERR "PM: Cannot find swsusp signature!\n");
  1491 + error = -ENODEV;
  1492 + }
  1493 +
  1494 + /*
  1495 + * We just returned from suspend, we don't need the image any more.
  1496 + */
  1497 + free_all_swap_pages(root_swap);
  1498 +
  1499 + return error;
  1500 +}
  1501 +#endif
  1502 +
1475 1503 static int swsusp_header_init(void)
1476 1504 {
1477 1505 swsusp_header = (struct swsusp_header*) __get_free_page(GFP_KERNEL);