X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=kernel%2Fpower%2Fdisk.c;h=88fc5d7ac737402c18db0306049b5873effeed55;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=02b6764034dcbbe7781ba9e519f44e102dc9d5a7;hpb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;p=linux-2.6.git diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 02b676403..88fc5d7ac 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -16,24 +16,35 @@ #include #include #include -#include "power.h" - +#include +#include +#include +#include +#include -extern suspend_disk_method_t pm_disk_mode; -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); +#include "power.h" static int noresume = 0; char resume_file[256] = CONFIG_PM_STD_PARTITION; dev_t swsusp_resume_device; +sector_t swsusp_resume_block; + +/** + * 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. @@ -47,64 +58,27 @@ dev_t swsusp_resume_device; 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_shutdown(); - 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); } - -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) -{ - unsigned int i = 0; - unsigned int tmp; - unsigned long pages = 0; - char *p = "-\\|/"; - - printk("Freeing memory... "); - while ((tmp = shrink_all_memory(10000))) { - pages += tmp; - printk("\b%c", p[i]); - i++; - if (i > 3) - i = 0; - } - printk("\bdone (%li pages freed)\n", pages); -} - - static inline void platform_finish(void) { if (pm_disk_mode == PM_DISK_PLATFORM) { @@ -113,66 +87,54 @@ 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_processes(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; - return error; + goto thaw; } - if (pm_disk_mode == PM_DISK_PLATFORM) { - if (pm_ops && pm_ops->prepare) { - if ((error = pm_ops->prepare(PM_SUSPEND_DISK))) - return error; - } + if (pm_disk_mode == PM_DISK_TESTPROC) { + printk("swsusp debug: Waiting for 5 seconds.\n"); + mdelay(5000); + goto thaw; } + error = platform_prepare(); + if (error) + goto thaw; + /* Free memory before shutting down devices. */ - free_some_memory(); + if (!(error = swsusp_shrink_memory())) + return 0; - return 0; + platform_finish(); + thaw: + thaw_processes(); + enable_cpus: + enable_nonboot_cpus(); + pm_restore_console(); + return error; } static void unprepare_processes(void) { - enable_nonboot_cpus(); + platform_finish(); thaw_processes(); + enable_nonboot_cpus(); 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"); - platform_finish(); - enable_nonboot_cpus(); - return error; - } - - return 0; -} - /** - * 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. * @@ -185,18 +147,25 @@ int pm_suspend_disk(void) int error; error = prepare_processes(); - if (!error) { - error = prepare_devices(); - } + if (error) + return error; + if (pm_disk_mode == PM_DISK_TESTPROC) + return 0; + + suspend_console(); + error = device_suspend(PMSG_FREEZE); if (error) { - unprepare_processes(); - return error; + resume_console(); + printk("Some devices failed to suspend\n"); + goto Thaw; } - 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_TEST) { + printk("swsusp debug: Waiting for 5 seconds.\n"); + mdelay(5000); + goto Done; + } pr_debug("PM: snapshotting memory.\n"); in_suspend = 1; @@ -204,15 +173,26 @@ int pm_suspend_disk(void) goto Done; if (in_suspend) { + device_resume(); + resume_console(); pr_debug("PM: writing image.\n"); error = swsusp_write(); if (!error) power_down(pm_disk_mode); - } else + else { + swsusp_free(); + goto Thaw; + } + } else { pr_debug("PM: Image restored successfully.\n"); + } + swsusp_free(); Done: - finish(); + device_resume(); + resume_console(); + Thaw: + unprepare_processes(); return error; } @@ -233,11 +213,25 @@ 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; } @@ -250,30 +244,38 @@ static int software_resume(void) if ((error = prepare_processes())) { swsusp_close(); - goto Cleanup; + goto Done; } pr_debug("PM: Reading swsusp image.\n"); - if ((error = swsusp_read())) - goto Cleanup; + if ((error = swsusp_read())) { + swsusp_free(); + goto Thaw; + } pr_debug("PM: Preparing devices for restore.\n"); - if ((error = prepare_devices())) - goto Free; + 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"); swsusp_resume(); pr_debug("PM: Restore failed, recovering.n"); - finish(); - Free: - swsusp_free(); - Cleanup: + 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; } @@ -281,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", }; /** @@ -332,7 +336,7 @@ static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n) 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; @@ -340,21 +344,23 @@ 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; } @@ -366,35 +372,55 @@ static ssize_t resume_show(struct subsystem * subsys, char *buf) MINOR(swsusp_resume_device)); } -static ssize_t resume_store(struct subsystem * subsys, const char * buf, size_t n) +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; + 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; +} - p = memchr(buf, '\n', n); - len = p ? p - buf : n; +power_attr(resume); - 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(); - } +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 error >= 0 ? n : error; + return -EINVAL; } -power_attr(resume); +power_attr(image_size); static struct attribute * g[] = { &disk_attr.attr, &resume_attr.attr, + &image_size_attr.attr, NULL, }; @@ -421,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; @@ -428,4 +467,5 @@ static int __init noresume_setup(char *str) } __setup("noresume", noresume_setup); +__setup("resume_offset=", resume_offset_setup); __setup("resume=", resume_setup);