diff options
| author | Rafael J. Wysocki <rjw@sisk.pl> | 2008-03-11 19:34:57 -0400 | 
|---|---|---|
| committer | Len Brown <len.brown@intel.com> | 2008-03-11 23:15:55 -0400 | 
| commit | a82f7119fd940c1505fc9fdf93d835fa52bc075d (patch) | |
| tree | e8fc2aaa6dc73da9dc2626aa6d239bdcf8c9d44d | |
| parent | baadac8b10c5ac15ce3d26b68fa266c8889b163f (diff) | |
Hibernation: Fix mark_nosave_pages()
There is a problem in the hibernation code that triggers on some NUMA
systems on which pfn_valid() returns 'true' for some PFNs that don't
belong to any zone.  Namely, there is a BUG_ON() in
memory_bm_find_bit() that triggers for PFNs not belonging to any
zone and passing the pfn_valid() test.  On the affected systems it
triggers when we mark PFNs reported by the platform as not saveable,
because the PFNs in question belong to a region mapped directly using
iorepam() (i.e. the ACPI data area) and they pass the pfn_valid()
test.
Modify memory_bm_find_bit() so that it returns an error if given PFN
doesn't belong to any zone instead of crashing the kernel and ignore
the result returned by it in mark_nosave_pages(), while marking the
"nosave" memory regions.
This doesn't affect the hibernation functionality, as we won't touch
the PFNs in question anyway.
http://bugzilla.kernel.org/show_bug.cgi?id=9966 .
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Len Brown <len.brown@intel.com>
| -rw-r--r-- | kernel/power/snapshot.c | 41 | 
1 files changed, 34 insertions, 7 deletions
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 72a020cabb4c..5f91a07c4eac 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c  | |||
| @@ -447,7 +447,7 @@ static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free) | |||
| 447 | * of @bm->cur_zone_bm are updated. | 447 | * of @bm->cur_zone_bm are updated. | 
| 448 | */ | 448 | */ | 
| 449 | 449 | ||
| 450 | static void memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn, | 450 | static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn, | 
| 451 | void **addr, unsigned int *bit_nr) | 451 | void **addr, unsigned int *bit_nr) | 
| 452 | { | 452 | { | 
| 453 | struct zone_bitmap *zone_bm; | 453 | struct zone_bitmap *zone_bm; | 
| @@ -461,7 +461,8 @@ static void memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn, | |||
| 461 | while (pfn < zone_bm->start_pfn || pfn >= zone_bm->end_pfn) { | 461 | while (pfn < zone_bm->start_pfn || pfn >= zone_bm->end_pfn) { | 
| 462 | zone_bm = zone_bm->next; | 462 | zone_bm = zone_bm->next; | 
| 463 | 463 | ||
| 464 | BUG_ON(!zone_bm); | 464 | if (!zone_bm) | 
| 465 | return -EFAULT; | ||
| 465 | } | 466 | } | 
| 466 | bm->cur.zone_bm = zone_bm; | 467 | bm->cur.zone_bm = zone_bm; | 
| 467 | } | 468 | } | 
| @@ -479,23 +480,40 @@ static void memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn, | |||
| 479 | pfn -= bb->start_pfn; | 480 | pfn -= bb->start_pfn; | 
| 480 | *bit_nr = pfn % BM_BITS_PER_CHUNK; | 481 | *bit_nr = pfn % BM_BITS_PER_CHUNK; | 
| 481 | *addr = bb->data + pfn / BM_BITS_PER_CHUNK; | 482 | *addr = bb->data + pfn / BM_BITS_PER_CHUNK; | 
| 483 | return 0; | ||
| 482 | } | 484 | } | 
| 483 | 485 | ||
| 484 | static void memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn) | 486 | static void memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn) | 
| 485 | { | 487 | { | 
| 486 | void *addr; | 488 | void *addr; | 
| 487 | unsigned int bit; | 489 | unsigned int bit; | 
| 490 | int error; | ||
| 488 | 491 | ||
| 489 | memory_bm_find_bit(bm, pfn, &addr, &bit); | 492 | error = memory_bm_find_bit(bm, pfn, &addr, &bit); | 
| 493 | BUG_ON(error); | ||
| 490 | set_bit(bit, addr); | 494 | set_bit(bit, addr); | 
| 491 | } | 495 | } | 
| 492 | 496 | ||
| 497 | static int mem_bm_set_bit_check(struct memory_bitmap *bm, unsigned long pfn) | ||
| 498 | { | ||
| 499 | void *addr; | ||
| 500 | unsigned int bit; | ||
| 501 | int error; | ||
| 502 | |||
| 503 | error = memory_bm_find_bit(bm, pfn, &addr, &bit); | ||
| 504 | if (!error) | ||
| 505 | set_bit(bit, addr); | ||
| 506 | return error; | ||
| 507 | } | ||
| 508 | |||
| 493 | static void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn) | 509 | static void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn) | 
| 494 | { | 510 | { | 
| 495 | void *addr; | 511 | void *addr; | 
| 496 | unsigned int bit; | 512 | unsigned int bit; | 
| 513 | int error; | ||
| 497 | 514 | ||
| 498 | memory_bm_find_bit(bm, pfn, &addr, &bit); | 515 | error = memory_bm_find_bit(bm, pfn, &addr, &bit); | 
| 516 | BUG_ON(error); | ||
| 499 | clear_bit(bit, addr); | 517 | clear_bit(bit, addr); | 
| 500 | } | 518 | } | 
| 501 | 519 | ||
| @@ -503,8 +521,10 @@ static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn) | |||
| 503 | { | 521 | { | 
| 504 | void *addr; | 522 | void *addr; | 
| 505 | unsigned int bit; | 523 | unsigned int bit; | 
| 524 | int error; | ||
| 506 | 525 | ||
| 507 | memory_bm_find_bit(bm, pfn, &addr, &bit); | 526 | error = memory_bm_find_bit(bm, pfn, &addr, &bit); | 
| 527 | BUG_ON(error); | ||
| 508 | return test_bit(bit, addr); | 528 | return test_bit(bit, addr); | 
| 509 | } | 529 | } | 
| 510 | 530 | ||
| @@ -709,8 +729,15 @@ static void mark_nosave_pages(struct memory_bitmap *bm) | |||
| 709 | region->end_pfn << PAGE_SHIFT); | 729 | region->end_pfn << PAGE_SHIFT); | 
| 710 | 730 | ||
| 711 | for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++) | 731 | for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++) | 
| 712 | if (pfn_valid(pfn)) | 732 | if (pfn_valid(pfn)) { | 
| 713 | memory_bm_set_bit(bm, pfn); | 733 | /* | 
| 734 | * It is safe to ignore the result of | ||
| 735 | * mem_bm_set_bit_check() here, since we won't | ||
| 736 | * touch the PFNs for which the error is | ||
| 737 | * returned anyway. | ||
| 738 | */ | ||
| 739 | mem_bm_set_bit_check(bm, pfn); | ||
| 740 | } | ||
| 714 | } | 741 | } | 
| 715 | } | 742 | } | 
| 716 | 743 | ||
