X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=kernel%2Fpower%2Fdisk.c;h=88fc5d7ac737402c18db0306049b5873effeed55;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=6abcf99b7ada01e3450c5f260d97149daa7b3f25;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 6abcf99b7..88fc5d7ac 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -3,32 +3,48 @@ * * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab + * Copyright (c) 2004 Pavel Machek * * This file is released under the GPLv2. * */ -#define DEBUG - - #include #include #include #include +#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 pmdisk_save(void); -extern int pmdisk_write(void); -extern int pmdisk_read(void); -extern int pmdisk_restore(void); -extern int pmdisk_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; + + 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. @@ -40,53 +56,29 @@ extern int pmdisk_free(void); * 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); - device_power_down(PM_SUSPEND_DISK); switch(mode) { case PM_DISK_PLATFORM: - 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"); - machine_power_off(); + kernel_power_off(); break; case PM_DISK_REBOOT: - machine_restart(NULL); + kernel_restart(NULL); break; } - machine_halt(); - device_power_up(); - local_irq_restore(flags); - return 0; + 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); } - -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) { @@ -95,56 +87,58 @@ static inline void platform_finish(void) } } -static void finish(void) +static int prepare_processes(void) { - device_resume(); - platform_finish(); - 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; - 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: + 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. */ @@ -152,46 +146,62 @@ 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; - if ((error = pmdisk_save())) + if ((error = swsusp_suspend())) 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 = pmdisk_write(); - if (!error) { - error = power_down(pm_disk_mode); - pr_debug("PM: Power down failed.\n"); + error = swsusp_write(); + if (!error) + power_down(pm_disk_mode); + else { + swsusp_free(); + goto Thaw; } - } else + } else { pr_debug("PM: Image restored successfully.\n"); - pmdisk_free(); + } + + swsusp_free(); Done: - finish(); + device_resume(); + resume_console(); + Thaw: + unprepare_processes(); return error; } /** - * pm_resume - Resume from a saved image. + * 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 @@ -199,52 +209,87 @@ int pm_suspend_disk(void) * */ -static int pm_resume(void) +static int software_resume(void) { int error; - pr_debug("PM: Reading pmdisk image.\n"); + 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: Checking swsusp image.\n"); + + if ((error = swsusp_check())) + goto Done; + + pr_debug("PM: Preparing processes for restore.\n"); - if ((error = pmdisk_read())) + if ((error = prepare_processes())) { + swsusp_close(); goto Done; + } - pr_debug("PM: Preparing system for restore.\n"); + pr_debug("PM: Reading swsusp image.\n"); - if ((error = prepare())) - goto Free; + if ((error = swsusp_read())) { + swsusp_free(); + goto Thaw; + } - barrier(); - mb(); + pr_debug("PM: Preparing devices for restore.\n"); - /* FIXME: The following (comment and mdelay()) are from swsusp. - * Are they really necessary? - * - * We do not want some readahead with DMA to corrupt our memory, right? - * Do it with disabled interrupts for best effect. That way, if some - * driver scheduled DMA, we have good chance for DMA to finish ;-). - */ - pr_debug("PM: Waiting for DMAs to settle down.\n"); - mdelay(1000); + suspend_console(); + if ((error = device_suspend(PMSG_PRETHAW))) { + resume_console(); + printk("Some devices failed to suspend\n"); + swsusp_free(); + goto Thaw; + } + + mb(); pr_debug("PM: Restoring saved image.\n"); - pmdisk_restore(); + swsusp_resume(); pr_debug("PM: Restore failed, recovering.n"); - finish(); - Free: - pmdisk_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; } -late_initcall(pm_resume); +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", }; /** @@ -276,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]); } @@ -286,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; @@ -299,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, }; @@ -336,3 +436,36 @@ static int __init pm_disk_init(void) } core_initcall(pm_disk_init); + + +static int __init resume_setup(char *str) +{ + if (noresume) + return 1; + + strncpy( resume_file, str, 255 ); + 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; + return 1; +} + +__setup("noresume", noresume_setup); +__setup("resume_offset=", resume_offset_setup); +__setup("resume=", resume_setup);