diff options
Diffstat (limited to 'kernel/power/snapshot.c')
-rw-r--r-- | kernel/power/snapshot.c | 62 |
1 files changed, 48 insertions, 14 deletions
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 723f5179883e..96cc3e21e97d 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -269,9 +269,30 @@ void create_pbe_list(struct pbe *pblist, unsigned int nr_pages) | |||
269 | pr_debug("create_pbe_list(): initialized %d PBEs\n", num); | 269 | pr_debug("create_pbe_list(): initialized %d PBEs\n", num); |
270 | } | 270 | } |
271 | 271 | ||
272 | static void *alloc_image_page(void) | 272 | /** |
273 | * @safe_needed - on resume, for storing the PBE list and the image, | ||
274 | * we can only use memory pages that do not conflict with the pages | ||
275 | * which had been used before suspend. | ||
276 | * | ||
277 | * The unsafe pages are marked with the PG_nosave_free flag | ||
278 | * | ||
279 | * Allocated but unusable (ie eaten) memory pages should be marked | ||
280 | * so that swsusp_free() can release them | ||
281 | */ | ||
282 | |||
283 | static inline void *alloc_image_page(gfp_t gfp_mask, int safe_needed) | ||
273 | { | 284 | { |
274 | void *res = (void *)get_zeroed_page(GFP_ATOMIC | __GFP_COLD); | 285 | void *res; |
286 | |||
287 | if (safe_needed) | ||
288 | do { | ||
289 | res = (void *)get_zeroed_page(gfp_mask); | ||
290 | if (res && PageNosaveFree(virt_to_page(res))) | ||
291 | /* This is for swsusp_free() */ | ||
292 | SetPageNosave(virt_to_page(res)); | ||
293 | } while (res && PageNosaveFree(virt_to_page(res))); | ||
294 | else | ||
295 | res = (void *)get_zeroed_page(gfp_mask); | ||
275 | if (res) { | 296 | if (res) { |
276 | SetPageNosave(virt_to_page(res)); | 297 | SetPageNosave(virt_to_page(res)); |
277 | SetPageNosaveFree(virt_to_page(res)); | 298 | SetPageNosaveFree(virt_to_page(res)); |
@@ -279,6 +300,11 @@ static void *alloc_image_page(void) | |||
279 | return res; | 300 | return res; |
280 | } | 301 | } |
281 | 302 | ||
303 | unsigned long get_safe_page(gfp_t gfp_mask) | ||
304 | { | ||
305 | return (unsigned long)alloc_image_page(gfp_mask, 1); | ||
306 | } | ||
307 | |||
282 | /** | 308 | /** |
283 | * alloc_pagedir - Allocate the page directory. | 309 | * alloc_pagedir - Allocate the page directory. |
284 | * | 310 | * |
@@ -292,7 +318,7 @@ static void *alloc_image_page(void) | |||
292 | * On each page we set up a list of struct_pbe elements. | 318 | * On each page we set up a list of struct_pbe elements. |
293 | */ | 319 | */ |
294 | 320 | ||
295 | struct pbe *alloc_pagedir(unsigned int nr_pages) | 321 | struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask, int safe_needed) |
296 | { | 322 | { |
297 | unsigned int num; | 323 | unsigned int num; |
298 | struct pbe *pblist, *pbe; | 324 | struct pbe *pblist, *pbe; |
@@ -301,12 +327,12 @@ struct pbe *alloc_pagedir(unsigned int nr_pages) | |||
301 | return NULL; | 327 | return NULL; |
302 | 328 | ||
303 | pr_debug("alloc_pagedir(): nr_pages = %d\n", nr_pages); | 329 | pr_debug("alloc_pagedir(): nr_pages = %d\n", nr_pages); |
304 | pblist = alloc_image_page(); | 330 | pblist = alloc_image_page(gfp_mask, safe_needed); |
305 | /* FIXME: rewrite this ugly loop */ | 331 | /* FIXME: rewrite this ugly loop */ |
306 | for (pbe = pblist, num = PBES_PER_PAGE; pbe && num < nr_pages; | 332 | for (pbe = pblist, num = PBES_PER_PAGE; pbe && num < nr_pages; |
307 | pbe = pbe->next, num += PBES_PER_PAGE) { | 333 | pbe = pbe->next, num += PBES_PER_PAGE) { |
308 | pbe += PB_PAGE_SKIP; | 334 | pbe += PB_PAGE_SKIP; |
309 | pbe->next = alloc_image_page(); | 335 | pbe->next = alloc_image_page(gfp_mask, safe_needed); |
310 | } | 336 | } |
311 | if (!pbe) { /* get_zeroed_page() failed */ | 337 | if (!pbe) { /* get_zeroed_page() failed */ |
312 | free_pagedir(pblist); | 338 | free_pagedir(pblist); |
@@ -354,24 +380,32 @@ static int enough_free_mem(unsigned int nr_pages) | |||
354 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); | 380 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); |
355 | } | 381 | } |
356 | 382 | ||
383 | int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed) | ||
384 | { | ||
385 | struct pbe *p; | ||
386 | |||
387 | for_each_pbe (p, pblist) { | ||
388 | p->address = (unsigned long)alloc_image_page(gfp_mask, safe_needed); | ||
389 | if (!p->address) | ||
390 | return -ENOMEM; | ||
391 | } | ||
392 | return 0; | ||
393 | } | ||
357 | 394 | ||
358 | static struct pbe *swsusp_alloc(unsigned int nr_pages) | 395 | static struct pbe *swsusp_alloc(unsigned int nr_pages) |
359 | { | 396 | { |
360 | struct pbe *pblist, *p; | 397 | struct pbe *pblist; |
361 | 398 | ||
362 | if (!(pblist = alloc_pagedir(nr_pages))) { | 399 | if (!(pblist = alloc_pagedir(nr_pages, GFP_ATOMIC | __GFP_COLD, 0))) { |
363 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); | 400 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); |
364 | return NULL; | 401 | return NULL; |
365 | } | 402 | } |
366 | create_pbe_list(pblist, nr_pages); | 403 | create_pbe_list(pblist, nr_pages); |
367 | 404 | ||
368 | for_each_pbe (p, pblist) { | 405 | if (alloc_data_pages(pblist, GFP_ATOMIC | __GFP_COLD, 0)) { |
369 | p->address = (unsigned long)alloc_image_page(); | 406 | printk(KERN_ERR "suspend: Allocating image pages failed.\n"); |
370 | if (!p->address) { | 407 | swsusp_free(); |
371 | printk(KERN_ERR "suspend: Allocating image pages failed.\n"); | 408 | return NULL; |
372 | swsusp_free(); | ||
373 | return NULL; | ||
374 | } | ||
375 | } | 409 | } |
376 | 410 | ||
377 | return pblist; | 411 | return pblist; |