aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/suspend.h2
-rw-r--r--kernel/power/disk.c30
-rw-r--r--kernel/power/power.h14
-rw-r--r--kernel/power/snapshot.c65
-rw-r--r--kernel/power/swsusp.c52
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
25extern suspend_disk_method_t pm_disk_mode; 25extern suspend_disk_method_t pm_disk_mode;
26 26
27extern int swsusp_shrink_memory(void);
27extern int swsusp_suspend(void); 28extern int swsusp_suspend(void);
28extern int swsusp_write(struct pbe *pblist, unsigned int nr_pages); 29extern int swsusp_write(struct pbe *pblist, unsigned int nr_pages);
29extern int swsusp_check(void); 30extern int swsusp_check(void);
@@ -73,31 +74,6 @@ static void power_down(suspend_disk_method_t mode)
73static int in_suspend __nosavedata = 0; 74static 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
85static 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
101static inline void platform_finish(void) 77static 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;
132thaw: 108thaw:
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);
49extern int pm_prepare_console(void); 49extern int pm_prepare_console(void);
50extern void pm_restore_console(void); 50extern void pm_restore_console(void);
51 51
52
53/* References to section boundaries */ 52/* References to section boundaries */
54extern const void __nosave_begin, __nosave_end; 53extern const void __nosave_begin, __nosave_end;
55 54
56extern unsigned int nr_copy_pages; 55extern unsigned int nr_copy_pages;
57extern suspend_pagedir_t *pagedir_nosave; 56extern struct pbe *pagedir_nosave;
58extern 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
60extern asmlinkage int swsusp_arch_suspend(void); 66extern asmlinkage int swsusp_arch_suspend(void);
61extern asmlinkage int swsusp_arch_resume(void); 67extern asmlinkage int swsusp_arch_resume(void);
62 68
69extern unsigned int count_data_pages(void);
63extern void free_pagedir(struct pbe *pblist); 70extern void free_pagedir(struct pbe *pblist);
71extern void release_eaten_pages(void);
64extern struct pbe *alloc_pagedir(unsigned nr_pages, gfp_t gfp_mask, int safe_needed); 72extern struct pbe *alloc_pagedir(unsigned nr_pages, gfp_t gfp_mask, int safe_needed);
65extern void swsusp_free(void); 73extern void swsusp_free(void);
66extern int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed); 74extern 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;
37unsigned int nr_copy_pages; 37unsigned int nr_copy_pages;
38 38
39#ifdef CONFIG_HIGHMEM 39#ifdef CONFIG_HIGHMEM
40unsigned 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
40struct highmem_page { 65struct 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
165static unsigned count_data_pages(void) 188unsigned 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
299struct eaten_page {
300 struct eaten_page *next;
301 char padding[PAGE_SIZE - sizeof(void *)];
302};
303
304static struct eaten_page *eaten_pages = NULL;
305
306void 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
73unsigned int count_highmem_pages(void);
73int save_highmem(void); 74int save_highmem(void);
74int restore_highmem(void); 75int restore_highmem(void);
75#else 76#else
76static int save_highmem(void) { return 0; } 77static int save_highmem(void) { return 0; }
77static int restore_highmem(void) { return 0; } 78static int restore_highmem(void) { return 0; }
79static unsigned int count_highmem_pages(void) { return 0; }
78#endif 80#endif
79 81
80extern char resume_file[]; 82extern 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
627int 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
614int swsusp_suspend(void) 662int 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 }