diff options
Diffstat (limited to 'kernel/power/snapshot.c')
-rw-r--r-- | kernel/power/snapshot.c | 83 |
1 files changed, 51 insertions, 32 deletions
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 723f5179883e..4a6dbcefd378 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -88,8 +88,7 @@ static int save_highmem_zone(struct zone *zone) | |||
88 | return 0; | 88 | return 0; |
89 | } | 89 | } |
90 | 90 | ||
91 | 91 | int save_highmem(void) | |
92 | static int save_highmem(void) | ||
93 | { | 92 | { |
94 | struct zone *zone; | 93 | struct zone *zone; |
95 | int res = 0; | 94 | int res = 0; |
@@ -120,11 +119,7 @@ int restore_highmem(void) | |||
120 | } | 119 | } |
121 | return 0; | 120 | return 0; |
122 | } | 121 | } |
123 | #else | 122 | #endif |
124 | static int save_highmem(void) { return 0; } | ||
125 | int restore_highmem(void) { return 0; } | ||
126 | #endif /* CONFIG_HIGHMEM */ | ||
127 | |||
128 | 123 | ||
129 | static int pfn_is_nosave(unsigned long pfn) | 124 | static int pfn_is_nosave(unsigned long pfn) |
130 | { | 125 | { |
@@ -216,7 +211,7 @@ static void copy_data_pages(struct pbe *pblist) | |||
216 | * free_pagedir - free pages allocated with alloc_pagedir() | 211 | * free_pagedir - free pages allocated with alloc_pagedir() |
217 | */ | 212 | */ |
218 | 213 | ||
219 | static void free_pagedir(struct pbe *pblist) | 214 | void free_pagedir(struct pbe *pblist) |
220 | { | 215 | { |
221 | struct pbe *pbe; | 216 | struct pbe *pbe; |
222 | 217 | ||
@@ -269,9 +264,30 @@ void create_pbe_list(struct pbe *pblist, unsigned int nr_pages) | |||
269 | pr_debug("create_pbe_list(): initialized %d PBEs\n", num); | 264 | pr_debug("create_pbe_list(): initialized %d PBEs\n", num); |
270 | } | 265 | } |
271 | 266 | ||
272 | static void *alloc_image_page(void) | 267 | /** |
268 | * @safe_needed - on resume, for storing the PBE list and the image, | ||
269 | * we can only use memory pages that do not conflict with the pages | ||
270 | * which had been used before suspend. | ||
271 | * | ||
272 | * The unsafe pages are marked with the PG_nosave_free flag | ||
273 | * | ||
274 | * Allocated but unusable (ie eaten) memory pages should be marked | ||
275 | * so that swsusp_free() can release them | ||
276 | */ | ||
277 | |||
278 | static inline void *alloc_image_page(gfp_t gfp_mask, int safe_needed) | ||
273 | { | 279 | { |
274 | void *res = (void *)get_zeroed_page(GFP_ATOMIC | __GFP_COLD); | 280 | void *res; |
281 | |||
282 | if (safe_needed) | ||
283 | do { | ||
284 | res = (void *)get_zeroed_page(gfp_mask); | ||
285 | if (res && PageNosaveFree(virt_to_page(res))) | ||
286 | /* This is for swsusp_free() */ | ||
287 | SetPageNosave(virt_to_page(res)); | ||
288 | } while (res && PageNosaveFree(virt_to_page(res))); | ||
289 | else | ||
290 | res = (void *)get_zeroed_page(gfp_mask); | ||
275 | if (res) { | 291 | if (res) { |
276 | SetPageNosave(virt_to_page(res)); | 292 | SetPageNosave(virt_to_page(res)); |
277 | SetPageNosaveFree(virt_to_page(res)); | 293 | SetPageNosaveFree(virt_to_page(res)); |
@@ -279,6 +295,11 @@ static void *alloc_image_page(void) | |||
279 | return res; | 295 | return res; |
280 | } | 296 | } |
281 | 297 | ||
298 | unsigned long get_safe_page(gfp_t gfp_mask) | ||
299 | { | ||
300 | return (unsigned long)alloc_image_page(gfp_mask, 1); | ||
301 | } | ||
302 | |||
282 | /** | 303 | /** |
283 | * alloc_pagedir - Allocate the page directory. | 304 | * alloc_pagedir - Allocate the page directory. |
284 | * | 305 | * |
@@ -292,7 +313,7 @@ static void *alloc_image_page(void) | |||
292 | * On each page we set up a list of struct_pbe elements. | 313 | * On each page we set up a list of struct_pbe elements. |
293 | */ | 314 | */ |
294 | 315 | ||
295 | struct pbe *alloc_pagedir(unsigned int nr_pages) | 316 | struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask, int safe_needed) |
296 | { | 317 | { |
297 | unsigned int num; | 318 | unsigned int num; |
298 | struct pbe *pblist, *pbe; | 319 | struct pbe *pblist, *pbe; |
@@ -301,12 +322,12 @@ struct pbe *alloc_pagedir(unsigned int nr_pages) | |||
301 | return NULL; | 322 | return NULL; |
302 | 323 | ||
303 | pr_debug("alloc_pagedir(): nr_pages = %d\n", nr_pages); | 324 | pr_debug("alloc_pagedir(): nr_pages = %d\n", nr_pages); |
304 | pblist = alloc_image_page(); | 325 | pblist = alloc_image_page(gfp_mask, safe_needed); |
305 | /* FIXME: rewrite this ugly loop */ | 326 | /* FIXME: rewrite this ugly loop */ |
306 | for (pbe = pblist, num = PBES_PER_PAGE; pbe && num < nr_pages; | 327 | for (pbe = pblist, num = PBES_PER_PAGE; pbe && num < nr_pages; |
307 | pbe = pbe->next, num += PBES_PER_PAGE) { | 328 | pbe = pbe->next, num += PBES_PER_PAGE) { |
308 | pbe += PB_PAGE_SKIP; | 329 | pbe += PB_PAGE_SKIP; |
309 | pbe->next = alloc_image_page(); | 330 | pbe->next = alloc_image_page(gfp_mask, safe_needed); |
310 | } | 331 | } |
311 | if (!pbe) { /* get_zeroed_page() failed */ | 332 | if (!pbe) { /* get_zeroed_page() failed */ |
312 | free_pagedir(pblist); | 333 | free_pagedir(pblist); |
@@ -354,24 +375,32 @@ static int enough_free_mem(unsigned int nr_pages) | |||
354 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); | 375 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); |
355 | } | 376 | } |
356 | 377 | ||
378 | int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed) | ||
379 | { | ||
380 | struct pbe *p; | ||
381 | |||
382 | for_each_pbe (p, pblist) { | ||
383 | p->address = (unsigned long)alloc_image_page(gfp_mask, safe_needed); | ||
384 | if (!p->address) | ||
385 | return -ENOMEM; | ||
386 | } | ||
387 | return 0; | ||
388 | } | ||
357 | 389 | ||
358 | static struct pbe *swsusp_alloc(unsigned int nr_pages) | 390 | static struct pbe *swsusp_alloc(unsigned int nr_pages) |
359 | { | 391 | { |
360 | struct pbe *pblist, *p; | 392 | struct pbe *pblist; |
361 | 393 | ||
362 | if (!(pblist = alloc_pagedir(nr_pages))) { | 394 | if (!(pblist = alloc_pagedir(nr_pages, GFP_ATOMIC | __GFP_COLD, 0))) { |
363 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); | 395 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); |
364 | return NULL; | 396 | return NULL; |
365 | } | 397 | } |
366 | create_pbe_list(pblist, nr_pages); | 398 | create_pbe_list(pblist, nr_pages); |
367 | 399 | ||
368 | for_each_pbe (p, pblist) { | 400 | if (alloc_data_pages(pblist, GFP_ATOMIC | __GFP_COLD, 0)) { |
369 | p->address = (unsigned long)alloc_image_page(); | 401 | printk(KERN_ERR "suspend: Allocating image pages failed.\n"); |
370 | if (!p->address) { | 402 | swsusp_free(); |
371 | printk(KERN_ERR "suspend: Allocating image pages failed.\n"); | 403 | return NULL; |
372 | swsusp_free(); | ||
373 | return NULL; | ||
374 | } | ||
375 | } | 404 | } |
376 | 405 | ||
377 | return pblist; | 406 | return pblist; |
@@ -382,11 +411,6 @@ asmlinkage int swsusp_save(void) | |||
382 | unsigned int nr_pages; | 411 | unsigned int nr_pages; |
383 | 412 | ||
384 | pr_debug("swsusp: critical section: \n"); | 413 | pr_debug("swsusp: critical section: \n"); |
385 | if (save_highmem()) { | ||
386 | printk(KERN_CRIT "swsusp: Not enough free pages for highmem\n"); | ||
387 | restore_highmem(); | ||
388 | return -ENOMEM; | ||
389 | } | ||
390 | 414 | ||
391 | drain_local_pages(); | 415 | drain_local_pages(); |
392 | nr_pages = count_data_pages(); | 416 | nr_pages = count_data_pages(); |
@@ -406,11 +430,6 @@ asmlinkage int swsusp_save(void) | |||
406 | return -ENOMEM; | 430 | return -ENOMEM; |
407 | } | 431 | } |
408 | 432 | ||
409 | if (!enough_swap(nr_pages)) { | ||
410 | printk(KERN_ERR "swsusp: Not enough free swap\n"); | ||
411 | return -ENOSPC; | ||
412 | } | ||
413 | |||
414 | pagedir_nosave = swsusp_alloc(nr_pages); | 433 | pagedir_nosave = swsusp_alloc(nr_pages); |
415 | if (!pagedir_nosave) | 434 | if (!pagedir_nosave) |
416 | return -ENOMEM; | 435 | return -ENOMEM; |