From 13c9f31aba86054cbe508c28a9001dd516f04910 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 19:25:59 -0700 Subject: [Power Mgmt] Share variables between pmdisk and swsusp. - In pmdisk, change pm_pagedir_nosave back to pagedir_nosave, and pmdisk_pages back to nr_copy_pages. - Mark them, and other page count/pagedir variables extern. - Make sure they're not static in swsusp. - Remove mention from include/linux/suspend.h, since no one else needs them. --- include/linux/suspend.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/suspend.h b/include/linux/suspend.h index d0955f06c9b4..2daac52c53a3 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -45,9 +45,6 @@ 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) { -- cgit v1.2.3 From 44ea4dc07e7a1281293386e755f9a96d523e84b3 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 21:11:04 -0700 Subject: [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. --- arch/i386/power/Makefile | 1 - arch/i386/power/swsusp.S | 76 +++++++++---------------------- include/linux/suspend.h | 6 --- kernel/power/disk.c | 8 ++-- kernel/power/pmdisk.c | 89 ------------------------------------ kernel/power/swsusp.c | 115 +++++++++++++++++++---------------------------- 6 files changed, 70 insertions(+), 225 deletions(-) (limited to 'include') diff --git a/arch/i386/power/Makefile b/arch/i386/power/Makefile index 2e1c9ab34d4c..8cfa4e8a719d 100644 --- a/arch/i386/power/Makefile +++ b/arch/i386/power/Makefile @@ -1,3 +1,2 @@ obj-$(CONFIG_PM) += cpu.o -obj-$(CONFIG_PM_DISK) += pmdisk.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o diff --git a/arch/i386/power/swsusp.S b/arch/i386/power/swsusp.S index 8e4a7bacaadf..221ac27a204f 100644 --- a/arch/i386/power/swsusp.S +++ b/arch/i386/power/swsusp.S @@ -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 diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 2daac52c53a3..496d17f40291 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -75,12 +75,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; diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 6abcf99b7ada..f3a4a4b1bb56 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -23,10 +23,10 @@ extern u32 pm_disk_mode; extern struct pm_ops * pm_ops; -extern int pmdisk_save(void); +extern int swsusp_suspend(void); extern int pmdisk_write(void); extern int pmdisk_read(void); -extern int pmdisk_restore(void); +extern int swsusp_resume(void); extern int pmdisk_free(void); @@ -161,7 +161,7 @@ int pm_suspend_disk(void) pr_debug("PM: snapshotting memory.\n"); in_suspend = 1; - if ((error = pmdisk_save())) + if ((error = swsusp_save())) goto Done; if (in_suspend) { @@ -227,7 +227,7 @@ static int pm_resume(void) mdelay(1000); pr_debug("PM: Restoring saved image.\n"); - pmdisk_restore(); + swsusp_resume(); pr_debug("PM: Restore failed, recovering.n"); finish(); Free: diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index f10253a3840a..bbe93fdc1cdf 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -249,28 +249,7 @@ static int write_suspend_image(void) 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) 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 */ @@ -563,28 +512,6 @@ static int __init read_suspend_image(void) 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. @@ -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. */ diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 91d8390dc2f5..7de6b26f4efe 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -893,80 +893,58 @@ static void suspend_finish(void) #endif } -/* - * Magic happens here - */ -asmlinkage void do_magic_resume_1(void) -{ - barrier(); - mb(); - spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ +extern asmlinkage int swsusp_arch_suspend(void); +extern asmlinkage int swsusp_arch_resume(void); - 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); - BUG_ON (pagedir_order_check != pagedir_order); + int error = 0; - __flush_tlb_global(); /* Even mappings of "global" things (vmalloc) need to be fixed */ - device_power_up(); - spin_unlock_irq(&suspend_pagedir_lock); + if ((error = swsusp_swap_check())) + return error; + return suspend_prepare_image(); } -/* do_magic() is implemented in arch/?/kernel/suspend_asm.S, and basically does: - - if (!resume) { - do_magic_suspend_1(); - save_processor_state(); - SAVE_REGISTERS - do_magic_suspend_2(); - return; - } - GO_TO_SWAPPER_PAGE_TABLES - do_magic_resume_1(); - COPY_PAGES_BACK - RESTORE_REGISTERS +int swsusp_suspend(void) +{ + int error; + if ((error = arch_prepare_suspend())) + return error; + local_irq_disable(); + save_processor_state(); + error = swsusp_arch_suspend(); 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(); - barrier(); - BUG_ON(in_atomic()); - spin_lock_irq(&suspend_pagedir_lock); + 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; } -asmlinkage void do_magic_suspend_2(void) +int swsusp_resume(void) { - int is_problem; - swsusp_swap_check(); - device_power_down(3); - is_problem = suspend_prepare_image(); - device_power_up(); - spin_unlock_irq(&suspend_pagedir_lock); - if (!is_problem) { - 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 ?! */ - } + int error; + local_irq_disable(); + save_processor_state(); + error = swsusp_arch_resume(); + restore_processor_state(); + local_irq_enable(); + return error; +} - 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 @@ -998,16 +976,15 @@ int software_suspend(void) /* Save state of all device drivers, and stop them. */ printk("Suspending devices... "); if ((res = device_suspend(3))==0) { - /* If stopping device drivers worked, we proceed basically into - * suspend_save_image. - * - * do_magic(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 - * using normal kernel mechanism. - */ - do_magic(0); + in_suspend = 1; + + res = swsusp_save(); + + if (!res && in_suspend) { + suspend_save_image(); + suspend_power_down(); + } + in_suspend = 0; suspend_finish(); } thaw_processes(); @@ -1352,7 +1329,7 @@ static int __init software_resume(void) /* FIXME: Should we stop processes here, just to be safer? */ disable_nonboot_cpus(); device_suspend(3); - do_magic(1); + swsusp_resume(); panic("This never returns"); read_failure: -- cgit v1.2.3 From 62ca4334fa3b23492b79ab2887b62ac4bb4c2ed2 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 22:23:27 -0700 Subject: [Power Mgmt] Merge swsusp and pmdisk info headers. - Move definition of struct pmdsik_info to power.h and rename to struct swsusp_info. - Kill struct suspend_header. - Move helpers from pmdisk into swsusp: init_header(), dump_info(), write_header(), sanity_check(), check_header(). - Fix up calls in pmdisk to call the right ones. - Clean up swsusp code to use helpers; delete duplicates. --- include/linux/suspend.h | 10 --- kernel/power/pmdisk.c | 131 ++++-------------------------------- kernel/power/power.h | 16 ++++- kernel/power/swsusp.c | 175 +++++++++++++++++++++++++++++------------------- 4 files changed, 135 insertions(+), 197 deletions(-) (limited to 'include') diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 496d17f40291..6b63016b55a9 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -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) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index ef0d74f351b4..53b47002f5e3 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -28,7 +28,6 @@ #include #include #include -#include #include @@ -61,17 +60,7 @@ extern suspend_pagedir_t *pagedir_nosave; extern suspend_pagedir_t *pagedir_save; extern int pagedir_order; - -struct pmdisk_info { - struct new_utsname uts; - u32 version_code; - unsigned long num_physpages; - int cpus; - unsigned long image_pages; - unsigned long pagedir_pages; - swp_entry_t pagedir[768]; -} __attribute__((aligned(PAGE_SIZE))) pmdisk_info; - +extern struct swsusp_info swsusp_info; #define PMDISK_SIG "pmdisk-swap1" @@ -139,11 +128,11 @@ extern void swsusp_data_free(void); static void free_pagedir_entries(void) { - int num = pmdisk_info.pagedir_pages; + int num = swsusp_info.pagedir_pages; int i; for (i = 0; i < num; i++) - swap_free(pmdisk_info.pagedir[i]); + swap_free(swsusp_info.pagedir[i]); } @@ -159,64 +148,15 @@ static int write_pagedir(void) int n = SUSPEND_PD_PAGES(nr_copy_pages); int i; - pmdisk_info.pagedir_pages = n; + swsusp_info.pagedir_pages = n; printk( "Writing pagedir (%d pages)\n", n); for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) - error = swsusp_write_page(addr,&pmdisk_info.pagedir[i]); + error = swsusp_write_page(addr,&swsusp_info.pagedir[i]); return error; } - -#ifdef DEBUG -static void dump_pmdisk_info(void) -{ - printk(" pmdisk: Version: %u\n",pmdisk_info.version_code); - printk(" pmdisk: Num Pages: %ld\n",pmdisk_info.num_physpages); - printk(" pmdisk: UTS Sys: %s\n",pmdisk_info.uts.sysname); - printk(" pmdisk: UTS Node: %s\n",pmdisk_info.uts.nodename); - printk(" pmdisk: UTS Release: %s\n",pmdisk_info.uts.release); - printk(" pmdisk: UTS Version: %s\n",pmdisk_info.uts.version); - printk(" pmdisk: UTS Machine: %s\n",pmdisk_info.uts.machine); - printk(" pmdisk: UTS Domain: %s\n",pmdisk_info.uts.domainname); - printk(" pmdisk: CPUs: %d\n",pmdisk_info.cpus); - printk(" pmdisk: Image: %ld Pages\n",pmdisk_info.image_pages); - printk(" pmdisk: Pagedir: %ld Pages\n",pmdisk_info.pagedir_pages); -} -#else -static void dump_pmdisk_info(void) -{ - -} -#endif - -static void init_header(void) -{ - memset(&pmdisk_info,0,sizeof(pmdisk_info)); - pmdisk_info.version_code = LINUX_VERSION_CODE; - pmdisk_info.num_physpages = num_physpages; - memcpy(&pmdisk_info.uts,&system_utsname,sizeof(system_utsname)); - - pmdisk_info.cpus = num_online_cpus(); - pmdisk_info.image_pages = nr_copy_pages; - dump_pmdisk_info(); -} - -/** - * write_header - Fill and write the suspend header. - * @entry: Location of the last swap entry used. - * - * Allocate a page, fill header, write header. - * - * @entry is the location of the last pagedir entry written on - * entrance. On exit, it contains the location of the header. - */ - -static int write_header(swp_entry_t * entry) -{ - return swsusp_write_page((unsigned long)&pmdisk_info,entry); -} - - +extern void swsusp_init_header(void); +extern int swsusp_write_header(swp_entry_t*); /** * write_suspend_image - Write entire image and metadata. @@ -228,13 +168,14 @@ static int write_suspend_image(void) int error; swp_entry_t prev = { 0 }; + swsusp_init_header(); if ((error = swsusp_data_write())) goto FreeData; if ((error = write_pagedir())) goto FreePagedir; - if ((error = write_header(&prev))) + if ((error = swsusp_write_header(&prev))) goto FreePagedir; error = mark_swapfiles(prev); @@ -310,57 +251,10 @@ static int __init check_sig(void) } -/* - * Sanity check if this image makes sense with this kernel/swap context - * I really don't think that it's foolproof but more than nothing.. - */ - -static const char * __init sanity_check(void) -{ - dump_pmdisk_info(); - if(pmdisk_info.version_code != LINUX_VERSION_CODE) - return "kernel version"; - if(pmdisk_info.num_physpages != num_physpages) - return "memory size"; - if (strcmp(pmdisk_info.uts.sysname,system_utsname.sysname)) - return "system type"; - if (strcmp(pmdisk_info.uts.release,system_utsname.release)) - return "kernel release"; - if (strcmp(pmdisk_info.uts.version,system_utsname.version)) - return "version"; - if (strcmp(pmdisk_info.uts.machine,system_utsname.machine)) - return "machine"; - if(pmdisk_info.cpus != num_online_cpus()) - return "number of cpus"; - return NULL; -} - - -static int __init check_header(void) -{ - const char * reason = NULL; - int error; - - init_header(); - - if ((error = bio_read_page(swp_offset(pmdisk_header.pmdisk_info), - &pmdisk_info))) - return error; - - /* Is this same machine? */ - if ((reason = sanity_check())) { - printk(KERN_ERR "pmdisk: Resume mismatch: %s\n",reason); - return -EPERM; - } - nr_copy_pages = pmdisk_info.image_pages; - return error; -} - - static int __init read_pagedir(void) { unsigned long addr; - int i, n = pmdisk_info.pagedir_pages; + int i, n = swsusp_info.pagedir_pages; int error = 0; pagedir_order = get_bitmask_order(n); @@ -373,7 +267,7 @@ static int __init read_pagedir(void) pr_debug("pmdisk: Reading pagedir (%d Pages)\n",n); for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) { - unsigned long offset = swp_offset(pmdisk_info.pagedir[i]); + unsigned long offset = swp_offset(swsusp_info.pagedir[i]); if (offset) error = bio_read_page(offset, (void *)addr); else @@ -388,12 +282,13 @@ static int __init read_pagedir(void) static int __init read_suspend_image(void) { extern int swsusp_data_read(void); + extern int swsusp_check_header(swp_entry_t); int error = 0; if ((error = check_sig())) return error; - if ((error = check_header())) + if ((error = swsusp_check_header(pmdisk_header.pmdisk_info))) return error; if ((error = read_pagedir())) return error; diff --git a/kernel/power/power.h b/kernel/power/power.h index 92424ccc2076..69c693cea1b5 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -1,4 +1,5 @@ - +#include +#include /* With SUSPEND_CONSOLE defined, it suspend looks *really* cool, but we probably do not take enough locks for switching consoles, etc, @@ -9,6 +10,19 @@ #endif +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_PM_DISK extern int pm_suspend_disk(void); diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 21cb666a7a81..9cb01d8cc92d 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -111,6 +111,8 @@ suspend_pagedir_t *pagedir_nosave __nosavedata = NULL; suspend_pagedir_t *pagedir_save; int pagedir_order __nosavedata = 0; +struct swsusp_info swsusp_info; + struct link { char dummy[PAGE_SIZE - sizeof(swp_entry_t)]; swp_entry_t next; @@ -119,7 +121,6 @@ struct link { union diskpage { union swap_header swh; struct link link; - struct suspend_header sh; }; /* @@ -155,27 +156,6 @@ static const char name_resume[] = "Resume Machine: "; * Saving part... */ -static __inline__ int fill_suspend_header(struct suspend_header *sh) -{ - memset((char *)sh, 0, sizeof(*sh)); - - sh->version_code = LINUX_VERSION_CODE; - sh->num_physpages = num_physpages; - strncpy(sh->machine, system_utsname.machine, 8); - strncpy(sh->version, system_utsname.version, 20); - /* FIXME: Is this bogus? --RR */ - sh->num_cpus = num_online_cpus(); - sh->page_size = PAGE_SIZE; - sh->suspend_pagedir = pagedir_nosave; - BUG_ON (pagedir_save != pagedir_nosave); - sh->num_pbes = nr_copy_pages; - /* TODO: needed? mounted fs' last mounted date comparison - * [so they haven't been mounted since last suspend. - * Maybe it isn't.] [we'd need to do this for _all_ fs-es] - */ - return 0; -} - /* We memorize in swapfile_used what swap devices are used for suspension */ #define SWAPFILE_UNUSED 0 #define SWAPFILE_SUSPEND 1 /* This is the suspending device */ @@ -375,7 +355,55 @@ int swsusp_data_write(void) return error; } +#ifdef DEBUG +static void dump_info(void) +{ + printk(" swsusp: Version: %u\n",swsusp_info.version_code); + printk(" swsusp: Num Pages: %ld\n",swsusp_info.num_physpages); + printk(" swsusp: UTS Sys: %s\n",swsusp_info.uts.sysname); + printk(" swsusp: UTS Node: %s\n",swsusp_info.uts.nodename); + printk(" swsusp: UTS Release: %s\n",swsusp_info.uts.release); + printk(" swsusp: UTS Version: %s\n",swsusp_info.uts.version); + printk(" swsusp: UTS Machine: %s\n",swsusp_info.uts.machine); + printk(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname); + printk(" swsusp: CPUs: %d\n",swsusp_info.cpus); + printk(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages); + printk(" swsusp: Pagedir: %ld Pages\n",swsusp_info.pagedir_pages); +} +#else +static void dump_info(void) +{ + +} +#endif + +void swsusp_init_header(void) +{ + memset(&swsusp_info,0,sizeof(swsusp_info)); + swsusp_info.version_code = LINUX_VERSION_CODE; + swsusp_info.num_physpages = num_physpages; + memcpy(&swsusp_info.uts,&system_utsname,sizeof(system_utsname)); + + swsusp_info.suspend_pagedir = pagedir_nosave; + swsusp_info.cpus = num_online_cpus(); + swsusp_info.image_pages = nr_copy_pages; + dump_info(); +} + +/** + * write_header - Fill and write the suspend header. + * @entry: Location of the last swap entry used. + * + * Allocate a page, fill header, write header. + * + * @entry is the location of the last pagedir entry written on + * entrance. On exit, it contains the location of the header. + */ +int swsusp_write_header(swp_entry_t * entry) +{ + return swsusp_write_page((unsigned long)&swsusp_info,entry); +} /** * write_suspend_image - Write entire image to disk. @@ -411,17 +439,12 @@ static int write_suspend_image(void) error = swsusp_write_page(addr,&entry); } printk("H"); - BUG_ON (sizeof(struct suspend_header) > PAGE_SIZE-sizeof(swp_entry_t)); BUG_ON (sizeof(union diskpage) != PAGE_SIZE); BUG_ON (sizeof(struct link) != PAGE_SIZE); - cur = (void *) buffer; - if (fill_suspend_header(&cur->sh)) - BUG(); /* Not a BUG_ON(): we want fill_suspend_header to be called, always */ - - cur->link.next = entry; - if ((error = swsusp_write_page((unsigned long)cur,&entry))) - return error; + swsusp_init_header(); + swsusp_info.pagedir[0] = entry; + error = swsusp_write_header(&entry); printk( "S" ); mark_swapfiles(entry, MARK_SWAP_SUSPEND); printk( "|\n" ); @@ -734,7 +757,7 @@ static int alloc_image_pages(void) static int enough_free_mem(void) { if(nr_free_pages() < (nr_copy_pages + PAGES_FOR_IO)) { - pr_debug("pmdisk: Not enough free pages: Have %d\n", + pr_debug("swsusp: Not enough free pages: Have %d\n", nr_free_pages()); return 0; } @@ -758,7 +781,7 @@ static int enough_swap(void) si_swapinfo(&i); if (i.freeswap < (nr_copy_pages + PAGES_FOR_IO)) { - pr_debug("pmdisk: Not enough swap. Need %ld\n",i.freeswap); + pr_debug("swsusp: Not enough swap. Need %ld\n",i.freeswap); return 0; } return 1; @@ -1087,35 +1110,6 @@ static int __init swsusp_pagedir_relocate(void) return check_pagedir(); } -/* - * Sanity check if this image makes sense with this kernel/swap context - * I really don't think that it's foolproof but more than nothing.. - */ - -static int sanity_check_failed(char *reason) -{ - printk(KERN_ERR "%s%s\n", name_resume, reason); - return -EPERM; -} - -static int sanity_check(struct suspend_header *sh) -{ - if (sh->version_code != LINUX_VERSION_CODE) - return sanity_check_failed("Incorrect kernel version"); - if (sh->num_physpages != num_physpages) - return sanity_check_failed("Incorrect memory size"); - if (strncmp(sh->machine, system_utsname.machine, 8)) - return sanity_check_failed("Incorrect machine type"); - if (strncmp(sh->version, system_utsname.version, 20)) - return sanity_check_failed("Incorrect version"); - if (sh->num_cpus != num_online_cpus()) - return sanity_check_failed("Incorrect number of cpus"); - if (sh->page_size != PAGE_SIZE) - return sanity_check_failed("Incorrect PAGE_SIZE"); - return 0; -} - - /** * Using bio to read from swap. * This code requires a bit more work than just using buffer heads @@ -1172,7 +1166,7 @@ static int submit(int rw, pgoff_t page_off, void * page) bio->bi_end_io = end_io; if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) { - printk("pmdisk: ERROR: adding page to bio at %ld\n",page_off); + printk("swsusp: ERROR: adding page to bio at %ld\n",page_off); error = -EFAULT; goto Done; } @@ -1197,6 +1191,50 @@ int bio_write_page(pgoff_t page_off, void * page) return submit(WRITE,page_off,page); } +/* + * Sanity check if this image makes sense with this kernel/swap context + * I really don't think that it's foolproof but more than nothing.. + */ + +static const char * __init sanity_check(void) +{ + dump_info(); + if(swsusp_info.version_code != LINUX_VERSION_CODE) + return "kernel version"; + if(swsusp_info.num_physpages != num_physpages) + return "memory size"; + if (strcmp(swsusp_info.uts.sysname,system_utsname.sysname)) + return "system type"; + if (strcmp(swsusp_info.uts.release,system_utsname.release)) + return "kernel release"; + if (strcmp(swsusp_info.uts.version,system_utsname.version)) + return "version"; + if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) + return "machine"; + if(swsusp_info.cpus != num_online_cpus()) + return "number of cpus"; + return NULL; +} + + +int __init swsusp_check_header(swp_entry_t loc) +{ + const char * reason = NULL; + int error; + + if ((error = bio_read_page(swp_offset(loc), &swsusp_info))) + return error; + + /* Is this same machine? */ + if ((reason = sanity_check())) { + printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason); + return -EPERM; + } + nr_copy_pages = swsusp_info.image_pages; + return error; +} + + /** * swsusp_read_data - Read image pages from swap. @@ -1276,13 +1314,14 @@ static int __init __read_suspend_image(int noresume) printk( "%sSignature found, resuming\n", name_resume ); MDELAY(1000); - if (bio_read_page(next.val, cur)) return -EIO; - if (sanity_check(&cur->sh)) /* Is this same machine? */ - return -EPERM; - PREPARENEXT; + if ((error = swsusp_check_header(next))) + return error; + + next = swsusp_info.pagedir[0]; + next.val = swp_offset(next); - pagedir_save = cur->sh.suspend_pagedir; - nr_copy_pages = cur->sh.num_pbes; + pagedir_save = swsusp_info.suspend_pagedir; + nr_copy_pages = swsusp_info.image_pages; nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages); pagedir_order = get_bitmask_order(nr_pgdir_pages); -- cgit v1.2.3 From e2676af65065dbf62e1a8e59016ed8536d0fee4a Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sat, 17 Jul 2004 00:09:36 -0700 Subject: [Power Mgmt] Merge swsusp entry points with the PM core. - Add {enable,disable}_nonboot_cpus() to prepare() and finish() in kernel/power/disk.c - Move swsusp __setup options there. Remove resume_status variable in favor of simpler 'noresume' variable, and check it in pm_resume(). - Remove software_resume() from swsusp; rename pm_resume() to software_resume(). The latter is considerably cleaner, and leverages the core code. - Move software_suspend() to kernel/power/main.c and shrink it down a wrapper that takes pm_sem and calls pm_suspend_disk(), which does the same as the old software_suspend() did. This keeps the same entry points (via ACPI sleep and the reboot() syscall), but actually allows those to use the low-level power states of the system rather than always shutting off the system. - Remove now-unused functions from swsusp. --- include/linux/suspend.h | 1 - kernel/power/disk.c | 49 ++++++++---- kernel/power/main.c | 15 ++++ kernel/power/swsusp.c | 200 +----------------------------------------------- 4 files changed, 52 insertions(+), 213 deletions(-) (limited to 'include') diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 6b63016b55a9..932dc2ca4152 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -41,7 +41,6 @@ static inline int software_suspend(void) printk("Warning: fake suspend called\n"); return -EPERM; } -#define software_resume() do { } while(0) #endif /* CONFIG_SOFTWARE_SUSPEND */ diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 6d4119cf40cc..ab49261f078f 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -30,6 +30,9 @@ 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 @@ -99,6 +102,7 @@ static void finish(void) { device_resume(); platform_finish(); + enable_nonboot_cpus(); thaw_processes(); pm_restore_console(); } @@ -126,6 +130,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 +138,7 @@ static int prepare(void) Finish: platform_finish(); Thaw: + enable_nonboot_cpus(); thaw_processes(); pm_restore_console(); return error; @@ -188,7 +194,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,10 +205,18 @@ 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 = swsusp_read())) @@ -216,16 +230,6 @@ 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"); swsusp_resume(); pr_debug("PM: Restore failed, recovering.n"); @@ -237,7 +241,7 @@ static int pm_resume(void) return 0; } -late_initcall(pm_resume); +late_initcall(software_resume); static char * pm_disk_modes[] = { @@ -336,3 +340,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); diff --git a/kernel/power/main.c b/kernel/power/main.c index d582906fecc6..3e37ade640f6 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -169,6 +169,21 @@ 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) +{ + int error; + + if (down_trylock(&pm_sem)) + return -EBUSY; + error = pm_suspend_disk(); + up(&pm_sem); + return error; +} + /** * pm_suspend - Externally visible function for suspending system. diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 74805928f5d8..e478c7e37bc6 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -71,11 +71,6 @@ #include "power.h" -unsigned char software_suspend_enabled = 0; - -#define NORESUME 1 -#define RESUME_SPECIFIED 2 - /* References to section boundaries */ extern char __nosave_begin, __nosave_end; @@ -85,8 +80,7 @@ extern int is_head_of_free_region(struct page *); int pagedir_order_check; int nr_copy_pages_check; -static int resume_status; -static char resume_file[256] = CONFIG_PM_STD_PARTITION; +extern char resume_file[]; static dev_t resume_device; /* Local variables that should not be affected by save */ unsigned int nr_copy_pages __nosavedata = 0; @@ -664,30 +658,6 @@ void swsusp_free(void) free_pages(p, pagedir_order); } -static int prepare_suspend_processes(void) -{ - sys_sync(); /* Syncing needs pdflushd, so do it before stopping processes */ - if (freeze_processes()) { - printk( KERN_ERR "Suspend failed: Not all processes stopped!\n" ); - thaw_processes(); - return 1; - } - return 0; -} - -/* - * 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 void calc_order(void) { @@ -878,48 +848,6 @@ int swsusp_write(void) } -static void suspend_power_down(void) -{ - extern int C_A_D; - C_A_D = 0; - printk(KERN_EMERG "%s%s Trying to power down.\n", name_suspend, TEST_SWSUSP ? "Disable TEST_SWSUSP. NOT ": ""); -#ifdef CONFIG_VT - PRINTK(KERN_EMERG "shift_state: %04x\n", shift_state); - mdelay(1000); - if (TEST_SWSUSP ^ (!!(shift_state & (1 << KG_CTRL)))) - machine_restart(NULL); - else -#endif - { - device_suspend(3); - device_shutdown(); - machine_power_off(); - } - - printk(KERN_EMERG "%sProbably not capable for powerdown. System halted.\n", name_suspend); - machine_halt(); - while (1); - /* NOTREACHED */ -} - - -static void suspend_finish(void) -{ - free_pages((unsigned long) pagedir_nosave, pagedir_order); - -#ifdef CONFIG_HIGHMEM - printk( "Restoring highmem\n" ); - restore_highmem(); -#endif - device_resume(); - -#ifdef SUSPEND_CONSOLE - acquire_console_sem(); - update_screen(fg_console); - release_console_sem(); -#endif -} - extern asmlinkage int swsusp_arch_suspend(void); extern asmlinkage int swsusp_arch_resume(void); @@ -971,59 +899,6 @@ int swsusp_resume(void) -static int in_suspend __nosavedata = 0; - -/* - * This is main interface to the outside world. It needs to be - * called from process context. - */ -int software_suspend(void) -{ - int res; - if (!software_suspend_enabled) - return -EAGAIN; - - software_suspend_enabled = 0; - might_sleep(); - - if (arch_prepare_suspend()) { - printk("%sArchitecture failed to prepare\n", name_suspend); - return -EPERM; - } - if (pm_prepare_console()) - printk( "%sCan't allocate a console... proceeding\n", name_suspend); - if (!prepare_suspend_processes()) { - - /* At this point, all user processes and "dangerous" - kernel threads are stopped. Free some memory, as we - need half of memory free. */ - - free_some_memory(); - disable_nonboot_cpus(); - /* Save state of all device drivers, and stop them. */ - printk("Suspending devices... "); - if ((res = device_suspend(3))==0) { - in_suspend = 1; - - res = swsusp_save(); - - if (!res && in_suspend) { - swsusp_write(); - suspend_power_down(); - } - in_suspend = 0; - suspend_finish(); - } - thaw_processes(); - enable_nonboot_cpus(); - } else - res = -EBUSY; - software_suspend_enabled = 1; - MDELAY(1000); - pm_restore_console(); - return res; -} - /* More restore stuff */ #define does_collide(addr) does_collide_order(pagedir_nosave, addr, 0) @@ -1370,76 +1245,3 @@ int __init swsusp_read(void) pr_debug("pmdisk: Error %d resuming\n", error); return error; } - -/** - * software_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, then 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 __init software_resume(void) -{ - if (num_online_cpus() > 1) { - printk(KERN_WARNING "Software Suspend has malfunctioning SMP support. Disabled :(\n"); - return -EINVAL; - } - /* We enable the possibility of machine suspend */ - software_suspend_enabled = 1; - if (!resume_status) - return 0; - - printk( "%s", name_resume ); - if (resume_status == NORESUME) { - /* - * FIXME: If noresume is specified, we need to handle by finding - * the right partition and resettting the signature. - */ - return 0; - } - MDELAY(1000); - - if (pm_prepare_console()) - printk("swsusp: Can't allocate a console... proceeding\n"); - - if (swsusp_read()) - goto read_failure; - /* FIXME: Should we stop processes here, just to be safer? */ - disable_nonboot_cpus(); - device_suspend(3); - swsusp_resume(); - panic("This never returns"); - -read_failure: - pm_restore_console(); - return 0; -} - -late_initcall(software_resume); - -static int __init resume_setup(char *str) -{ - if (resume_status == NORESUME) - return 1; - - strncpy( resume_file, str, 255 ); - resume_status = RESUME_SPECIFIED; - - return 1; -} - -static int __init noresume_setup(char *str) -{ - resume_status = NORESUME; - return 1; -} - -__setup("noresume", noresume_setup); -__setup("resume=", resume_setup); - -EXPORT_SYMBOL(software_suspend); -EXPORT_SYMBOL(software_suspend_enabled); -- cgit v1.2.3 From d5df4e65d9b5a9a46ff174ca9e87d5332bf65afb Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 24 Sep 2004 04:51:10 -0700 Subject: [power mgmt] Make system state enums match device state values. Changes the PM_SUSPEND_MEM (and PM_SUSPEND_DISK) enum values so that they make sense as PCI device power states. (a) Fixes bugs whereby PCI drivers are being given bogus values. This should resolve OSDL bugid 2886 without changing the PCI API (its PM calls still act as on 2.4 kernels). (b) Doesn't change the awkward assumption in the 2.6 PMcore that the /sys/bus/*/devices/power/state, /proc/acpi/sleep, dev->power.power_state, and dev->detach_state files share the same numeric codes ... even for busses very unlike PCI, or systems with several "on" policies as well as STD and STR, or with device-PM transtions with no system-wide equivalent. Really we need to move away from "u32" codes that are easily confused with each other, towards typed values (probably struct pointers), but that's a long-term change and we need the PCI issue fixed sooner. Signed-off-by: David Brownell --- include/linux/pm.h | 11 ++++++----- kernel/power/main.c | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/pm.h b/include/linux/pm.h index d54bc441daff..7bfd2d43963e 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -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 { diff --git a/kernel/power/main.c b/kernel/power/main.c index 6b5c59cc26ba..2f08a432f0d5 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -230,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) -- cgit v1.2.3