From: Shaohua Li 1. Add architecture specific pages save/restore support. Next two patches will use this to save/restore 'ACPI NVS' pages. 2. Allow reserved pages 'nosave'. This could avoid save/restore BIOS reserved pages. Signed-off-by: Shaohua Li Cc: Pavel Machek Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton --- include/linux/suspend.h | 1 kernel/power/power.h | 4 + kernel/power/snapshot.c | 100 +++++++++++++++++++++++++++++++++++++- kernel/power/swsusp.c | 18 +----- 4 files changed, 108 insertions(+), 15 deletions(-) diff -puN include/linux/suspend.h~swsusp-add-architecture-special-saveable-pages-support include/linux/suspend.h --- devel/include/linux/suspend.h~swsusp-add-architecture-special-saveable-pages-support 2006-05-11 15:18:50.000000000 -0700 +++ devel-akpm/include/linux/suspend.h 2006-05-11 15:18:50.000000000 -0700 @@ -71,6 +71,7 @@ struct saved_context; void __save_processor_state(struct saved_context *ctxt); void __restore_processor_state(struct saved_context *ctxt); unsigned long get_safe_page(gfp_t gfp_mask); +int swsusp_add_arch_pages(unsigned long start, unsigned long end); /* * XXX: We try to keep some more pages free so that I/O operations succeed diff -puN kernel/power/power.h~swsusp-add-architecture-special-saveable-pages-support kernel/power/power.h --- devel/kernel/power/power.h~swsusp-add-architecture-special-saveable-pages-support 2006-05-11 15:18:50.000000000 -0700 +++ devel-akpm/kernel/power/power.h 2006-05-11 15:18:50.000000000 -0700 @@ -105,6 +105,10 @@ extern struct bitmap_page *alloc_bitmap( extern unsigned long alloc_swap_page(int swap, struct bitmap_page *bitmap); extern void free_all_swap_pages(int swap, struct bitmap_page *bitmap); +extern unsigned int count_special_pages(void); +extern int save_special_mem(void); +extern int restore_special_mem(void); + extern int swsusp_check(void); extern int swsusp_shrink_memory(void); extern void swsusp_free(void); diff -puN kernel/power/snapshot.c~swsusp-add-architecture-special-saveable-pages-support kernel/power/snapshot.c --- devel/kernel/power/snapshot.c~swsusp-add-architecture-special-saveable-pages-support 2006-05-11 15:18:50.000000000 -0700 +++ devel-akpm/kernel/power/snapshot.c 2006-05-11 15:18:50.000000000 -0700 @@ -39,6 +39,78 @@ static unsigned int nr_copy_pages; static unsigned int nr_meta_pages; static unsigned long *buffer; +struct arch_saveable_page { + unsigned long pfn; + void *data; + struct arch_saveable_page *next; +}; +static struct arch_saveable_page *arch_pages; + +int swsusp_add_arch_pages(unsigned long start, unsigned long end) +{ + struct arch_saveable_page *tmp; + + while (start <= end) { + tmp = kzalloc(sizeof(struct arch_saveable_page), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + tmp->pfn = start; + tmp->next = arch_pages; + start++; + arch_pages = tmp; + } + return 0; +} + +static unsigned int count_arch_pages(void) +{ + unsigned int count = 0; + struct arch_saveable_page *tmp = arch_pages; + while (tmp) { + count++; + tmp = tmp->next; + } + return count; +} + +static int save_arch_mem(void) +{ + void *kaddr; + struct arch_saveable_page *tmp = arch_pages; + + pr_debug("swsusp: Saving arch specific memory"); + while (tmp) { + tmp->data = (void *)__get_free_page(GFP_ATOMIC); + if (!tmp->data) + return -ENOMEM; + /* arch pages might haven't a 'struct page' */ + kaddr = kmap_atomic_pfn(tmp->pfn, KM_USER0); + memcpy(tmp->data, kaddr, PAGE_SIZE); + kunmap_atomic(kaddr, KM_USER0); + + tmp = tmp->next; + } + return 0; +} + +static int restore_arch_mem(void) +{ + void *kaddr; + struct arch_saveable_page *tmp = arch_pages; + + while (tmp) { + if (!tmp->data) + continue; + kaddr = kmap_atomic_pfn(tmp->pfn, KM_USER0); + memcpy(kaddr, tmp->data, PAGE_SIZE); + kunmap_atomic(kaddr, KM_USER0); + free_page((long)tmp->data); + tmp->data = NULL; + tmp = tmp->next; + } + return 0; +} + #ifdef CONFIG_HIGHMEM unsigned int count_highmem_pages(void) { @@ -150,8 +222,35 @@ int restore_highmem(void) } return 0; } +#else +static unsigned int count_highmem_pages(void) {return 0;} +static int save_highmem(void) {return 0;} +static int restore_highmem(void) {return 0;} #endif +unsigned int count_special_pages(void) +{ + return count_arch_pages() + count_highmem_pages(); +} + +int save_special_mem(void) +{ + int ret; + ret = save_arch_mem(); + if (!ret) + ret = save_highmem(); + return ret; +} + +int restore_special_mem(void) +{ + int ret; + ret = restore_arch_mem(); + if (!ret) + ret = restore_highmem(); + return ret; +} + static int pfn_is_nosave(unsigned long pfn) { unsigned long nosave_begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT; @@ -177,7 +276,6 @@ static int saveable(struct zone *zone, u return 0; page = pfn_to_page(pfn); - BUG_ON(PageReserved(page) && PageNosave(page)); if (PageNosave(page)) return 0; if (PageReserved(page) && pfn_is_nosave(pfn)) diff -puN kernel/power/swsusp.c~swsusp-add-architecture-special-saveable-pages-support kernel/power/swsusp.c --- devel/kernel/power/swsusp.c~swsusp-add-architecture-special-saveable-pages-support 2006-05-11 15:18:50.000000000 -0700 +++ devel-akpm/kernel/power/swsusp.c 2006-05-11 15:18:50.000000000 -0700 @@ -62,16 +62,6 @@ unsigned long image_size = 500 * 1024 * int in_suspend __nosavedata = 0; -#ifdef CONFIG_HIGHMEM -unsigned int count_highmem_pages(void); -int save_highmem(void); -int restore_highmem(void); -#else -static int save_highmem(void) { return 0; } -static int restore_highmem(void) { return 0; } -static unsigned int count_highmem_pages(void) { return 0; } -#endif - /** * The following functions are used for tracing the allocated * swap pages, so that they can be freed in case of an error. @@ -192,7 +182,7 @@ int swsusp_shrink_memory(void) printk("Shrinking memory... "); do { - size = 2 * count_highmem_pages(); + size = 2 * count_special_pages(); size += size / 50 + count_data_pages(); size += (size + PBES_PER_PAGE - 1) / PBES_PER_PAGE + PAGES_FOR_IO; @@ -234,7 +224,7 @@ int swsusp_suspend(void) goto Enable_irqs; } - if ((error = save_highmem())) { + if ((error = save_special_mem())) { printk(KERN_ERR "swsusp: Not enough free pages for highmem\n"); goto Restore_highmem; } @@ -245,7 +235,7 @@ int swsusp_suspend(void) /* Restore control flow magically appears here */ restore_processor_state(); Restore_highmem: - restore_highmem(); + restore_special_mem(); device_power_up(); Enable_irqs: local_irq_enable(); @@ -271,7 +261,7 @@ int swsusp_resume(void) */ swsusp_free(); restore_processor_state(); - restore_highmem(); + restore_special_mem(); touch_softlockup_watchdog(); device_power_up(); local_irq_enable(); _