X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=kernel%2Fpower%2Fdisk.c;h=81d4d982f3f097f55f02f427381f8871cac6f8cf;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=312aa169c5665cd379783cca6400e1ec5c7dfac9;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 312aa169c..81d4d982f 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,15 @@ #include #include #include -#include "power.h" - - -extern u32 pm_disk_mode; -extern struct pm_ops * pm_ops; +#include +#include -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); +#include "power.h" static int noresume = 0; char resume_file[256] = CONFIG_PM_STD_PARTITION; +dev_t swsusp_resume_device; /** * power_down - Shut machine down for hibernate. @@ -41,57 +36,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); + kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); error = 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 +67,42 @@ static inline void platform_finish(void) } } -static void finish(void) -{ - device_resume(); - platform_finish(); - enable_nonboot_cpus(); - thaw_processes(); - pm_restore_console(); -} - - -static int prepare(void) +static int prepare_processes(void) { int error; pm_prepare_console(); + disable_nonboot_cpus(); - sys_sync(); if (freeze_processes()) { error = -EBUSY; - 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; - } + goto thaw; } /* Free memory before shutting down devices. */ - free_some_memory(); - - disable_nonboot_cpus(); - if ((error = device_suspend(PM_SUSPEND_DISK))) - goto Finish; - - return 0; - Finish: - platform_finish(); - Thaw: - enable_nonboot_cpus(); + if (!(error = swsusp_shrink_memory())) + return 0; +thaw: thaw_processes(); + 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. * * 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 +110,16 @@ 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); + error = device_suspend(PMSG_FREEZE); + if (error) { + printk("Some devices failed to suspend\n"); + unprepare_processes(); + return error; + } pr_debug("PM: snapshotting memory.\n"); in_suspend = 1; @@ -173,24 +127,23 @@ int pm_suspend_disk(void) goto Done; if (in_suspend) { + device_resume(); 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(); + unprepare_processes(); + return error; } } else pr_debug("PM: Image restored successfully.\n"); + swsusp_free(); Done: - finish(); + device_resume(); + unprepare_processes(); return error; } @@ -199,7 +152,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 +164,66 @@ static int software_resume(void) { int error; + down(&pm_sem); + if (!swsusp_resume_device) { + if (!strlen(resume_file)) { + up(&pm_sem); + 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. */ + up(&pm_sem); return 0; } - pr_debug("PM: Reading pmdisk image.\n"); + pr_debug("PM: Checking swsusp image.\n"); + + if ((error = swsusp_check())) + goto Done; + + pr_debug("PM: Preparing processes for restore.\n"); - if ((error = swsusp_read())) + if ((error = prepare_processes())) { + swsusp_close(); goto Done; + } + + pr_debug("PM: Reading swsusp image.\n"); + + if ((error = swsusp_read())) { + swsusp_free(); + goto Thaw; + } - pr_debug("PM: Preparing system for restore.\n"); + pr_debug("PM: Preparing devices for restore.\n"); - if ((error = prepare())) - goto Free; + if ((error = device_suspend(PMSG_FREEZE))) { + 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(); + Thaw: + unprepare_processes(); Done: + /* For success case, the suspend path will release the lock */ + up(&pm_sem); pr_debug("PM: Resume from disk failed.\n"); return 0; } @@ -282,7 +267,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,7 +277,7 @@ 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; @@ -325,8 +310,61 @@ 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) +{ + 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; + + down(&pm_sem); + swsusp_resume_device = res; + up(&pm_sem); + 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, };