vserver 2.0 rc7
[linux-2.6.git] / kernel / power / disk.c
index b9b3f58..02b6764 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
-#include <linux/device.h>
 #include "power.h"
 
 
@@ -25,13 +24,16 @@ extern struct pm_ops * pm_ops;
 
 extern int swsusp_suspend(void);
 extern int swsusp_write(void);
+extern int swsusp_check(void);
 extern int swsusp_read(void);
+extern void swsusp_close(void);
 extern int swsusp_resume(void);
 extern int swsusp_free(void);
 
 
 static int noresume = 0;
 char resume_file[256] = CONFIG_PM_STD_PARTITION;
+dev_t swsusp_resume_device;
 
 /**
  *     power_down - Shut machine down for hibernate.
@@ -51,7 +53,7 @@ static void power_down(suspend_disk_method_t mode)
        local_irq_save(flags);
        switch(mode) {
        case PM_DISK_PLATFORM:
-               device_power_down(PMSG_SUSPEND);
+               device_shutdown();
                error = pm_ops->enter(PM_SUSPEND_DISK);
                break;
        case PM_DISK_SHUTDOWN:
@@ -121,45 +123,54 @@ static void finish(void)
 }
 
 
-static int prepare(void)
+static int prepare_processes(void)
 {
        int error;
 
        pm_prepare_console();
 
        sys_sync();
+
        if (freeze_processes()) {
                error = -EBUSY;
-               goto Thaw;
+               return error;
        }
 
        if (pm_disk_mode == PM_DISK_PLATFORM) {
                if (pm_ops && pm_ops->prepare) {
                        if ((error = pm_ops->prepare(PM_SUSPEND_DISK)))
-                               goto Thaw;
+                               return error;
                }
        }
 
        /* Free memory before shutting down devices. */
        free_some_memory();
 
+       return 0;
+}
+
+static void unprepare_processes(void)
+{
+       enable_nonboot_cpus();
+       thaw_processes();
+       pm_restore_console();
+}
+
+static int prepare_devices(void)
+{
+       int error;
+
        disable_nonboot_cpus();
        if ((error = device_suspend(PMSG_FREEZE))) {
                printk("Some devices failed to suspend\n");
-               goto Finish;
+               platform_finish();
+               enable_nonboot_cpus();
+               return error;
        }
 
        return 0;
- Finish:
-       platform_finish();
- Thaw:
-       enable_nonboot_cpus();
-       thaw_processes();
-       pm_restore_console();
-       return error;
 }
 
-
 /**
  *     pm_suspend_disk - The granpappy of power management.
  *
@@ -173,8 +184,15 @@ int pm_suspend_disk(void)
 {
        int error;
 
-       if ((error = prepare()))
+       error = prepare_processes();
+       if (!error) {
+               error = prepare_devices();
+       }
+
+       if (error) {
+               unprepare_processes();
                return error;
+       }
 
        pr_debug("PM: Attempting to suspend to disk.\n");
        if (pm_disk_mode == PM_DISK_FIRMWARE)
@@ -223,17 +241,28 @@ static int software_resume(void)
                return 0;
        }
 
+       pr_debug("PM: Checking swsusp image.\n");
+
+       if ((error = swsusp_check()))
+               goto Done;
+
+       pr_debug("PM: Preparing processes for restore.\n");
+
+       if ((error = prepare_processes())) {
+               swsusp_close();
+               goto Cleanup;
+       }
+
        pr_debug("PM: Reading swsusp image.\n");
 
        if ((error = swsusp_read()))
-               goto Done;
+               goto Cleanup;
 
-       pr_debug("PM: Preparing system for restore.\n");
+       pr_debug("PM: Preparing devices for restore.\n");
 
-       if ((error = prepare()))
+       if ((error = prepare_devices()))
                goto Free;
 
-       barrier();
        mb();
 
        pr_debug("PM: Restoring saved image.\n");
@@ -242,6 +271,8 @@ static int software_resume(void)
        finish();
  Free:
        swsusp_free();
+ Cleanup:
+       unprepare_processes();
  Done:
        pr_debug("PM: Resume from disk failed.\n");
        return 0;
@@ -329,8 +360,41 @@ static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n)
 
 power_attr(disk);
 
+static ssize_t resume_show(struct subsystem * subsys, char *buf)
+{
+       return sprintf(buf,"%d:%d\n", MAJOR(swsusp_resume_device),
+                      MINOR(swsusp_resume_device));
+}
+
+static ssize_t resume_store(struct subsystem * subsys, const char * buf, size_t n)
+{
+       int len;
+       char *p;
+       unsigned int maj, min;
+       int error = -EINVAL;
+       dev_t res;
+
+       p = memchr(buf, '\n', n);
+       len = p ? p - buf : n;
+
+       if (sscanf(buf, "%u:%u", &maj, &min) == 2) {
+               res = MKDEV(maj,min);
+               if (maj == MAJOR(res) && min == MINOR(res)) {
+                       swsusp_resume_device = res;
+                       printk("Attempting manual resume\n");
+                       noresume = 0;
+                       software_resume();
+               }
+       }
+
+       return error >= 0 ? n : error;
+}
+
+power_attr(resume);
+
 static struct attribute * g[] = {
        &disk_attr.attr,
+       &resume_attr.attr,
        NULL,
 };