diff options
Diffstat (limited to 'kernel/power/snapshot.c')
| -rw-r--r-- | kernel/power/snapshot.c | 100 | 
1 files changed, 59 insertions, 41 deletions
| diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 42a628704398..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 | { | 
| @@ -168,9 +163,8 @@ static unsigned count_data_pages(void) | |||
| 168 | { | 163 | { | 
| 169 | struct zone *zone; | 164 | struct zone *zone; | 
| 170 | unsigned long zone_pfn; | 165 | unsigned long zone_pfn; | 
| 171 | unsigned n; | 166 | unsigned int n = 0; | 
| 172 | 167 | ||
| 173 | n = 0; | ||
| 174 | for_each_zone (zone) { | 168 | for_each_zone (zone) { | 
| 175 | if (is_highmem(zone)) | 169 | if (is_highmem(zone)) | 
| 176 | continue; | 170 | continue; | 
| @@ -217,7 +211,7 @@ static void copy_data_pages(struct pbe *pblist) | |||
| 217 | * free_pagedir - free pages allocated with alloc_pagedir() | 211 | * free_pagedir - free pages allocated with alloc_pagedir() | 
| 218 | */ | 212 | */ | 
| 219 | 213 | ||
| 220 | static void free_pagedir(struct pbe *pblist) | 214 | void free_pagedir(struct pbe *pblist) | 
| 221 | { | 215 | { | 
| 222 | struct pbe *pbe; | 216 | struct pbe *pbe; | 
| 223 | 217 | ||
| @@ -250,10 +244,10 @@ static inline void fill_pb_page(struct pbe *pbpage) | |||
| 250 | * of memory pages allocated with alloc_pagedir() | 244 | * of memory pages allocated with alloc_pagedir() | 
| 251 | */ | 245 | */ | 
| 252 | 246 | ||
| 253 | void create_pbe_list(struct pbe *pblist, unsigned nr_pages) | 247 | void create_pbe_list(struct pbe *pblist, unsigned int nr_pages) | 
| 254 | { | 248 | { | 
| 255 | struct pbe *pbpage, *p; | 249 | struct pbe *pbpage, *p; | 
| 256 | unsigned num = PBES_PER_PAGE; | 250 | unsigned int num = PBES_PER_PAGE; | 
| 257 | 251 | ||
| 258 | for_each_pb_page (pbpage, pblist) { | 252 | for_each_pb_page (pbpage, pblist) { | 
| 259 | if (num >= nr_pages) | 253 | if (num >= nr_pages) | 
| @@ -270,9 +264,30 @@ void create_pbe_list(struct pbe *pblist, unsigned nr_pages) | |||
| 270 | pr_debug("create_pbe_list(): initialized %d PBEs\n", num); | 264 | pr_debug("create_pbe_list(): initialized %d PBEs\n", num); | 
| 271 | } | 265 | } | 
| 272 | 266 | ||
| 273 | 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) | ||
| 274 | { | 279 | { | 
| 275 | 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); | ||
| 276 | if (res) { | 291 | if (res) { | 
| 277 | SetPageNosave(virt_to_page(res)); | 292 | SetPageNosave(virt_to_page(res)); | 
| 278 | SetPageNosaveFree(virt_to_page(res)); | 293 | SetPageNosaveFree(virt_to_page(res)); | 
| @@ -280,6 +295,11 @@ static void *alloc_image_page(void) | |||
| 280 | return res; | 295 | return res; | 
| 281 | } | 296 | } | 
| 282 | 297 | ||
| 298 | unsigned long get_safe_page(gfp_t gfp_mask) | ||
| 299 | { | ||
| 300 | return (unsigned long)alloc_image_page(gfp_mask, 1); | ||
| 301 | } | ||
| 302 | |||
| 283 | /** | 303 | /** | 
| 284 | * alloc_pagedir - Allocate the page directory. | 304 | * alloc_pagedir - Allocate the page directory. | 
| 285 | * | 305 | * | 
| @@ -293,21 +313,21 @@ static void *alloc_image_page(void) | |||
| 293 | * 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. | 
| 294 | */ | 314 | */ | 
| 295 | 315 | ||
| 296 | struct pbe *alloc_pagedir(unsigned nr_pages) | 316 | struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask, int safe_needed) | 
| 297 | { | 317 | { | 
| 298 | unsigned num; | 318 | unsigned int num; | 
| 299 | struct pbe *pblist, *pbe; | 319 | struct pbe *pblist, *pbe; | 
| 300 | 320 | ||
| 301 | if (!nr_pages) | 321 | if (!nr_pages) | 
| 302 | return NULL; | 322 | return NULL; | 
| 303 | 323 | ||
| 304 | pr_debug("alloc_pagedir(): nr_pages = %d\n", nr_pages); | 324 | pr_debug("alloc_pagedir(): nr_pages = %d\n", nr_pages); | 
| 305 | pblist = alloc_image_page(); | 325 | pblist = alloc_image_page(gfp_mask, safe_needed); | 
| 306 | /* FIXME: rewrite this ugly loop */ | 326 | /* FIXME: rewrite this ugly loop */ | 
| 307 | for (pbe = pblist, num = PBES_PER_PAGE; pbe && num < nr_pages; | 327 | for (pbe = pblist, num = PBES_PER_PAGE; pbe && num < nr_pages; | 
| 308 | pbe = pbe->next, num += PBES_PER_PAGE) { | 328 | pbe = pbe->next, num += PBES_PER_PAGE) { | 
| 309 | pbe += PB_PAGE_SKIP; | 329 | pbe += PB_PAGE_SKIP; | 
| 310 | pbe->next = alloc_image_page(); | 330 | pbe->next = alloc_image_page(gfp_mask, safe_needed); | 
| 311 | } | 331 | } | 
| 312 | if (!pbe) { /* get_zeroed_page() failed */ | 332 | if (!pbe) { /* get_zeroed_page() failed */ | 
| 313 | free_pagedir(pblist); | 333 | free_pagedir(pblist); | 
| @@ -329,7 +349,7 @@ void swsusp_free(void) | |||
| 329 | for_each_zone(zone) { | 349 | for_each_zone(zone) { | 
| 330 | for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) | 350 | for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) | 
| 331 | if (pfn_valid(zone_pfn + zone->zone_start_pfn)) { | 351 | if (pfn_valid(zone_pfn + zone->zone_start_pfn)) { | 
| 332 | struct page * page; | 352 | struct page *page; | 
| 333 | page = pfn_to_page(zone_pfn + zone->zone_start_pfn); | 353 | page = pfn_to_page(zone_pfn + zone->zone_start_pfn); | 
| 334 | if (PageNosave(page) && PageNosaveFree(page)) { | 354 | if (PageNosave(page) && PageNosaveFree(page)) { | 
| 335 | ClearPageNosave(page); | 355 | ClearPageNosave(page); | 
| @@ -348,31 +368,39 @@ void swsusp_free(void) | |||
| 348 | * free pages. | 368 | * free pages. | 
| 349 | */ | 369 | */ | 
| 350 | 370 | ||
| 351 | static int enough_free_mem(unsigned nr_pages) | 371 | static int enough_free_mem(unsigned int nr_pages) | 
| 352 | { | 372 | { | 
| 353 | pr_debug("swsusp: available memory: %u pages\n", nr_free_pages()); | 373 | pr_debug("swsusp: available memory: %u pages\n", nr_free_pages()); | 
| 354 | return nr_free_pages() > (nr_pages + PAGES_FOR_IO + | 374 | return nr_free_pages() > (nr_pages + PAGES_FOR_IO + | 
| 355 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); | 375 | (nr_pages + PBES_PER_PAGE - 1) / PBES_PER_PAGE); | 
| 356 | } | 376 | } | 
| 357 | 377 | ||
| 378 | int alloc_data_pages(struct pbe *pblist, gfp_t gfp_mask, int safe_needed) | ||
| 379 | { | ||
| 380 | struct pbe *p; | ||
| 358 | 381 | ||
| 359 | static struct pbe *swsusp_alloc(unsigned nr_pages) | 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 | } | ||
| 389 | |||
| 390 | static struct pbe *swsusp_alloc(unsigned int nr_pages) | ||
| 360 | { | 391 | { | 
| 361 | struct pbe *pblist, *p; | 392 | struct pbe *pblist; | 
| 362 | 393 | ||
| 363 | if (!(pblist = alloc_pagedir(nr_pages))) { | 394 | if (!(pblist = alloc_pagedir(nr_pages, GFP_ATOMIC | __GFP_COLD, 0))) { | 
| 364 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); | 395 | printk(KERN_ERR "suspend: Allocating pagedir failed.\n"); | 
| 365 | return NULL; | 396 | return NULL; | 
| 366 | } | 397 | } | 
| 367 | create_pbe_list(pblist, nr_pages); | 398 | create_pbe_list(pblist, nr_pages); | 
| 368 | 399 | ||
| 369 | for_each_pbe (p, pblist) { | 400 | if (alloc_data_pages(pblist, GFP_ATOMIC | __GFP_COLD, 0)) { | 
| 370 | p->address = (unsigned long)alloc_image_page(); | 401 | printk(KERN_ERR "suspend: Allocating image pages failed.\n"); | 
| 371 | if (!p->address) { | 402 | swsusp_free(); | 
| 372 | printk(KERN_ERR "suspend: Allocating image pages failed.\n"); | 403 | return NULL; | 
| 373 | swsusp_free(); | ||
| 374 | return NULL; | ||
| 375 | } | ||
| 376 | } | 404 | } | 
| 377 | 405 | ||
| 378 | return pblist; | 406 | return pblist; | 
| @@ -380,14 +408,9 @@ static struct pbe *swsusp_alloc(unsigned nr_pages) | |||
| 380 | 408 | ||
| 381 | asmlinkage int swsusp_save(void) | 409 | asmlinkage int swsusp_save(void) | 
| 382 | { | 410 | { | 
| 383 | unsigned nr_pages; | 411 | unsigned int nr_pages; | 
| 384 | 412 | ||
| 385 | pr_debug("swsusp: critical section: \n"); | 413 | pr_debug("swsusp: critical section: \n"); | 
| 386 | if (save_highmem()) { | ||
| 387 | printk(KERN_CRIT "swsusp: Not enough free pages for highmem\n"); | ||
| 388 | restore_highmem(); | ||
| 389 | return -ENOMEM; | ||
| 390 | } | ||
| 391 | 414 | ||
| 392 | drain_local_pages(); | 415 | drain_local_pages(); | 
| 393 | nr_pages = count_data_pages(); | 416 | nr_pages = count_data_pages(); | 
| @@ -407,11 +430,6 @@ asmlinkage int swsusp_save(void) | |||
| 407 | return -ENOMEM; | 430 | return -ENOMEM; | 
| 408 | } | 431 | } | 
| 409 | 432 | ||
| 410 | if (!enough_swap(nr_pages)) { | ||
| 411 | printk(KERN_ERR "swsusp: Not enough free swap\n"); | ||
| 412 | return -ENOSPC; | ||
| 413 | } | ||
| 414 | |||
| 415 | pagedir_nosave = swsusp_alloc(nr_pages); | 433 | pagedir_nosave = swsusp_alloc(nr_pages); | 
| 416 | if (!pagedir_nosave) | 434 | if (!pagedir_nosave) | 
| 417 | return -ENOMEM; | 435 | return -ENOMEM; | 
