Commit 72a97e08394a3b2e75481ff680ec2a0591e3cba4

Authored by Rafael J. Wysocki
Committed by Linus Torvalds
1 parent 7088a5c001

[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>

Showing 5 changed files with 126 additions and 37 deletions Side-by-side Diff

include/linux/suspend.h
... ... @@ -73,7 +73,7 @@
73 73 * XXX: We try to keep some more pages free so that I/O operations succeed
74 74 * without paging. Might this be more?
75 75 */
76   -#define PAGES_FOR_IO 512
  76 +#define PAGES_FOR_IO 1024
77 77  
78 78 #endif /* _LINUX_SWSUSP_H */
... ... @@ -24,6 +24,7 @@
24 24  
25 25 extern suspend_disk_method_t pm_disk_mode;
26 26  
  27 +extern int swsusp_shrink_memory(void);
27 28 extern int swsusp_suspend(void);
28 29 extern int swsusp_write(struct pbe *pblist, unsigned int nr_pages);
29 30 extern int swsusp_check(void);
... ... @@ -73,31 +74,6 @@
73 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 77 static inline void platform_finish(void)
102 78 {
103 79 if (pm_disk_mode == PM_DISK_PLATFORM) {
... ... @@ -127,8 +103,8 @@
127 103 }
128 104  
129 105 /* Free memory before shutting down devices. */
130   - free_some_memory();
131   - return 0;
  106 + if (!(error = swsusp_shrink_memory()))
  107 + return 0;
132 108 thaw:
133 109 thaw_processes();
134 110 enable_nonboot_cpus();
kernel/power/power.h
... ... @@ -49,18 +49,26 @@
49 49 extern int pm_prepare_console(void);
50 50 extern void pm_restore_console(void);
51 51  
52   -
53 52 /* References to section boundaries */
54 53 extern const void __nosave_begin, __nosave_end;
55 54  
56 55 extern unsigned int nr_copy_pages;
57   -extern suspend_pagedir_t *pagedir_nosave;
58   -extern suspend_pagedir_t *pagedir_save;
  56 +extern struct pbe *pagedir_nosave;
59 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
  65 +
60 66 extern asmlinkage int swsusp_arch_suspend(void);
61 67 extern asmlinkage int swsusp_arch_resume(void);
62 68  
  69 +extern unsigned int count_data_pages(void);
63 70 extern void free_pagedir(struct pbe *pblist);
  71 +extern void release_eaten_pages(void);
64 72 extern struct pbe *alloc_pagedir(unsigned nr_pages, gfp_t gfp_mask, int safe_needed);
65 73 extern void swsusp_free(void);
66 74 extern int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed);
kernel/power/snapshot.c
... ... @@ -37,6 +37,31 @@
37 37 unsigned int nr_copy_pages;
38 38  
39 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 65 struct highmem_page {
41 66 char *data;
42 67 struct page *page;
43 68  
44 69  
... ... @@ -152,17 +177,15 @@
152 177 BUG_ON(PageReserved(page) && PageNosave(page));
153 178 if (PageNosave(page))
154 179 return 0;
155   - if (PageReserved(page) && pfn_is_nosave(pfn)) {
156   - pr_debug("[nosave pfn 0x%lx]", pfn);
  180 + if (PageReserved(page) && pfn_is_nosave(pfn))
157 181 return 0;
158   - }
159 182 if (PageNosaveFree(page))
160 183 return 0;
161 184  
162 185 return 1;
163 186 }
164 187  
165   -static unsigned count_data_pages(void)
  188 +unsigned int count_data_pages(void)
166 189 {
167 190 struct zone *zone;
168 191 unsigned long zone_pfn;
... ... @@ -267,6 +290,35 @@
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 322 * @safe_needed - on resume, for storing the PBE list and the image,
271 323 * we can only use memory pages that do not conflict with the pages
272 324 * which had been used before suspend.
273 325  
... ... @@ -284,9 +336,12 @@
284 336 if (safe_needed)
285 337 do {
286 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 340 /* This is for swsusp_free() */
289 341 SetPageNosave(virt_to_page(res));
  342 + ((struct eaten_page *)res)->next = eaten_pages;
  343 + eaten_pages = res;
  344 + }
290 345 } while (res && PageNosaveFree(virt_to_page(res)));
291 346 else
292 347 res = (void *)get_zeroed_page(gfp_mask);
kernel/power/swsusp.c
... ... @@ -70,11 +70,13 @@
70 70 #include "power.h"
71 71  
72 72 #ifdef CONFIG_HIGHMEM
  73 +unsigned int count_highmem_pages(void);
73 74 int save_highmem(void);
74 75 int restore_highmem(void);
75 76 #else
76 77 static int save_highmem(void) { return 0; }
77 78 static int restore_highmem(void) { return 0; }
  79 +static unsigned int count_highmem_pages(void) { return 0; }
78 80 #endif
79 81  
80 82 extern char resume_file[];
... ... @@ -611,6 +613,52 @@
611 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 662 int swsusp_suspend(void)
615 663 {
616 664 int error;
617 665  
... ... @@ -1030,8 +1078,10 @@
1030 1078 /* Allocate memory for the image and read the data from swap */
1031 1079 if (!error)
1032 1080 error = alloc_data_pages(pblist, GFP_ATOMIC, 1);
1033   - if (!error)
  1081 + if (!error) {
  1082 + release_eaten_pages();
1034 1083 error = load_image_data(pblist, &handle, nr_pages);
  1084 + }
1035 1085 if (!error)
1036 1086 *pblist_ptr = pblist;
1037 1087 }