X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=kernel%2Fpower%2Fdisk.c;h=88fc5d7ac737402c18db0306049b5873effeed55;hb=refs%2Fheads%2Fvserver;hp=312aa169c5665cd379783cca6400e1ec5c7dfac9;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 312aa169c..88fc5d7ac 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -3,6 +3,7 @@ * * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab + * Copyright (c) 2004 Pavel Machek * * This file is released under the GPLv2. * @@ -15,21 +16,35 @@ #include #include #include +#include +#include +#include +#include +#include + #include "power.h" -extern u32 pm_disk_mode; -extern struct pm_ops * pm_ops; +static int noresume = 0; +char resume_file[256] = CONFIG_PM_STD_PARTITION; +dev_t swsusp_resume_device; +sector_t swsusp_resume_block; -extern int swsusp_suspend(void); -extern int swsusp_write(void); -extern int swsusp_read(void); -extern int swsusp_resume(void); -extern int swsusp_free(void); +/** + * platform_prepare - prepare the machine for hibernation using the + * platform driver if so configured and return an error code if it fails + */ +static inline int platform_prepare(void) +{ + int error = 0; -static int noresume = 0; -char resume_file[256] = CONFIG_PM_STD_PARTITION; + if (pm_disk_mode == PM_DISK_PLATFORM) { + if (pm_ops && pm_ops->prepare) + error = pm_ops->prepare(PM_SUSPEND_DISK); + } + return error; +} /** * power_down - Shut machine down for hibernate. @@ -41,57 +56,29 @@ char resume_file[256] = CONFIG_PM_STD_PARTITION; * there ain't no turning back. */ -static int power_down(u32 mode) +static void power_down(suspend_disk_method_t mode) { - unsigned long flags; - int error = 0; - - local_irq_save(flags); switch(mode) { case PM_DISK_PLATFORM: - device_power_down(PM_SUSPEND_DISK); - error = pm_ops->enter(PM_SUSPEND_DISK); - break; + if (pm_ops && pm_ops->enter) { + kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); + pm_ops->enter(PM_SUSPEND_DISK); + break; + } case PM_DISK_SHUTDOWN: - printk("Powering off system\n"); - device_shutdown(); - machine_power_off(); + kernel_power_off(); break; case PM_DISK_REBOOT: - device_shutdown(); - machine_restart(NULL); + kernel_restart(NULL); break; } - machine_halt(); + kernel_halt(); /* Valid image is on the disk, if we continue we risk serious data corruption after resume. */ printk(KERN_CRIT "Please power me down manually\n"); while(1); - return 0; -} - - -static int in_suspend __nosavedata = 0; - - -/** - * free_some_memory - Try to free as much memory as possible - * - * ... but do not OOM-kill anyone - * - * Notice: all userland should be stopped at this point, or - * livelock is possible. - */ - -static void free_some_memory(void) -{ - printk("Freeing memory: "); - while (shrink_all_memory(10000)) - printk("."); - printk("|\n"); } - static inline void platform_finish(void) { if (pm_disk_mode == PM_DISK_PLATFORM) { @@ -100,59 +87,58 @@ static inline void platform_finish(void) } } -static void finish(void) +static int prepare_processes(void) { - device_resume(); - platform_finish(); - enable_nonboot_cpus(); - thaw_processes(); - pm_restore_console(); -} - - -static int prepare(void) -{ - int error; + int error = 0; pm_prepare_console(); - sys_sync(); + error = disable_nonboot_cpus(); + if (error) + goto enable_cpus; + if (freeze_processes()) { error = -EBUSY; - goto Thaw; + goto thaw; } - if (pm_disk_mode == PM_DISK_PLATFORM) { - if (pm_ops && pm_ops->prepare) { - if ((error = pm_ops->prepare(PM_SUSPEND_DISK))) - goto Thaw; - } + if (pm_disk_mode == PM_DISK_TESTPROC) { + printk("swsusp debug: Waiting for 5 seconds.\n"); + mdelay(5000); + goto thaw; } - /* Free memory before shutting down devices. */ - free_some_memory(); + error = platform_prepare(); + if (error) + goto thaw; - disable_nonboot_cpus(); - if ((error = device_suspend(PM_SUSPEND_DISK))) - goto Finish; + /* Free memory before shutting down devices. */ + if (!(error = swsusp_shrink_memory())) + return 0; - return 0; - Finish: platform_finish(); - Thaw: - enable_nonboot_cpus(); + thaw: thaw_processes(); + enable_cpus: + enable_nonboot_cpus(); pm_restore_console(); return error; } +static void unprepare_processes(void) +{ + platform_finish(); + thaw_processes(); + enable_nonboot_cpus(); + pm_restore_console(); +} /** - * pm_suspend_disk - The granpappy of power management. + * pm_suspend_disk - The granpappy of hibernation power management. * * If we're going through the firmware, then get it over with quickly. * - * If not, then call pmdis to do it's thing, then figure out how + * If not, then call swsusp to do its thing, then figure out how * to power down the system. */ @@ -160,12 +146,26 @@ int pm_suspend_disk(void) { int error; - if ((error = prepare())) + error = prepare_processes(); + if (error) return error; - pr_debug("PM: Attempting to suspend to disk.\n"); - if (pm_disk_mode == PM_DISK_FIRMWARE) - return pm_ops->enter(PM_SUSPEND_DISK); + if (pm_disk_mode == PM_DISK_TESTPROC) + return 0; + + suspend_console(); + error = device_suspend(PMSG_FREEZE); + if (error) { + resume_console(); + printk("Some devices failed to suspend\n"); + goto Thaw; + } + + if (pm_disk_mode == PM_DISK_TEST) { + printk("swsusp debug: Waiting for 5 seconds.\n"); + mdelay(5000); + goto Done; + } pr_debug("PM: snapshotting memory.\n"); in_suspend = 1; @@ -173,24 +173,26 @@ int pm_suspend_disk(void) goto Done; if (in_suspend) { + device_resume(); + resume_console(); pr_debug("PM: writing image.\n"); - - /* - * FIXME: Leftover from swsusp. Are they necessary? - */ - mb(); - barrier(); - error = swsusp_write(); - if (!error) { - error = power_down(pm_disk_mode); - pr_debug("PM: Power down failed.\n"); + if (!error) + power_down(pm_disk_mode); + else { + swsusp_free(); + goto Thaw; } - } else + } else { pr_debug("PM: Image restored successfully.\n"); + } + swsusp_free(); Done: - finish(); + device_resume(); + resume_console(); + Thaw: + unprepare_processes(); return error; } @@ -199,7 +201,7 @@ int pm_suspend_disk(void) * software_resume - Resume from a saved image. * * Called as a late_initcall (so all devices are discovered and - * initialized), we call pmdisk to see if we have a saved image or not. + * initialized), we call swsusp to see if we have a saved image or not. * If so, we quiesce devices, the restore the saved image. We will * return above (in pm_suspend_disk() ) if everything goes well. * Otherwise, we fail gracefully and return to the normally @@ -211,34 +213,69 @@ static int software_resume(void) { int error; + mutex_lock(&pm_mutex); + if (!swsusp_resume_device) { + if (!strlen(resume_file)) { + mutex_unlock(&pm_mutex); + return -ENOENT; + } + swsusp_resume_device = name_to_dev_t(resume_file); + pr_debug("swsusp: Resume From Partition %s\n", resume_file); + } else { + pr_debug("swsusp: Resume From Partition %d:%d\n", + MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device)); + } + if (noresume) { /** * FIXME: If noresume is specified, we need to find the partition * and reset it back to normal swap space. */ + mutex_unlock(&pm_mutex); return 0; } - pr_debug("PM: Reading pmdisk image.\n"); + pr_debug("PM: Checking swsusp image.\n"); - if ((error = swsusp_read())) + if ((error = swsusp_check())) goto Done; - pr_debug("PM: Preparing system for restore.\n"); + pr_debug("PM: Preparing processes for restore.\n"); + + if ((error = prepare_processes())) { + swsusp_close(); + goto Done; + } - if ((error = prepare())) - goto Free; + pr_debug("PM: Reading swsusp image.\n"); + + if ((error = swsusp_read())) { + swsusp_free(); + goto Thaw; + } + + pr_debug("PM: Preparing devices for restore.\n"); + + suspend_console(); + if ((error = device_suspend(PMSG_PRETHAW))) { + resume_console(); + printk("Some devices failed to suspend\n"); + swsusp_free(); + goto Thaw; + } - barrier(); mb(); pr_debug("PM: Restoring saved image.\n"); swsusp_resume(); pr_debug("PM: Restore failed, recovering.n"); - finish(); - Free: - swsusp_free(); + device_resume(); + resume_console(); + Thaw: + unprepare_processes(); Done: + /* For success case, the suspend path will release the lock */ + mutex_unlock(&pm_mutex); pr_debug("PM: Resume from disk failed.\n"); return 0; } @@ -246,11 +283,13 @@ static int software_resume(void) late_initcall(software_resume); -static char * pm_disk_modes[] = { +static const char * const pm_disk_modes[] = { [PM_DISK_FIRMWARE] = "firmware", [PM_DISK_PLATFORM] = "platform", [PM_DISK_SHUTDOWN] = "shutdown", [PM_DISK_REBOOT] = "reboot", + [PM_DISK_TEST] = "test", + [PM_DISK_TESTPROC] = "testproc", }; /** @@ -282,7 +321,7 @@ static char * pm_disk_modes[] = { static ssize_t disk_show(struct subsystem * subsys, char * buf) { - return sprintf(buf,"%s\n",pm_disk_modes[pm_disk_mode]); + return sprintf(buf, "%s\n", pm_disk_modes[pm_disk_mode]); } @@ -292,12 +331,12 @@ static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n) int i; int len; char *p; - u32 mode = 0; + suspend_disk_method_t mode = 0; p = memchr(buf, '\n', n); len = p ? p - buf : n; - down(&pm_sem); + mutex_lock(&pm_mutex); for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) { if (!strncmp(buf, pm_disk_modes[i], len)) { mode = i; @@ -305,28 +344,83 @@ static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n) } } if (mode) { - if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT) + if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT || + mode == PM_DISK_TEST || mode == PM_DISK_TESTPROC) { pm_disk_mode = mode; - else { + } else { if (pm_ops && pm_ops->enter && (mode == pm_ops->pm_disk_mode)) pm_disk_mode = mode; else error = -EINVAL; } - } else + } else { error = -EINVAL; + } pr_debug("PM: suspend-to-disk mode set to '%s'\n", pm_disk_modes[mode]); - up(&pm_sem); + mutex_unlock(&pm_mutex); return error ? error : 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) +{ + unsigned int maj, min; + dev_t res; + int ret = -EINVAL; + + if (sscanf(buf, "%u:%u", &maj, &min) != 2) + goto out; + + res = MKDEV(maj,min); + if (maj != MAJOR(res) || min != MINOR(res)) + goto out; + + mutex_lock(&pm_mutex); + swsusp_resume_device = res; + mutex_unlock(&pm_mutex); + printk("Attempting manual resume\n"); + noresume = 0; + software_resume(); + ret = n; + out: + return ret; +} + +power_attr(resume); + +static ssize_t image_size_show(struct subsystem * subsys, char *buf) +{ + return sprintf(buf, "%lu\n", image_size); +} + +static ssize_t image_size_store(struct subsystem * subsys, const char * buf, size_t n) +{ + unsigned long size; + + if (sscanf(buf, "%lu", &size) == 1) { + image_size = size; + return n; + } + + return -EINVAL; +} + +power_attr(image_size); + static struct attribute * g[] = { &disk_attr.attr, + &resume_attr.attr, + &image_size_attr.attr, NULL, }; @@ -353,6 +447,19 @@ static int __init resume_setup(char *str) return 1; } +static int __init resume_offset_setup(char *str) +{ + unsigned long long offset; + + if (noresume) + return 1; + + if (sscanf(str, "%llu", &offset) == 1) + swsusp_resume_block = offset; + + return 1; +} + static int __init noresume_setup(char *str) { noresume = 1; @@ -360,4 +467,5 @@ static int __init noresume_setup(char *str) } __setup("noresume", noresume_setup); +__setup("resume_offset=", resume_offset_setup); __setup("resume=", resume_setup);