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 /kernel | |
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>
Diffstat (limited to 'kernel')
-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 | ||