diff options
| author | Patrick Mochel <mochel@osdl.org> | 2003-09-01 05:55:08 -0700 |
|---|---|---|
| committer | Patrick Mochel <mochel@osdl.org> | 2003-09-01 05:55:08 -0700 |
| commit | aad408d0015e779e04167fe3043fd4999db5a0bd (patch) | |
| tree | 252a5131acfaf3eaa9bdb5080165c5652111c125 | |
| parent | 054cb79567437ca10de7bdfa14131c558df98819 (diff) | |
| parent | 69181b5b8626b5b94e8897ca1cf58c0006128f59 (diff) | |
Merge osdl.org:/home/mochel/src/kernel/linux-2.5-virgin
into osdl.org:/home/mochel/src/kernel/linux-2.5-power
| -rw-r--r-- | arch/i386/Makefile | 1 | ||||
| -rw-r--r-- | arch/i386/kernel/Makefile | 2 | ||||
| -rw-r--r-- | arch/i386/power/Makefile | 2 | ||||
| -rw-r--r-- | arch/i386/power/cpu.c (renamed from arch/i386/kernel/suspend.c) | 0 | ||||
| -rw-r--r-- | arch/i386/power/swsusp.S (renamed from arch/i386/kernel/suspend_asm.S) | 10 | ||||
| -rw-r--r-- | drivers/acpi/sleep/main.c | 53 | ||||
| -rw-r--r-- | drivers/acpi/sleep/proc.c | 73 | ||||
| -rw-r--r-- | drivers/acpi/sleep/sleep.h | 3 | ||||
| -rw-r--r-- | drivers/base/core.c | 33 | ||||
| -rw-r--r-- | drivers/base/power/main.c | 13 | ||||
| -rw-r--r-- | drivers/base/power/power.h | 3 | ||||
| -rw-r--r-- | drivers/base/power/resume.c | 21 | ||||
| -rw-r--r-- | drivers/base/power/suspend.c | 10 | ||||
| -rw-r--r-- | include/asm-i386/suspend.h | 7 | ||||
| -rw-r--r-- | include/linux/suspend.h | 1 | ||||
| -rw-r--r-- | kernel/power/Makefile | 2 | ||||
| -rw-r--r-- | kernel/power/console.c | 2 | ||||
| -rw-r--r-- | kernel/power/disk.c | 337 | ||||
| -rw-r--r-- | kernel/power/main.c | 384 | ||||
| -rw-r--r-- | kernel/power/power.h | 39 | ||||
| -rw-r--r-- | kernel/power/swsusp.c | 341 | ||||
| -rw-r--r-- | kernel/sys.c | 2 |
22 files changed, 777 insertions, 562 deletions
diff --git a/arch/i386/Makefile b/arch/i386/Makefile index c49b4749cf95..ca5d7812f35c 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -96,6 +96,7 @@ drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/ drivers-$(CONFIG_PCI) += arch/i386/pci/ # must be linked after kernel/ drivers-$(CONFIG_OPROFILE) += arch/i386/oprofile/ +drivers-$(CONFIG_PM) += arch/i386/power/ CFLAGS += $(mflags-y) AFLAGS += $(mflags-y) diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 00d75afcaa67..b5ed0976d4db 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -17,9 +17,7 @@ obj-$(CONFIG_MCA) += mca.o obj-$(CONFIG_X86_MSR) += msr.o obj-$(CONFIG_X86_CPUID) += cpuid.o obj-$(CONFIG_MICROCODE) += microcode.o -obj-$(CONFIG_PM) += suspend.o obj-$(CONFIG_APM) += apm.o -obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend_asm.o obj-$(CONFIG_X86_SMP) += smp.o smpboot.o obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o obj-$(CONFIG_X86_MPPARSE) += mpparse.o diff --git a/arch/i386/power/Makefile b/arch/i386/power/Makefile new file mode 100644 index 000000000000..8cfa4e8a719d --- /dev/null +++ b/arch/i386/power/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_PM) += cpu.o +obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o diff --git a/arch/i386/kernel/suspend.c b/arch/i386/power/cpu.c index b71175ff7559..b71175ff7559 100644 --- a/arch/i386/kernel/suspend.c +++ b/arch/i386/power/cpu.c diff --git a/arch/i386/kernel/suspend_asm.S b/arch/i386/power/swsusp.S index 3d3bb7121c2c..239483113cae 100644 --- a/arch/i386/kernel/suspend_asm.S +++ b/arch/i386/power/swsusp.S @@ -8,11 +8,10 @@ .text -ENTRY(do_magic) +ENTRY(swsusp_arch_suspend) pushl %ebx cmpl $0,8(%esp) jne .L1450 - call do_magic_suspend_1 call save_processor_state movl %esp, saved_context_esp @@ -25,14 +24,13 @@ ENTRY(do_magic) movl %edi, saved_context_edi pushfl ; popl saved_context_eflags - call do_magic_suspend_2 + call swsusp_suspend jmp .L1449 .p2align 4,,7 .L1450: movl $swapper_pg_dir-__PAGE_OFFSET,%ecx movl %ecx,%cr3 - call do_magic_resume_1 movl $0,loop cmpl $0,nr_copy_pages je .L1453 @@ -80,7 +78,7 @@ ENTRY(do_magic) movl saved_context_edi, %edi call restore_processor_state pushl saved_context_eflags ; popfl - call do_magic_resume_2 + call swsusp_resume .L1449: popl %ebx ret @@ -91,4 +89,4 @@ loop: loop2: .quad 0 .previous -
\ No newline at end of file + diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index d3f29ce924ae..1338cc96b724 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -41,7 +41,6 @@ static u32 acpi_suspend_states[] = { static int acpi_pm_prepare(u32 state) { - int error = 0; u32 acpi_state = acpi_suspend_states[state]; if (!sleep_states[acpi_state]) @@ -56,21 +55,9 @@ static int acpi_pm_prepare(u32 state) acpi_set_firmware_waking_vector( (acpi_physical_address) acpi_wakeup_address); } - ACPI_FLUSH_CPU_CACHE(); - - /* Do arch specific saving of state. */ - if (state > PM_SUSPEND_STANDBY) { - if ((error = acpi_save_state_mem())) - goto Err; - } - acpi_enter_sleep_state_prep(acpi_state); - return 0; - Err: - acpi_set_firmware_waking_vector(0); - return error; } @@ -90,6 +77,15 @@ static int acpi_pm_enter(u32 state) u32 acpi_state = acpi_suspend_states[state]; ACPI_FLUSH_CPU_CACHE(); + + /* Do arch specific saving of state. */ + if (state > PM_SUSPEND_STANDBY) { + int error = acpi_save_state_mem(); + if (error) + return error; + } + + local_irq_save(flags); switch (state) { @@ -114,6 +110,15 @@ static int acpi_pm_enter(u32 state) local_irq_restore(flags); printk(KERN_DEBUG "Back to C!\n"); + /* restore processor state + * We should only be here if we're coming back from STR or STD. + * And, in the case of the latter, the memory image should have already + * been loaded from disk. + */ + if (state > PM_SUSPEND_STANDBY) + acpi_restore_state_mem(); + + return ACPI_SUCCESS(status) ? 0 : -EFAULT; } @@ -130,14 +135,6 @@ static int acpi_pm_finish(u32 state) { acpi_leave_sleep_state(state); - /* restore processor state - * We should only be here if we're coming back from STR or STD. - * And, in the case of the latter, the memory image should have already - * been loaded from disk. - */ - if (state > ACPI_STATE_S1) - acpi_restore_state_mem(); - /* reset firmware waking vector */ acpi_set_firmware_waking_vector((acpi_physical_address) 0); @@ -149,6 +146,20 @@ static int acpi_pm_finish(u32 state) } +int acpi_suspend(u32 acpi_state) +{ + u32 states[] = { + [1] = PM_SUSPEND_STANDBY, + [3] = PM_SUSPEND_MEM, + [4] = PM_SUSPEND_DISK, + }; + + if (acpi_state <= 4 && states[acpi_state]) + return pm_suspend(states[acpi_state]); + return -EINVAL; +} + + static struct pm_ops acpi_pm_ops = { .prepare = acpi_pm_prepare, .enter = acpi_pm_enter, diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 41cbde00b785..4a15b0000aa8 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -13,12 +13,71 @@ #include "sleep.h" +#define ACPI_SYSTEM_FILE_SLEEP "sleep" #define ACPI_SYSTEM_FILE_ALARM "alarm" #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME ("sleep") +static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset) +{ + int i; + + ACPI_FUNCTION_TRACE("acpi_system_sleep_seq_show"); + + for (i = 0; i <= ACPI_STATE_S5; i++) { + if (sleep_states[i]) { + seq_printf(seq,"S%d ", i); + if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f) + seq_printf(seq, "S4bios "); + } + } + + seq_puts(seq, "\n"); + + return 0; +} + +static int acpi_system_sleep_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_system_sleep_seq_show, PDE(inode)->data); +} + +static int +acpi_system_write_sleep ( + struct file *file, + const char *buffer, + size_t count, + loff_t *ppos) +{ + char str[12]; + u32 state = 0; + int error = 0; + + if (count > sizeof(str) - 1) + goto Done; + memset(str,0,sizeof(str)); + if (copy_from_user(str, buffer, count)) + return -EFAULT; + + /* Check for S4 bios request */ + if (!strcmp(str,"4b")) { + error = acpi_suspend(4); + goto Done; + } + state = simple_strtoul(str, NULL, 0); +#ifdef CONFIG_SOFTWARE_SUSPEND + if (state == 4) { + error = software_suspend(); + goto Done; + } +#endif + error = acpi_suspend(state); + Done: + return error ? error : count; +} + static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset) { u32 sec, min, hr; @@ -294,6 +353,14 @@ end: } +static struct file_operations acpi_system_sleep_fops = { + .open = acpi_system_sleep_open_fs, + .read = seq_read, + .write = acpi_system_write_sleep, + .llseek = seq_lseek, + .release = single_release, +}; + static struct file_operations acpi_system_alarm_fops = { .open = acpi_system_alarm_open_fs, .read = seq_read, @@ -307,6 +374,12 @@ static int acpi_sleep_proc_init(void) { struct proc_dir_entry *entry = NULL; + /* 'sleep' [R/W]*/ + entry = create_proc_entry(ACPI_SYSTEM_FILE_SLEEP, + S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir); + if (entry) + entry->proc_fops = &acpi_system_sleep_fops; + /* 'alarm' [R/W] */ entry = create_proc_entry(ACPI_SYSTEM_FILE_ALARM, S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir); diff --git a/drivers/acpi/sleep/sleep.h b/drivers/acpi/sleep/sleep.h index 97b72323f35e..ad38f4153a34 100644 --- a/drivers/acpi/sleep/sleep.h +++ b/drivers/acpi/sleep/sleep.h @@ -1,5 +1,4 @@ extern u8 sleep_states[]; - -extern acpi_status acpi_suspend (u32 state); +extern int acpi_suspend (u32 state); diff --git a/drivers/base/core.c b/drivers/base/core.c index 5d98d04c1786..db0750f492ac 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -225,28 +225,30 @@ int device_add(struct device *dev) dev->kobj.parent = &parent->kobj; if ((error = kobject_add(&dev->kobj))) - goto register_done; - - /* now take care of our own registration */ - + goto Error; + if ((error = device_pm_add(dev))) + goto PMError; + if ((error = bus_add_device(dev))) + goto BusError; down_write(&devices_subsys.rwsem); if (parent) list_add_tail(&dev->node,&parent->children); up_write(&devices_subsys.rwsem); - bus_add_device(dev); - - device_pm_add(dev); - /* notify platform of device entry */ if (platform_notify) platform_notify(dev); - - register_done: - if (error && parent) - put_device(parent); + Done: put_device(dev); return error; + BusError: + device_pm_remove(dev); + PMError: + kobject_unregister(&dev->kobj); + Error: + if (parent) + put_device(parent); + goto Done; } @@ -312,8 +314,6 @@ void device_del(struct device * dev) { struct device * parent = dev->parent; - device_pm_remove(dev); - down_write(&devices_subsys.rwsem); if (parent) list_del_init(&dev->node); @@ -324,14 +324,11 @@ void device_del(struct device * dev) */ if (platform_notify_remove) platform_notify_remove(dev); - bus_remove_device(dev); - + device_pm_remove(dev); kobject_del(&dev->kobj); - if (parent) put_device(parent); - } /** diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 611a69accdd0..be37de737bb9 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -36,12 +36,14 @@ DECLARE_MUTEX(dpm_sem); static inline void device_pm_hold(struct device * dev) { - atomic_inc(&dev->power.pm_users); + if (dev) + atomic_inc(&dev->power.pm_users); } static inline void device_pm_release(struct device * dev) { - atomic_inc(&dev->power.pm_users); + if (dev) + atomic_dec(&dev->power.pm_users); } @@ -61,11 +63,9 @@ static inline void device_pm_release(struct device * dev) void device_pm_set_parent(struct device * dev, struct device * parent) { struct device * old_parent = dev->power.pm_parent; - if (old_parent) - device_pm_release(old_parent); + device_pm_release(old_parent); dev->power.pm_parent = parent; - if (parent) - device_pm_hold(parent); + device_pm_hold(parent); } EXPORT_SYMBOL(device_pm_set_parent); @@ -91,6 +91,7 @@ void device_pm_remove(struct device * dev) dev->bus ? dev->bus->name : "No Bus", dev->kobj.name); down(&dpm_sem); dpm_sysfs_remove(dev); + device_pm_release(dev->power.pm_parent); list_del(&dev->power.entry); up(&dpm_sem); } diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index fde72b37f938..b930ccef5785 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -58,7 +58,8 @@ extern void dpm_sysfs_remove(struct device *); /* * resume.c */ -extern int dpm_resume(void); + +extern void dpm_resume(void); extern void dpm_power_up(void); extern int resume_device(struct device *); diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 9db84a9e41e3..637c52ad6ec6 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -28,6 +28,19 @@ int resume_device(struct device * dev) } + +void dpm_resume(void) +{ + while(!list_empty(&dpm_off)) { + struct list_head * entry = dpm_off.next; + struct device * dev = to_device(entry); + list_del_init(entry); + resume_device(dev); + list_add_tail(entry,&dpm_active); + } +} + + /** * device_resume - Restore state of each device in system. * @@ -38,13 +51,7 @@ int resume_device(struct device * dev) void device_resume(void) { down(&dpm_sem); - while(!list_empty(&dpm_off)) { - struct list_head * entry = dpm_off.next; - struct device * dev = to_device(entry); - list_del_init(entry); - resume_device(dev); - list_add_tail(entry,&dpm_active); - } + dpm_resume(); up(&dpm_sem); } diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 6da8cdd69dce..19e660a21ecf 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -81,14 +81,18 @@ int device_suspend(u32 state) while(!list_empty(&dpm_active)) { struct list_head * entry = dpm_active.prev; struct device * dev = to_device(entry); - if ((error = suspend_device(dev,state))) - goto Error; + if ((error = suspend_device(dev,state))) { + if (error != -EAGAIN) + goto Error; + else + error = 0; + } } Done: up(&dpm_sem); return error; Error: - device_resume(); + dpm_resume(); goto Done; } diff --git a/include/asm-i386/suspend.h b/include/asm-i386/suspend.h index 0d22ec30019b..2febd2d28532 100644 --- a/include/asm-i386/suspend.h +++ b/include/asm-i386/suspend.h @@ -6,11 +6,12 @@ #include <asm/desc.h> #include <asm/i387.h> -static inline void +static inline int arch_prepare_suspend(void) { if (!cpu_has_pse) - panic("pse required"); + return -EPERM; + return 0; } /* image of the saved processor state */ @@ -38,8 +39,6 @@ struct saved_context { extern void save_processor_state(void); extern void restore_processor_state(void); -extern int do_magic(int resume); - #ifdef CONFIG_ACPI_SLEEP extern unsigned long saved_eip; extern unsigned long saved_esp; diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 132db86c961a..ed8d796f1849 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -53,6 +53,7 @@ extern suspend_pagedir_t *pagedir_nosave __nosavedata; extern void do_suspend_lowlevel(int resume); extern void do_suspend_lowlevel_s4bios(int resume); +extern int software_suspend(void); #else /* CONFIG_SOFTWARE_SUSPEND */ static inline int software_suspend(void) { diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 9640751c4338..7f127b848827 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -1,4 +1,4 @@ obj-y := main.o process.o console.o pm.o -obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o +obj-$(CONFIG_SOFTWARE_SUSPEND) += disk.o swsusp.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff --git a/kernel/power/console.c b/kernel/power/console.c index 35b1f50d97de..c8a48236619b 100644 --- a/kernel/power/console.c +++ b/kernel/power/console.c @@ -8,7 +8,7 @@ #include <linux/kbd_kern.h> #include "power.h" -static int new_loglevel = 7; +static int new_loglevel = 10; static int orig_loglevel; static int orig_fgconsole, orig_kmsg; diff --git a/kernel/power/disk.c b/kernel/power/disk.c new file mode 100644 index 000000000000..f828e6a6559f --- /dev/null +++ b/kernel/power/disk.c @@ -0,0 +1,337 @@ +/* + * kernel/power/disk.c - Suspend-to-disk support. + * + * Copyright (c) 2003 Patrick Mochel + * Copyright (c) 2003 Open Source Development Lab + * + * This file is release under the GPLv2 + * + */ + +#define DEBUG + + +#include <linux/suspend.h> +#include <linux/reboot.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include "power.h" + + +extern u32 pm_disk_mode; +extern struct pm_ops * pm_ops; + +extern int swsusp_save(void); +extern int swsusp_write(void); +extern int swsusp_read(void); +extern int swsusp_restore(void); +extern int swsusp_free(void); + +extern long sys_sync(void); + + +/** + * power_down - Shut machine down for hibernate. + * @mode: Suspend-to-disk mode + * + * Use the platform driver, if configured so, and return gracefully if it + * fails. + * Otherwise, try to power off and reboot. If they fail, halt the machine, + * there ain't no turning back. + */ + +static int power_down(u32 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; + case PM_DISK_SHUTDOWN: + printk("Powering off system\n"); + machine_power_off(); + break; + case PM_DISK_REBOOT: + machine_restart(NULL); + break; + } + machine_halt(); + device_power_up(); + local_irq_restore(flags); + 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"); + blk_run_queues(); +} + + +static inline void platform_finish(void) +{ + if (pm_disk_mode == PM_DISK_PLATFORM) { + if (pm_ops && pm_ops->finish) + pm_ops->finish(PM_SUSPEND_DISK); + } +} + +static void finish(void) +{ + device_resume(); + platform_finish(); + thaw_processes(); + pm_restore_console(); +} + + +static int prepare(void) +{ + int error; + + pm_prepare_console(); + + 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; + } + } + + /* Free memory before shutting down devices. */ + free_some_memory(); + + if ((error = device_suspend(PM_SUSPEND_DISK))) + goto Finish; + + return 0; + Finish: + platform_finish(); + Thaw: + thaw_processes(); + pm_restore_console(); + return error; +} + + +/** + * 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 swsusp to do it's thing, then figure out how + * to power down the system. + */ + +int pm_suspend_disk(void) +{ + int error; + + if ((error = prepare())) + 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); + + pr_debug("PM: snapshotting memory.\n"); + in_suspend = 1; + local_irq_disable(); + if ((error = swsusp_save())) + goto Done; + + pr_debug("PM: writing image.\n"); + + /* + * FIXME: Leftover from swsusp. Are they necessary? + */ + mb(); + barrier(); + + error = swsusp_write(); + if (!error && in_suspend) { + error = power_down(pm_disk_mode); + pr_debug("PM: Power down failed.\n"); + } else + pr_debug("PM: Image restored successfully.\n"); + swsusp_free(); + Done: + local_irq_enable(); + finish(); + return error; +} + + +/** + * pm_resume - Resume from a saved image. + * + * Called as a late_initcall (so all devices are discovered and + * 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 + * scheduled program. + * + */ + +static int pm_resume(void) +{ + int error; + + pr_debug("PM: Reading swsusp image.\n"); + + if ((error = swsusp_read())) + goto Done; + + pr_debug("PM: Preparing system for restore.\n"); + + if ((error = prepare())) + goto Free; + + barrier(); + mb(); + local_irq_disable(); + + /* 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); + + pr_debug("PM: Restoring saved image.\n"); + swsusp_restore(); + local_irq_enable(); + pr_debug("PM: Restore failed, recovering.n"); + finish(); + Free: + swsusp_free(); + Done: + pr_debug("PM: Resume from disk failed.\n"); + return 0; +} + +late_initcall(pm_resume); + + +static char * pm_disk_modes[] = { + [PM_DISK_FIRMWARE] = "firmware", + [PM_DISK_PLATFORM] = "platform", + [PM_DISK_SHUTDOWN] = "shutdown", + [PM_DISK_REBOOT] = "reboot", +}; + +/** + * disk - Control suspend-to-disk mode + * + * Suspend-to-disk can be handled in several ways. The greatest + * distinction is who writes memory to disk - the firmware or the OS. + * If the firmware does it, we assume that it also handles suspending + * the system. + * If the OS does it, then we have three options for putting the system + * to sleep - using the platform driver (e.g. ACPI or other PM registers), + * powering off the system or rebooting the system (for testing). + * + * The system will support either 'firmware' or 'platform', and that is + * known a priori (and encoded in pm_ops). But, the user may choose + * 'shutdown' or 'reboot' as alternatives. + * + * show() will display what the mode is currently set to. + * store() will accept one of + * + * 'firmware' + * 'platform' + * 'shutdown' + * 'reboot' + * + * It will only change to 'firmware' or 'platform' if the system + * supports it (as determined from pm_ops->pm_disk_mode). + */ + +static ssize_t disk_show(struct subsystem * subsys, char * buf) +{ + return sprintf(buf,"%s\n",pm_disk_modes[pm_disk_mode]); +} + + +static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n) +{ + int error = 0; + int i; + u32 mode = 0; + + down(&pm_sem); + for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) { + if (!strcmp(buf,pm_disk_modes[i])) { + mode = i; + break; + } + } + if (mode) { + if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT) + pm_disk_mode = mode; + else { + if (pm_ops && pm_ops->enter && + (mode == pm_ops->pm_disk_mode)) + pm_disk_mode = mode; + else + error = -EINVAL; + } + } else + error = -EINVAL; + + pr_debug("PM: suspend-to-disk mode set to '%s'\n", + pm_disk_modes[mode]); + up(&pm_sem); + return error ? error : n; +} + +power_attr(disk); + +static struct attribute * g[] = { + &disk_attr.attr, + NULL, +}; + + +static struct attribute_group attr_group = { + .attrs = g, +}; + + +static int __init pm_disk_init(void) +{ + return sysfs_create_group(&power_subsys.kset.kobj,&attr_group); +} + +core_initcall(pm_disk_init); diff --git a/kernel/power/main.c b/kernel/power/main.c index 1b92f13d9a77..be4473cbf300 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -8,32 +8,23 @@ * */ +#define DEBUG + #include <linux/suspend.h> #include <linux/kobject.h> -#include <linux/reboot.h> #include <linux/string.h> +#include <linux/delay.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/pm.h> -#include <linux/fs.h> #include "power.h" -static DECLARE_MUTEX(pm_sem); - -static struct pm_ops * pm_ops = NULL; - -static u32 pm_disk_mode = PM_DISK_SHUTDOWN; - -#ifdef CONFIG_SOFTWARE_SUSPEND -static int have_swsusp = 1; -#else -static int have_swsusp = 0; -#endif - -extern long sys_sync(void); +DECLARE_MUTEX(pm_sem); +struct pm_ops * pm_ops = NULL; +u32 pm_disk_mode = PM_DISK_SHUTDOWN; /** * pm_set_ops - Set the global power method table. @@ -51,171 +42,6 @@ void pm_set_ops(struct pm_ops * ops) /** - * pm_suspend_standby - Enter 'standby' state. - * - * 'standby' is also known as 'Power-On Suspend'. Here, we power down - * devices, disable interrupts, and enter the state. - */ - -static int pm_suspend_standby(void) -{ - int error = 0; - unsigned long flags; - - if (!pm_ops || !pm_ops->enter) - return -EPERM; - - local_irq_save(flags); - if ((error = device_power_down(PM_SUSPEND_STANDBY))) - goto Done; - error = pm_ops->enter(PM_SUSPEND_STANDBY); - local_irq_restore(flags); - device_power_up(); - Done: - return error; -} - - -/** - * pm_suspend_mem - Enter suspend-to-RAM state. - * - * Identical to pm_suspend_standby() - we power down devices, disable - * interrupts, and enter the low-power state. - */ - -static int pm_suspend_mem(void) -{ - int error = 0; - unsigned long flags; - - if (!pm_ops || !pm_ops->enter) - return -EPERM; - - local_irq_save(flags); - if ((error = device_power_down(PM_SUSPEND_STANDBY))) - goto Done; - error = pm_ops->enter(PM_SUSPEND_STANDBY); - local_irq_restore(flags); - device_power_up(); - Done: - return error; -} - - -/** - * power_down - Shut machine down for hibernate. - * @mode: Suspend-to-disk mode - * - * Use the platform driver, if configured so, and return gracefully if it - * fails. - * Otherwise, try to power off and reboot. If they fail, halt the machine, - * there ain't no turning back. - */ - -static int power_down(u32 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); - if (error) { - device_power_up(); - local_irq_restore(flags); - return error; - } - case PM_DISK_SHUTDOWN: - machine_power_off(); - break; - case PM_DISK_REBOOT: - machine_restart(NULL); - break; - } - machine_halt(); - 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"); - blk_run_queues(); -} - - -/** - * 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 swsusp to do it's thing, then figure out how - * to power down the system. - */ - -static int pm_suspend_disk(void) -{ - int 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 (!have_swsusp) - return -EPERM; - - pr_debug("PM: snapshotting memory.\n"); - in_suspend = 1; - if ((error = swsusp_save())) - goto Done; - - if (in_suspend) { - pr_debug("PM: writing image.\n"); - error = swsusp_write(); - if (!error) - error = power_down(pm_disk_mode); - pr_debug("PM: Power down failed.\n"); - } else - pr_debug("PM: Image restored successfully.\n"); - swsusp_free(); - Done: - return error; -} - - - -#define decl_state(_name) \ - { .name = __stringify(_name), .fn = pm_suspend_##_name } - -struct pm_state { - char * name; - int (*fn)(void); -} pm_states[] = { - [PM_SUSPEND_STANDBY] = decl_state(standby), - [PM_SUSPEND_MEM] = decl_state(mem), - [PM_SUSPEND_DISK] = decl_state(disk), - { NULL }, -}; - - -/** * suspend_prepare - Do prep work before entering low-power state. * @state: State we're entering. * @@ -228,23 +54,21 @@ static int suspend_prepare(u32 state) { int error = 0; + if (!pm_ops || !pm_ops->enter) + return -EPERM; + pm_prepare_console(); - sys_sync(); if (freeze_processes()) { error = -EAGAIN; goto Thaw; } - if (pm_ops && pm_ops->prepare) { + if (pm_ops->prepare) { if ((error = pm_ops->prepare(state))) goto Thaw; } - /* Free memory before shutting down devices. */ - if (state == PM_SUSPEND_DISK) - free_some_memory(); - if ((error = device_suspend(state))) goto Finish; @@ -253,7 +77,7 @@ static int suspend_prepare(u32 state) pm_restore_console(); return error; Finish: - if (pm_ops && pm_ops->finish) + if (pm_ops->finish) pm_ops->finish(state); Thaw: thaw_processes(); @@ -261,6 +85,22 @@ static int suspend_prepare(u32 state) } +static int suspend_enter(u32 state) +{ + int error = 0; + unsigned long flags; + + local_irq_save(flags); + if ((error = device_power_down(state))) + goto Done; + error = pm_ops->enter(state); + local_irq_restore(flags); + device_power_up(); + Done: + return error; +} + + /** * suspend_finish - Do final work before exiting suspend sequence. * @state: State we're coming out of. @@ -279,6 +119,16 @@ static void suspend_finish(u32 state) } + + +char * pm_states[] = { + [PM_SUSPEND_STANDBY] = "standby", + [PM_SUSPEND_MEM] = "mem", + [PM_SUSPEND_DISK] = "disk", + NULL, +}; + + /** * enter_state - Do common work of entering low-power state. * @state: pm_state structure for state we're entering. @@ -293,7 +143,6 @@ static void suspend_finish(u32 state) static int enter_state(u32 state) { int error; - struct pm_state * s = &pm_states[state]; if (down_trylock(&pm_sem)) return -EBUSY; @@ -304,12 +153,17 @@ static int enter_state(u32 state) goto Unlock; } - pr_debug("PM: Preparing system for suspend.\n"); + if (state == PM_SUSPEND_DISK) { + error = pm_suspend_disk(); + goto Unlock; + } + + pr_debug("PM: Preparing system for suspend\n"); if ((error = suspend_prepare(state))) goto Unlock; pr_debug("PM: Entering state.\n"); - error = s->fn(); + error = suspend_enter(state); pr_debug("PM: Finishing up.\n"); suspend_finish(state); @@ -335,138 +189,10 @@ int pm_suspend(u32 state) } -/** - * pm_resume - Resume from a saved image. - * - * Called as a late_initcall (so all devices are discovered and - * 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 - * scheduled program. - * - */ - -static int pm_resume(void) -{ - int error; - - if (!have_swsusp) - return 0; - - pr_debug("PM: Reading swsusp image.\n"); - - if ((error = swsusp_read())) - goto Done; - - pr_debug("PM: Preparing system for restore.\n"); - - if ((error = suspend_prepare(PM_SUSPEND_DISK))) - goto Free; - - pr_debug("PM: Restoring saved image.\n"); - swsusp_restore(); - - pr_debug("PM: Restore failed, recovering.n"); - suspend_finish(PM_SUSPEND_DISK); - Free: - swsusp_free(); - Done: - pr_debug("PM: Resume from disk failed.\n"); - return 0; -} - -late_initcall(pm_resume); - decl_subsys(power,NULL,NULL); -#define power_attr(_name) \ -static struct subsys_attribute _name##_attr = { \ - .attr = { \ - .name = __stringify(_name), \ - .mode = 0644, \ - }, \ - .show = _name##_show, \ - .store = _name##_store, \ -} - - -static char * pm_disk_modes[] = { - [PM_DISK_FIRMWARE] = "firmware", - [PM_DISK_PLATFORM] = "platform", - [PM_DISK_SHUTDOWN] = "shutdown", - [PM_DISK_REBOOT] = "reboot", -}; - -/** - * disk - Control suspend-to-disk mode - * - * Suspend-to-disk can be handled in several ways. The greatest - * distinction is who writes memory to disk - the firmware or the OS. - * If the firmware does it, we assume that it also handles suspending - * the system. - * If the OS does it, then we have three options for putting the system - * to sleep - using the platform driver (e.g. ACPI or other PM registers), - * powering off the system or rebooting the system (for testing). - * - * The system will support either 'firmware' or 'platform', and that is - * known a priori (and encoded in pm_ops). But, the user may choose - * 'shutdown' or 'reboot' as alternatives. - * - * show() will display what the mode is currently set to. - * store() will accept one of - * - * 'firmware' - * 'platform' - * 'shutdown' - * 'reboot' - * - * It will only change to 'firmware' or 'platform' if the system - * supports it (as determined from pm_ops->pm_disk_mode). - */ - -static ssize_t disk_show(struct subsystem * subsys, char * buf) -{ - return sprintf(buf,"%s\n",pm_disk_modes[pm_disk_mode]); -} - - -static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n) -{ - int error = 0; - int i; - u32 mode = 0; - - down(&pm_sem); - for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) { - if (!strcmp(buf,pm_disk_modes[i])) { - mode = i; - break; - } - } - if (mode) { - if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT) - pm_disk_mode = mode; - else { - if (pm_ops && pm_ops->enter && - (mode == pm_ops->pm_disk_mode)) - pm_disk_mode = mode; - else - error = -EINVAL; - } - } else - error = -EINVAL; - - pr_debug("PM: suspend-to-disk mode set to '%s'\n", - pm_disk_modes[mode]); - up(&pm_sem); - return error ? error : n; -} - -power_attr(disk); - /** * state - control system power state. * @@ -480,27 +206,28 @@ power_attr(disk); static ssize_t state_show(struct subsystem * subsys, char * buf) { - struct pm_state * state; + int i; char * s = buf; - for (state = &pm_states[0]; state->name; state++) - s += sprintf(s,"%s ",state->name); + for (i = 0; i < PM_SUSPEND_MAX; i++) { + if (pm_states[i]) + s += sprintf(s,"%s ",pm_states[i]); + } s += sprintf(s,"\n"); return (s - buf); } static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n) { - u32 state; - struct pm_state * s; + u32 state = PM_SUSPEND_STANDBY; + char ** s; int error; - for (state = 0; state < PM_SUSPEND_MAX; state++) { - s = &pm_states[state]; - if (s->name && !strcmp(buf,s->name)) + for (s = &pm_states[state]; *s; s++, state++) { + if (!strcmp(buf,*s)) break; } - if (s) + if (*s) error = enter_state(state); else error = -EINVAL; @@ -511,7 +238,6 @@ power_attr(state); static struct attribute * g[] = { &state_attr.attr, - &disk_attr.attr, NULL, }; @@ -520,7 +246,7 @@ static struct attribute_group attr_group = { }; -static int pm_init(void) +static int __init pm_init(void) { int error = subsystem_register(&power_subsys); if (!error) diff --git a/kernel/power/power.h b/kernel/power/power.h index e98de640155d..e0874ed266f5 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -10,34 +10,27 @@ #ifdef CONFIG_SOFTWARE_SUSPEND -extern int swsusp_save(void); -extern int swsusp_write(void); -extern int swsusp_read(void); -extern int swsusp_restore(void); -extern int swsusp_free(void); +extern int pm_suspend_disk(void); + #else -static inline int swsusp_save(void) -{ - return 0; -} -static inline int swsusp_write(void) +static inline int pm_suspend_disk(void) { - return 0; -} -static inline int swsusp_read(void) -{ - return 0; -} -static inline int swsusp_restore(void) -{ - return 0; -} -static inline int swsusp_free(void) -{ - return 0; + return -EPERM; } #endif +extern struct semaphore pm_sem; +#define power_attr(_name) \ +static struct subsys_attribute _name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0644, \ + }, \ + .show = _name##_show, \ + .store = _name##_store, \ +} + +extern struct subsystem power_subsys; extern int freeze_processes(void); extern void thaw_processes(void); diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index c849a18c45f4..c2c33c0cf76c 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -34,38 +34,21 @@ * For TODOs,FIXMEs also look in Documentation/swsusp.txt */ -#include <linux/module.h> #include <linux/mm.h> +#include <linux/bio.h> #include <linux/suspend.h> -#include <linux/smp_lock.h> -#include <linux/file.h> -#include <linux/utsname.h> #include <linux/version.h> -#include <linux/delay.h> #include <linux/reboot.h> -#include <linux/bitops.h> -#include <linux/vt_kern.h> -#include <linux/kbd_kern.h> -#include <linux/keyboard.h> -#include <linux/spinlock.h> -#include <linux/genhd.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/swap.h> -#include <linux/pm.h> #include <linux/device.h> -#include <linux/buffer_head.h> #include <linux/swapops.h> #include <linux/bootmem.h> -#include <asm/uaccess.h> #include <asm/mmu_context.h> -#include <asm/pgtable.h> -#include <asm/io.h> #include "power.h" -unsigned char software_suspend_enabled = 1; + +extern int swsusp_arch_suspend(int resume); #define __ADDRESS(x) ((unsigned long) phys_to_virt(x)) #define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT) @@ -76,9 +59,6 @@ extern char __nosave_begin, __nosave_end; extern int is_head_of_free_region(struct page *); -/* Locks */ -spinlock_t suspend_pagedir_lock __nosavedata = SPIN_LOCK_UNLOCKED; - /* Variables to be preserved over suspend */ static int pagedir_order_check; static int nr_copy_pages_check; @@ -488,21 +468,30 @@ static int suspend_prepare_image(void) return 0; } + +/** + * suspend_save_image - Prepare and write saved image to swap. + * + * IRQs are re-enabled here so we can resume devices and safely write + * to the swap devices. We disable them again before we leave. + * + * The second lock_swapdevices() will unlock ignored swap devices since + * writing is finished. + * It is important _NOT_ to umount filesystems at this point. We want + * them synced (in case something goes wrong) but we DO not want to mark + * filesystem clean: it is not. (And it does not matter, if we resume + * correctly, we'll mark system clean, anyway.) + */ + static int suspend_save_image(void) { int error; - + local_irq_enable(); device_resume(); - lock_swapdevices(); error = write_suspend_image(); - lock_swapdevices(); /* This will unlock ignored swap devices since writing is finished */ - - /* It is important _NOT_ to umount filesystems at this point. We want - * them synced (in case something goes wrong) but we DO not want to mark - * filesystem clean: it is not. (And it does not matter, if we resume - * correctly, we'll mark system clean, anyway.) - */ + lock_swapdevices(); + local_irq_disable(); return error; } @@ -510,66 +499,49 @@ static int suspend_save_image(void) * Magic happens here */ -void do_magic_resume_1(void) -{ - barrier(); - mb(); - spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ - PRINTK( "Waiting for DMAs to settle down...\n"); - /* 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 ;-). */ - mdelay(1000); -} - -void do_magic_resume_2(void) +int swsusp_resume(void) { BUG_ON (nr_copy_pages_check != nr_copy_pages); BUG_ON (pagedir_order_check != pagedir_order); /* Even mappings of "global" things (vmalloc) need to be fixed */ __flush_tlb_global(); - spin_unlock_irq(&suspend_pagedir_lock); + return 0; } -/* do_magic() is implemented in arch/?/kernel/suspend_asm.S, and basically does: +/* swsusp_arch_suspend() is implemented in arch/?/power/swsusp.S, + and basically does: if (!resume) { - do_magic_suspend_1(); save_processor_state(); SAVE_REGISTERS - do_magic_suspend_2(); + swsusp_suspend(); return; } GO_TO_SWAPPER_PAGE_TABLES - do_magic_resume_1(); COPY_PAGES_BACK RESTORE_REGISTERS restore_processor_state(); - do_magic_resume_2(); + swsusp_resume(); */ -void do_magic_suspend_1(void) -{ - mb(); - barrier(); - spin_lock_irq(&suspend_pagedir_lock); -} -int do_magic_suspend_2(void) +int swsusp_suspend(void) { - int is_problem; + int error; read_swapfiles(); - is_problem = suspend_prepare_image(); - spin_unlock_irq(&suspend_pagedir_lock); - if (!is_problem) - return suspend_save_image(); - printk(KERN_EMERG "%sSuspend failed, trying to recover...\n", name_suspend); - barrier(); - mb(); - mdelay(1000); - return -EFAULT; + error = suspend_prepare_image(); + if (!error) + error = suspend_save_image(); + if (error) { + printk(KERN_EMERG "%sSuspend failed, trying to recover...\n", + name_suspend); + barrier(); + mb(); + mdelay(1000); + } + return error; } /* More restore stuff */ @@ -701,61 +673,146 @@ static int __init sanity_check(struct suspend_header *sh) return 0; } -static int __init bdev_read_page(struct block_device *bdev, - long pos, void *buf) +static struct block_device * resume_bdev; + + +/** + * Using bio to read from swap. + * This code requires a bit more work than just using buffer heads + * but, it is the recommended way for 2.5/2.6. + * The following are to signal the beginning and end of I/O. Bios + * finish asynchronously, while we want them to happen synchronously. + * A simple atomic_t, and a wait loop take care of this problem. + */ + +static atomic_t io_done = ATOMIC_INIT(0); + +static void start_io(void) { - struct buffer_head *bh; - BUG_ON (pos%PAGE_SIZE); - bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE); - if (!bh || (!bh->b_data)) { - return -1; - } - memcpy(buf, bh->b_data, PAGE_SIZE); /* FIXME: may need kmap() */ - BUG_ON(!buffer_uptodate(bh)); - brelse(bh); + atomic_set(&io_done,1); +} + +static int end_io(struct bio * bio, unsigned int num, int err) +{ + atomic_set(&io_done,0); return 0; -} +} + +static void wait_io(void) +{ + blk_run_queues(); + while(atomic_read(&io_done)) + io_schedule(); +} + + +/** + * submit - submit BIO request. + * @rw: READ or WRITE. + * @off physical offset of page. + * @page: page we're reading or writing. + * + * Straight from the textbook - allocate and initialize the bio. + * If we're writing, make sure the page is marked as dirty. + * Then submit it and wait. + */ + +static int submit(int rw, pgoff_t page_off, void * page) +{ + int error = 0; + struct bio * bio; + + bio = bio_alloc(GFP_ATOMIC,1); + if (!bio) + return -ENOMEM; + bio->bi_sector = page_off * (PAGE_SIZE >> 9); + bio_get(bio); + bio->bi_bdev = resume_bdev; + bio->bi_end_io = end_io; + + if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) { + printk("ERROR: adding page to bio at %ld\n",page_off); + error = -EFAULT; + goto Done; + } + + if (rw == WRITE) + bio_set_pages_dirty(bio); + start_io(); + submit_bio(rw,bio); + wait_io(); + Done: + bio_put(bio); + return error; +} + +static int +read_page(pgoff_t page_off, void * page) +{ + return submit(READ,page_off,page); +} + +static int +write_page(pgoff_t page_off, void * page) +{ + return submit(WRITE,page_off,page); +} + extern dev_t __init name_to_dev_t(const char *line); -static int __init read_suspend_image(struct block_device *bdev, - union diskpage *cur) + +#define next_entry(diskpage) diskpage->link.next + +static int __init read_suspend_image(void) { swp_entry_t next; int i, nr_pgdir_pages; + union diskpage *cur; + int error = 0; -#define PREPARENEXT \ - { next = cur->link.next; \ - next.val = swp_offset(next) * PAGE_SIZE; \ - } - - if (bdev_read_page(bdev, 0, cur)) return -EIO; + cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC); + if (!cur) + return -ENOMEM; - if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) || - (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) { - printk(KERN_ERR "%sThis is normal swap space\n", name_resume ); - return -EINVAL; - } + if ((error = read_page(0, cur))) + goto Done; - PREPARENEXT; /* We have to read next position before we overwrite it */ + /* + * We have to read next position before we overwrite it + */ + next = next_entry(cur); if (!memcmp("S1",cur->swh.magic.magic,2)) memcpy(cur->swh.magic.magic,"SWAP-SPACE",10); else if (!memcmp("S2",cur->swh.magic.magic,2)) memcpy(cur->swh.magic.magic,"SWAPSPACE2",10); - else { - printk("swsusp: %s: Unable to find suspended-data signature (%.10s - misspelled?\n", - name_resume, cur->swh.magic.magic); - return -EFAULT; + else if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) || + (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) { + printk(KERN_ERR "swsusp: Partition is normal swap space\n"); + error = -EINVAL; + goto Done; + } else { + printk(KERN_ERR "swsusp: Invalid partition type.\n"); + error = -EINVAL; + goto Done; } + /* + * Reset swap signature now. + */ + if ((error = write_page(0,cur))) + goto Done; + printk( "%sSignature found, resuming\n", name_resume ); MDELAY(1000); - if (bdev_read_page(bdev, next.val, cur)) return -EIO; - if (sanity_check(&cur->sh)) /* Is this same machine? */ - return -EPERM; - PREPARENEXT; + if ((error = read_page(swp_offset(next), cur))) + goto Done; + /* Is this same machine? */ + if ((error = sanity_check(&cur->sh))) + goto Done; + next = next_entry(cur); pagedir_save = cur->sh.suspend_pagedir; nr_copy_pages = cur->sh.num_pbes; @@ -763,8 +820,10 @@ static int __init read_suspend_image(struct block_device *bdev, pagedir_order = get_bitmask_order(nr_pgdir_pages); pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order); - if (!pagedir_nosave) - return -ENOMEM; + if (!pagedir_nosave) { + error = -ENOMEM; + goto Done; + } PRINTK( "%sReading pagedir, ", name_resume ); @@ -772,15 +831,17 @@ static int __init read_suspend_image(struct block_device *bdev, for (i=nr_pgdir_pages-1; i>=0; i--) { BUG_ON (!next.val); cur = (union diskpage *)((char *) pagedir_nosave)+i; - if (bdev_read_page(bdev, next.val, cur)) return -EIO; - PREPARENEXT; + error = read_page(swp_offset(next), cur); + if (error) + goto FreePagedir; + next = next_entry(cur); } BUG_ON (next.val); - if (relocate_pagedir()) - return -ENOMEM; - if (check_pagedir()) - return -ENOMEM; + if ((error = relocate_pagedir())) + goto FreePagedir; + if ((error = check_pagedir())) + goto FreePagedir; printk( "Reading image data (%d pages): ", nr_copy_pages ); for(i=0; i < nr_copy_pages; i++) { @@ -789,11 +850,18 @@ static int __init read_suspend_image(struct block_device *bdev, printk( "." ); /* You do not need to check for overlaps... ... check_pagedir already did this work */ - if (bdev_read_page(bdev, swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address))) - return -EIO; + error = read_page(swp_offset(swap_address), + (char *)((pagedir_nosave+i)->address)); + if (error) + goto FreePagedir; } printk( "|\n" ); - return 0; + Done: + free_page((unsigned long)cur); + return error; + FreePagedir: + free_pages((unsigned long)pagedir_nosave,pagedir_order); + goto Done; } /** @@ -806,24 +874,23 @@ int swsusp_save(void) printk("swsusp is not supported with high- or discontig-mem.\n"); return -EPERM; #endif - return 0; + return arch_prepare_suspend(); } /** * swsusp_write - Write saved memory image to swap. * - * do_magic(0) returns after system is resumed. + * swsusp_arch_suspend(0) returns after system is resumed. * - * do_magic() copies all "used" memory to "free" memory, then - * unsuspends all device drivers, and writes memory to disk + * swsusp_arch_suspend() copies all "used" memory to "free" memory, + * then unsuspends all device drivers, and writes memory to disk * using normal kernel mechanism. */ int swsusp_write(void) { - arch_prepare_suspend(); - return do_magic(0); + return swsusp_arch_suspend(0); } @@ -833,7 +900,6 @@ int swsusp_write(void) int __init swsusp_read(void) { - union diskpage *cur; int error; char b[BDEVNAME_SIZE]; @@ -844,19 +910,13 @@ int __init swsusp_read(void) printk("swsusp: Resume From Partition: %s, Device: %s\n", resume_file, __bdevname(resume_device, b)); - cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC); - if (cur) { - struct block_device *bdev; - bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW); - if (!IS_ERR(bdev)) { - set_blocksize(bdev, PAGE_SIZE); - error = read_suspend_image(bdev, cur); - blkdev_put(bdev, BDEV_RAW); - } else - error = PTR_ERR(bdev); - free_page((unsigned long)cur); + resume_bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW); + if (!IS_ERR(resume_bdev)) { + set_blocksize(resume_bdev, PAGE_SIZE); + error = read_suspend_image(); + blkdev_put(resume_bdev, BDEV_RAW); } else - error = -ENOMEM; + error = PTR_ERR(resume_bdev); if (!error) PRINTK("Reading resume file was successful\n"); @@ -873,7 +933,7 @@ int __init swsusp_read(void) int __init swsusp_restore(void) { - return do_magic(1); + return swsusp_arch_suspend(1); } @@ -885,13 +945,20 @@ int swsusp_free(void) { PRINTK( "Freeing prev allocated pagedir\n" ); free_suspend_pagedir((unsigned long) pagedir_save); - - PRINTK( "Fixing swap signatures... " ); - mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME); - PRINTK( "ok\n" ); return 0; } + +int software_suspend(void) +{ + struct pm_ops swsusp_ops = { + .pm_disk_mode = PM_DISK_SHUTDOWN, + }; + + pm_set_ops(&swsusp_ops); + return pm_suspend(PM_SUSPEND_DISK); +} + static int __init resume_setup(char *str) { if (strlen(str)) diff --git a/kernel/sys.c b/kernel/sys.c index 02b5a12dfd59..d77453173d29 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -456,7 +456,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user #ifdef CONFIG_SOFTWARE_SUSPEND case LINUX_REBOOT_CMD_SW_SUSPEND: - if (!pm_suspend(PM_SUSPEND_DISK)) + if (!software_suspend()) break; do_exit(0); break; |
