diff options
-rw-r--r-- | include/linux/suspend.h | 2 | ||||
-rw-r--r-- | kernel/power/disk.c | 30 | ||||
-rw-r--r-- | kernel/power/power.h | 14 | ||||
-rw-r--r-- | kernel/power/snapshot.c | 65 | ||||
-rw-r--r-- | kernel/power/swsusp.c | 52 |
5 files changed, 126 insertions, 37 deletions
diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 33bbaea23aaf..5dc94e777fab 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h | |||
@@ -73,6 +73,6 @@ unsigned long get_safe_page(gfp_t gfp_mask); | |||
73 | * XXX: We try to keep some more pages free so that I/O operations succeed | 73 | * XXX: We try to keep some more pages free so that I/O operations succeed |
74 | * without paging. Might this be more? | 74 | * without paging. Might this be more? |
75 | */ | 75 | */ |
76 | #define PAGES_FOR_IO 512 | 76 | #define PAGES_FOR_IO 1024 |
77 | 77 | ||
78 | #endif /* _LINUX_SWSUSP_H */ | 78 | #endif /* _LINUX_SWSUSP_H */ |
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 76a5131b0e80..9e51cdf7b78d 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | extern suspend_disk_method_t pm_disk_mode; | 25 | extern suspend_disk_method_t pm_disk_mode; |
26 | 26 | ||
27 | extern int swsusp_shrink_memory(void); | ||
27 | extern int swsusp_suspend(void); | 28 | extern int swsusp_suspend(void); |
28 | extern int swsusp_write(struct pbe *pblist, unsigned int nr_pages); | 29 | extern int swsusp_write(struct pbe *pblist, unsigned int nr_pages); |
29 | extern int swsusp_check(void); | 30 | extern int swsusp_check(void); |
@@ -73,31 +74,6 @@ static void power_down(suspend_disk_method_t mode) | |||
73 | static int in_suspend __nosavedata = 0; | 74 | static int in_suspend __nosavedata = 0; |
74 | 75 | ||
75 | 76 | ||
76 | /** | ||
77 | * free_some_memory - Try to free as much memory as possible | ||
78 | * | ||
79 | * ... but do not OOM-kill anyone | ||
80 | * | ||
81 | * Notice: all userland should be stopped at this point, or | ||
82 | * livelock is possible. | ||
83 | */ | ||
84 | |||
85 | static void free_some_memory(void) | ||
86 | { | ||
87 | unsigned int i = 0; | ||
88 | unsigned int tmp; | ||
89 | unsigned long pages = 0; | ||
90 | char *p = "-\\|/"; | ||
91 | |||
92 | printk("Freeing memory... "); | ||
93 | while ((tmp = shrink_all_memory(10000))) { | ||
94 | pages += tmp; | ||
95 | printk("\b%c", p[i++ % 4]); | ||
96 | } | ||
97 | printk("\bdone (%li pages freed)\n", pages); | ||
98 | } | ||
99 | |||
100 | |||
101 | static inline void platform_finish(void) | 77 | static inline void platform_finish(void) |
102 | { | 78 | { |
103 | if (pm_disk_mode == PM_DISK_PLATFORM) { | 79 | if (pm_disk_mode == PM_DISK_PLATFORM) { |
@@ -127,8 +103,8 @@ static int prepare_processes(void) | |||
127 | } | 103 | } |
128 | 104 | ||
129 | /* Free memory before shutting down devices. */ | 105 | /* Free memory before shutting down devices. */ |
130 | free_some_memory(); | 106 | if (!(error = swsusp_shrink_memory())) |
131 | return 0; | 107 | return 0; |
132 | thaw: | 108 | thaw: |
133 | thaw_processes(); | 109 | thaw_processes(); |
134 | enable_nonboot_cpus(); | 110 | enable_nonboot_cpus(); |
diff --git a/kernel/power/power.h b/kernel/power/power.h index 977877c6dcfc..acdc83b3d890 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -49,18 +49,26 @@ extern void thaw_processes(void); | |||
49 | extern int pm_prepare_console(void); | 49 | extern int pm_prepare_console(void); |
50 | extern void pm_restore_console(void); | 50 | extern void pm_restore_console(void); |
51 | 51 | ||
52 | |||
53 | /* References to section boundaries */ | 52 | /* References to section boundaries */ |
54 | extern const void __nosave_begin, __nosave_end; | 53 | extern const void __nosave_begin, __nosave_end; |
55 | 54 | ||
56 | extern unsigned int nr_copy_pages; | 55 | extern unsigned int nr_copy_pages; |
57 | extern suspend_pagedir_t *pagedir_nosave; | 56 | extern struct pbe *pagedir_nosave; |
58 | extern suspend_pagedir_t *pagedir_save; | 57 | |
58 | /* | ||
59 | * This compilation switch determines the way in which memory will be freed | ||
60 | * during suspend. If defined, only as much memory will be freed as needed | ||
61 | * to complete the suspend, which will make it go faster. Otherwise, the | ||
62 | * largest possible amount of memory will be freed. | ||
63 | */ | ||
64 | #define FAST_FREE 1 | ||
59 | 65 | ||
60 | extern asmlinkage int swsusp_arch_suspend(void); | 66 | extern asmlinkage int swsusp_arch_suspend(void); |
61 | extern asmlinkage int swsusp_arch_resume(void); | 67 | extern asmlinkage int swsusp_arch_resume(void); |
62 | 68 | ||
69 | extern unsigned int count_data_pages(void); | ||
63 | extern void free_pagedir(struct pbe *pblist); | 70 | extern void free_pagedir(struct pbe *pblist); |
71 | extern void release_eaten_pages(void); | ||
64 | extern struct pbe *alloc_pagedir(unsigned nr_pages, gfp_t gfp_mask, int safe_needed); | 72 | extern struct pbe *alloc_pagedir(unsigned nr_pages, gfp_t gfp_mask, int safe_needed); |
65 | extern void swsusp_free(void); | 73 | extern void swsusp_free(void); |
66 | extern int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed); | 74 | extern int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed); |
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 152d56cdf017..e80d282dbf58 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -37,6 +37,31 @@ struct pbe *pagedir_nosave; | |||
37 | unsigned int nr_copy_pages; | 37 | unsigned int nr_copy_pages; |
38 | 38 | ||
39 | #ifdef CONFIG_HIGHMEM | 39 | #ifdef CONFIG_HIGHMEM |
40 | unsigned int count_highmem_pages(void) | ||
41 | { | ||
42 | struct zone *zone; | ||
43 | unsigned long zone_pfn; | ||
44 | unsigned int n = 0; | ||
45 | |||
46 | for_each_zone (zone) | ||
47 | if (is_highmem(zone)) { | ||
48 | mark_free_pages(zone); | ||
49 | for (zone_pfn = 0; zone_pfn < zone->spanned_pages; zone_pfn++) { | ||
50 | struct page *page; | ||
51 | unsigned long pfn = zone_pfn + zone->zone_start_pfn; | ||
52 | if (!pfn_valid(pfn)) | ||
53 | continue; | ||
54 | page = pfn_to_page(pfn); | ||
55 | if (PageReserved(page)) | ||
56 | continue; | ||
57 | if (PageNosaveFree(page)) | ||
58 | continue; | ||
59 | n++; | ||
60 | } | ||
61 | } | ||
62 | return n; | ||
63 | } | ||
64 | |||
40 | struct highmem_page { | 65 | struct highmem_page { |
41 | char *data; | 66 | char *data; |
42 | struct page *page; | 67 | struct page *page; |
@@ -152,17 +177,15 @@ static int saveable(struct zone *zone, unsigned long *zone_pfn) | |||
152 | BUG_ON(PageReserved(page) && PageNosave(page)); | 177 | BUG_ON(PageReserved(page) && PageNosave(page)); |
153 | if (PageNosave(page)) | 178 | if (PageNosave(page)) |
154 | return 0; | 179 | return 0; |
155 | if (PageReserved(page) && pfn_is_nosave(pfn)) { | 180 | if (PageReserved(page) && pfn_is_nosave(pfn)) |
156 | pr_debug("[nosave pfn 0x%lx]", pfn); | ||
157 | return 0; | 181 | return 0; |
158 | } | ||
159 | if (PageNosaveFree(page)) | 182 | if (PageNosaveFree(page)) |
160 | return 0; | 183 | return 0; |
161 | 184 | ||
162 | return 1; | 185 | return 1; |
163 | } | 186 | } |
164 | 187 | ||
165 | static unsigned count_data_pages(void) | 188 | unsigned int count_data_pages(void) |
166 | { | 189 | { |
167 | struct zone *zone; | 190 | struct zone *zone; |
168 | unsigned long zone_pfn; | 191 | unsigned long zone_pfn; |
@@ -267,6 +290,35 @@ static inline void create_pbe_list(struct pbe *pblist, unsigned int nr_pages) | |||
267 | } | 290 | } |
268 | 291 | ||
269 | /** | 292 | /** |
293 | * On resume it is necessary to trace and eventually free the unsafe | ||
294 | * pages that have been allocated, because they are needed for I/O | ||
295 | * (on x86-64 we likely will "eat" these pages once again while | ||
296 | * creating the temporary page translation tables) | ||
297 | */ | ||
298 | |||
299 | struct eaten_page { | ||
300 | struct eaten_page *next; | ||
301 | char padding[PAGE_SIZE - sizeof(void *)]; | ||
302 | }; | ||
303 | |||
304 | static struct eaten_page *eaten_pages = NULL; | ||
305 | |||
306 | void release_eaten_pages(void) | ||
307 | { | ||
308 | struct eaten_page *p, *q; | ||
309 | |||
310 | p = eaten_pages; | ||
311 | while (p) { | ||
312 | q = p->next; | ||
313 | /* We don't want swsusp_free() to free this page again */ | ||
314 | ClearPageNosave(virt_to_page(p)); | ||
315 | free_page((unsigned long)p); | ||
316 | p = q; | ||
317 | } | ||
318 | eaten_pages = NULL; | ||
319 | } | ||
320 | |||
321 | /** | ||
270 | * @safe_needed - on resume, for storing the PBE list and the image, | 322 | * @safe_needed - on resume, for storing the PBE list and the image, |
271 | * we can only use memory pages that do not conflict with the pages | 323 | * we can only use memory pages that do not conflict with the pages |
272 | * which had been used before suspend. | 324 | * which had been used before suspend. |
@@ -284,9 +336,12 @@ static inline void *alloc_image_page(gfp_t gfp_mask, int safe_needed) | |||
284 | if (safe_needed) | 336 | if (safe_needed) |
285 | do { | 337 | do { |
286 | res = (void *)get_zeroed_page(gfp_mask); | 338 | res = (void *)get_zeroed_page(gfp_mask); |
287 | if (res && PageNosaveFree(virt_to_page(res))) | 339 | if (res && PageNosaveFree(virt_to_page(res))) { |
288 | /* This is for swsusp_free() */ | 340 | /* This is for swsusp_free() */ |
289 | SetPageNosave(virt_to_page(res)); | 341 | SetPageNosave(virt_to_page(res)); |
342 | ((struct eaten_page *)res)->next = eaten_pages; | ||
343 | eaten_pages = res; | ||
344 | } | ||
290 | } while (res && PageNosaveFree(virt_to_page(res))); | 345 | } while (res && PageNosaveFree(virt_to_page(res))); |
291 | else | 346 | else |
292 | res = (void *)get_zeroed_page(gfp_mask); | 347 | res = (void *)get_zeroed_page(gfp_mask); |
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index b09bd7c0998d..f77f9397a364 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c | |||
@@ -70,11 +70,13 @@ | |||
70 | #include "power.h" | 70 | #include "power.h" |
71 | 71 | ||
72 | #ifdef CONFIG_HIGHMEM | 72 | #ifdef CONFIG_HIGHMEM |
73 | unsigned int count_highmem_pages(void); | ||
73 | int save_highmem(void); | 74 | int save_highmem(void); |
74 | int restore_highmem(void); | 75 | int restore_highmem(void); |
75 | #else | 76 | #else |
76 | static int save_highmem(void) { return 0; } | 77 | static int save_highmem(void) { return 0; } |
77 | static int restore_highmem(void) { return 0; } | 78 | static int restore_highmem(void) { return 0; } |
79 | static unsigned int count_highmem_pages(void) { return 0; } | ||
78 | #endif | 80 | #endif |
79 | 81 | ||
80 | extern char resume_file[]; | 82 | extern char resume_file[]; |
@@ -611,6 +613,52 @@ int swsusp_write(struct pbe *pblist, unsigned int nr_pages) | |||
611 | return error; | 613 | return error; |
612 | } | 614 | } |
613 | 615 | ||
616 | /** | ||
617 | * swsusp_shrink_memory - Try to free as much memory as needed | ||
618 | * | ||
619 | * ... but do not OOM-kill anyone | ||
620 | * | ||
621 | * Notice: all userland should be stopped before it is called, or | ||
622 | * livelock is possible. | ||
623 | */ | ||
624 | |||
625 | #define SHRINK_BITE 10000 | ||
626 | |||
627 | int swsusp_shrink_memory(void) | ||
628 | { | ||
629 | long tmp; | ||
630 | struct zone *zone; | ||
631 | unsigned long pages = 0; | ||
632 | unsigned int i = 0; | ||
633 | char *p = "-\\|/"; | ||
634 | |||
635 | printk("Shrinking memory... "); | ||
636 | do { | ||
637 | #ifdef FAST_FREE | ||
638 | tmp = 2 * count_highmem_pages(); | ||
639 | tmp += tmp / 50 + count_data_pages(); | ||
640 | tmp += (tmp + PBES_PER_PAGE - 1) / PBES_PER_PAGE + | ||
641 | PAGES_FOR_IO; | ||
642 | for_each_zone (zone) | ||
643 | if (!is_highmem(zone)) | ||
644 | tmp -= zone->free_pages; | ||
645 | if (tmp > 0) { | ||
646 | tmp = shrink_all_memory(SHRINK_BITE); | ||
647 | if (!tmp) | ||
648 | return -ENOMEM; | ||
649 | pages += tmp; | ||
650 | } | ||
651 | #else | ||
652 | tmp = shrink_all_memory(SHRINK_BITE); | ||
653 | pages += tmp; | ||
654 | #endif | ||
655 | printk("\b%c", p[i++%4]); | ||
656 | } while (tmp > 0); | ||
657 | printk("\bdone (%lu pages freed)\n", pages); | ||
658 | |||
659 | return 0; | ||
660 | } | ||
661 | |||
614 | int swsusp_suspend(void) | 662 | int swsusp_suspend(void) |
615 | { | 663 | { |
616 | int error; | 664 | int error; |
@@ -1030,8 +1078,10 @@ static int read_suspend_image(struct pbe **pblist_ptr) | |||
1030 | /* Allocate memory for the image and read the data from swap */ | 1078 | /* Allocate memory for the image and read the data from swap */ |
1031 | if (!error) | 1079 | if (!error) |
1032 | error = alloc_data_pages(pblist, GFP_ATOMIC, 1); | 1080 | error = alloc_data_pages(pblist, GFP_ATOMIC, 1); |
1033 | if (!error) | 1081 | if (!error) { |
1082 | release_eaten_pages(); | ||
1034 | error = load_image_data(pblist, &handle, nr_pages); | 1083 | error = load_image_data(pblist, &handle, nr_pages); |
1084 | } | ||
1035 | if (!error) | 1085 | if (!error) |
1036 | *pblist_ptr = pblist; | 1086 | *pblist_ptr = pblist; |
1037 | } | 1087 | } |