diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2006-01-06 03:13:46 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-06 11:33:40 -0500 |
commit | 72a97e08394a3b2e75481ff680ec2a0591e3cba4 (patch) | |
tree | d8ba72d6e81ad31ed10876386d3d2067921979e3 /kernel/power/snapshot.c | |
parent | 7088a5c00103ef48782d6c359cd12b13a10666e6 (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/power/snapshot.c')
-rw-r--r-- | kernel/power/snapshot.c | 65 |
1 files changed, 60 insertions, 5 deletions
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); |