Commit 44ea4dc0 authored by Patrick Mochel's avatar Patrick Mochel

[Power Mgmt] Consolidate pmdisk and swsusp low-level handling.

- Split do_magic into swsusp_arch_suspend() and swsusp_arch_resume().
- Clean up based on pmdisk implementation
  - Only save registers we need to.
  - Use rep;movsl for copying, rather than doing each byte.
- Create swsusp_suspend and swsusp_resume wrappers for calling the assmebly
  routines that:
  - Call {save,restore}_processor_state() in each. 
  - Disable/enable interrupts in each. 
- Call swsusp_{suspend,restore} in software_{suspend,resume}
- Kill all the do_magic_* functions.
- Remove prototypes from linux/suspend.h
- Remove similar pmdisk functions.
- Update calls in kernel/power/disk.c to use swsusp versions.
parent 2e633f4f
obj-$(CONFIG_PM) += cpu.o obj-$(CONFIG_PM) += cpu.o
obj-$(CONFIG_PM_DISK) += pmdisk.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
...@@ -15,83 +15,47 @@ ...@@ -15,83 +15,47 @@
.text .text
ENTRY(do_magic) ENTRY(swsusp_arch_suspend)
pushl %ebx
cmpl $0,8(%esp)
jne resume
call do_magic_suspend_1
call save_processor_state
movl %esp, saved_context_esp movl %esp, saved_context_esp
movl %eax, saved_context_eax
movl %ebx, saved_context_ebx movl %ebx, saved_context_ebx
movl %ecx, saved_context_ecx
movl %edx, saved_context_edx
movl %ebp, saved_context_ebp movl %ebp, saved_context_ebp
movl %esi, saved_context_esi movl %esi, saved_context_esi
movl %edi, saved_context_edi movl %edi, saved_context_edi
pushfl ; popl saved_context_eflags pushfl ; popl saved_context_eflags
call do_magic_suspend_2 call swsusp_save
popl %ebx
ret ret
resume: ENTRY(swsusp_arch_resume)
movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx
movl %ecx,%cr3 movl %ecx,%cr3
call do_magic_resume_1 movl pagedir_nosave,%ebx
movl $0,loop xorl %eax, %eax
cmpl $0,nr_copy_pages xorl %edx, %edx
je copy_done
copy_loop:
movl $0,loop2
.p2align 4,,7 .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 copy_loop:
leal 1(%eax),%edx movl 4(%ebx,%edx),%edi
movl %edx,loop2 movl (%ebx,%edx),%esi
movl %edx,%eax
cmpl $4095,%eax movl $1024, %ecx
jbe copy_one_page rep
movl loop,%eax movsl
leal 1(%eax),%edx
movl %edx,loop incl %eax
movl %edx,%eax addl $16, %edx
cmpl nr_copy_pages,%eax cmpl nr_copy_pages,%eax
jb copy_loop jb copy_loop
.p2align 4,,7
copy_done:
movl $__USER_DS,%eax
movw %ax, %ds
movw %ax, %es
movl saved_context_esp, %esp movl saved_context_esp, %esp
movl saved_context_ebp, %ebp movl saved_context_ebp, %ebp
movl saved_context_eax, %eax
movl saved_context_ebx, %ebx movl saved_context_ebx, %ebx
movl saved_context_ecx, %ecx
movl saved_context_edx, %edx
movl saved_context_esi, %esi movl saved_context_esi, %esi
movl saved_context_edi, %edi movl saved_context_edi, %edi
call restore_processor_state
pushl saved_context_eflags ; popfl pushl saved_context_eflags ; popfl
call do_magic_resume_2 call swsusp_restore
popl %ebx
ret ret
.section .data.nosave
loop:
.quad 0
loop2:
.quad 0
.previous
...@@ -75,12 +75,6 @@ static inline void disable_nonboot_cpus(void) {} ...@@ -75,12 +75,6 @@ static inline void disable_nonboot_cpus(void) {}
static inline void enable_nonboot_cpus(void) {} static inline void enable_nonboot_cpus(void) {}
#endif #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 save_processor_state(void);
void restore_processor_state(void); void restore_processor_state(void);
struct saved_context; struct saved_context;
......
...@@ -23,10 +23,10 @@ ...@@ -23,10 +23,10 @@
extern u32 pm_disk_mode; extern u32 pm_disk_mode;
extern struct pm_ops * pm_ops; extern struct pm_ops * pm_ops;
extern int pmdisk_save(void); extern int swsusp_suspend(void);
extern int pmdisk_write(void); extern int pmdisk_write(void);
extern int pmdisk_read(void); extern int pmdisk_read(void);
extern int pmdisk_restore(void); extern int swsusp_resume(void);
extern int pmdisk_free(void); extern int pmdisk_free(void);
...@@ -161,7 +161,7 @@ int pm_suspend_disk(void) ...@@ -161,7 +161,7 @@ int pm_suspend_disk(void)
pr_debug("PM: snapshotting memory.\n"); pr_debug("PM: snapshotting memory.\n");
in_suspend = 1; in_suspend = 1;
if ((error = pmdisk_save())) if ((error = swsusp_save()))
goto Done; goto Done;
if (in_suspend) { if (in_suspend) {
...@@ -227,7 +227,7 @@ static int pm_resume(void) ...@@ -227,7 +227,7 @@ static int pm_resume(void)
mdelay(1000); mdelay(1000);
pr_debug("PM: Restoring saved image.\n"); pr_debug("PM: Restoring saved image.\n");
pmdisk_restore(); swsusp_resume();
pr_debug("PM: Restore failed, recovering.n"); pr_debug("PM: Restore failed, recovering.n");
finish(); finish();
Free: Free:
......
...@@ -249,28 +249,7 @@ static int write_suspend_image(void) ...@@ -249,28 +249,7 @@ static int write_suspend_image(void)
extern void free_suspend_pagedir(unsigned long); extern void free_suspend_pagedir(unsigned long);
extern int suspend_prepare_image(void);
/**
* pmdisk_suspend - Atomically snapshot the system.
*
* This must be called with interrupts disabled, to prevent the
* system changing at all from underneath us.
*
* To do this, we count the number of pages in the system that we
* need to save; make sure we have enough memory and swap to clone
* the pages and save them in swap, allocate the space to hold them,
* and then snapshot them all.
*/
int pmdisk_suspend(void)
{
int error = 0;
if ((error = swsusp_swap_check()))
return error;
return suspend_prepare_image();
}
/** /**
...@@ -297,36 +276,6 @@ static int suspend_save_image(void) ...@@ -297,36 +276,6 @@ static int suspend_save_image(void)
return error; return error;
} }
/*
* Magic happens here
*/
int pmdisk_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();
return 0;
}
/* pmdisk_arch_suspend() is implemented in arch/?/power/pmdisk.S,
and basically does:
if (!resume) {
save_processor_state();
SAVE_REGISTERS
return pmdisk_suspend();
}
GO_TO_SWAPPER_PAGE_TABLES
COPY_PAGES_BACK
RESTORE_REGISTERS
restore_processor_state();
return pmdisk_resume();
*/
/* More restore stuff */ /* More restore stuff */
...@@ -563,28 +512,6 @@ static int __init read_suspend_image(void) ...@@ -563,28 +512,6 @@ static int __init read_suspend_image(void)
goto Done; goto Done;
} }
/**
* pmdisk_save - Snapshot memory
*/
int pmdisk_save(void)
{
int error;
#if defined (CONFIG_HIGHMEM) || defined (CONFIG_DISCONTIGMEM)
pr_debug("pmdisk: not supported with high- or discontig-mem.\n");
return -EPERM;
#endif
if ((error = arch_prepare_suspend()))
return error;
local_irq_disable();
save_processor_state();
error = pmdisk_arch_suspend(0);
restore_processor_state();
local_irq_enable();
return error;
}
/** /**
* pmdisk_write - Write saved memory image to swap. * pmdisk_write - Write saved memory image to swap.
...@@ -632,22 +559,6 @@ int __init pmdisk_read(void) ...@@ -632,22 +559,6 @@ int __init pmdisk_read(void)
} }
/**
* pmdisk_restore - Replace running kernel with saved image.
*/
int __init pmdisk_restore(void)
{
int error;
local_irq_disable();
save_processor_state();
error = pmdisk_arch_suspend(1);
restore_processor_state();
local_irq_enable();
return error;
}
/** /**
* pmdisk_free - Free memory allocated to hold snapshot. * pmdisk_free - Free memory allocated to hold snapshot.
*/ */
......
...@@ -893,80 +893,58 @@ static void suspend_finish(void) ...@@ -893,80 +893,58 @@ static void suspend_finish(void)
#endif #endif
} }
/*
* Magic happens here
*/
asmlinkage void do_magic_resume_1(void) extern asmlinkage int swsusp_arch_suspend(void);
{ extern asmlinkage int swsusp_arch_resume(void);
barrier();
mb();
spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */
device_power_down(3);
PRINTK( "Waiting for DMAs to settle down...\n");
mdelay(1000); /* 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 ;-). */
}
asmlinkage void do_magic_resume_2(void) asmlinkage int swsusp_save(void)
{ {
BUG_ON (nr_copy_pages_check != nr_copy_pages); int error = 0;
BUG_ON (pagedir_order_check != pagedir_order);
__flush_tlb_global(); /* Even mappings of "global" things (vmalloc) need to be fixed */ if ((error = swsusp_swap_check()))
device_power_up(); return error;
spin_unlock_irq(&suspend_pagedir_lock); return suspend_prepare_image();
} }
/* do_magic() is implemented in arch/?/kernel/suspend_asm.S, and basically does: int swsusp_suspend(void)
{
if (!resume) { int error;
do_magic_suspend_1(); if ((error = arch_prepare_suspend()))
return error;
local_irq_disable();
save_processor_state(); save_processor_state();
SAVE_REGISTERS error = swsusp_arch_suspend();
do_magic_suspend_2();
return;
}
GO_TO_SWAPPER_PAGE_TABLES
do_magic_resume_1();
COPY_PAGES_BACK
RESTORE_REGISTERS
restore_processor_state(); restore_processor_state();
do_magic_resume_2(); local_irq_enable();
return error;
}
*/
asmlinkage void do_magic_suspend_1(void) asmlinkage int swsusp_restore(void)
{ {
mb(); BUG_ON (nr_copy_pages_check != nr_copy_pages);
barrier(); BUG_ON (pagedir_order_check != pagedir_order);
BUG_ON(in_atomic());
spin_lock_irq(&suspend_pagedir_lock); /* Even mappings of "global" things (vmalloc) need to be fixed */
__flush_tlb_global();
return 0;
} }
asmlinkage void do_magic_suspend_2(void) int swsusp_resume(void)
{ {
int is_problem; int error;
swsusp_swap_check(); local_irq_disable();
device_power_down(3); save_processor_state();
is_problem = suspend_prepare_image(); error = swsusp_arch_resume();
device_power_up(); restore_processor_state();
spin_unlock_irq(&suspend_pagedir_lock); local_irq_enable();
if (!is_problem) { return error;
kernel_fpu_end(); /* save_processor_state() does kernel_fpu_begin, and we need to revert it in order to pass in_atomic() checks */ }
BUG_ON(in_atomic());
suspend_save_image();
suspend_power_down(); /* FIXME: if suspend_power_down is commented out, console is lost after few suspends ?! */
}
printk(KERN_EMERG "%sSuspend failed, trying to recover...\n", name_suspend);
MDELAY(1000); /* So user can wait and report us messages if armageddon comes :-) */
barrier();
mb(); static int in_suspend __nosavedata = 0;
}
/* /*
* This is main interface to the outside world. It needs to be * This is main interface to the outside world. It needs to be
...@@ -998,16 +976,15 @@ int software_suspend(void) ...@@ -998,16 +976,15 @@ int software_suspend(void)
/* Save state of all device drivers, and stop them. */ /* Save state of all device drivers, and stop them. */
printk("Suspending devices... "); printk("Suspending devices... ");
if ((res = device_suspend(3))==0) { if ((res = device_suspend(3))==0) {
/* If stopping device drivers worked, we proceed basically into in_suspend = 1;
* suspend_save_image.
* res = swsusp_save();
* do_magic(0) returns after system is resumed.
* if (!res && in_suspend) {
* do_magic() copies all "used" memory to "free" memory, then suspend_save_image();
* unsuspends all device drivers, and writes memory to disk suspend_power_down();
* using normal kernel mechanism. }
*/ in_suspend = 0;
do_magic(0);
suspend_finish(); suspend_finish();
} }
thaw_processes(); thaw_processes();
...@@ -1352,7 +1329,7 @@ static int __init software_resume(void) ...@@ -1352,7 +1329,7 @@ static int __init software_resume(void)
/* FIXME: Should we stop processes here, just to be safer? */ /* FIXME: Should we stop processes here, just to be safer? */
disable_nonboot_cpus(); disable_nonboot_cpus();
device_suspend(3); device_suspend(3);
do_magic(1); swsusp_resume();
panic("This never returns"); panic("This never returns");
read_failure: read_failure:
......
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