PM / Hibernate: Enable suspend to both for in-kernel hibernation.
authorBojan Smojver <bojan@rexursive.com>
Fri, 15 Jun 2012 22:09:58 +0000 (00:09 +0200)
committerRafael J. Wysocki <rjw@sisk.pl>
Sun, 1 Jul 2012 11:31:22 +0000 (13:31 +0200)
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>
Documentation/power/swsusp.txt
kernel/power/hibernate.c
kernel/power/power.h
kernel/power/swap.c

index ac190cf1963e05e310edf6a96df2466f01eb1355..92341b84250dfaf137b89376c115597a4bd9c222 100644 (file)
@@ -33,6 +33,11 @@ echo shutdown > /sys/power/disk; echo disk > /sys/power/state
 
 echo platform > /sys/power/disk; echo disk > /sys/power/state
 
+. If you would like to write hibernation image to swap and then suspend
+to RAM (provided your platform supports it), you can try
+
+echo suspend > /sys/power/disk; echo disk > /sys/power/state
+
 . If you have SATA disks, you'll need recent kernels with SATA suspend
 support. For suspend and resume to work, make sure your disk drivers
 are built into kernel -- not modules. [There's way to make
index 8b53db38a279e50a1fe3a8719752fe245201e7a0..21ad3fe3164f0e74f28e3a2075fb0145ae8e6561 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright (c) 2003 Open Source Development Lab
  * Copyright (c) 2004 Pavel Machek <pavel@ucw.cz>
  * Copyright (c) 2009 Rafael J. Wysocki, Novell Inc.
+ * Copyright (C) 2012 Bojan Smojver <bojan@rexursive.com>
  *
  * This file is released under the GPLv2.
  */
@@ -46,6 +47,9 @@ enum {
        HIBERNATION_PLATFORM,
        HIBERNATION_SHUTDOWN,
        HIBERNATION_REBOOT,
+#ifdef CONFIG_SUSPEND
+       HIBERNATION_SUSPEND,
+#endif
        /* keep last */
        __HIBERNATION_AFTER_LAST
 };
@@ -574,6 +578,10 @@ int hibernation_platform_enter(void)
  */
 static void power_down(void)
 {
+#ifdef CONFIG_SUSPEND
+       int error;
+#endif
+
        switch (hibernation_mode) {
        case HIBERNATION_REBOOT:
                kernel_restart(NULL);
@@ -583,6 +591,25 @@ static void power_down(void)
        case HIBERNATION_SHUTDOWN:
                kernel_power_off();
                break;
+#ifdef CONFIG_SUSPEND
+       case HIBERNATION_SUSPEND:
+               error = suspend_devices_and_enter(PM_SUSPEND_MEM);
+               if (error) {
+                       if (hibernation_ops)
+                               hibernation_mode = HIBERNATION_PLATFORM;
+                       else
+                               hibernation_mode = HIBERNATION_SHUTDOWN;
+                       power_down();
+               }
+               /*
+                * Restore swap signature.
+                */
+               error = swsusp_unmark();
+               if (error)
+                       printk(KERN_ERR "PM: Swap will be unusable! "
+                                       "Try swapon -a.\n");
+               return;
+#endif
        }
        kernel_halt();
        /*
@@ -827,6 +854,9 @@ static const char * const hibernation_modes[] = {
        [HIBERNATION_PLATFORM]  = "platform",
        [HIBERNATION_SHUTDOWN]  = "shutdown",
        [HIBERNATION_REBOOT]    = "reboot",
+#ifdef CONFIG_SUSPEND
+       [HIBERNATION_SUSPEND]   = "suspend",
+#endif
 };
 
 /*
@@ -867,6 +897,9 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,
                switch (i) {
                case HIBERNATION_SHUTDOWN:
                case HIBERNATION_REBOOT:
+#ifdef CONFIG_SUSPEND
+               case HIBERNATION_SUSPEND:
+#endif
                        break;
                case HIBERNATION_PLATFORM:
                        if (hibernation_ops)
@@ -907,6 +940,9 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
                switch (mode) {
                case HIBERNATION_SHUTDOWN:
                case HIBERNATION_REBOOT:
+#ifdef CONFIG_SUSPEND
+               case HIBERNATION_SUSPEND:
+#endif
                        hibernation_mode = mode;
                        break;
                case HIBERNATION_PLATFORM:
index b0bd4beaebfe7caa84c4c3b4ded37d3e47acacd6..7d4b7ffb3c1d4371f19e81fe83c67ea88ac83759 100644 (file)
@@ -156,6 +156,9 @@ extern void swsusp_free(void);
 extern int swsusp_read(unsigned int *flags_p);
 extern int swsusp_write(unsigned int flags);
 extern void swsusp_close(fmode_t);
+#ifdef CONFIG_SUSPEND
+extern int swsusp_unmark(void);
+#endif
 
 /* kernel/power/block_io.c */
 extern struct block_device *hib_resume_bdev;
index 11e22c068e8b25ab8c6ba1127be955d2d4d33e39..83d505142b00d39a15447779f7b6343f87b87090 100644 (file)
@@ -1472,6 +1472,34 @@ void swsusp_close(fmode_t mode)
        blkdev_put(hib_resume_bdev, mode);
 }
 
+/**
+ *      swsusp_unmark - Unmark swsusp signature in the resume device
+ */
+
+#ifdef CONFIG_SUSPEND
+int swsusp_unmark(void)
+{
+       int error;
+
+       hib_bio_read_page(swsusp_resume_block, swsusp_header, NULL);
+       if (!memcmp(HIBERNATE_SIG,swsusp_header->sig, 10)) {
+               memcpy(swsusp_header->sig,swsusp_header->orig_sig, 10);
+               error = hib_bio_write_page(swsusp_resume_block,
+                                       swsusp_header, NULL);
+       } else {
+               printk(KERN_ERR "PM: Cannot find swsusp signature!\n");
+               error = -ENODEV;
+       }
+
+       /*
+        * We just returned from suspend, we don't need the image any more.
+        */
+       free_all_swap_pages(root_swap);
+
+       return error;
+}
+#endif
+
 static int swsusp_header_init(void)
 {
        swsusp_header = (struct swsusp_header*) __get_free_page(GFP_KERNEL);