aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2006-01-06 03:13:46 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-06 11:33:40 -0500
commit72a97e08394a3b2e75481ff680ec2a0591e3cba4 (patch)
treed8ba72d6e81ad31ed10876386d3d2067921979e3 /kernel
parent7088a5c00103ef48782d6c359cd12b13a10666e6 (diff)
[PATCH] swsusp: improve freeing of memory
This patch makes swsusp free only as much memory as needed to complete the suspend and not as much as possible.  In the most of cases this should speed up the suspend and make the system much more responsive after resume, especially if a GUI (eg. X Windows) is used. If needed, the old behavior (ie to free as much memory as possible during suspend) can be restored by unsetting FAST_FREE in power.h Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Pavel Machek <pavel@suse.cz> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel')
-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
4 files changed, 125 insertions, 36 deletions
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 }