diff options
author | Dave Hansen <dave@sr71.net> | 2014-01-23 18:52:49 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-23 19:36:50 -0500 |
commit | f0b791a34cb3cffd2bbc3ca4365c9b719fa2c9f3 (patch) | |
tree | c4aa42cd8ecd15eca056a944788f5457e3a370ef /mm/page_alloc.c | |
parent | 12ab028be0008640de712ca890dc1a9ae224934d (diff) |
mm: print more details for bad_page()
bad_page() is cool in that it prints out a bunch of data about the page.
But, I can never remember which page flags are good and which are bad,
or whether ->index or ->mapping is required to be NULL.
This patch allows bad/dump_page() callers to specify a string about why
they are dumping the page and adds explanation strings to a number of
places. It also adds a 'bad_flags' argument to bad_page(), which it
then dumps out separately from the flags which are actually set.
This way, the messages will show specifically why the page was bad,
*specifically* which flags it is complaining about, if it was a page
flag combination which was the problem.
[akpm@linux-foundation.org: switch to pr_alert]
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Christoph Lameter <cl@linux.com>
Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/page_alloc.c')
-rw-r--r-- | mm/page_alloc.c | 72 |
1 files changed, 54 insertions, 18 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 533e2147d14f..1939f4446a36 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
@@ -295,7 +295,7 @@ static inline int bad_range(struct zone *zone, struct page *page) | |||
295 | } | 295 | } |
296 | #endif | 296 | #endif |
297 | 297 | ||
298 | static void bad_page(struct page *page) | 298 | static void bad_page(struct page *page, char *reason, unsigned long bad_flags) |
299 | { | 299 | { |
300 | static unsigned long resume; | 300 | static unsigned long resume; |
301 | static unsigned long nr_shown; | 301 | static unsigned long nr_shown; |
@@ -329,7 +329,7 @@ static void bad_page(struct page *page) | |||
329 | 329 | ||
330 | printk(KERN_ALERT "BUG: Bad page state in process %s pfn:%05lx\n", | 330 | printk(KERN_ALERT "BUG: Bad page state in process %s pfn:%05lx\n", |
331 | current->comm, page_to_pfn(page)); | 331 | current->comm, page_to_pfn(page)); |
332 | dump_page(page); | 332 | dump_page_badflags(page, reason, bad_flags); |
333 | 333 | ||
334 | print_modules(); | 334 | print_modules(); |
335 | dump_stack(); | 335 | dump_stack(); |
@@ -383,7 +383,7 @@ static int destroy_compound_page(struct page *page, unsigned long order) | |||
383 | int bad = 0; | 383 | int bad = 0; |
384 | 384 | ||
385 | if (unlikely(compound_order(page) != order)) { | 385 | if (unlikely(compound_order(page) != order)) { |
386 | bad_page(page); | 386 | bad_page(page, "wrong compound order", 0); |
387 | bad++; | 387 | bad++; |
388 | } | 388 | } |
389 | 389 | ||
@@ -392,8 +392,11 @@ static int destroy_compound_page(struct page *page, unsigned long order) | |||
392 | for (i = 1; i < nr_pages; i++) { | 392 | for (i = 1; i < nr_pages; i++) { |
393 | struct page *p = page + i; | 393 | struct page *p = page + i; |
394 | 394 | ||
395 | if (unlikely(!PageTail(p) || (p->first_page != page))) { | 395 | if (unlikely(!PageTail(p))) { |
396 | bad_page(page); | 396 | bad_page(page, "PageTail not set", 0); |
397 | bad++; | ||
398 | } else if (unlikely(p->first_page != page)) { | ||
399 | bad_page(page, "first_page not consistent", 0); | ||
397 | bad++; | 400 | bad++; |
398 | } | 401 | } |
399 | __ClearPageTail(p); | 402 | __ClearPageTail(p); |
@@ -618,12 +621,23 @@ out: | |||
618 | 621 | ||
619 | static inline int free_pages_check(struct page *page) | 622 | static inline int free_pages_check(struct page *page) |
620 | { | 623 | { |
621 | if (unlikely(page_mapcount(page) | | 624 | char *bad_reason = NULL; |
622 | (page->mapping != NULL) | | 625 | unsigned long bad_flags = 0; |
623 | (atomic_read(&page->_count) != 0) | | 626 | |
624 | (page->flags & PAGE_FLAGS_CHECK_AT_FREE) | | 627 | if (unlikely(page_mapcount(page))) |
625 | (mem_cgroup_bad_page_check(page)))) { | 628 | bad_reason = "nonzero mapcount"; |
626 | bad_page(page); | 629 | if (unlikely(page->mapping != NULL)) |
630 | bad_reason = "non-NULL mapping"; | ||
631 | if (unlikely(atomic_read(&page->_count) != 0)) | ||
632 | bad_reason = "nonzero _count"; | ||
633 | if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_FREE)) { | ||
634 | bad_reason = "PAGE_FLAGS_CHECK_AT_FREE flag(s) set"; | ||
635 | bad_flags = PAGE_FLAGS_CHECK_AT_FREE; | ||
636 | } | ||
637 | if (unlikely(mem_cgroup_bad_page_check(page))) | ||
638 | bad_reason = "cgroup check failed"; | ||
639 | if (unlikely(bad_reason)) { | ||
640 | bad_page(page, bad_reason, bad_flags); | ||
627 | return 1; | 641 | return 1; |
628 | } | 642 | } |
629 | page_cpupid_reset_last(page); | 643 | page_cpupid_reset_last(page); |
@@ -843,12 +857,23 @@ static inline void expand(struct zone *zone, struct page *page, | |||
843 | */ | 857 | */ |
844 | static inline int check_new_page(struct page *page) | 858 | static inline int check_new_page(struct page *page) |
845 | { | 859 | { |
846 | if (unlikely(page_mapcount(page) | | 860 | char *bad_reason = NULL; |
847 | (page->mapping != NULL) | | 861 | unsigned long bad_flags = 0; |
848 | (atomic_read(&page->_count) != 0) | | 862 | |
849 | (page->flags & PAGE_FLAGS_CHECK_AT_PREP) | | 863 | if (unlikely(page_mapcount(page))) |
850 | (mem_cgroup_bad_page_check(page)))) { | 864 | bad_reason = "nonzero mapcount"; |
851 | bad_page(page); | 865 | if (unlikely(page->mapping != NULL)) |
866 | bad_reason = "non-NULL mapping"; | ||
867 | if (unlikely(atomic_read(&page->_count) != 0)) | ||
868 | bad_reason = "nonzero _count"; | ||
869 | if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_PREP)) { | ||
870 | bad_reason = "PAGE_FLAGS_CHECK_AT_PREP flag set"; | ||
871 | bad_flags = PAGE_FLAGS_CHECK_AT_PREP; | ||
872 | } | ||
873 | if (unlikely(mem_cgroup_bad_page_check(page))) | ||
874 | bad_reason = "cgroup check failed"; | ||
875 | if (unlikely(bad_reason)) { | ||
876 | bad_page(page, bad_reason, bad_flags); | ||
852 | return 1; | 877 | return 1; |
853 | } | 878 | } |
854 | return 0; | 879 | return 0; |
@@ -6494,12 +6519,23 @@ static void dump_page_flags(unsigned long flags) | |||
6494 | printk(")\n"); | 6519 | printk(")\n"); |
6495 | } | 6520 | } |
6496 | 6521 | ||
6497 | void dump_page(struct page *page) | 6522 | void dump_page_badflags(struct page *page, char *reason, unsigned long badflags) |
6498 | { | 6523 | { |
6499 | printk(KERN_ALERT | 6524 | printk(KERN_ALERT |
6500 | "page:%p count:%d mapcount:%d mapping:%p index:%#lx\n", | 6525 | "page:%p count:%d mapcount:%d mapping:%p index:%#lx\n", |
6501 | page, atomic_read(&page->_count), page_mapcount(page), | 6526 | page, atomic_read(&page->_count), page_mapcount(page), |
6502 | page->mapping, page->index); | 6527 | page->mapping, page->index); |
6503 | dump_page_flags(page->flags); | 6528 | dump_page_flags(page->flags); |
6529 | if (reason) | ||
6530 | pr_alert("page dumped because: %s\n", reason); | ||
6531 | if (page->flags & badflags) { | ||
6532 | pr_alert("bad because of flags:\n"); | ||
6533 | dump_page_flags(page->flags & badflags); | ||
6534 | } | ||
6504 | mem_cgroup_print_bad_page(page); | 6535 | mem_cgroup_print_bad_page(page); |
6505 | } | 6536 | } |
6537 | |||
6538 | void dump_page(struct page *page, char *reason) | ||
6539 | { | ||
6540 | dump_page_badflags(page, reason, 0); | ||
6541 | } | ||