From 0ce7a58733132f2873786615bf4ea5acd12a63ad Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 19:01:18 -0700 Subject: [Power Mgmt] Make pmdisk dependent on swsusp. --- kernel/power/Kconfig | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 6bb62269f3eb..8ffc76fec91a 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -43,25 +43,16 @@ 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 + bool "PMDisk Support" + depends on SOFTWARE_SUSPEND && 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. + This option enables an alternative implementation of Suspend-to-Disk. + It is functionally equivalent to Software Suspend, and uses many of + the same internal routines. But, it offers a slightly different + interface. - 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. + If unsure, Say N. config PM_DISK_PARTITION string "Default resume partition" -- cgit v1.2.3 From 849973418f349eb79eb68d17cbc90a92c87c1970 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 19:07:44 -0700 Subject: [Power Mgmt] Remove duplicate relocate_pagedir() from pmdisk. - Expose and use version in swsusp. --- kernel/power/pmdisk.c | 91 +-------------------------------------------------- kernel/power/power.h | 2 ++ kernel/power/swsusp.c | 12 +++---- 3 files changed, 8 insertions(+), 97 deletions(-) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index 318bfb9fa5f8..9855c7a23336 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -732,93 +732,6 @@ int pmdisk_resume(void) /* More restore stuff */ -#define does_collide(addr) does_collide_order(pm_pagedir_nosave, addr, 0) - -/* - * Returns true if given address/order collides with any orig_address - */ -static int __init does_collide_order(suspend_pagedir_t *pagedir, - unsigned long addr, int order) -{ - int i; - unsigned long addre = addr + (PAGE_SIZE<orig_address >= addr && - (pagedir+i)->orig_address < addre) - return 1; - - return 0; -} - -/* - * We check here that pagedir & pages it points to won't collide with pages - * where we're going to restore from the loaded pages later - */ -static int __init check_pagedir(void) -{ - int i; - - for(i=0; i < pmdisk_pages; i++) { - unsigned long addr; - - do { - addr = get_zeroed_page(GFP_ATOMIC); - if(!addr) - return -ENOMEM; - } while (does_collide(addr)); - - (pm_pagedir_nosave+i)->address = addr; - } - return 0; -} - -static int __init relocate_pagedir(void) -{ - /* - * We have to avoid recursion (not to overflow kernel stack), - * and that's why code looks pretty cryptic - */ - suspend_pagedir_t *old_pagedir = pm_pagedir_nosave; - void **eaten_memory = NULL; - void **c = eaten_memory, *m, *f; - int err; - - pr_debug("pmdisk: Relocating pagedir\n"); - - if(!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) { - pr_debug("pmdisk: Relocation not necessary\n"); - return 0; - } - - err = -ENOMEM; - while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order)) != NULL) { - if (!does_collide_order(old_pagedir, (unsigned long)m, - pagedir_order)) { - pm_pagedir_nosave = - memcpy(m, old_pagedir, - PAGE_SIZE << pagedir_order); - err = 0; - break; - } - eaten_memory = m; - printk( "." ); - *eaten_memory = c; - c = eaten_memory; - } - - c = eaten_memory; - while(c) { - printk(":"); - f = c; - c = *c; - free_pages((unsigned long)f, pagedir_order); - } - printk("|\n"); - return err; -} - - static struct block_device * resume_bdev; @@ -1041,9 +954,7 @@ static int __init read_suspend_image(void) return error; if ((error = read_pagedir())) return error; - if ((error = relocate_pagedir())) - goto FreePagedir; - if ((error = check_pagedir())) + if ((error = swsusp_pagedir_relocate())) goto FreePagedir; if ((error = read_image_data())) goto FreePagedir; diff --git a/kernel/power/power.h b/kernel/power/power.h index d180b0a192cd..2414245e612e 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -19,6 +19,8 @@ static inline int pm_suspend_disk(void) } #endif +extern int swsusp_pagedir_relocate(void); + extern struct semaphore pm_sem; #define power_attr(_name) \ static struct subsys_attribute _name##_attr = { \ diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 081b65103abd..41b92fa02d01 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -874,7 +874,7 @@ int software_suspend(void) /* * Returns true if given address/order collides with any orig_address */ -static int does_collide_order(suspend_pagedir_t *pagedir, unsigned long addr, +static int __init does_collide_order(suspend_pagedir_t *pagedir, unsigned long addr, int order) { int i; @@ -892,7 +892,7 @@ static int does_collide_order(suspend_pagedir_t *pagedir, unsigned long addr, * We check here that pagedir & pages it points to won't collide with pages * where we're going to restore from the loaded pages later */ -static int check_pagedir(void) +static int __init check_pagedir(void) { int i; @@ -910,7 +910,7 @@ static int check_pagedir(void) return 0; } -static int relocate_pagedir(void) +int __init swsusp_pagedir_relocate(void) { /* * We have to avoid recursion (not to overflow kernel stack), @@ -953,7 +953,7 @@ static int relocate_pagedir(void) free_pages((unsigned long)f, pagedir_order); } printk("|\n"); - return ret; + return check_pagedir(); } /* @@ -1089,9 +1089,7 @@ static int __init __read_suspend_image(struct block_device *bdev, union diskpage } BUG_ON (next.val); - if (relocate_pagedir()) - return -ENOMEM; - if (check_pagedir()) + if (swsusp_pagedir_relocate()) return -ENOMEM; printk( "Reading image data (%d pages): ", nr_copy_pages ); -- cgit v1.2.3 From 3646a7f987f2699dfb571c9691a748f690d4db42 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 19:18:23 -0700 Subject: [Power Mgmt] Remove more duplicate code from pmdisk. - Use read_swapfiles() in swsusp and rename to swsusp_swap_check(). - Use lock_swapdevices() in swsusp and rename to swsusp_swap_lock(). --- kernel/power/pmdisk.c | 64 +++++++-------------------------------------------- kernel/power/swsusp.c | 26 +++++++++++++-------- 2 files changed, 24 insertions(+), 66 deletions(-) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index 9855c7a23336..47191bbdc0eb 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -109,8 +109,10 @@ struct pmdisk_header { #define SWAPFILE_SUSPEND 1 /* This is the suspending device */ #define SWAPFILE_IGNORED 2 /* Those are other swap devices ignored for suspension */ -static unsigned short swapfile_used[MAX_SWAPFILES]; -static unsigned short root_swap; +extern unsigned short swapfile_used[MAX_SWAPFILES]; +extern unsigned short root_swap; +extern int swsusp_swap_check(void); +extern void swsusp_swap_lock(void); static int mark_swapfiles(swp_entry_t prev) @@ -136,56 +138,6 @@ static int mark_swapfiles(swp_entry_t prev) return error; } -static int read_swapfiles(void) /* This is called before saving image */ -{ - int i, len; - - len=strlen(resume_file); - root_swap = 0xFFFF; - - swap_list_lock(); - for(i=0; i 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. --- arch/i386/power/pmdisk.S | 4 +-- include/linux/suspend.h | 3 -- kernel/power/pmdisk.c | 76 ++++++++++++++++++++++++------------------------ kernel/power/swsusp.c | 8 ++--- 4 files changed, 44 insertions(+), 47 deletions(-) diff --git a/arch/i386/power/pmdisk.S b/arch/i386/power/pmdisk.S index b8106ae23397..c248edf0737e 100644 --- a/arch/i386/power/pmdisk.S +++ b/arch/i386/power/pmdisk.S @@ -24,7 +24,7 @@ ENTRY(pmdisk_arch_suspend) movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx movl %ecx,%cr3 - movl pm_pagedir_nosave,%ebx + movl pagedir_nosave,%ebx xorl %eax, %eax xorl %edx, %edx .p2align 4,,7 @@ -41,7 +41,7 @@ ENTRY(pmdisk_arch_suspend) incl %eax addl $16, %edx - cmpl pmdisk_pages,%eax + cmpl nr_copy_pages,%eax jb .L1455 .p2align 4,,7 .L1453: 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) { diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index 47191bbdc0eb..68be4bf9fa83 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -47,15 +47,15 @@ extern char __nosave_begin, __nosave_end; extern int is_head_of_free_region(struct page *); /* Variables to be preserved over suspend */ -static int pagedir_order_check; -static int nr_copy_pages_check; +extern int pagedir_order_check; +extern int nr_copy_pages_check; /* For resume= kernel option */ static char resume_file[256] = CONFIG_PM_DISK_PARTITION; static dev_t resume_device; /* Local variables that should not be affected by save */ -unsigned int pmdisk_pages __nosavedata = 0; +extern unsigned int nr_copy_pages; /* Suspend pagedir is allocated before final copy, therefore it must be freed after resume @@ -66,9 +66,9 @@ unsigned int pmdisk_pages __nosavedata = 0; allocated at time of resume, that travels through memory not to collide with anything. */ -suspend_pagedir_t *pm_pagedir_nosave __nosavedata = NULL; -static suspend_pagedir_t *pagedir_save; -static int pagedir_order __nosavedata = 0; +extern suspend_pagedir_t *pagedir_nosave; +extern suspend_pagedir_t *pagedir_save; +extern int pagedir_order; struct pmdisk_info { @@ -184,13 +184,13 @@ static void free_data(void) swp_entry_t entry; int i; - for (i = 0; i < pmdisk_pages; i++) { - entry = (pm_pagedir_nosave + i)->swap_address; + for (i = 0; i < nr_copy_pages; i++) { + entry = (pagedir_nosave + i)->swap_address; if (entry.val) swap_free(entry); else break; - (pm_pagedir_nosave + i)->swap_address = (swp_entry_t){0}; + (pagedir_nosave + i)->swap_address = (swp_entry_t){0}; } } @@ -206,12 +206,12 @@ static int write_data(void) int error = 0; int i; - printk( "Writing data to swap (%d pages): ", pmdisk_pages ); - for (i = 0; i < pmdisk_pages && !error; i++) { + printk( "Writing data to swap (%d pages): ", nr_copy_pages ); + for (i = 0; i < nr_copy_pages && !error; i++) { if (!(i%100)) printk( "." ); - error = write_swap_page((pm_pagedir_nosave+i)->address, - &((pm_pagedir_nosave+i)->swap_address)); + error = write_swap_page((pagedir_nosave+i)->address, + &((pagedir_nosave+i)->swap_address)); } printk(" %d Pages done.\n",i); return error; @@ -239,9 +239,9 @@ static void free_pagedir_entries(void) static int write_pagedir(void) { - unsigned long addr = (unsigned long)pm_pagedir_nosave; + unsigned long addr = (unsigned long)pagedir_nosave; int error = 0; - int n = SUSPEND_PD_PAGES(pmdisk_pages); + int n = SUSPEND_PD_PAGES(nr_copy_pages); int i; pmdisk_info.pagedir_pages = n; @@ -282,7 +282,7 @@ static void init_header(void) memcpy(&pmdisk_info.uts,&system_utsname,sizeof(system_utsname)); pmdisk_info.cpus = num_online_cpus(); - pmdisk_info.image_pages = pmdisk_pages; + pmdisk_info.image_pages = nr_copy_pages; } /** @@ -396,7 +396,7 @@ static void count_pages(void) if (saveable(&pfn)) n++; } - pmdisk_pages = n; + nr_copy_pages = n; } @@ -424,7 +424,7 @@ static void copy_pages(void) p++; } } - BUG_ON(n != pmdisk_pages); + BUG_ON(n != nr_copy_pages); } @@ -437,7 +437,7 @@ static void free_image_pages(void) struct pbe * p; int i; - for (i = 0, p = pagedir_save; i < pmdisk_pages; i++, p++) { + for (i = 0, p = pagedir_save; i < nr_copy_pages; i++, p++) { ClearPageNosave(virt_to_page(p->address)); free_page(p->address); } @@ -460,13 +460,13 @@ static void calc_order(void) int diff; int order; - order = get_bitmask_order(SUSPEND_PD_PAGES(pmdisk_pages)); - pmdisk_pages += 1 << order; + order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)); + nr_copy_pages += 1 << order; do { - diff = get_bitmask_order(SUSPEND_PD_PAGES(pmdisk_pages)) - order; + diff = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)) - order; if (diff) { order += diff; - pmdisk_pages += 1 << diff; + nr_copy_pages += 1 << diff; } } while(diff); pagedir_order = order; @@ -488,7 +488,7 @@ static int alloc_pagedir(void) if(!pagedir_save) return -ENOMEM; memset(pagedir_save,0,(1 << pagedir_order) * PAGE_SIZE); - pm_pagedir_nosave = pagedir_save; + pagedir_nosave = pagedir_save; return 0; } @@ -503,7 +503,7 @@ static int alloc_image_pages(void) struct pbe * p; int i; - for (i = 0, p = pagedir_save; i < pmdisk_pages; i++, p++) { + for (i = 0, p = pagedir_save; i < nr_copy_pages; i++, p++) { p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD); if(!p->address) goto Error; @@ -529,7 +529,7 @@ static int alloc_image_pages(void) static int enough_free_mem(void) { - if(nr_free_pages() < (pmdisk_pages + PAGES_FOR_IO)) { + if(nr_free_pages() < (nr_copy_pages + PAGES_FOR_IO)) { pr_debug("pmdisk: Not enough free pages: Have %d\n", nr_free_pages()); return 0; @@ -553,7 +553,7 @@ static int enough_swap(void) struct sysinfo i; si_swapinfo(&i); - if (i.freeswap < (pmdisk_pages + PAGES_FOR_IO)) { + if (i.freeswap < (nr_copy_pages + PAGES_FOR_IO)) { pr_debug("pmdisk: Not enough swap. Need %ld\n",i.freeswap); return 0; } @@ -582,12 +582,12 @@ int pmdisk_suspend(void) drain_local_pages(); - pm_pagedir_nosave = NULL; + pagedir_nosave = NULL; pr_debug("pmdisk: Counting pages to copy.\n" ); count_pages(); pr_debug("pmdisk: (pages needed: %d + %d free: %d)\n", - pmdisk_pages,PAGES_FOR_IO,nr_free_pages()); + nr_copy_pages,PAGES_FOR_IO,nr_free_pages()); if (!enough_free_mem()) return -ENOMEM; @@ -605,7 +605,7 @@ int pmdisk_suspend(void) return error; } - nr_copy_pages_check = pmdisk_pages; + nr_copy_pages_check = nr_copy_pages; pagedir_order_check = pagedir_order; /* During allocating of suspend pagedir, new cold pages may appear. @@ -622,7 +622,7 @@ int pmdisk_suspend(void) * touch swap space! Except we must write out our image of course. */ - pr_debug("pmdisk: %d pages copied\n", pmdisk_pages ); + pr_debug("pmdisk: %d pages copied\n", nr_copy_pages ); return 0; } @@ -657,7 +657,7 @@ static int suspend_save_image(void) int pmdisk_resume(void) { - BUG_ON (nr_copy_pages_check != pmdisk_pages); + 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 */ @@ -838,7 +838,7 @@ static int __init check_header(void) printk(KERN_ERR "pmdisk: Resume mismatch: %s\n",reason); return -EPERM; } - pmdisk_pages = pmdisk_info.image_pages; + nr_copy_pages = pmdisk_info.image_pages; return error; } @@ -854,7 +854,7 @@ static int __init read_pagedir(void) addr =__get_free_pages(GFP_ATOMIC, pagedir_order); if (!addr) return -ENOMEM; - pm_pagedir_nosave = (struct pbe *)addr; + pagedir_nosave = (struct pbe *)addr; pr_debug("pmdisk: Reading pagedir (%d Pages)\n",n); @@ -866,7 +866,7 @@ static int __init read_pagedir(void) error = -EFAULT; } if (error) - free_pages((unsigned long)pm_pagedir_nosave,pagedir_order); + free_pages((unsigned long)pagedir_nosave,pagedir_order); return error; } @@ -884,8 +884,8 @@ static int __init read_image_data(void) int error = 0; int i; - printk( "Reading image data (%d pages): ", pmdisk_pages ); - for(i = 0, p = pm_pagedir_nosave; i < pmdisk_pages && !error; i++, p++) { + printk( "Reading image data (%d pages): ", nr_copy_pages ); + for(i = 0, p = pagedir_nosave; i < nr_copy_pages && !error; i++, p++) { if (!(i%100)) printk( "." ); error = read_page(swp_offset(p->swap_address), @@ -913,7 +913,7 @@ static int __init read_suspend_image(void) Done: return error; FreePagedir: - free_pages((unsigned long)pm_pagedir_nosave,pagedir_order); + free_pages((unsigned long)pagedir_nosave,pagedir_order); goto Done; } diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 8d52d0e255c3..0ec8c8bbfda9 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -84,8 +84,8 @@ extern int is_head_of_free_region(struct page *); 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; +int pagedir_order_check; +int nr_copy_pages_check; static int resume_status; static char resume_file[256] = ""; /* For resume= kernel option */ @@ -107,8 +107,8 @@ unsigned int nr_copy_pages __nosavedata = 0; MMU hardware. */ suspend_pagedir_t *pagedir_nosave __nosavedata = NULL; -static suspend_pagedir_t *pagedir_save; -static int pagedir_order __nosavedata = 0; +suspend_pagedir_t *pagedir_save; +int pagedir_order __nosavedata = 0; struct link { char dummy[PAGE_SIZE - sizeof(swp_entry_t)]; -- cgit v1.2.3 From d324cd65541e8ac8be478455b1354123084a475b Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 19:47:13 -0700 Subject: [Power Mgmt] Merge first part of image writing code. - Introduce helpers to swsusp - swsusp_write_page(), swsusp_data_write() and swsusp_data_free(). - Delete duplicate copies from pmdisk and fixup names in calls. - Clean up write_suspend_image() in swsusp and use the helpers. --- kernel/power/pmdisk.c | 94 +++----------------------------- kernel/power/swsusp.c | 148 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 103 insertions(+), 139 deletions(-) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index 68be4bf9fa83..bd4d6a0aab9f 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -138,85 +138,9 @@ static int mark_swapfiles(swp_entry_t prev) return error; } - - -/** - * write_swap_page - Write one page to a fresh swap location. - * @addr: Address we're writing. - * @loc: Place to store the entry we used. - * - * Allocate a new swap entry and 'sync' it. Note we discard -EIO - * errors. That is an artifact left over from swsusp. It did not - * check the return of rw_swap_page_sync() at all, since most pages - * written back to swap would return -EIO. - * This is a partial improvement, since we will at least return other - * errors, though we need to eventually fix the damn code. - */ - -static int write_swap_page(unsigned long addr, swp_entry_t * loc) -{ - swp_entry_t entry; - int error = 0; - - entry = get_swap_page(); - if (swp_offset(entry) && - swapfile_used[swp_type(entry)] == SWAPFILE_SUSPEND) { - error = rw_swap_page_sync(WRITE, entry, - virt_to_page(addr)); - if (error == -EIO) - error = 0; - if (!error) - *loc = entry; - } else - error = -ENOSPC; - return error; -} - - -/** - * free_data - Free the swap entries used by the saved image. - * - * Walk the list of used swap entries and free each one. - */ - -static void free_data(void) -{ - swp_entry_t entry; - int i; - - for (i = 0; i < nr_copy_pages; i++) { - entry = (pagedir_nosave + i)->swap_address; - if (entry.val) - swap_free(entry); - else - break; - (pagedir_nosave + i)->swap_address = (swp_entry_t){0}; - } -} - - -/** - * write_data - Write saved image to swap. - * - * Walk the list of pages in the image and sync each one to swap. - */ - -static int write_data(void) -{ - int error = 0; - int i; - - printk( "Writing data to swap (%d pages): ", nr_copy_pages ); - for (i = 0; i < nr_copy_pages && !error; i++) { - if (!(i%100)) - printk( "." ); - error = write_swap_page((pagedir_nosave+i)->address, - &((pagedir_nosave+i)->swap_address)); - } - printk(" %d Pages done.\n",i); - return error; -} - +extern int swsusp_write_page(unsigned long addr, swp_entry_t * entry); +extern int swsusp_data_write(void); +extern void swsusp_data_free(void); /** * free_pagedir - Free pages used by the page directory. @@ -247,7 +171,7 @@ static int write_pagedir(void) pmdisk_info.pagedir_pages = n; printk( "Writing pagedir (%d pages)\n", n); for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) - error = write_swap_page(addr,&pmdisk_info.pagedir[i]); + error = swsusp_write_page(addr,&pmdisk_info.pagedir[i]); return error; } @@ -283,6 +207,7 @@ static void init_header(void) pmdisk_info.cpus = num_online_cpus(); pmdisk_info.image_pages = nr_copy_pages; + dump_pmdisk_info(); } /** @@ -297,8 +222,7 @@ static void init_header(void) static int write_header(swp_entry_t * entry) { - dump_pmdisk_info(); - return write_swap_page((unsigned long)&pmdisk_info,entry); + return swsusp_write_page((unsigned long)&pmdisk_info,entry); } @@ -313,9 +237,7 @@ static int write_suspend_image(void) int error; swp_entry_t prev = { 0 }; - init_header(); - - if ((error = write_data())) + if ((error = swsusp_data_write())) goto FreeData; if ((error = write_pagedir())) @@ -330,7 +252,7 @@ static int write_suspend_image(void) FreePagedir: free_pagedir_entries(); FreeData: - free_data(); + swsusp_data_free(); goto Done; } diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 0ec8c8bbfda9..8961cd9d2d2a 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -295,6 +295,87 @@ void swsusp_swap_lock(void) swap_list_unlock(); } + + +/** + * write_swap_page - Write one page to a fresh swap location. + * @addr: Address we're writing. + * @loc: Place to store the entry we used. + * + * Allocate a new swap entry and 'sync' it. Note we discard -EIO + * errors. That is an artifact left over from swsusp. It did not + * check the return of rw_swap_page_sync() at all, since most pages + * written back to swap would return -EIO. + * This is a partial improvement, since we will at least return other + * errors, though we need to eventually fix the damn code. + */ + +int swsusp_write_page(unsigned long addr, swp_entry_t * loc) +{ + swp_entry_t entry; + int error = 0; + + entry = get_swap_page(); + if (swp_offset(entry) && + swapfile_used[swp_type(entry)] == SWAPFILE_SUSPEND) { + error = rw_swap_page_sync(WRITE, entry, + virt_to_page(addr)); + if (error == -EIO) + error = 0; + if (!error) + *loc = entry; + } else + error = -ENOSPC; + return error; +} + + +/** + * free_data - Free the swap entries used by the saved image. + * + * Walk the list of used swap entries and free each one. + */ + +void swsusp_data_free(void) +{ + swp_entry_t entry; + int i; + + for (i = 0; i < nr_copy_pages; i++) { + entry = (pagedir_nosave + i)->swap_address; + if (entry.val) + swap_free(entry); + else + break; + (pagedir_nosave + i)->swap_address = (swp_entry_t){0}; + } +} + + +/** + * write_data - Write saved image to swap. + * + * Walk the list of pages in the image and sync each one to swap. + */ + +int swsusp_data_write(void) +{ + int error = 0; + int i; + + printk( "Writing data to swap (%d pages): ", nr_copy_pages ); + for (i = 0; i < nr_copy_pages && !error; i++) { + if (!(i%100)) + printk( "." ); + error = swsusp_write_page((pagedir_nosave+i)->address, + &((pagedir_nosave+i)->swap_address)); + } + printk(" %d Pages done.\n",i); + return error; +} + + + /** * write_suspend_image - Write entire image to disk. * @@ -309,78 +390,39 @@ void swsusp_swap_lock(void) static int write_suspend_image(void) { int i; - swp_entry_t entry, prev = { 0 }; + int error = 0; + swp_entry_t entry = { 0 }; int nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages); union diskpage *cur, *buffer = (union diskpage *)get_zeroed_page(GFP_ATOMIC); - unsigned long address; - struct page *page; + unsigned long addr; if (!buffer) return -ENOMEM; - printk( "Writing data to swap (%d pages): ", nr_copy_pages ); - for (i=0; iaddress; - page = virt_to_page(address); - rw_swap_page_sync(WRITE, entry, page); - (pagedir_nosave+i)->swap_address = entry; - } - printk( "|\n" ); + swsusp_data_write(); + printk( "Writing pagedir (%d pages): ", nr_pgdir_pages); - for (i=0; ilink.next = entry; printk( "." ); - entry = get_swap_page(); - if (!entry.val) { - printk(KERN_CRIT "Not enough swapspace when writing pgdir\n" ); - panic("Don't know how to recover"); - free_page((unsigned long) buffer); - return -ENOSPC; - } - - if(swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND) - panic("\nNot enough swapspace for pagedir on suspend device" ); - - BUG_ON (sizeof(swp_entry_t) != sizeof(long)); - BUG_ON (PAGE_SIZE % sizeof(struct pbe)); - - cur->link.next = prev; - page = virt_to_page((unsigned long)cur); - rw_swap_page_sync(WRITE, entry, page); - prev = entry; + 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); - entry = get_swap_page(); - if (!entry.val) - panic( "\nNot enough swapspace when writing header" ); - if (swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND) - panic("\nNot enough swapspace for header on suspend device" ); 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 = prev; - - page = virt_to_page((unsigned long)cur); - rw_swap_page_sync(WRITE, entry, page); - prev = entry; - + cur->link.next = entry; + if ((error = swsusp_write_page((unsigned long)cur,&entry))) + return error; printk( "S" ); - mark_swapfiles(prev, MARK_SWAP_SUSPEND); + mark_swapfiles(entry, MARK_SWAP_SUSPEND); printk( "|\n" ); MDELAY(1000); -- cgit v1.2.3 From 154f17228dc3059ab8418fbb79cf24ea660ef81e Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 20:07:17 -0700 Subject: [Power Mgmt] Consolidate code for allocating image pages in pmdisk and swsusp - Move helpers calc_order(), alloc_pagedir(), alloc_image_pages(), enough_free_mem(), and enough_swap() into swsusp. - Wrap them all with a new function - swsusp_alloc(). - Fix up pmdisk to just call that. - Fix up suspend_prepare_image() to call that, instead of doing it inline. --- kernel/power/pmdisk.c | 161 ++--------------------------------------- kernel/power/swsusp.c | 196 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 143 insertions(+), 214 deletions(-) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index bd4d6a0aab9f..633452b73370 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -350,138 +350,9 @@ static void copy_pages(void) } -/** - * free_image_pages - Free each page allocated for snapshot. - */ - -static void free_image_pages(void) -{ - struct pbe * p; - int i; - - for (i = 0, p = pagedir_save; i < nr_copy_pages; i++, p++) { - ClearPageNosave(virt_to_page(p->address)); - free_page(p->address); - } -} - - -/** - * free_pagedir - Free the page directory. - */ - -static void free_pagedir(void) -{ - free_image_pages(); - free_pages((unsigned long)pagedir_save, pagedir_order); -} - - -static void calc_order(void) -{ - int diff; - int order; - - order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)); - nr_copy_pages += 1 << order; - do { - diff = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)) - order; - if (diff) { - order += diff; - nr_copy_pages += 1 << diff; - } - } while(diff); - pagedir_order = order; -} - - -/** - * alloc_pagedir - Allocate the page directory. - * - * First, determine exactly how many contiguous pages we need, - * allocate them, then mark each 'unsavable'. - */ - -static int alloc_pagedir(void) -{ - calc_order(); - pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, - pagedir_order); - if(!pagedir_save) - return -ENOMEM; - memset(pagedir_save,0,(1 << pagedir_order) * PAGE_SIZE); - pagedir_nosave = pagedir_save; - return 0; -} - - -/** - * alloc_image_pages - Allocate pages for the snapshot. - * - */ - -static int alloc_image_pages(void) -{ - struct pbe * p; - int i; - - for (i = 0, p = pagedir_save; i < nr_copy_pages; i++, p++) { - p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD); - if(!p->address) - goto Error; - SetPageNosave(virt_to_page(p->address)); - } - return 0; - Error: - do { - if (p->address) - free_page(p->address); - p->address = 0; - } while (p-- > pagedir_save); - return -ENOMEM; -} - - -/** - * enough_free_mem - Make sure we enough free memory to snapshot. - * - * Returns TRUE or FALSE after checking the number of available - * free pages. - */ - -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", - nr_free_pages()); - return 0; - } - return 1; -} - - -/** - * enough_swap - Make sure we have enough swap to save the image. - * - * Returns TRUE or FALSE after checking the total amount of swap - * space avaiable. - * - * FIXME: si_swapinfo(&i) returns all swap devices information. - * We should only consider resume_device. - */ - -static int enough_swap(void) -{ - struct sysinfo i; - - si_swapinfo(&i); - if (i.freeswap < (nr_copy_pages + PAGES_FOR_IO)) { - pr_debug("pmdisk: Not enough swap. Need %ld\n",i.freeswap); - return 0; - } - return 1; -} +extern int swsusp_alloc(void); +extern void free_suspend_pagedir(unsigned long); /** * pmdisk_suspend - Atomically snapshot the system. @@ -503,33 +374,11 @@ int pmdisk_suspend(void) return error; drain_local_pages(); - - pagedir_nosave = NULL; pr_debug("pmdisk: Counting pages to copy.\n" ); count_pages(); - - pr_debug("pmdisk: (pages needed: %d + %d free: %d)\n", - nr_copy_pages,PAGES_FOR_IO,nr_free_pages()); - - if (!enough_free_mem()) - return -ENOMEM; - - if (!enough_swap()) - return -ENOSPC; - - if ((error = alloc_pagedir())) { - pr_debug("pmdisk: Allocating pagedir failed.\n"); - return error; - } - if ((error = alloc_image_pages())) { - pr_debug("pmdisk: Allocating image pages failed.\n"); - free_pagedir(); - return error; - } - - nr_copy_pages_check = nr_copy_pages; - pagedir_order_check = pagedir_order; + error = swsusp_alloc(); + /* During allocating of suspend pagedir, new cold pages may appear. * Kill them */ @@ -931,7 +780,7 @@ int __init pmdisk_restore(void) int pmdisk_free(void) { pr_debug( "Freeing prev allocated pagedir\n" ); - free_pagedir(); + free_suspend_pagedir((unsigned long)pagedir_save); return 0; } diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 8961cd9d2d2a..6f7af4a16101 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -594,7 +594,7 @@ static void free_suspend_pagedir_zone(struct zone *zone, unsigned long pagedir) } } -static void free_suspend_pagedir(unsigned long this_pagedir) +void free_suspend_pagedir(unsigned long this_pagedir) { struct zone *zone; for_each_zone(zone) { @@ -604,36 +604,6 @@ static void free_suspend_pagedir(unsigned long this_pagedir) free_pages(this_pagedir, pagedir_order); } -static suspend_pagedir_t *create_suspend_pagedir(int nr_copy_pages) -{ - int i; - suspend_pagedir_t *pagedir; - struct pbe *p; - struct page *page; - - pagedir_order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)); - - p = pagedir = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, pagedir_order); - if (!pagedir) - return NULL; - - page = virt_to_page(pagedir); - for(i=0; i < 1<address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD); - if (!p->address) { - free_suspend_pagedir((unsigned long) pagedir); - return NULL; - } - SetPageNosave(virt_to_page(p->address)); - p->orig_address = 0; - p++; - } - return pagedir; -} - static int prepare_suspend_processes(void) { sys_sync(); /* Syncing needs pdflushd, so do it before stopping processes */ @@ -658,12 +628,145 @@ static void free_some_memory(void) printk("|\n"); } -static int suspend_prepare_image(void) + +static void calc_order(void) +{ + int diff; + int order; + + order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)); + nr_copy_pages += 1 << order; + do { + diff = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)) - order; + if (diff) { + order += diff; + nr_copy_pages += 1 << diff; + } + } while(diff); + pagedir_order = order; +} + + +/** + * alloc_pagedir - Allocate the page directory. + * + * First, determine exactly how many contiguous pages we need, + * allocate them, then mark each 'unsavable'. + */ + +static int alloc_pagedir(void) +{ + calc_order(); + pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, + pagedir_order); + if(!pagedir_save) + return -ENOMEM; + memset(pagedir_save,0,(1 << pagedir_order) * PAGE_SIZE); + pagedir_nosave = pagedir_save; + return 0; +} + + +/** + * alloc_image_pages - Allocate pages for the snapshot. + * + */ + +static int alloc_image_pages(void) +{ + struct pbe * p; + int i; + + for (i = 0, p = pagedir_save; i < nr_copy_pages; i++, p++) { + p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD); + if(!p->address) + goto Error; + SetPageNosave(virt_to_page(p->address)); + } + return 0; + Error: + do { + if (p->address) + free_page(p->address); + p->address = 0; + } while (p-- > pagedir_save); + return -ENOMEM; +} + + +/** + * enough_free_mem - Make sure we enough free memory to snapshot. + * + * Returns TRUE or FALSE after checking the number of available + * free pages. + */ + +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", + nr_free_pages()); + return 0; + } + return 1; +} + + +/** + * enough_swap - Make sure we have enough swap to save the image. + * + * Returns TRUE or FALSE after checking the total amount of swap + * space avaiable. + * + * FIXME: si_swapinfo(&i) returns all swap devices information. + * We should only consider resume_device. + */ + +static int enough_swap(void) { struct sysinfo i; - unsigned int nr_needed_pages = 0; + + si_swapinfo(&i); + if (i.freeswap < (nr_copy_pages + PAGES_FOR_IO)) { + pr_debug("pmdisk: Not enough swap. Need %ld\n",i.freeswap); + return 0; + } + return 1; +} + +int swsusp_alloc(void) +{ + int error; + + pr_debug("suspend: (pages needed: %d + %d free: %d)\n", + nr_copy_pages,PAGES_FOR_IO,nr_free_pages()); pagedir_nosave = NULL; + if (!enough_free_mem()) + return -ENOMEM; + + if (!enough_swap()) + return -ENOSPC; + + if ((error = alloc_pagedir())) { + pr_debug("suspend: Allocating pagedir failed.\n"); + return error; + } + if ((error = alloc_image_pages())) { + pr_debug("suspend: Allocating image pages failed.\n"); + free_suspend_pagedir((unsigned long)pagedir_save); + return error; + } + + nr_copy_pages_check = nr_copy_pages; + pagedir_order_check = pagedir_order; + return 0; +} + +static int suspend_prepare_image(void) +{ + unsigned int nr_needed_pages = 0; + printk( "/critical section: "); #ifdef CONFIG_HIGHMEM printk( "handling highmem" ); @@ -678,32 +781,9 @@ static int suspend_prepare_image(void) drain_local_pages(); nr_copy_pages = count_and_copy_data_pages(NULL); nr_needed_pages = nr_copy_pages + PAGES_FOR_IO; - - printk(" (pages needed: %d+%d=%d free: %d)\n",nr_copy_pages,PAGES_FOR_IO,nr_needed_pages,nr_free_pages()); - if(nr_free_pages() < nr_needed_pages) { - printk(KERN_CRIT "%sCouldn't get enough free pages, on %d pages short\n", - name_suspend, nr_needed_pages-nr_free_pages()); - root_swap = 0xFFFF; - return -ENOMEM; - } - si_swapinfo(&i); /* FIXME: si_swapinfo(&i) returns all swap devices information. - We should only consider resume_device. */ - if (i.freeswap < nr_needed_pages) { - printk(KERN_CRIT "%sThere's not enough swap space available, on %ld pages short\n", - name_suspend, nr_needed_pages-i.freeswap); - return -ENOSPC; - } - - PRINTK( "Alloc pagedir\n" ); - pagedir_save = pagedir_nosave = create_suspend_pagedir(nr_copy_pages); - if (!pagedir_nosave) { - /* Pagedir is big, one-chunk allocation. It is easily possible for this allocation to fail */ - printk(KERN_CRIT "%sCouldn't allocate continuous pagedir\n", name_suspend); - return -ENOMEM; - } - nr_copy_pages_check = nr_copy_pages; - pagedir_order_check = pagedir_order; + swsusp_alloc(); + drain_local_pages(); /* During allocating of suspend pagedir, new cold pages may appear. Kill them */ if (nr_copy_pages != count_and_copy_data_pages(pagedir_nosave)) /* copy */ BUG(); -- cgit v1.2.3 From 7b8d56b1d068d9bebaf2132dfb21ed5525f7e9ff Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 20:35:38 -0700 Subject: [Power Mgmt] Consolidate page count/copy code of pmdisk and swsusp. - Split count_and_copy_data_pages() into count_data_pages() and copy_data_pages(). - Move helper saveable() from pmdisk to swsusp, and update to work with page zones. - Get rid of uneeded defines in pmdisk. --- kernel/power/pmdisk.c | 129 +------------------------------------------------- kernel/power/swsusp.c | 119 +++++++++++++++++++++++++++++----------------- 2 files changed, 77 insertions(+), 171 deletions(-) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index 633452b73370..f10253a3840a 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -37,15 +37,6 @@ extern asmlinkage int pmdisk_arch_suspend(int resume); -#define __ADDRESS(x) ((unsigned long) phys_to_virt(x)) -#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT) -#define ADDRESS2(x) __ADDRESS(__pa(x)) /* Needed for x86-64 where some pages are in memory twice */ - -/* References to section boundaries */ -extern char __nosave_begin, __nosave_end; - -extern int is_head_of_free_region(struct page *); - /* Variables to be preserved over suspend */ extern int pagedir_order_check; extern int nr_copy_pages_check; @@ -257,102 +248,8 @@ static int write_suspend_image(void) } - -/** - * saveable - Determine whether a page should be cloned or not. - * @pfn: The page - * - * We save a page if it's Reserved, and not in the range of pages - * statically defined as 'unsaveable', or if it isn't reserved, and - * isn't part of a free chunk of pages. - * If it is part of a free chunk, we update @pfn to point to the last - * page of the chunk. - */ - -static int saveable(unsigned long * pfn) -{ - struct page * page = pfn_to_page(*pfn); - - if (PageNosave(page)) - return 0; - - if (!PageReserved(page)) { - int chunk_size; - - if ((chunk_size = is_head_of_free_region(page))) { - *pfn += chunk_size - 1; - return 0; - } - } else if (PageReserved(page)) { - /* Just copy whole code segment. - * Hopefully it is not that big. - */ - if ((ADDRESS(*pfn) >= (unsigned long) ADDRESS2(&__nosave_begin)) && - (ADDRESS(*pfn) < (unsigned long) ADDRESS2(&__nosave_end))) { - pr_debug("[nosave %lx]\n", ADDRESS(*pfn)); - return 0; - } - /* Hmm, perhaps copying all reserved pages is not - * too healthy as they may contain - * critical bios data? - */ - } - return 1; -} - - - -/** - * count_pages - Determine size of page directory. - * - * Iterate over all the pages in the system and tally the number - * we need to clone. - */ - -static void count_pages(void) -{ - unsigned long pfn; - int n = 0; - - for (pfn = 0; pfn < max_pfn; pfn++) { - if (saveable(&pfn)) - n++; - } - nr_copy_pages = n; -} - - -/** - * copy_pages - Atomically snapshot memory. - * - * Iterate over all the pages in the system and copy each one - * into its corresponding location in the pagedir. - * We rely on the fact that the number of pages that we're snap- - * shotting hasn't changed since we counted them. - */ - -static void copy_pages(void) -{ - struct pbe * p = pagedir_save; - unsigned long pfn; - int n = 0; - - for (pfn = 0; pfn < max_pfn; pfn++) { - if (saveable(&pfn)) { - n++; - p->orig_address = ADDRESS(pfn); - copy_page((void *) p->address, - (void *) p->orig_address); - p++; - } - } - BUG_ON(n != nr_copy_pages); -} - - - -extern int swsusp_alloc(void); extern void free_suspend_pagedir(unsigned long); +extern int suspend_prepare_image(void); /** * pmdisk_suspend - Atomically snapshot the system. @@ -372,29 +269,7 @@ int pmdisk_suspend(void) if ((error = swsusp_swap_check())) return error; - - drain_local_pages(); - pr_debug("pmdisk: Counting pages to copy.\n" ); - count_pages(); - - error = swsusp_alloc(); - - /* During allocating of suspend pagedir, new cold pages may appear. - * Kill them - */ - drain_local_pages(); - - /* copy */ - copy_pages(); - - /* - * End of critical section. From now on, we can write to memory, - * but we should not touch disk. This specially means we must _not_ - * touch swap space! Except we must write out our image of course. - */ - - pr_debug("pmdisk: %d pages copied\n", nr_copy_pages ); - return 0; + return suspend_prepare_image(); } diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 6f7af4a16101..e07e40c18712 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -523,57 +523,86 @@ static int pfn_is_nosave(unsigned long pfn) return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn); } -/* if *pagedir_p != NULL it also copies the counted pages */ -static int count_and_copy_zone(struct zone *zone, struct pbe **pagedir_p) +/** + * saveable - Determine whether a page should be cloned or not. + * @pfn: The page + * + * We save a page if it's Reserved, and not in the range of pages + * statically defined as 'unsaveable', or if it isn't reserved, and + * isn't part of a free chunk of pages. + * If it is part of a free chunk, we update @pfn to point to the last + * page of the chunk. + */ + +static int saveable(struct zone * zone, unsigned long * zone_pfn) { - unsigned long zone_pfn, chunk_size, nr_copy_pages = 0; - struct pbe *pbe = *pagedir_p; - for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) { - struct page *page; - unsigned long pfn = zone_pfn + zone->zone_start_pfn; + unsigned long pfn = *zone_pfn + zone->zone_start_pfn; + unsigned long chunk_size; + struct page * page; - if (!(pfn%1000)) - printk("."); - if (!pfn_valid(pfn)) - continue; - page = pfn_to_page(pfn); - BUG_ON(PageReserved(page) && PageNosave(page)); - if (PageNosave(page)) - continue; - if (PageReserved(page) && pfn_is_nosave(pfn)) { - PRINTK("[nosave pfn 0x%lx]", pfn); - continue; - } - if ((chunk_size = is_head_of_free_region(page))) { - pfn += chunk_size - 1; - zone_pfn += chunk_size - 1; - continue; + if (!pfn_valid(pfn)) + return 0; + + if (!(pfn%1000)) + printk("."); + page = pfn_to_page(pfn); + BUG_ON(PageReserved(page) && PageNosave(page)); + if (PageNosave(page)) + return 0; + if (PageReserved(page) && pfn_is_nosave(pfn)) { + PRINTK("[nosave pfn 0x%lx]", pfn); + return 0; + } + if ((chunk_size = is_head_of_free_region(page))) { + zone_pfn += chunk_size - 1; + return 0; + } + + return 1; +} + +static void count_data_pages(void) +{ + struct zone *zone; + unsigned long zone_pfn; + + nr_copy_pages = 0; + + for_each_zone(zone) { + if (!is_highmem(zone)) { + for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) + nr_copy_pages += saveable(zone, &zone_pfn); } - nr_copy_pages++; - if (!pbe) - continue; - pbe->orig_address = (long) page_address(page); - /* Copy page is dangerous: it likes to mess with - preempt count on specific cpus. Wrong preempt count is then copied, - oops. */ - copy_page((void *)pbe->address, (void *)pbe->orig_address); - pbe++; } - *pagedir_p = pbe; - return nr_copy_pages; } -static int count_and_copy_data_pages(struct pbe *pagedir_p) + +static void copy_data_pages(struct pbe * pbe) { - int nr_copy_pages = 0; struct zone *zone; + unsigned long zone_pfn; + + for_each_zone(zone) { if (!is_highmem(zone)) - nr_copy_pages += count_and_copy_zone(zone, &pagedir_p); + for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) { + if (saveable(zone, &zone_pfn)) { + struct page * page; + page = pfn_to_page(zone_pfn + zone->zone_start_pfn); + pbe->orig_address = (long) page_address(page); + /* Copy page is dangerous: it likes to mess with + preempt count on specific cpus. Wrong preempt + count is then copied, oops. + */ + copy_page((void *)pbe->address, + (void *)pbe->orig_address); + pbe++; + } + } } - return nr_copy_pages; } + static void free_suspend_pagedir_zone(struct zone *zone, unsigned long pagedir) { unsigned long zone_pfn, pagedir_end, pagedir_pfn, pagedir_end_pfn; @@ -734,7 +763,7 @@ static int enough_swap(void) return 1; } -int swsusp_alloc(void) +static int swsusp_alloc(void) { int error; @@ -763,7 +792,7 @@ int swsusp_alloc(void) return 0; } -static int suspend_prepare_image(void) +int suspend_prepare_image(void) { unsigned int nr_needed_pages = 0; @@ -779,14 +808,16 @@ static int suspend_prepare_image(void) printk("counting pages to copy" ); drain_local_pages(); - nr_copy_pages = count_and_copy_data_pages(NULL); + count_data_pages(); nr_needed_pages = nr_copy_pages + PAGES_FOR_IO; swsusp_alloc(); - drain_local_pages(); /* During allocating of suspend pagedir, new cold pages may appear. Kill them */ - if (nr_copy_pages != count_and_copy_data_pages(pagedir_nosave)) /* copy */ - BUG(); + /* During allocating of suspend pagedir, new cold pages may appear. + * Kill them. + */ + drain_local_pages(); + copy_data_pages(pagedir_nosave); /* * End of critical section. From now on, we can write to memory, -- cgit v1.2.3 From 2e633f4f6ed2cff8f3d46bad6cf0b24da81a56a1 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 20:45:08 -0700 Subject: [swsusp] Add helper suspend_finish, move common code there. - Move call out of assembly-callbacks and into software_suspend() after do_magic() returns. --- kernel/power/swsusp.c | 56 +++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index e07e40c18712..91d8390dc2f5 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -869,6 +869,30 @@ static void suspend_power_down(void) /* NOTREACHED */ } + +static void suspend_finish(void) +{ + spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ + + free_pages((unsigned long) pagedir_nosave, pagedir_order); + spin_unlock_irq(&suspend_pagedir_lock); + +#ifdef CONFIG_HIGHMEM + printk( "Restoring highmem\n" ); + restore_highmem(); +#endif + device_resume(); + PRINTK( "Fixing swap signatures... " ); + mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME); + PRINTK( "ok\n" ); + +#ifdef SUSPEND_CONSOLE + acquire_console_sem(); + update_screen(fg_console); + release_console_sem(); +#endif +} + /* * Magic happens here */ @@ -892,30 +916,8 @@ asmlinkage void do_magic_resume_2(void) BUG_ON (pagedir_order_check != pagedir_order); __flush_tlb_global(); /* Even mappings of "global" things (vmalloc) need to be fixed */ - - PRINTK( "Freeing prev allocated pagedir\n" ); - free_suspend_pagedir((unsigned long) pagedir_save); - -#ifdef CONFIG_HIGHMEM - printk( "Restoring highmem\n" ); - restore_highmem(); -#endif - printk("done, devices\n"); - device_power_up(); spin_unlock_irq(&suspend_pagedir_lock); - device_resume(); - - /* Fixme: this is too late; we should do this ASAP to avoid "infinite reboots" problem */ - PRINTK( "Fixing swap signatures... " ); - mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME); - PRINTK( "ok\n" ); - -#ifdef SUSPEND_CONSOLE - acquire_console_sem(); - update_screen(fg_console); - release_console_sem(); -#endif } /* do_magic() is implemented in arch/?/kernel/suspend_asm.S, and basically does: @@ -964,15 +966,6 @@ asmlinkage void do_magic_suspend_2(void) barrier(); mb(); - spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ - - free_pages((unsigned long) pagedir_nosave, pagedir_order); - spin_unlock_irq(&suspend_pagedir_lock); - - device_resume(); - PRINTK( "Fixing swap signatures... " ); - mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME); - PRINTK( "ok\n" ); } /* @@ -1015,6 +1008,7 @@ int software_suspend(void) * using normal kernel mechanism. */ do_magic(0); + suspend_finish(); } thaw_processes(); enable_nonboot_cpus(); -- 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(-) 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 334584dae1f8e828c2a796741d129447df7feaad Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 21:12:12 -0700 Subject: [Power Mgmt] Remove arch/i386/power/pmdisk.S --- arch/i386/power/pmdisk.S | 56 ------------------------------------------------ 1 file changed, 56 deletions(-) delete mode 100644 arch/i386/power/pmdisk.S diff --git a/arch/i386/power/pmdisk.S b/arch/i386/power/pmdisk.S deleted file mode 100644 index c248edf0737e..000000000000 --- a/arch/i386/power/pmdisk.S +++ /dev/null @@ -1,56 +0,0 @@ -/* Originally gcc generated, modified by hand */ - -#include -#include -#include - - .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 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 nr_copy_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 -- cgit v1.2.3 From 6df01ca8ac5c49fd3042ae0f512ed814bed19ec1 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 21:13:16 -0700 Subject: [Power Mgmt] Fix up call in kernel/power/disk.c to swsusp_suspend(). --- kernel/power/disk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/power/disk.c b/kernel/power/disk.c index f3a4a4b1bb56..cb54fa82f3bf 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -161,7 +161,7 @@ int pm_suspend_disk(void) pr_debug("PM: snapshotting memory.\n"); in_suspend = 1; - if ((error = swsusp_save())) + if ((error = swsusp_suspend())) goto Done; if (in_suspend) { -- cgit v1.2.3 From b52d040a8309877f006aa176bbd097ca0b10552e Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 21:45:01 -0700 Subject: [Power Mgmt] Consolidate pmdisk and swsusp early swap access. - Move bio helpers to swsusp. - Convert swsusp to use them, rathen buffer_heads. - Expose and fix up calls in pmdisk. - Clean up swsusp::read_suspend_image() a bit. --- kernel/power/pmdisk.c | 97 +++-------------------------- kernel/power/swsusp.c | 169 ++++++++++++++++++++++++++++---------------------- 2 files changed, 104 insertions(+), 162 deletions(-) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index bbe93fdc1cdf..128a85f39ec6 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -279,90 +279,9 @@ static int suspend_save_image(void) /* More restore stuff */ -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) -{ - 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) -{ - 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("pmdisk: 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 | (1 << BIO_RW_SYNC), 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 struct block_device * resume_bdev; +extern int bio_read_page(pgoff_t page_off, void * page); +extern int bio_write_page(pgoff_t page_off, void * page); extern dev_t __init name_to_dev_t(const char *line); @@ -372,7 +291,7 @@ static int __init check_sig(void) int error; memset(&pmdisk_header,0,sizeof(pmdisk_header)); - if ((error = read_page(0,&pmdisk_header))) + if ((error = bio_read_page(0,&pmdisk_header))) return error; if (!memcmp(PMDISK_SIG,pmdisk_header.sig,10)) { memcpy(pmdisk_header.sig,pmdisk_header.orig_sig,10); @@ -380,7 +299,7 @@ static int __init check_sig(void) /* * Reset swap signature now. */ - error = write_page(0,&pmdisk_header); + error = bio_write_page(0,&pmdisk_header); } else { pr_debug(KERN_ERR "pmdisk: Invalid partition type.\n"); return -EINVAL; @@ -424,7 +343,7 @@ static int __init check_header(void) init_header(); - if ((error = read_page(swp_offset(pmdisk_header.pmdisk_info), + if ((error = bio_read_page(swp_offset(pmdisk_header.pmdisk_info), &pmdisk_info))) return error; @@ -456,7 +375,7 @@ static int __init read_pagedir(void) for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) { unsigned long offset = swp_offset(pmdisk_info.pagedir[i]); if (offset) - error = read_page(offset, (void *)addr); + error = bio_read_page(offset, (void *)addr); else error = -EFAULT; } @@ -483,7 +402,7 @@ static int __init read_image_data(void) for(i = 0, p = pagedir_nosave; i < nr_copy_pages && !error; i++, p++) { if (!(i%100)) printk( "." ); - error = read_page(swp_offset(p->swap_address), + error = bio_read_page(swp_offset(p->swap_address), (void *)p->address); } printk(" %d done.\n",i); diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 7de6b26f4efe..15fd0d24bff0 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -1114,55 +1115,107 @@ static int sanity_check(struct suspend_header *sh) return 0; } -static int bdev_read_page(struct block_device *bdev, long pos, void *buf) + +/** + * 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) +{ + while(atomic_read(&io_done)) + io_schedule(); +} + + +struct block_device * resume_bdev; + +/** + * 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 bdev_write_page(struct block_device *bdev, long pos, void *buf) +static int submit(int rw, pgoff_t page_off, void * page) { -#if 0 - struct buffer_head *bh; - BUG_ON (pos%PAGE_SIZE); - bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE); - if (!bh || (!bh->b_data)) { - return -1; + 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("pmdisk: ERROR: adding page to bio at %ld\n",page_off); + error = -EFAULT; + goto Done; } - memcpy(bh->b_data, buf, PAGE_SIZE); /* FIXME: may need kmap() */ - BUG_ON(!buffer_uptodate(bh)); - generic_make_request(WRITE, bh); - if (!buffer_uptodate(bh)) - printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unsuccessful...\n", name_resume, resume_file); - wait_on_buffer(bh); - brelse(bh); - return 0; -#endif - printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unimplemented...\n", name_resume, resume_file); - return 0; + + if (rw == WRITE) + bio_set_pages_dirty(bio); + start_io(); + submit_bio(rw | (1 << BIO_RW_SYNC), bio); + wait_io(); + Done: + bio_put(bio); + return error; +} + +int bio_read_page(pgoff_t page_off, void * page) +{ + return submit(READ,page_off,page); } +int bio_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, int noresume) +static int __init __read_suspend_image(int noresume) { + union diskpage *cur; swp_entry_t next; int i, nr_pgdir_pages; + cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC); + if (!cur) + return -ENOMEM; + #define PREPARENEXT \ { next = cur->link.next; \ next.val = swp_offset(next) * PAGE_SIZE; \ } - if (bdev_read_page(bdev, 0, cur)) return -EIO; + if (bio_read_page(0, cur)) return -EIO; if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) || (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) { @@ -1188,13 +1241,13 @@ static int __init __read_suspend_image(struct block_device *bdev, union diskpage We need to write swap, but swap is *not* enabled so we must write the device directly */ printk("%s: Fixing swap signatures %s...\n", name_resume, resume_file); - bdev_write_page(bdev, 0, cur); + bio_write_page(0, cur); } printk( "%sSignature found, resuming\n", name_resume ); MDELAY(1000); - if (bdev_read_page(bdev, next.val, cur)) return -EIO; + if (bio_read_page(next.val, cur)) return -EIO; if (sanity_check(&cur->sh)) /* Is this same machine? */ return -EPERM; PREPARENEXT; @@ -1214,7 +1267,7 @@ static int __init __read_suspend_image(struct block_device *bdev, union diskpage 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; + if (bio_read_page(next.val, cur)) return -EIO; PREPARENEXT; } BUG_ON (next.val); @@ -1229,7 +1282,7 @@ static int __init __read_suspend_image(struct block_device *bdev, union diskpage 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))) + if (bio_read_page(swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address))) return -EIO; } printk( "|\n" ); @@ -1238,48 +1291,18 @@ static int __init __read_suspend_image(struct block_device *bdev, union diskpage static int __init read_suspend_image(const char * specialfile, int noresume) { - union diskpage *cur; - unsigned long scratch_page = 0; int error; char b[BDEVNAME_SIZE]; resume_device = name_to_dev_t(specialfile); - scratch_page = get_zeroed_page(GFP_ATOMIC); - cur = (void *) scratch_page; - if (cur) { - struct block_device *bdev; - printk("Resuming from device %s\n", - __bdevname(resume_device, b)); - bdev = open_by_devnum(resume_device, FMODE_READ); - if (IS_ERR(bdev)) { - error = PTR_ERR(bdev); - } else { - set_blocksize(bdev, PAGE_SIZE); - error = __read_suspend_image(bdev, cur, noresume); - blkdev_put(bdev); - } - } else error = -ENOMEM; - - if (scratch_page) - free_page(scratch_page); - switch (error) { - case 0: - PRINTK("Reading resume file was successful\n"); - break; - case -EINVAL: - break; - case -EIO: - printk( "%sI/O error\n", name_resume); - break; - case -ENOENT: - printk( "%s%s: No such file or directory\n", name_resume, specialfile); - break; - case -ENOMEM: - printk( "%sNot enough memory\n", name_resume); - break; - default: - printk( "%sError %d resuming\n", name_resume, error ); - } + printk("Resuming from device %s\n", __bdevname(resume_device, b)); + resume_bdev = open_by_devnum(resume_device, FMODE_READ); + if (!IS_ERR(resume_bdev)) { + set_blocksize(resume_bdev, PAGE_SIZE); + error = __read_suspend_image(noresume); + blkdev_put(resume_bdev); + } else + error = PTR_ERR(resume_bdev); MDELAY(1000); return error; } -- cgit v1.2.3 From 5beeb715e367c5ec185bd338d0f5722f7d9382e6 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 21:52:51 -0700 Subject: [Power Mgmt] Merge pmdisk/swsusp image reading code. - Create swsusp_data_read() and call from read_suspend_image() in both swsusp and pmdisk. - Mark swsusp_pagedir_relocate() as static again. --- kernel/power/pmdisk.c | 38 +++++--------------------------------- kernel/power/power.h | 3 --- kernel/power/swsusp.c | 48 +++++++++++++++++++++++++++++++++--------------- 3 files changed, 38 insertions(+), 51 deletions(-) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index 128a85f39ec6..ef0d74f351b4 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -385,33 +385,10 @@ static int __init read_pagedir(void) } -/** - * read_image_data - Read image pages from swap. - * - * You do not need to check for overlaps, check_pagedir() - * already did that. - */ - -static int __init read_image_data(void) -{ - struct pbe * p; - int error = 0; - int i; - - printk( "Reading image data (%d pages): ", nr_copy_pages ); - for(i = 0, p = pagedir_nosave; i < nr_copy_pages && !error; i++, p++) { - if (!(i%100)) - printk( "." ); - error = bio_read_page(swp_offset(p->swap_address), - (void *)p->address); - } - printk(" %d done.\n",i); - return error; -} - - static int __init read_suspend_image(void) { + extern int swsusp_data_read(void); + int error = 0; if ((error = check_sig())) @@ -420,15 +397,10 @@ static int __init read_suspend_image(void) return error; if ((error = read_pagedir())) return error; - if ((error = swsusp_pagedir_relocate())) - goto FreePagedir; - if ((error = read_image_data())) - goto FreePagedir; - Done: + if ((error = swsusp_data_read())) { + free_pages((unsigned long)pagedir_nosave,pagedir_order); + } return error; - FreePagedir: - free_pages((unsigned long)pagedir_nosave,pagedir_order); - goto Done; } diff --git a/kernel/power/power.h b/kernel/power/power.h index 2414245e612e..92424ccc2076 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -18,9 +18,6 @@ static inline int pm_suspend_disk(void) return -EPERM; } #endif - -extern int swsusp_pagedir_relocate(void); - extern struct semaphore pm_sem; #define power_attr(_name) \ static struct subsys_attribute _name##_attr = { \ diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 15fd0d24bff0..21cb666a7a81 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -1041,7 +1041,7 @@ static int __init check_pagedir(void) return 0; } -int __init swsusp_pagedir_relocate(void) +static int __init swsusp_pagedir_relocate(void) { /* * We have to avoid recursion (not to overflow kernel stack), @@ -1198,6 +1198,34 @@ int bio_write_page(pgoff_t page_off, void * page) } +/** + * swsusp_read_data - Read image pages from swap. + * + * You do not need to check for overlaps, check_pagedir() + * already did that. + */ + +int __init swsusp_data_read(void) +{ + struct pbe * p; + int error; + int i; + + if ((error = swsusp_pagedir_relocate())) + return error; + + printk( "Reading image data (%d pages): ", nr_copy_pages ); + for(i = 0, p = pagedir_nosave; i < nr_copy_pages && !error; i++, p++) { + if (!(i%100)) + printk( "." ); + error = bio_read_page(swp_offset(p->swap_address), + (void *)p->address); + } + printk(" %d done.\n",i); + return error; + +} + extern dev_t __init name_to_dev_t(const char *line); static int __init __read_suspend_image(int noresume) @@ -1205,6 +1233,7 @@ static int __init __read_suspend_image(int noresume) union diskpage *cur; swp_entry_t next; int i, nr_pgdir_pages; + int error; cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC); if (!cur) @@ -1272,20 +1301,9 @@ static int __init __read_suspend_image(int noresume) } BUG_ON (next.val); - if (swsusp_pagedir_relocate()) - return -ENOMEM; - - printk( "Reading image data (%d pages): ", nr_copy_pages ); - for(i=0; i < nr_copy_pages; i++) { - swp_entry_t swap_address = (pagedir_nosave+i)->swap_address; - if (!(i%100)) - printk( "." ); - /* You do not need to check for overlaps... - ... check_pagedir already did this work */ - if (bio_read_page(swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address))) - return -EIO; - } - printk( "|\n" ); + error = swsusp_data_read(); + if (!error) + printk( "|\n" ); return 0; } -- 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(-) 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 4196815a90f99def73738fec6669c8d1cc3d7478 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 22:24:57 -0700 Subject: [swsusp] Fix nasty bug in calculating next address to read from. - The bio code already does this for us.. --- kernel/power/swsusp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 9cb01d8cc92d..7d8a47c5501b 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -1279,7 +1279,7 @@ static int __init __read_suspend_image(int noresume) #define PREPARENEXT \ { next = cur->link.next; \ - next.val = swp_offset(next) * PAGE_SIZE; \ + next.val = swp_offset(next); \ } if (bio_read_page(0, cur)) return -EIO; -- cgit v1.2.3 From b6fb7271ae8b312fd662085a16fd418d56d9e134 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 22:46:29 -0700 Subject: [Power Mgmt] Merge pmdisk and swsusp signature handling. - Move struct pmdisk_header definition to swsusp and change name to struct swsusp_header. - Statically allocate one (swsusp_header), and use it during mark_swapfiles() and when checking sig on resume. - Move check_sig() from pmdisk to swsusp. - Wrap with swsusp_verify(), and move check_header() there. - Fix up calls in pmdisk and swsusp. - Make new wrapper swsusp_close_swap() and call from write_suspend_image(). - Look for swsusp_info info in swsusp_header.swsusp_info, instead of magic location at end of struct. --- kernel/power/pmdisk.c | 71 ++-------------------- kernel/power/swsusp.c | 165 +++++++++++++++++++++++++------------------------- 2 files changed, 88 insertions(+), 148 deletions(-) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index 53b47002f5e3..fb8edb468b79 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -63,15 +63,6 @@ extern int pagedir_order; extern struct swsusp_info swsusp_info; -#define PMDISK_SIG "pmdisk-swap1" - -struct pmdisk_header { - char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)]; - swp_entry_t pmdisk_info; - char orig_sig[10]; - char sig[10]; -} __attribute__((packed, aligned(PAGE_SIZE))) pmdisk_header; - /* * XXX: We try to keep some more pages free so that I/O operations succeed * without paging. Might this be more? @@ -89,35 +80,10 @@ struct pmdisk_header { #define SWAPFILE_SUSPEND 1 /* This is the suspending device */ #define SWAPFILE_IGNORED 2 /* Those are other swap devices ignored for suspension */ -extern unsigned short swapfile_used[MAX_SWAPFILES]; -extern unsigned short root_swap; extern int swsusp_swap_check(void); extern void swsusp_swap_lock(void); -static int mark_swapfiles(swp_entry_t prev) -{ - int error; - - rw_swap_page_sync(READ, - swp_entry(root_swap, 0), - virt_to_page((unsigned long)&pmdisk_header)); - if (!memcmp("SWAP-SPACE",pmdisk_header.sig,10) || - !memcmp("SWAPSPACE2",pmdisk_header.sig,10)) { - memcpy(pmdisk_header.orig_sig,pmdisk_header.sig,10); - memcpy(pmdisk_header.sig,PMDISK_SIG,10); - pmdisk_header.pmdisk_info = prev; - error = rw_swap_page_sync(WRITE, - swp_entry(root_swap, 0), - virt_to_page((unsigned long) - &pmdisk_header)); - } else { - pr_debug("pmdisk: Partition is not swap space.\n"); - error = -ENODEV; - } - return error; -} - extern int swsusp_write_page(unsigned long addr, swp_entry_t * entry); extern int swsusp_data_write(void); extern void swsusp_data_free(void); @@ -156,7 +122,7 @@ static int write_pagedir(void) } extern void swsusp_init_header(void); -extern int swsusp_write_header(swp_entry_t*); +extern int swsusp_close_swap(void); /** * write_suspend_image - Write entire image and metadata. @@ -166,7 +132,6 @@ extern int swsusp_write_header(swp_entry_t*); static int write_suspend_image(void) { int error; - swp_entry_t prev = { 0 }; swsusp_init_header(); if ((error = swsusp_data_write())) @@ -175,10 +140,8 @@ static int write_suspend_image(void) if ((error = write_pagedir())) goto FreePagedir; - if ((error = swsusp_write_header(&prev))) + if ((error = swsusp_close_swap())) goto FreePagedir; - - error = mark_swapfiles(prev); Done: return error; FreePagedir: @@ -227,30 +190,6 @@ extern int bio_write_page(pgoff_t page_off, void * page); extern dev_t __init name_to_dev_t(const char *line); -static int __init check_sig(void) -{ - int error; - - memset(&pmdisk_header,0,sizeof(pmdisk_header)); - if ((error = bio_read_page(0,&pmdisk_header))) - return error; - if (!memcmp(PMDISK_SIG,pmdisk_header.sig,10)) { - memcpy(pmdisk_header.sig,pmdisk_header.orig_sig,10); - - /* - * Reset swap signature now. - */ - error = bio_write_page(0,&pmdisk_header); - } else { - pr_debug(KERN_ERR "pmdisk: Invalid partition type.\n"); - return -EINVAL; - } - if (!error) - pr_debug("pmdisk: Signature found, resuming\n"); - return error; -} - - static int __init read_pagedir(void) { unsigned long addr; @@ -282,13 +221,11 @@ 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); + extern int swsusp_verify(void); int error = 0; - if ((error = check_sig())) - return error; - if ((error = swsusp_check_header(pmdisk_header.pmdisk_info))) + if ((error = swsusp_verify())) return error; if ((error = read_pagedir())) return error; diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 7d8a47c5501b..2559489dc26c 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -111,6 +111,15 @@ suspend_pagedir_t *pagedir_nosave __nosavedata = NULL; suspend_pagedir_t *pagedir_save; int pagedir_order __nosavedata = 0; +#define SWSUSP_SIG "S1SUSPEND" + +struct swsusp_header { + char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)]; + swp_entry_t swsusp_info; + char orig_sig[10]; + char sig[10]; +} __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header; + struct swsusp_info swsusp_info; struct link { @@ -161,49 +170,32 @@ static const char name_resume[] = "Resume Machine: "; #define SWAPFILE_SUSPEND 1 /* This is the suspending device */ #define SWAPFILE_IGNORED 2 /* Those are other swap devices ignored for suspension */ -unsigned short swapfile_used[MAX_SWAPFILES]; -unsigned short root_swap; -#define MARK_SWAP_SUSPEND 0 -#define MARK_SWAP_RESUME 2 +static unsigned short swapfile_used[MAX_SWAPFILES]; +static unsigned short root_swap; -static void mark_swapfiles(swp_entry_t prev, int mode) +static int mark_swapfiles(swp_entry_t prev) { - swp_entry_t entry; - union diskpage *cur; - struct page *page; + int error; - if (root_swap == 0xFFFF) /* ignored */ - return; - - page = alloc_page(GFP_ATOMIC); - if (!page) - panic("Out of memory in mark_swapfiles"); - cur = page_address(page); - /* XXX: this is dirty hack to get first page of swap file */ - entry = swp_entry(root_swap, 0); - rw_swap_page_sync(READ, entry, page); - - if (mode == MARK_SWAP_RESUME) { - 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("%sUnable to find suspended-data signature (%.10s - misspelled?\n", - name_resume, cur->swh.magic.magic); + rw_swap_page_sync(READ, + swp_entry(root_swap, 0), + virt_to_page((unsigned long)&swsusp_header)); + if (!memcmp("SWAP-SPACE",swsusp_header.sig,10) || + !memcmp("SWAPSPACE2",swsusp_header.sig,10)) { + memcpy(swsusp_header.orig_sig,swsusp_header.sig,10); + memcpy(swsusp_header.sig,SWSUSP_SIG,10); + swsusp_header.swsusp_info = prev; + error = rw_swap_page_sync(WRITE, + swp_entry(root_swap, 0), + virt_to_page((unsigned long) + &swsusp_header)); } else { - if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10))) - memcpy(cur->swh.magic.magic,"S1SUSP....",10); - else if ((!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) - memcpy(cur->swh.magic.magic,"S2SUSP....",10); - else panic("\nSwapspace is not swapspace (%.10s)\n", cur->swh.magic.magic); - cur->link.next = prev; /* prev is the first/last swap page of the resume area */ - /* link.next lies *no more* in last 4/8 bytes of magic */ + pr_debug("swsusp: Partition is not swap space.\n"); + error = -ENODEV; } - rw_swap_page_sync(WRITE, entry, page); - __free_page(page); + return error; } - /* * Check whether the swap device is the specified resume * device, irrespective of whether they are specified by @@ -400,11 +392,25 @@ void swsusp_init_header(void) * entrance. On exit, it contains the location of the header. */ -int swsusp_write_header(swp_entry_t * entry) +static int write_header(swp_entry_t * entry) { return swsusp_write_page((unsigned long)&swsusp_info,entry); } + +int swsusp_close_swap(void) +{ + swp_entry_t entry; + int error; + error = write_header(&entry); + + printk( "S" ); + if (!error) + error = mark_swapfiles(entry); + printk( "|\n" ); + return error; +} + /** * write_suspend_image - Write entire image to disk. * @@ -429,6 +435,7 @@ static int write_suspend_image(void) return -ENOMEM; swsusp_data_write(); + swsusp_init_header(); printk( "Writing pagedir (%d pages): ", nr_pgdir_pages); addr = (unsigned long)pagedir_nosave; @@ -442,12 +449,8 @@ static int write_suspend_image(void) BUG_ON (sizeof(union diskpage) != PAGE_SIZE); BUG_ON (sizeof(struct link) != PAGE_SIZE); - swsusp_init_header(); swsusp_info.pagedir[0] = entry; - error = swsusp_write_header(&entry); - printk( "S" ); - mark_swapfiles(entry, MARK_SWAP_SUSPEND); - printk( "|\n" ); + swsusp_close_swap(); MDELAY(1000); return 0; @@ -906,9 +909,6 @@ static void suspend_finish(void) restore_highmem(); #endif device_resume(); - PRINTK( "Fixing swap signatures... " ); - mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME); - PRINTK( "ok\n" ); #ifdef SUSPEND_CONSOLE acquire_console_sem(); @@ -1217,12 +1217,12 @@ static const char * __init sanity_check(void) } -int __init swsusp_check_header(swp_entry_t loc) +static int __init check_header(void) { const char * reason = NULL; int error; - if ((error = bio_read_page(swp_offset(loc), &swsusp_info))) + if ((error = bio_read_page(swp_offset(swsusp_header.swsusp_info), &swsusp_info))) return error; /* Is this same machine? */ @@ -1234,6 +1234,38 @@ int __init swsusp_check_header(swp_entry_t loc) return error; } +static int __init check_sig(void) +{ + int error; + + memset(&swsusp_header,0,sizeof(swsusp_header)); + if ((error = bio_read_page(0,&swsusp_header))) + return error; + if (!memcmp(SWSUSP_SIG,swsusp_header.sig,10)) { + memcpy(swsusp_header.sig,swsusp_header.orig_sig,10); + + /* + * Reset swap signature now. + */ + error = bio_write_page(0,&swsusp_header); + } else { + pr_debug(KERN_ERR "swsusp: Invalid partition type.\n"); + return -EINVAL; + } + if (!error) + pr_debug("swsusp: Signature found, resuming\n"); + return error; +} + + +int __init swsusp_verify(void) +{ + int error; + + if (!(error = check_sig())) + error = check_header(); + return error; +} /** @@ -1273,6 +1305,9 @@ static int __init __read_suspend_image(int noresume) int i, nr_pgdir_pages; int error; + if ((error = swsusp_verify())) + return error; + cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC); if (!cur) return -ENOMEM; @@ -1282,40 +1317,8 @@ static int __init __read_suspend_image(int noresume) next.val = swp_offset(next); \ } - if (bio_read_page(0, cur)) return -EIO; - - 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; - } - - PREPARENEXT; /* We have to read next position before we overwrite it */ - - 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 { - if (noresume) - return -EINVAL; - panic("%sUnable to find suspended-data signature (%.10s - misspelled?\n", - name_resume, cur->swh.magic.magic); - } - if (noresume) { - /* We don't do a sanity check here: we want to restore the swap - whatever version of kernel made the suspend image; - We need to write swap, but swap is *not* enabled so - we must write the device directly */ - printk("%s: Fixing swap signatures %s...\n", name_resume, resume_file); - bio_write_page(0, cur); - } - - printk( "%sSignature found, resuming\n", name_resume ); - MDELAY(1000); - - if ((error = swsusp_check_header(next))) - return error; + if (noresume) + return 0; next = swsusp_info.pagedir[0]; next.val = swp_offset(next); -- cgit v1.2.3 From a65ee5aa5d4c480718958508545e831c10442061 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 23:08:46 -0700 Subject: [Power Mgmt] Merge pmdisk and swsusp pagedir handling. This embodies the core of the swsusp->pmdisk cleanups. Instead of using the ->dummy variable at the end of each pagedir for a linked list of the page dirs, this uses a static array, which is kept in the empty space of the swsusp header. There are 768 entries, and could be scaled up based on the size of the page and the amount of room remaining. 768 should be enough anyway, since each entry is a swp_entry_t to a page-length array of pages. With larger systems and more memory come larger pages, so each page-sized array will automatically scale up. This replaces the read_suspend_image() and write_suspend_image() in swsusp with the much more concise pmdisk versions (not that big of change at this point) and fixes up the callers so software_resume() gets it right. Also, mark the helpers only used in swsusp as static again. --- kernel/power/pmdisk.c | 159 +--------------------------------------- kernel/power/swsusp.c | 195 +++++++++++++++++++++++++------------------------- 2 files changed, 99 insertions(+), 255 deletions(-) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index fb8edb468b79..29ffffc06303 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -33,129 +33,20 @@ #include "power.h" - -extern asmlinkage int pmdisk_arch_suspend(int resume); - -/* Variables to be preserved over suspend */ -extern int pagedir_order_check; -extern int nr_copy_pages_check; - /* For resume= kernel option */ static char resume_file[256] = CONFIG_PM_DISK_PARTITION; static dev_t resume_device; -/* Local variables that should not be affected by save */ -extern unsigned int nr_copy_pages; - -/* Suspend pagedir is allocated before final copy, therefore it - must be freed after resume - Warning: this is evil. There are actually two pagedirs at time of - resume. One is "pagedir_save", which is empty frame allocated at - time of suspend, that must be freed. Second is "pagedir_nosave", - allocated at time of resume, that travels through memory not to - collide with anything. - */ -extern suspend_pagedir_t *pagedir_nosave; extern suspend_pagedir_t *pagedir_save; -extern int pagedir_order; - -extern struct swsusp_info swsusp_info; - - -/* - * XXX: We try to keep some more pages free so that I/O operations succeed - * without paging. Might this be more? - */ -#define PAGES_FOR_IO 512 - /* * Saving part... */ -/* 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 */ -#define SWAPFILE_IGNORED 2 /* Those are other swap devices ignored for suspension */ - -extern int swsusp_swap_check(void); extern void swsusp_swap_lock(void); - -extern int swsusp_write_page(unsigned long addr, swp_entry_t * entry); -extern int swsusp_data_write(void); -extern void swsusp_data_free(void); - -/** - * free_pagedir - Free pages used by the page directory. - */ - -static void free_pagedir_entries(void) -{ - int num = swsusp_info.pagedir_pages; - int i; - - for (i = 0; i < num; i++) - swap_free(swsusp_info.pagedir[i]); -} - - -/** - * write_pagedir - Write the array of pages holding the page directory. - * @last: Last swap entry we write (needed for header). - */ - -static int write_pagedir(void) -{ - unsigned long addr = (unsigned long)pagedir_nosave; - int error = 0; - int n = SUSPEND_PD_PAGES(nr_copy_pages); - int i; - - 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,&swsusp_info.pagedir[i]); - return error; -} - -extern void swsusp_init_header(void); -extern int swsusp_close_swap(void); - -/** - * write_suspend_image - Write entire image and metadata. - * - */ - -static int write_suspend_image(void) -{ - int error; - - swsusp_init_header(); - if ((error = swsusp_data_write())) - goto FreeData; - - if ((error = write_pagedir())) - goto FreePagedir; - - if ((error = swsusp_close_swap())) - goto FreePagedir; - Done: - return error; - FreePagedir: - free_pagedir_entries(); - FreeData: - swsusp_data_free(); - goto Done; -} - - -extern void free_suspend_pagedir(unsigned long); - - - /** * suspend_save_image - Prepare and write saved image to swap. * @@ -172,6 +63,7 @@ extern void free_suspend_pagedir(unsigned long); static int suspend_save_image(void) { + extern int write_suspend_image(void); int error; device_resume(); swsusp_swap_lock(); @@ -184,57 +76,10 @@ static int suspend_save_image(void) /* More restore stuff */ extern struct block_device * resume_bdev; -extern int bio_read_page(pgoff_t page_off, void * page); -extern int bio_write_page(pgoff_t page_off, void * page); extern dev_t __init name_to_dev_t(const char *line); -static int __init read_pagedir(void) -{ - unsigned long addr; - int i, n = swsusp_info.pagedir_pages; - int error = 0; - - pagedir_order = get_bitmask_order(n); - - addr =__get_free_pages(GFP_ATOMIC, pagedir_order); - if (!addr) - return -ENOMEM; - pagedir_nosave = (struct pbe *)addr; - - pr_debug("pmdisk: Reading pagedir (%d Pages)\n",n); - - for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) { - unsigned long offset = swp_offset(swsusp_info.pagedir[i]); - if (offset) - error = bio_read_page(offset, (void *)addr); - else - error = -EFAULT; - } - if (error) - free_pages((unsigned long)pagedir_nosave,pagedir_order); - return error; -} - - -static int __init read_suspend_image(void) -{ - extern int swsusp_data_read(void); - extern int swsusp_verify(void); - - int error = 0; - - if ((error = swsusp_verify())) - return error; - if ((error = read_pagedir())) - return error; - if ((error = swsusp_data_read())) { - free_pages((unsigned long)pagedir_nosave,pagedir_order); - } - return error; -} - /** * pmdisk_write - Write saved memory image to swap. @@ -258,6 +103,7 @@ int pmdisk_write(void) int __init pmdisk_read(void) { + extern int read_suspend_image(void); int error; if (!strlen(resume_file)) @@ -288,6 +134,7 @@ int __init pmdisk_read(void) int pmdisk_free(void) { + extern void free_suspend_pagedir(unsigned long this_pagedir); pr_debug( "Freeing prev allocated pagedir\n" ); free_suspend_pagedir((unsigned long)pagedir_save); return 0; diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 2559489dc26c..73b747fbfbd2 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -122,16 +122,6 @@ struct swsusp_header { struct swsusp_info swsusp_info; -struct link { - char dummy[PAGE_SIZE - sizeof(swp_entry_t)]; - swp_entry_t next; -}; - -union diskpage { - union swap_header swh; - struct link link; -}; - /* * XXX: We try to keep some more pages free so that I/O operations succeed * without paging. Might this be more? @@ -283,7 +273,7 @@ void swsusp_swap_lock(void) * errors, though we need to eventually fix the damn code. */ -int swsusp_write_page(unsigned long addr, swp_entry_t * loc) +static int write_page(unsigned long addr, swp_entry_t * loc) { swp_entry_t entry; int error = 0; @@ -309,7 +299,7 @@ int swsusp_write_page(unsigned long addr, swp_entry_t * loc) * Walk the list of used swap entries and free each one. */ -void swsusp_data_free(void) +static void data_free(void) { swp_entry_t entry; int i; @@ -331,7 +321,7 @@ void swsusp_data_free(void) * Walk the list of pages in the image and sync each one to swap. */ -int swsusp_data_write(void) +static int data_write(void) { int error = 0; int i; @@ -340,7 +330,7 @@ int swsusp_data_write(void) for (i = 0; i < nr_copy_pages && !error; i++) { if (!(i%100)) printk( "." ); - error = swsusp_write_page((pagedir_nosave+i)->address, + error = write_page((pagedir_nosave+i)->address, &((pagedir_nosave+i)->swap_address)); } printk(" %d Pages done.\n",i); @@ -369,7 +359,7 @@ static void dump_info(void) } #endif -void swsusp_init_header(void) +static void init_header(void) { memset(&swsusp_info,0,sizeof(swsusp_info)); swsusp_info.version_code = LINUX_VERSION_CODE; @@ -394,11 +384,11 @@ void swsusp_init_header(void) static int write_header(swp_entry_t * entry) { - return swsusp_write_page((unsigned long)&swsusp_info,entry); + return write_page((unsigned long)&swsusp_info,entry); } -int swsusp_close_swap(void) +static int close_swap(void) { swp_entry_t entry; int error; @@ -412,50 +402,66 @@ int swsusp_close_swap(void) } /** - * write_suspend_image - Write entire image to disk. - * - * After writing suspend signature to the disk, suspend may no - * longer fail: we have ready-to-run image in swap, and rollback - * would happen on next reboot -- corrupting data. - * - * Note: The buffer we allocate to use to write the suspend header is - * not freed; its not needed since the system is going down anyway - * (plus it causes an oops and I'm lazy^H^H^H^Htoo busy). + * free_pagedir - Free pages used by the page directory. */ -static int write_suspend_image(void) + +static void free_pagedir_entries(void) { + int num = swsusp_info.pagedir_pages; int i; + + for (i = 0; i < num; i++) + swap_free(swsusp_info.pagedir[i]); +} + + +/** + * write_pagedir - Write the array of pages holding the page directory. + * @last: Last swap entry we write (needed for header). + */ + +static int write_pagedir(void) +{ + unsigned long addr = (unsigned long)pagedir_nosave; int error = 0; - swp_entry_t entry = { 0 }; - int nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages); - union diskpage *cur, *buffer = (union diskpage *)get_zeroed_page(GFP_ATOMIC); - unsigned long addr; + int n = SUSPEND_PD_PAGES(nr_copy_pages); + int i; - if (!buffer) - return -ENOMEM; + swsusp_info.pagedir_pages = n; + printk( "Writing pagedir (%d pages)\n", n); + for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) + error = write_page(addr,&swsusp_info.pagedir[i]); + return error; +} + +/** + * write_suspend_image - Write entire image and metadata. + * + */ - swsusp_data_write(); - swsusp_init_header(); +int write_suspend_image(void) +{ + int error; - printk( "Writing pagedir (%d pages): ", nr_pgdir_pages); - addr = (unsigned long)pagedir_nosave; - for (i=0; ilink.next = entry; - printk( "." ); - error = swsusp_write_page(addr,&entry); - } - printk("H"); - BUG_ON (sizeof(union diskpage) != PAGE_SIZE); - BUG_ON (sizeof(struct link) != PAGE_SIZE); + init_header(); + if ((error = data_write())) + goto FreeData; - swsusp_info.pagedir[0] = entry; - swsusp_close_swap(); + if ((error = write_pagedir())) + goto FreePagedir; - MDELAY(1000); - return 0; + if ((error = close_swap())) + goto FreePagedir; + Done: + return error; + FreePagedir: + free_pagedir_entries(); + FreeData: + data_free(); + goto Done; } + #ifdef CONFIG_HIGHMEM struct highmem_page { char *data; @@ -1258,7 +1264,7 @@ static int __init check_sig(void) } -int __init swsusp_verify(void) +int __init verify(void) { int error; @@ -1275,7 +1281,7 @@ int __init swsusp_verify(void) * already did that. */ -int __init swsusp_data_read(void) +static int __init data_read(void) { struct pbe * p; int error; @@ -1298,58 +1304,48 @@ int __init swsusp_data_read(void) extern dev_t __init name_to_dev_t(const char *line); -static int __init __read_suspend_image(int noresume) +static int __init read_pagedir(void) { - union diskpage *cur; - swp_entry_t next; - int i, nr_pgdir_pages; - int error; - - if ((error = swsusp_verify())) - return error; - - cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC); - if (!cur) - return -ENOMEM; - -#define PREPARENEXT \ - { next = cur->link.next; \ - next.val = swp_offset(next); \ - } - - if (noresume) - return 0; - - next = swsusp_info.pagedir[0]; - next.val = swp_offset(next); + unsigned long addr; + int i, n = swsusp_info.pagedir_pages; + int error = 0; - 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); + pagedir_order = get_bitmask_order(n); - pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order); - if (!pagedir_nosave) + addr =__get_free_pages(GFP_ATOMIC, pagedir_order); + if (!addr) return -ENOMEM; + pagedir_nosave = (struct pbe *)addr; - PRINTK( "%sReading pagedir, ", name_resume ); + pr_debug("pmdisk: Reading pagedir (%d Pages)\n",n); - /* We get pages in reverse order of saving! */ - for (i=nr_pgdir_pages-1; i>=0; i--) { - BUG_ON (!next.val); - cur = (union diskpage *)((char *) pagedir_nosave)+i; - if (bio_read_page(next.val, cur)) return -EIO; - PREPARENEXT; + for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) { + unsigned long offset = swp_offset(swsusp_info.pagedir[i]); + if (offset) + error = bio_read_page(offset, (void *)addr); + else + error = -EFAULT; } - BUG_ON (next.val); + if (error) + free_pages((unsigned long)pagedir_nosave,pagedir_order); + return error; +} - error = swsusp_data_read(); - if (!error) - printk( "|\n" ); - return 0; +int __init read_suspend_image(void) +{ + int error = 0; + + if ((error = verify())) + return error; + if ((error = read_pagedir())) + return error; + if ((error = data_read())) { + free_pages((unsigned long)pagedir_nosave,pagedir_order); + } + return error; } -static int __init read_suspend_image(const char * specialfile, int noresume) +static int __init __read_suspend_image(const char * specialfile) { int error; char b[BDEVNAME_SIZE]; @@ -1359,7 +1355,7 @@ static int __init read_suspend_image(const char * specialfile, int noresume) resume_bdev = open_by_devnum(resume_device, FMODE_READ); if (!IS_ERR(resume_bdev)) { set_blocksize(resume_bdev, PAGE_SIZE); - error = __read_suspend_image(noresume); + error = read_suspend_image(); blkdev_put(resume_bdev); } else error = PTR_ERR(resume_bdev); @@ -1391,9 +1387,10 @@ static int __init software_resume(void) printk( "%s", name_resume ); if (resume_status == NORESUME) { - if(resume_file[0]) - read_suspend_image(resume_file, 1); - printk( "disabled\n" ); + /* + * FIXME: If noresume is specified, we need to handle by finding + * the right partition and resettting the signature. + */ return 0; } MDELAY(1000); @@ -1407,7 +1404,7 @@ static int __init software_resume(void) } printk( "resuming from %s\n", resume_file); - if (read_suspend_image(resume_file, 0)) + if (__read_suspend_image(resume_file)) goto read_failure; /* FIXME: Should we stop processes here, just to be safer? */ disable_nonboot_cpus(); -- cgit v1.2.3 From a5b568fe6489270c25dba0abf13e740e6c72dab1 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 23:16:31 -0700 Subject: [Power Mgmt] Merge pmdisk and swsusp read wrappers. - Merge pmdisk_read() and __read_suspend_image() and rename to swsusp_read() - Fix up call in kernel/power/disk.c to call new name. - Remove extra error checking from software_resume(). --- kernel/power/disk.c | 4 ++-- kernel/power/pmdisk.c | 43 ------------------------------------------- kernel/power/swsusp.c | 33 +++++++++++++++++++-------------- 3 files changed, 21 insertions(+), 59 deletions(-) diff --git a/kernel/power/disk.c b/kernel/power/disk.c index cb54fa82f3bf..6c3a7defcaa5 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -25,7 +25,7 @@ extern struct pm_ops * pm_ops; extern int swsusp_suspend(void); extern int pmdisk_write(void); -extern int pmdisk_read(void); +extern int swsusp_read(void); extern int swsusp_resume(void); extern int pmdisk_free(void); @@ -205,7 +205,7 @@ static int pm_resume(void) 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"); diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index 29ffffc06303..0d2f10171fb4 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -35,9 +35,6 @@ /* For resume= kernel option */ static char resume_file[256] = CONFIG_PM_DISK_PARTITION; - -static dev_t resume_device; - extern suspend_pagedir_t *pagedir_save; /* @@ -73,14 +70,6 @@ static int suspend_save_image(void) } -/* More restore stuff */ - -extern struct block_device * resume_bdev; - -extern dev_t __init name_to_dev_t(const char *line); - - - /** * pmdisk_write - Write saved memory image to swap. * @@ -96,38 +85,6 @@ int pmdisk_write(void) return suspend_save_image(); } - -/** - * pmdisk_read - Read saved image from swap. - */ - -int __init pmdisk_read(void) -{ - extern int read_suspend_image(void); - int error; - - if (!strlen(resume_file)) - return -ENOENT; - - resume_device = name_to_dev_t(resume_file); - pr_debug("pmdisk: Resume From Partition: %s\n", resume_file); - - resume_bdev = open_by_devnum(resume_device, FMODE_READ); - if (!IS_ERR(resume_bdev)) { - set_blocksize(resume_bdev, PAGE_SIZE); - error = read_suspend_image(); - blkdev_put(resume_bdev); - } else - error = PTR_ERR(resume_bdev); - - if (!error) - pr_debug("Reading resume file was successful\n"); - else - pr_debug("pmdisk: Error %d resuming\n", error); - return error; -} - - /** * pmdisk_free - Free memory allocated to hold snapshot. */ diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 73b747fbfbd2..562e60f40d7b 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -1145,7 +1145,7 @@ static void wait_io(void) } -struct block_device * resume_bdev; +static struct block_device * resume_bdev; /** * submit - submit BIO request. @@ -1331,7 +1331,7 @@ static int __init read_pagedir(void) return error; } -int __init read_suspend_image(void) +static int __init read_suspend_image(void) { int error = 0; @@ -1345,13 +1345,20 @@ int __init read_suspend_image(void) return error; } -static int __init __read_suspend_image(const char * specialfile) +/** + * pmdisk_read - Read saved image from swap. + */ + +int __init swsusp_read(void) { int error; - char b[BDEVNAME_SIZE]; - resume_device = name_to_dev_t(specialfile); - printk("Resuming from device %s\n", __bdevname(resume_device, b)); + if (!strlen(resume_file)) + return -ENOENT; + + resume_device = name_to_dev_t(resume_file); + pr_debug("swsusp: Resume From Partition: %s\n", resume_file); + resume_bdev = open_by_devnum(resume_device, FMODE_READ); if (!IS_ERR(resume_bdev)) { set_blocksize(resume_bdev, PAGE_SIZE); @@ -1359,7 +1366,11 @@ static int __init __read_suspend_image(const char * specialfile) blkdev_put(resume_bdev); } else error = PTR_ERR(resume_bdev); - MDELAY(1000); + + if (!error) + pr_debug("Reading resume file was successful\n"); + else + pr_debug("pmdisk: Error %d resuming\n", error); return error; } @@ -1398,13 +1409,7 @@ static int __init software_resume(void) if (pm_prepare_console()) printk("swsusp: Can't allocate a console... proceeding\n"); - if (!resume_file[0] && resume_status == RESUME_SPECIFIED) { - printk( "suspension device unspecified\n" ); - return -EINVAL; - } - - printk( "resuming from %s\n", resume_file); - if (__read_suspend_image(resume_file)) + if (swsusp_read()) goto read_failure; /* FIXME: Should we stop processes here, just to be safer? */ disable_nonboot_cpus(); -- cgit v1.2.3 From c2cd403ce1dbf10ae43f912bbe068373a5b9c8bf Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 23:21:29 -0700 Subject: [Power Mgmt] Merge pmdisk and swsusp write wrappers. - Merge suspend_save_image() from both into one. - Rename to swsusp_write(). - Remove pmdisk_write(). - Fixup call in kernel/power/disk.c and software_suspend(). - Mark lock_swapdevices() static again. --- kernel/power/disk.c | 4 ++-- kernel/power/pmdisk.c | 43 ------------------------------------------- kernel/power/swsusp.c | 28 +++++++++++++++------------- 3 files changed, 17 insertions(+), 58 deletions(-) diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 6c3a7defcaa5..4ed48e0e895b 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -24,7 +24,7 @@ extern u32 pm_disk_mode; extern struct pm_ops * pm_ops; extern int swsusp_suspend(void); -extern int pmdisk_write(void); +extern int swsusp_write(void); extern int swsusp_read(void); extern int swsusp_resume(void); extern int pmdisk_free(void); @@ -173,7 +173,7 @@ 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"); diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index 0d2f10171fb4..81795b4f3165 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -42,49 +42,6 @@ extern suspend_pagedir_t *pagedir_save; */ -extern void swsusp_swap_lock(void); - -/** - * 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 swsusp_swap_lock() 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) -{ - extern int write_suspend_image(void); - int error; - device_resume(); - swsusp_swap_lock(); - error = write_suspend_image(); - swsusp_swap_lock(); - return error; -} - - -/** - * pmdisk_write - Write saved memory image to swap. - * - * pmdisk_arch_suspend(0) returns after system is resumed. - * - * pmdisk_arch_suspend() copies all "used" memory to "free" memory, - * then unsuspends all device drivers, and writes memory to disk - * using normal kernel mechanism. - */ - -int pmdisk_write(void) -{ - return suspend_save_image(); -} - /** * pmdisk_free - Free memory allocated to hold snapshot. */ diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 562e60f40d7b..2ec72ec57680 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -246,7 +246,7 @@ int swsusp_swap_check(void) /* This is called before saving image */ * we make the device unusable. A new call to * lock_swapdevices can unlock the devices. */ -void swsusp_swap_lock(void) +static void lock_swapdevices(void) { int i; @@ -439,7 +439,7 @@ static int write_pagedir(void) * */ -int write_suspend_image(void) +static int write_suspend_image(void) { int error; @@ -862,20 +862,22 @@ int suspend_prepare_image(void) return 0; } -static void suspend_save_image(void) + +/* 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.) + */ +int swsusp_write(void) { + int error; device_resume(); - - swsusp_swap_lock(); - write_suspend_image(); + lock_swapdevices(); + error = write_suspend_image(); /* This will unlock ignored swap devices since writing is finished */ - swsusp_swap_lock(); + lock_swapdevices(); + return error; - /* 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 void suspend_power_down(void) @@ -1011,7 +1013,7 @@ int software_suspend(void) res = swsusp_save(); if (!res && in_suspend) { - suspend_save_image(); + swsusp_write(); suspend_power_down(); } in_suspend = 0; -- cgit v1.2.3 From c3c080bad832d2faf0edbd64996e129791656284 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 23:25:16 -0700 Subject: [Power Mgmt] Remove pmdisk_free() - Change name of free_suspend_pagedir() to swsusp_free(). - Call from kernel/power/disk.c --- kernel/power/disk.c | 6 +++--- kernel/power/pmdisk.c | 12 ------------ kernel/power/swsusp.c | 9 +++++---- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 4ed48e0e895b..6d4119cf40cc 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -27,7 +27,7 @@ extern int swsusp_suspend(void); extern int swsusp_write(void); extern int swsusp_read(void); extern int swsusp_resume(void); -extern int pmdisk_free(void); +extern int swsusp_free(void); /** @@ -180,7 +180,7 @@ int pm_suspend_disk(void) } } else pr_debug("PM: Image restored successfully.\n"); - pmdisk_free(); + swsusp_free(); Done: finish(); return error; @@ -231,7 +231,7 @@ static int pm_resume(void) pr_debug("PM: Restore failed, recovering.n"); finish(); Free: - pmdisk_free(); + swsusp_free(); Done: pr_debug("PM: Resume from disk failed.\n"); return 0; diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index 81795b4f3165..de66ff105997 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -42,18 +42,6 @@ extern suspend_pagedir_t *pagedir_save; */ -/** - * pmdisk_free - Free memory allocated to hold snapshot. - */ - -int pmdisk_free(void) -{ - extern void free_suspend_pagedir(unsigned long this_pagedir); - pr_debug( "Freeing prev allocated pagedir\n" ); - free_suspend_pagedir((unsigned long)pagedir_save); - return 0; -} - static int __init pmdisk_setup(char *str) { if (strlen(str)) { diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 2ec72ec57680..785f3709e7a2 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -656,14 +656,15 @@ static void free_suspend_pagedir_zone(struct zone *zone, unsigned long pagedir) } } -void free_suspend_pagedir(unsigned long this_pagedir) +void swsusp_free(void) { + unsigned long p = (unsigned long)pagedir_save; struct zone *zone; for_each_zone(zone) { if (!is_highmem(zone)) - free_suspend_pagedir_zone(zone, this_pagedir); + free_suspend_pagedir_zone(zone, p); } - free_pages(this_pagedir, pagedir_order); + free_pages(p, pagedir_order); } static int prepare_suspend_processes(void) @@ -816,7 +817,7 @@ static int swsusp_alloc(void) } if ((error = alloc_image_pages())) { pr_debug("suspend: Allocating image pages failed.\n"); - free_suspend_pagedir((unsigned long)pagedir_save); + swsusp_free(); return error; } -- cgit v1.2.3 From acddc01882a2d058fe96018f4b3326160b49d45a Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 23:27:57 -0700 Subject: [Power Mgmt] Make default partition config option part of swsusp. - Remove from pmdisk. - Remove pmdisk= command line option. --- kernel/power/Kconfig | 14 ++++---------- kernel/power/pmdisk.c | 23 ----------------------- kernel/power/swsusp.c | 2 +- 3 files changed, 5 insertions(+), 34 deletions(-) diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 8ffc76fec91a..817e30833e6a 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -54,12 +54,12 @@ config PM_DISK 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. @@ -68,16 +68,10 @@ config PM_DISK_PARTITION The partition specified can be overridden by specifying: - pmdisk=/dev/ + resume=/dev/ 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. diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index de66ff105997..21186ab859a3 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -33,26 +33,3 @@ #include "power.h" -/* For resume= kernel option */ -static char resume_file[256] = CONFIG_PM_DISK_PARTITION; -extern suspend_pagedir_t *pagedir_save; - -/* - * Saving part... - */ - - -static int __init pmdisk_setup(char *str) -{ - if (strlen(str)) { - if (!strcmp(str,"off")) - resume_file[0] = '\0'; - else - strncpy(resume_file, str, 255); - } else - resume_file[0] = '\0'; - return 1; -} - -__setup("pmdisk=", pmdisk_setup); - diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 785f3709e7a2..255c4451fb25 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -89,7 +89,7 @@ int pagedir_order_check; int nr_copy_pages_check; static int resume_status; -static char resume_file[256] = ""; /* For resume= kernel option */ +static char resume_file[256] = CONFIG_PM_STD_PARTITION; static dev_t resume_device; /* Local variables that should not be affected by save */ unsigned int nr_copy_pages __nosavedata = 0; -- cgit v1.2.3 From 0eabc61bec9ebef4114371cdd25c94b4ca30c318 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 23:30:02 -0700 Subject: [Power Mgmt] Remove pmdisk. - Remove kernel/power/pmdisk.c. - Remove CONFIG_PM_STD config option. - Fix up Makefile. --- kernel/power/Kconfig | 12 ------------ kernel/power/Makefile | 3 +-- kernel/power/pmdisk.c | 35 ----------------------------------- kernel/power/power.h | 2 +- 4 files changed, 2 insertions(+), 50 deletions(-) delete mode 100644 kernel/power/pmdisk.c diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 817e30833e6a..4d67db96e73b 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -42,18 +42,6 @@ config SOFTWARE_SUSPEND For more information take a look at Documentation/power/swsusp.txt. -config PM_DISK - bool "PMDisk Support" - depends on SOFTWARE_SUSPEND && X86 && !X86_64 - ---help--- - - This option enables an alternative implementation of Suspend-to-Disk. - It is functionally equivalent to Software Suspend, and uses many of - the same internal routines. But, it offers a slightly different - interface. - - If unsure, Say N. - config PM_STD_PARTITION string "Default resume partition" depends on SOFTWARE_SUSPEND diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 2509213f50f2..de024ffae3f3 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -2,7 +2,6 @@ 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 diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c deleted file mode 100644 index 21186ab859a3..000000000000 --- a/kernel/power/pmdisk.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * kernel/power/pmdisk.c - Suspend-to-disk implmentation - * - * This STD implementation is initially derived from swsusp (suspend-to-swap). - * The original copyright on that was: - * - * Copyright (C) 1998-2001 Gabor Kuti - * Copyright (C) 1998,2001,2002 Pavel Machek - * - * The additional parts are: - * - * Copyright (C) 2003 Patrick Mochel - * Copyright (C) 2003 Open Source Development Lab - * - * This file is released under the GPLv2. - * - * For more information, please see the text files in Documentation/power/ - * - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "power.h" - diff --git a/kernel/power/power.h b/kernel/power/power.h index 69c693cea1b5..cd6a3493cc0d 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -23,7 +23,7 @@ struct swsusp_info { -#ifdef CONFIG_PM_DISK +#ifdef CONFIG_SOFTWARE_SUSPEND extern int pm_suspend_disk(void); #else -- cgit v1.2.3 From 29221b810c59c94da43493103377068da6328263 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 23:52:19 -0700 Subject: [swsusp] Remove unneeded suspend_pagedir_lock. --- kernel/power/swsusp.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 255c4451fb25..74805928f5d8 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -81,9 +81,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 */ int pagedir_order_check; int nr_copy_pages_check; @@ -908,10 +905,7 @@ static void suspend_power_down(void) static void suspend_finish(void) { - spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ - free_pages((unsigned long) pagedir_nosave, pagedir_order); - spin_unlock_irq(&suspend_pagedir_lock); #ifdef CONFIG_HIGHMEM printk( "Restoring highmem\n" ); -- 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(-) 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 9556b782814592d10e2364cd7eebb9cb7a84825c Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sat, 17 Jul 2004 00:50:11 -0700 Subject: [swsusp] Fix nasty typo. --- kernel/power/swsusp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index e478c7e37bc6..167889d6c9a6 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -578,7 +578,7 @@ static int saveable(struct zone * zone, unsigned long * zone_pfn) return 0; } if ((chunk_size = is_head_of_free_region(page))) { - zone_pfn += chunk_size - 1; + *zone_pfn += chunk_size - 1; return 0; } @@ -601,11 +601,11 @@ static void count_data_pages(void) } -static void copy_data_pages(struct pbe * pbe) +static void copy_data_pages(void) { struct zone *zone; unsigned long zone_pfn; - + struct pbe * pbe = pagedir_nosave; for_each_zone(zone) { if (!is_highmem(zone)) @@ -807,9 +807,9 @@ int suspend_prepare_image(void) printk(", "); #endif - printk("counting pages to copy" ); drain_local_pages(); count_data_pages(); + printk("swsusp: Need to copy %u pages\n",nr_copy_pages); nr_needed_pages = nr_copy_pages + PAGES_FOR_IO; swsusp_alloc(); @@ -818,7 +818,7 @@ int suspend_prepare_image(void) * Kill them. */ drain_local_pages(); - copy_data_pages(pagedir_nosave); + copy_data_pages(); /* * End of critical section. From now on, we can write to memory, -- cgit v1.2.3 From 40bac04dbca219a73336dbe55d7ef2d596db8756 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sun, 1 Aug 2004 06:12:34 -0700 Subject: [swsusp] Add big fat comment to calc_order(). --- kernel/power/swsusp.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 167889d6c9a6..2d7783511c83 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -659,13 +659,38 @@ void swsusp_free(void) } +/** + * calc_order - Determine the order of allocation needed for pagedir_save. + * + * This looks tricky, but is just subtle. Please fix it some time. + * Since there are %nr_copy_pages worth of pages in the snapshot, we need + * to allocate enough contiguous space to hold + * (%nr_copy_pages * sizeof(struct pbe)), + * which has the saved/orig locations of the page.. + * + * SUSPEND_PD_PAGES() tells us how many pages we need to hold those + * structures, then we call get_bitmask_order(), which will tell us the + * last bit set in the number, starting with 1. (If we need 30 pages, that + * is 0x0000001e in hex. The last bit is the 5th, which is the order we + * would use to allocate 32 contiguous pages). + * + * Since we also need to save those pages, we add the number of pages that + * we need to nr_copy_pages, and in case of an overflow, do the + * calculation again to update the number of pages needed. + * + * With this model, we will tend to waste a lot of memory if we just cross + * an order boundary. Plus, the higher the order of allocation that we try + * to do, the more likely we are to fail in a low-memory situtation + * (though we're unlikely to get this far in such a case, since swsusp + * requires half of memory to be free anyway). + */ + + static void calc_order(void) { - int diff; - int order; + int diff = 0; + int order = 0; - order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)); - nr_copy_pages += 1 << order; do { diff = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)) - order; if (diff) { @@ -687,7 +712,7 @@ static void calc_order(void) static int alloc_pagedir(void) { calc_order(); - pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, + pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, pagedir_order); if(!pagedir_save) return -ENOMEM; -- cgit v1.2.3 From 4111c91339ade41a94a1ef06c231807147905bd8 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sun, 1 Aug 2004 06:36:45 -0700 Subject: [Power Mgmt] Add CONFIG_PM_DEBUG option - If enabled, add -DDEBUG to command line for kernel/power/ files. - Fixup redefinitions of DEBUG in main.c and disk.c - Kill useless DEBUG_* cruft in swsusp.c - Change printk()s in dump_info() to pr_debug(). --- kernel/power/Kconfig | 7 +++++++ kernel/power/Makefile | 4 ++++ kernel/power/disk.c | 3 --- kernel/power/main.c | 2 -- kernel/power/swsusp.c | 51 ++++++++++++--------------------------------------- 5 files changed, 23 insertions(+), 44 deletions(-) diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 4d67db96e73b..67f955286b6c 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -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 diff --git a/kernel/power/Makefile b/kernel/power/Makefile index de024ffae3f3..fbdc634135a7 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -1,4 +1,8 @@ +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 diff --git a/kernel/power/disk.c b/kernel/power/disk.c index ab49261f078f..feaf0a8ab2c0 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -8,9 +8,6 @@ * */ -#define DEBUG - - #include #include #include diff --git a/kernel/power/main.c b/kernel/power/main.c index 3e37ade640f6..62c96b329aa0 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -8,8 +8,6 @@ * */ -#define DEBUG - #include #include #include diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 2d7783511c83..2030529f2d3c 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -122,26 +122,6 @@ struct swsusp_info swsusp_info; static const char name_suspend[] = "Suspend Machine: "; static const char name_resume[] = "Resume Machine: "; -/* - * Debug - */ -#define DEBUG_DEFAULT -#undef DEBUG_PROCESS -#undef DEBUG_SLOW -#define TEST_SWSUSP 0 /* Set to 1 to reboot instead of halt machine after suspension */ - -#ifdef DEBUG_DEFAULT -# define PRINTK(f, a...) printk(f, ## a) -#else -# define PRINTK(f, a...) do { } while(0) -#endif - -#ifdef DEBUG_SLOW -#define MDELAY(a) mdelay(a) -#else -#define MDELAY(a) do { } while(0) -#endif - /* * Saving part... */ @@ -328,27 +308,20 @@ static int 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); + pr_debug(" swsusp: Version: %u\n",swsusp_info.version_code); + pr_debug(" swsusp: Num Pages: %ld\n",swsusp_info.num_physpages); + pr_debug(" swsusp: UTS Sys: %s\n",swsusp_info.uts.sysname); + pr_debug(" swsusp: UTS Node: %s\n",swsusp_info.uts.nodename); + pr_debug(" swsusp: UTS Release: %s\n",swsusp_info.uts.release); + pr_debug(" swsusp: UTS Version: %s\n",swsusp_info.uts.version); + pr_debug(" swsusp: UTS Machine: %s\n",swsusp_info.uts.machine); + pr_debug(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname); + pr_debug(" swsusp: CPUs: %d\n",swsusp_info.cpus); + pr_debug(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages); + pr_debug(" swsusp: Pagedir: %ld Pages\n",swsusp_info.pagedir_pages); } -#else -static void dump_info(void) -{ - -} -#endif static void init_header(void) { @@ -574,7 +547,7 @@ static int saveable(struct zone * zone, unsigned long * zone_pfn) if (PageNosave(page)) return 0; if (PageReserved(page) && pfn_is_nosave(pfn)) { - PRINTK("[nosave pfn 0x%lx]", pfn); + pr_debug("[nosave pfn 0x%lx]", pfn); return 0; } if ((chunk_size = is_head_of_free_region(page))) { -- cgit v1.2.3 From dc7bd03b7b1d326955c5351831b67bfd9c92453e Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sun, 1 Aug 2004 07:46:15 -0700 Subject: [swsusp] Kill unneeded write_header(). - Just inline and remove bad comment. --- kernel/power/swsusp.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 2030529f2d3c..0b0ccb1cfdc4 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -336,32 +336,17 @@ static void init_header(void) 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. - */ - -static int write_header(swp_entry_t * entry) -{ - return write_page((unsigned long)&swsusp_info,entry); -} - - static int close_swap(void) { swp_entry_t entry; int error; - error = write_header(&entry); - printk( "S" ); - if (!error) + error = write_page((unsigned long)&swsusp_info,&entry); + if (!error) { + printk( "S" ); error = mark_swapfiles(entry); - printk( "|\n" ); + printk( "|\n" ); + } return error; } -- cgit v1.2.3 From fa7f8704da3c054834f8418116e377029bd18c70 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sun, 1 Aug 2004 08:54:24 -0700 Subject: [swsusp] Make sure software_suspend() takes right path - Make sure it calls enter_state(), which does the proper locking and check for SMP, etc. --- kernel/power/main.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/kernel/power/main.c b/kernel/power/main.c index 62c96b329aa0..3461308043b9 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -173,13 +173,7 @@ static int enter_state(u32 state) */ int software_suspend(void) { - int error; - - if (down_trylock(&pm_sem)) - return -EBUSY; - error = pm_suspend_disk(); - up(&pm_sem); - return error; + return enter_state(PM_SUSPEND_DISK); } -- cgit v1.2.3 From 6bea24b9e60580779f731719811149978ae60ea2 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sun, 1 Aug 2004 08:57:39 -0700 Subject: [swsusp] Fix x86-64 low-level support. From Pavel Machek. --- arch/x86_64/kernel/suspend_asm.S | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/arch/x86_64/kernel/suspend_asm.S b/arch/x86_64/kernel/suspend_asm.S index 99a1adf868a7..48ad06289d49 100644 --- a/arch/x86_64/kernel/suspend_asm.S +++ b/arch/x86_64/kernel/suspend_asm.S @@ -1,22 +1,18 @@ -/* 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 #include #include -/* 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: -- cgit v1.2.3 From 11700b3cce269c668d82d5df94934fe17b2dff07 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sun, 1 Aug 2004 09:09:39 -0700 Subject: [swsusp] Make sure we call restore_highmem(). - Also, make highmem calls unconditional, with #ifdef inside functions. --- kernel/power/swsusp.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 0b0ccb1cfdc4..8971c8aaf4bc 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -467,22 +467,30 @@ static int save_highmem_zone(struct zone *zone) } return 0; } +#endif /* CONFIG_HIGHMEM */ + static int save_highmem(void) { +#ifdef CONFIG_HIGHMEM struct zone *zone; int res = 0; + + pr_debug("swsusp: Saving Highmem\n"); for_each_zone(zone) { if (is_highmem(zone)) res = save_highmem_zone(zone); if (res) return res; } +#endif return 0; } static int restore_highmem(void) { +#ifdef CONFIG_HIGHMEM + printk("swsusp: Restoring Highmem\n"); while (highmem_copy) { struct highmem_page *save = highmem_copy; void *kaddr; @@ -494,9 +502,10 @@ static int restore_highmem(void) free_page((long) save->data); kfree(save); } +#endif return 0; } -#endif + static int pfn_is_nosave(unsigned long pfn) { @@ -780,15 +789,11 @@ int suspend_prepare_image(void) { unsigned int nr_needed_pages = 0; - printk( "/critical section: "); -#ifdef CONFIG_HIGHMEM - printk( "handling highmem" ); + pr_debug("swsusp: critical section: \n"); if (save_highmem()) { printk(KERN_CRIT "%sNot enough free pages for highmem\n", name_suspend); return -ENOMEM; } - printk(", "); -#endif drain_local_pages(); count_data_pages(); @@ -809,7 +814,7 @@ int suspend_prepare_image(void) * touch swap space! Except we must write out our image of course. */ - printk( "critical section/: done (%d pages copied)\n", nr_copy_pages ); + printk("swsusp: critical section/: done (%d pages copied)\n", nr_copy_pages ); return 0; } @@ -876,6 +881,7 @@ int swsusp_resume(void) save_processor_state(); error = swsusp_arch_resume(); restore_processor_state(); + restore_highmem(); local_irq_enable(); return error; } -- cgit v1.2.3 From 4ebf0c4938a2f7311a366373f81547bb89bc48fc Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sun, 1 Aug 2004 09:29:42 -0700 Subject: [Power Mgmt] Make sure we shutdown devices on shutdown and reboot.. --- kernel/power/disk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/power/disk.c b/kernel/power/disk.c index feaf0a8ab2c0..f09de7ad0eff 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include "power.h" @@ -46,16 +47,18 @@ 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; } -- cgit v1.2.3 From a0eb3a4091315209f2cd2583b1fc8ab03988687b Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 24 Sep 2004 03:27:22 -0700 Subject: [PATCH] -mm swsusp: make sure we do not return to userspace where image is on disk From: Pavel Machek When image is already on the disk, returning back to user is dangerous. Signed-off-by: Andrew Morton --- kernel/power/disk.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/power/disk.c b/kernel/power/disk.c index f09de7ad0eff..1af45900f61f 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -63,8 +63,10 @@ static int power_down(u32 mode) 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("Please power me down manually\n"); + while(1); return 0; } -- cgit v1.2.3 From f832c11d0f05bd99a025b62498166b165ceb9479 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 24 Sep 2004 03:27:41 -0700 Subject: [PATCH] -mm swsusp: copy_page is harmfull From: Pavel Machek This is my fault from long time ago: copy_page can't be used for copying task struct, therefore we can't use it in swsusp. Signed-off-by: Andrew Morton --- kernel/power/swsusp.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 8971c8aaf4bc..c04e2a04dbbf 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -581,12 +581,8 @@ static void copy_data_pages(void) struct page * page; page = pfn_to_page(zone_pfn + zone->zone_start_pfn); pbe->orig_address = (long) page_address(page); - /* Copy page is dangerous: it likes to mess with - preempt count on specific cpus. Wrong preempt - count is then copied, oops. - */ - copy_page((void *)pbe->address, - (void *)pbe->orig_address); + /* copy_page is no usable for copying task structs. */ + memcpy((void *)pbe->address, (void *)pbe->orig_address, PAGE_SIZE); pbe++; } } -- cgit v1.2.3 From 818f9ec082ce566cb39be4b66f860fbe2e1b873f Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 24 Sep 2004 03:28:00 -0700 Subject: [PATCH] swsusp: do not disable platform swsusp because S4bios is available From: Pavel Machek Currently, when both S4 and S4bios are available, swsusp always chooses S4bios and makes S4 unavailable. Bad idea as S4bios needs completely different setup. Signed-off-by: Andrew Morton --- drivers/acpi/sleep/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 7b87041281e4..847a90ed5964 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -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; } } -- cgit v1.2.3 From 19318566fe545b1edbe68862a472f87bc42f15ad Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 24 Sep 2004 03:28:22 -0700 Subject: [PATCH] swsusp: fix default powerdown mode From: Pavel Machek I'd like new swsusp to default to powerdown mode (as it did before) before it goes up. Signed-off-by: Andrew Morton --- kernel/power/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/power/main.c b/kernel/power/main.c index 3461308043b9..6b5c59cc26ba 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -33,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); } -- cgit v1.2.3 From 1f585447137c1e19381b970fdfe5613044e6f5a6 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 24 Sep 2004 03:28:50 -0700 Subject: [PATCH] swsusp: do not oops after allocation failure From: Pavel Machek This checks error return from swsusp_alloc, preventing oops when memory can not be allocated. Signed-off-by: Andrew Morton --- kernel/power/swsusp.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index c04e2a04dbbf..11ea2b15f995 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -677,9 +677,9 @@ static int alloc_pagedir(void) calc_order(); pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, pagedir_order); - if(!pagedir_save) + if (!pagedir_save) return -ENOMEM; - memset(pagedir_save,0,(1 << pagedir_order) * PAGE_SIZE); + memset(pagedir_save, 0, (1 << pagedir_order) * PAGE_SIZE); pagedir_nosave = pagedir_save; return 0; } @@ -784,6 +784,7 @@ static int swsusp_alloc(void) int suspend_prepare_image(void) { unsigned int nr_needed_pages = 0; + int error; pr_debug("swsusp: critical section: \n"); if (save_highmem()) { @@ -796,7 +797,9 @@ int suspend_prepare_image(void) printk("swsusp: Need to copy %u pages\n",nr_copy_pages); nr_needed_pages = nr_copy_pages + PAGES_FOR_IO; - swsusp_alloc(); + error = swsusp_alloc(); + if (error) + return error; /* During allocating of suspend pagedir, new cold pages may appear. * Kill them. -- cgit v1.2.3 From 06793e87ef25b0112a2fe4b9467773fa51a59906 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 24 Sep 2004 03:29:10 -0700 Subject: [PATCH] swsusp: Documentation update From: Pavel Machek Signed-off-by: Andrew Morton --- Documentation/power/swsusp.txt | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt index 5cdab4c55e69..cbbbc9c0764f 100644 --- a/Documentation/power/swsusp.txt +++ b/Documentation/power/swsusp.txt @@ -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): -- cgit v1.2.3 From c1e09b2ce3c9c764d9fc675d5962971aeb263e7e Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 24 Sep 2004 03:29:30 -0700 Subject: [PATCH] Small cleanups for swsusp From: Pavel Machek Here are some small cleanups: whitespace fixes, added severity level, shuffle messages and kill unneccessary strings, add some statics and fixed misleading comments. Signed-off-by: Andrew Morton --- arch/i386/power/swsusp.S | 2 +- kernel/power/disk.c | 2 +- kernel/power/swsusp.c | 61 ++++++++++++++++++++++++------------------------ 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/arch/i386/power/swsusp.S b/arch/i386/power/swsusp.S index 221ac27a204f..6e6335f891ee 100644 --- a/arch/i386/power/swsusp.S +++ b/arch/i386/power/swsusp.S @@ -31,7 +31,7 @@ ENTRY(swsusp_arch_resume) movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx movl %ecx,%cr3 - movl pagedir_nosave,%ebx + movl pagedir_nosave, %ebx xorl %eax, %eax xorl %edx, %edx .p2align 4,,7 diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 1af45900f61f..312aa169c566 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -65,7 +65,7 @@ static int power_down(u32 mode) machine_halt(); /* Valid image is on the disk, if we continue we risk serious data corruption after resume. */ - printk("Please power me down manually\n"); + printk(KERN_CRIT "Please power me down manually\n"); while(1); return 0; } diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 11ea2b15f995..10af09a06bce 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -99,8 +99,8 @@ unsigned int nr_copy_pages __nosavedata = 0; MMU hardware. */ suspend_pagedir_t *pagedir_nosave __nosavedata = NULL; -suspend_pagedir_t *pagedir_save; -int pagedir_order __nosavedata = 0; +static suspend_pagedir_t *pagedir_save; +static int pagedir_order __nosavedata = 0; #define SWSUSP_SIG "S1SUSPEND" @@ -119,9 +119,6 @@ struct swsusp_info swsusp_info; */ #define PAGES_FOR_IO 512 -static const char name_suspend[] = "Suspend Machine: "; -static const char name_resume[] = "Resume Machine: "; - /* * Saving part... */ @@ -141,10 +138,10 @@ static int mark_swapfiles(swp_entry_t prev) rw_swap_page_sync(READ, swp_entry(root_swap, 0), virt_to_page((unsigned long)&swsusp_header)); - if (!memcmp("SWAP-SPACE",swsusp_header.sig,10) || - !memcmp("SWAPSPACE2",swsusp_header.sig,10)) { - memcpy(swsusp_header.orig_sig,swsusp_header.sig,10); - memcpy(swsusp_header.sig,SWSUSP_SIG,10); + if (!memcmp("SWAP-SPACE",swsusp_header.sig, 10) || + !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) { + memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); + memcpy(swsusp_header.sig,SWSUSP_SIG, 10); swsusp_header.swsusp_info = prev; error = rw_swap_page_sync(WRITE, swp_entry(root_swap, 0), @@ -265,9 +262,10 @@ static int write_page(unsigned long addr, swp_entry_t * loc) /** - * free_data - Free the swap entries used by the saved image. + * data_free - Free the swap entries used by the saved image. * * Walk the list of used swap entries and free each one. + * This is only used for cleanup when suspend fails. */ static void data_free(void) @@ -287,7 +285,7 @@ static void data_free(void) /** - * write_data - Write saved image to swap. + * data_write - Write saved image to swap. * * Walk the list of pages in the image and sync each one to swap. */ @@ -351,15 +349,16 @@ static int close_swap(void) } /** - * free_pagedir - Free pages used by the page directory. + * free_pagedir_entries - Free pages used by the page directory. + * + * This is used during suspend for error recovery. */ static void free_pagedir_entries(void) { - int num = swsusp_info.pagedir_pages; int i; - for (i = 0; i < num; i++) + for (i = 0; i < swsusp_info.pagedir_pages; i++) swap_free(swsusp_info.pagedir[i]); } @@ -379,7 +378,7 @@ static int write_pagedir(void) swsusp_info.pagedir_pages = n; printk( "Writing pagedir (%d pages)\n", n); for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) - error = write_page(addr,&swsusp_info.pagedir[i]); + error = write_page(addr, &swsusp_info.pagedir[i]); return error; } @@ -668,8 +667,8 @@ static void calc_order(void) /** * alloc_pagedir - Allocate the page directory. * - * First, determine exactly how many contiguous pages we need, - * allocate them, then mark each 'unsavable'. + * First, determine exactly how many contiguous pages we need and + * allocate them. */ static int alloc_pagedir(void) @@ -721,7 +720,7 @@ static int alloc_image_pages(void) static int enough_free_mem(void) { - if(nr_free_pages() < (nr_copy_pages + PAGES_FOR_IO)) { + if (nr_free_pages() < (nr_copy_pages + PAGES_FOR_IO)) { pr_debug("swsusp: Not enough free pages: Have %d\n", nr_free_pages()); return 0; @@ -757,7 +756,7 @@ static int swsusp_alloc(void) int error; pr_debug("suspend: (pages needed: %d + %d free: %d)\n", - nr_copy_pages,PAGES_FOR_IO,nr_free_pages()); + nr_copy_pages, PAGES_FOR_IO, nr_free_pages()); pagedir_nosave = NULL; if (!enough_free_mem()) @@ -788,7 +787,7 @@ int suspend_prepare_image(void) pr_debug("swsusp: critical section: \n"); if (save_highmem()) { - printk(KERN_CRIT "%sNot enough free pages for highmem\n", name_suspend); + printk(KERN_CRIT "Suspend machine: Not enough free pages for highmem\n"); return -ENOMEM; } @@ -900,8 +899,8 @@ static int __init does_collide_order(suspend_pagedir_t *pagedir, unsigned long a int i; unsigned long addre = addr + (PAGE_SIZE<orig_address >= addr && + for (i=0; i < nr_copy_pages; i++) + if ((pagedir+i)->orig_address >= addr && (pagedir+i)->orig_address < addre) return 1; @@ -1023,7 +1022,7 @@ static int submit(int rw, pgoff_t page_off, void * page) int error = 0; struct bio * bio; - bio = bio_alloc(GFP_ATOMIC,1); + bio = bio_alloc(GFP_ATOMIC, 1); if (!bio) return -ENOMEM; bio->bi_sector = page_off * (PAGE_SIZE >> 9); @@ -1049,12 +1048,12 @@ static int submit(int rw, pgoff_t page_off, void * page) int bio_read_page(pgoff_t page_off, void * page) { - return submit(READ,page_off,page); + return submit(READ, page_off, page); } int bio_write_page(pgoff_t page_off, void * page) { - return submit(WRITE,page_off,page); + return submit(WRITE, page_off, page); } /* @@ -1104,16 +1103,16 @@ static int __init check_sig(void) { int error; - memset(&swsusp_header,0,sizeof(swsusp_header)); - if ((error = bio_read_page(0,&swsusp_header))) + memset(&swsusp_header, 0, sizeof(swsusp_header)); + if ((error = bio_read_page(0, &swsusp_header))) return error; - if (!memcmp(SWSUSP_SIG,swsusp_header.sig,10)) { - memcpy(swsusp_header.sig,swsusp_header.orig_sig,10); + if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) { + memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10); /* * Reset swap signature now. */ - error = bio_write_page(0,&swsusp_header); + error = bio_write_page(0, &swsusp_header); } else { pr_debug(KERN_ERR "swsusp: Invalid partition type.\n"); return -EINVAL; @@ -1187,7 +1186,7 @@ static int __init read_pagedir(void) error = -EFAULT; } if (error) - free_pages((unsigned long)pagedir_nosave,pagedir_order); + free_pages((unsigned long)pagedir_nosave, pagedir_order); return error; } -- cgit v1.2.3 From bdf286baee964c01b1dec4ca7b5921d41c1ff17c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 24 Sep 2004 03:29:49 -0700 Subject: [PATCH] swsusp: another simplification From: Pavel Machek verify is not really descriptive name of function. Fortunately its insides are self-documenting which makes it easy to fix. Signed-off-by: Andrew Morton --- kernel/power/swsusp.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 10af09a06bce..5b7fcecb55c0 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -1122,17 +1122,6 @@ static int __init check_sig(void) return error; } - -int __init verify(void) -{ - int error; - - if (!(error = check_sig())) - error = check_header(); - return error; -} - - /** * swsusp_read_data - Read image pages from swap. * @@ -1194,13 +1183,14 @@ static int __init read_suspend_image(void) { int error = 0; - if ((error = verify())) + if ((error = check_sig())) + return error; + if ((error = check_header())) return error; if ((error = read_pagedir())) return error; - if ((error = data_read())) { - free_pages((unsigned long)pagedir_nosave,pagedir_order); - } + if ((error = data_read())) + free_pages((unsigned long)pagedir_nosave, pagedir_order); return error; } -- cgit v1.2.3 From 69d7d593cd286c125a39a040810c46da3eb9a79f Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 24 Sep 2004 03:30:09 -0700 Subject: [PATCH] swsusp: kill crash when too much memory is free From: Pavel Machek If too much memory is free, swsusp dies in quite a ugly way. Even when it is not neccessary to relocate pagedir, it is proably still neccessary to relocate individual pages. Thanks to Kurt Garloff and Stefan Seyfried. Signed-off-by: Andrew Morton --- kernel/power/swsusp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 5b7fcecb55c0..f65d295478c4 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -942,9 +942,9 @@ static int __init swsusp_pagedir_relocate(void) printk("Relocating pagedir "); - if(!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) { + if (!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) { printk("not necessary\n"); - return 0; + return check_pagedir(); } while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order)) != NULL) { -- 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(-) 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