Commit ce640f19 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://kernel.bkbits.net//home/mochel/linux-2.6-power

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents 6c4cd043 d5df4e65
......@@ -20,26 +20,6 @@ From kernel/suspend.c:
You need to append resume=/dev/your_swap_partition to kernel command
line. Then you suspend by echo 4 > /proc/acpi/sleep.
Pavel's unreliable guide to swsusp mess
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are currently two versions of swap suspend in the kernel, the old
"Pavel's" version in kernel/power/swsusp.c and the new "Patrick's"
version in kernel/power/pmdisk.c. They provide the same functionality;
the old version looks ugly but was tested, while the new version looks
nicer but did not receive so much testing. echo 4 > /proc/acpi/sleep
calls the old version, echo disk > /sys/power/state calls the new one.
[In the future, when the new version is stable enough, two things can
happen:
* the new version is moved into swsusp.c, and swsusp is renamed to swap
suspend (Pavel prefers this)
* pmdisk is kept as is and swsusp.c is removed from the kernel]
Article about goals and implementation of Software Suspend for Linux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Author: G‚ábor Kuti
......@@ -75,10 +55,6 @@ hardware!
About the code
Things to implement
- SMP support. I've done an SMP support but since I don't have access to a kind
of this one I cannot test it. Please SMP people test it. .. Tested it,
doesn't work. Had no time to figure out why. There is some mess with
interrupts AFAIK..
- We should only make a copy of data related to kernel segment, since any
process data won't be changed.
- Should make more sanity checks. Or are these enough?
......@@ -90,11 +66,6 @@ Not so important ideas for implementing
- We should not free pages at the beginning so aggressively, most of them
go there anyway..
Drivers that need support
- pc_keyb -- perhaps we can wait for vojtech's input patches
- do IDE cdroms need some kind of support?
- IDE CD-RW -- how to deal with that?
Sleep states summary (thanx, Ducrot)
====================================
......@@ -109,7 +80,8 @@ and perhaps
echo 4b > /proc/acpi/sleep # for suspend to disk via s4bios
FAQ:
Frequently Asked Questions
==========================
Q: well, suspending a server is IMHO a really stupid thing,
but... (Diego Zuccato):
......
obj-$(CONFIG_PM) += cpu.o
obj-$(CONFIG_PM_DISK) += pmdisk.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
/* Originally gcc generated, modified by hand */
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/page.h>
.text
ENTRY(pmdisk_arch_suspend)
cmpl $0,4(%esp)
jne .L1450
movl %esp, saved_context_esp
movl %ebx, saved_context_ebx
movl %ebp, saved_context_ebp
movl %esi, saved_context_esi
movl %edi, saved_context_edi
pushfl ; popl saved_context_eflags
call pmdisk_suspend
jmp .L1449
.p2align 4,,7
.L1450:
movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx
movl %ecx,%cr3
movl pm_pagedir_nosave,%ebx
xorl %eax, %eax
xorl %edx, %edx
.p2align 4,,7
.L1455:
movl 4(%ebx,%edx),%edi
movl (%ebx,%edx),%esi
movl $1024, %ecx
rep
movsl
movl %cr3, %ecx;
movl %ecx, %cr3; # flush TLB
incl %eax
addl $16, %edx
cmpl pmdisk_pages,%eax
jb .L1455
.p2align 4,,7
.L1453:
movl saved_context_esp, %esp
movl saved_context_ebp, %ebp
movl saved_context_ebx, %ebx
movl saved_context_esi, %esi
movl saved_context_edi, %edi
pushl saved_context_eflags ; popfl
call pmdisk_resume
.L1449:
ret
......@@ -15,83 +15,47 @@
.text
ENTRY(do_magic)
pushl %ebx
cmpl $0,8(%esp)
jne resume
call do_magic_suspend_1
call save_processor_state
ENTRY(swsusp_arch_suspend)
movl %esp, saved_context_esp
movl %eax, saved_context_eax
movl %ebx, saved_context_ebx
movl %ecx, saved_context_ecx
movl %edx, saved_context_edx
movl %ebp, saved_context_ebp
movl %esi, saved_context_esi
movl %edi, saved_context_edi
pushfl ; popl saved_context_eflags
call do_magic_suspend_2
popl %ebx
call swsusp_save
ret
resume:
ENTRY(swsusp_arch_resume)
movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx
movl %ecx,%cr3
call do_magic_resume_1
movl $0,loop
cmpl $0,nr_copy_pages
je copy_done
copy_loop:
movl $0,loop2
movl pagedir_nosave, %ebx
xorl %eax, %eax
xorl %edx, %edx
.p2align 4,,7
copy_one_page:
movl pagedir_nosave,%ecx
movl loop,%eax
movl loop2,%edx
sall $4,%eax
movl 4(%ecx,%eax),%ebx
movl (%ecx,%eax),%eax
movb (%edx,%eax),%al
movb %al,(%edx,%ebx)
movl loop2,%eax
leal 1(%eax),%edx
movl %edx,loop2
movl %edx,%eax
cmpl $4095,%eax
jbe copy_one_page
movl loop,%eax
leal 1(%eax),%edx
movl %edx,loop
movl %edx,%eax
cmpl nr_copy_pages,%eax
jb copy_loop
copy_loop:
movl 4(%ebx,%edx),%edi
movl (%ebx,%edx),%esi
movl $1024, %ecx
rep
movsl
copy_done:
movl $__USER_DS,%eax
incl %eax
addl $16, %edx
cmpl nr_copy_pages,%eax
jb copy_loop
.p2align 4,,7
movw %ax, %ds
movw %ax, %es
movl saved_context_esp, %esp
movl saved_context_ebp, %ebp
movl saved_context_eax, %eax
movl saved_context_ebx, %ebx
movl saved_context_ecx, %ecx
movl saved_context_edx, %edx
movl saved_context_esi, %esi
movl saved_context_edi, %edi
call restore_processor_state
pushl saved_context_eflags ; popfl
call do_magic_resume_2
popl %ebx
call swsusp_restore
ret
.section .data.nosave
loop:
.quad 0
loop2:
.quad 0
.previous
/* originally gcc generated, but now changed. don't overwrite. */
/* Originally gcc generated, modified by hand
*
* This may not use any stack, nor any variable that is not "NoSave":
*
* Its rewriting one kernel image with another. What is stack in "old"
* image could very well be data page in "new" image, and overwriting
* your own stack under you is bad idea.
*/
.text
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/page.h>
/* Input:
* rdi resume flag
*/
ENTRY(do_magic)
.LFB5:
subq $8, %rsp
.LCFI2:
testl %edi, %edi
jne .L90
call do_magic_suspend_1
call save_processor_state
ENTRY(swsusp_arch_suspend)
movq %rsp, saved_context_esp(%rip)
movq %rax, saved_context_eax(%rip)
......@@ -36,9 +32,10 @@ ENTRY(do_magic)
movq %r15, saved_context_r15(%rip)
pushfq ; popq saved_context_eflags(%rip)
addq $8, %rsp
jmp do_magic_suspend_2
.L90:
call swsusp_save
ret
ENTRY(swsusp_arch_resume)
/* set up cr3 */
leaq init_level4_pgt(%rip),%rax
subq $__START_KERNEL_map,%rax
......@@ -53,7 +50,6 @@ ENTRY(do_magic)
movq %rcx, %cr3;
movq %rax, %cr4; # turn PGE back on
call do_magic_resume_1
movl nr_copy_pages(%rip), %eax
xorl %ecx, %ecx
movq $0, loop(%rip)
......@@ -113,9 +109,8 @@ ENTRY(do_magic)
movq saved_context_r14(%rip), %r14
movq saved_context_r15(%rip), %r15
pushq saved_context_eflags(%rip) ; popfq
call restore_processor_state
addq $8, %rsp
jmp do_magic_resume_2
call swsusp_restore
ret
.section .data.nosave
loop:
......
......@@ -217,7 +217,8 @@ static int __init acpi_sleep_init(void)
sleep_states[i] = 1;
printk(" S4bios");
acpi_pm_ops.pm_disk_mode = PM_DISK_FIRMWARE;
} else if (sleep_states[i])
}
if (sleep_states[i])
acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM;
}
}
......
......@@ -194,11 +194,12 @@ extern void (*pm_idle)(void);
extern void (*pm_power_off)(void);
enum {
PM_SUSPEND_ON,
PM_SUSPEND_STANDBY,
PM_SUSPEND_MEM,
PM_SUSPEND_DISK,
PM_SUSPEND_MAX,
PM_SUSPEND_ON = 0,
PM_SUSPEND_STANDBY = 1,
/* NOTE: PM_SUSPEND_MEM == PCI_D3hot */
PM_SUSPEND_MEM = 3,
PM_SUSPEND_DISK = 4,
PM_SUSPEND_MAX = 5,
};
enum {
......
......@@ -23,16 +23,6 @@ typedef struct pbe {
#define SWAP_FILENAME_MAXLENGTH 32
struct suspend_header {
u32 version_code;
unsigned long num_physpages;
char machine[8];
char version[20];
int num_cpus;
int page_size;
suspend_pagedir_t *suspend_pagedir;
unsigned int num_pbes;
};
#define SUSPEND_PD_PAGES(x) (((x)*sizeof(struct pbe))/PAGE_SIZE+1)
......@@ -45,16 +35,12 @@ extern void drain_local_pages(void);
/* kernel/power/swsusp.c */
extern int software_suspend(void);
extern unsigned int nr_copy_pages __nosavedata;
extern suspend_pagedir_t *pagedir_nosave __nosavedata;
#else /* CONFIG_SOFTWARE_SUSPEND */
static inline int software_suspend(void)
{
printk("Warning: fake suspend called\n");
return -EPERM;
}
#define software_resume() do { } while(0)
#endif /* CONFIG_SOFTWARE_SUSPEND */
......@@ -78,12 +64,6 @@ static inline void disable_nonboot_cpus(void) {}
static inline void enable_nonboot_cpus(void) {}
#endif
asmlinkage void do_magic(int is_resume);
asmlinkage void do_magic_resume_1(void);
asmlinkage void do_magic_resume_2(void);
asmlinkage void do_magic_suspend_1(void);
asmlinkage void do_magic_suspend_2(void);
void save_processor_state(void);
void restore_processor_state(void);
struct saved_context;
......
......@@ -18,6 +18,13 @@ config PM
will issue the hlt instruction if nothing is to be done, thereby
sending the processor to sleep and saving power.
config PM_DEBUG
bool "Power Management Debug Support"
---help---
This option enables verbose debugging support in the Power Management
code. This is helpful when debugging and reporting various PM bugs,
like suspend support.
config SOFTWARE_SUSPEND
bool "Software Suspend (EXPERIMENTAL)"
depends on EXPERIMENTAL && PM && SWAP
......@@ -42,33 +49,12 @@ config SOFTWARE_SUSPEND
For more information take a look at Documentation/power/swsusp.txt.
config PM_DISK
bool "Suspend-to-Disk Support"
depends on PM && SWAP && X86 && !X86_64
---help---
Suspend-to-disk is a power management state in which the contents
of memory are stored on disk and the entire system is shut down or
put into a low-power state (e.g. ACPI S4). When the computer is
turned back on, the stored image is loaded from disk and execution
resumes from where it left off before suspending.
This config option enables the core infrastructure necessary to
perform the suspend and resume transition.
Currently, this suspend-to-disk implementation is based on a forked
version of the swsusp code base. As such, it's still experimental,
and still relies on CONFIG_SWAP.
More information can be found in Documentation/power/.
If unsure, Say N.
config PM_DISK_PARTITION
config PM_STD_PARTITION
string "Default resume partition"
depends on PM_DISK
depends on SOFTWARE_SUSPEND
default ""
---help---
The default resume partition is the partition that the pmdisk suspend-
The default resume partition is the partition that the suspend-
to-disk implementation will look for a suspended disk image.
The partition specified here will be different for almost every user.
......@@ -77,16 +63,10 @@ config PM_DISK_PARTITION
The partition specified can be overridden by specifying:
pmdisk=/dev/<other device>
resume=/dev/<other device>
which will set the resume partition to the device specified.
One may also do:
pmdisk=off
to inform the kernel not to perform a resume transition.
Note there is currently not a way to specify which device to save the
suspended image to. It will simply pick the first available swap
device.
......
ifeq ($(CONFIG_PM_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
swsusp-smp-$(CONFIG_SMP) += smp.o
obj-y := main.o process.o console.o pm.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o $(swsusp-smp-y)
obj-$(CONFIG_PM_DISK) += disk.o pmdisk.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o $(swsusp-smp-y) disk.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
......@@ -8,13 +8,11 @@
*
*/
#define DEBUG
#include <linux/suspend.h>
#include <linux/syscalls.h>
#include <linux/reboot.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include "power.h"
......@@ -23,13 +21,16 @@
extern u32 pm_disk_mode;
extern struct pm_ops * pm_ops;
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);
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);
static int noresume = 0;
char resume_file[256] = CONFIG_PM_STD_PARTITION;
/**
* power_down - Shut machine down for hibernate.
* @mode: Suspend-to-disk mode
......@@ -46,22 +47,26 @@ static int power_down(u32 mode)
int error = 0;
local_irq_save(flags);
device_power_down(PM_SUSPEND_DISK);
switch(mode) {
case PM_DISK_PLATFORM:
device_power_down(PM_SUSPEND_DISK);
error = pm_ops->enter(PM_SUSPEND_DISK);
break;
case PM_DISK_SHUTDOWN:
printk("Powering off system\n");
device_shutdown();
machine_power_off();
break;
case PM_DISK_REBOOT:
device_shutdown();
machine_restart(NULL);
break;
}
machine_halt();
device_power_up();
local_irq_restore(flags);
/* 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;
}
......@@ -99,6 +104,7 @@ static void finish(void)
{
device_resume();
platform_finish();
enable_nonboot_cpus();
thaw_processes();
pm_restore_console();
}
......@@ -126,6 +132,7 @@ static int prepare(void)
/* Free memory before shutting down devices. */
free_some_memory();
disable_nonboot_cpus();
if ((error = device_suspend(PM_SUSPEND_DISK)))
goto Finish;
......@@ -133,6 +140,7 @@ static int prepare(void)
Finish:
platform_finish();
Thaw:
enable_nonboot_cpus();
thaw_processes();
pm_restore_console();
return error;
......@@ -161,7 +169,7 @@ int pm_suspend_disk(void)
pr_debug("PM: snapshotting memory.\n");
in_suspend = 1;
if ((error = pmdisk_save()))
if ((error = swsusp_suspend()))
goto Done;
if (in_suspend) {
......@@ -173,14 +181,14 @@ int pm_suspend_disk(void)
mb();
barrier();
error = pmdisk_write();
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");
pmdisk_free();
swsusp_free();
Done:
finish();
return error;
......@@ -188,7 +196,7 @@ int pm_suspend_disk(void)
/**
* 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.
......@@ -199,13 +207,21 @@ int pm_suspend_disk(void)
*
*/
static int pm_resume(void)
static int software_resume(void)
{
int error;
if (noresume) {
/**
* FIXME: If noresume is specified, we need to find the partition
* and reset it back to normal swap space.
*/
return 0;
}
pr_debug("PM: Reading pmdisk image.\n");
if ((error = pmdisk_read()))
if ((error = swsusp_read()))
goto Done;
pr_debug("PM: Preparing system for restore.\n");
......@@ -216,28 +232,18 @@ static int pm_resume(void)
barrier();
mb();
/* 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");
pmdisk_restore();
swsusp_resume();
pr_debug("PM: Restore failed, recovering.n");
finish();
Free:
pmdisk_free();
swsusp_free();
Done:
pr_debug("PM: Resume from disk failed.\n");
return 0;
}
late_initcall(pm_resume);
late_initcall(software_resume);
static char * pm_disk_modes[] = {
......@@ -336,3 +342,22 @@ 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 noresume_setup(char *str)
{
noresume = 1;
return 1;
}
__setup("noresume", noresume_setup);
__setup("resume=", resume_setup);
......@@ -8,8 +8,6 @@
*
*/
#define DEBUG
#include <linux/suspend.h>
#include <linux/kobject.h>
#include <linux/string.h>
......@@ -35,8 +33,6 @@ void pm_set_ops(struct pm_ops * ops)
{
down(&pm_sem);
pm_ops = ops;
if (ops->pm_disk_mode && ops->pm_disk_mode < PM_DISK_MAX)
pm_disk_mode = ops->pm_disk_mode;
up(&pm_sem);
}
......@@ -169,6 +165,15 @@ static int enter_state(u32 state)
return error;
}
/*
* This is main interface to the outside world. It needs to be
* called from process context.
*/
int software_suspend(void)
{
return enter_state(PM_SUSPEND_DISK);
}
/**
* pm_suspend - Externally visible function for suspending system.
......@@ -225,8 +230,8 @@ static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
for (s = &pm_states[state]; *s; s++, state++) {
if (!strncmp(buf, *s, len))
for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
if (*s && !strncmp(buf, *s, len))
break;
}
if (*s)
......
This diff is collapsed.
#include <linux/suspend.h>
#include <linux/utsname.h>
/* With SUSPEND_CONSOLE defined, it suspend looks *really* cool, but
we probably do not take enough locks for switching consoles, etc,
......@@ -9,7 +10,20 @@
#endif
#ifdef CONFIG_PM_DISK
struct swsusp_info {
struct new_utsname uts;
u32 version_code;
unsigned long num_physpages;
int cpus;
unsigned long image_pages;
unsigned long pagedir_pages;
suspend_pagedir_t * suspend_pagedir;
swp_entry_t pagedir[768];
} __attribute__((aligned(PAGE_SIZE)));
#ifdef CONFIG_SOFTWARE_SUSPEND
extern int pm_suspend_disk(void);
#else
......@@ -18,7 +32,6 @@ static inline int pm_suspend_disk(void)
return -EPERM;
}
#endif
extern struct semaphore pm_sem;
#define power_attr(_name) \
static struct subsys_attribute _name##_attr = { \
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment