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 | |
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>
-rw-r--r-- | include/linux/mm.h | 4 | ||||
-rw-r--r-- | mm/balloon_compaction.c | 4 | ||||
-rw-r--r-- | mm/memory.c | 2 | ||||
-rw-r--r-- | mm/memory_hotplug.c | 2 | ||||
-rw-r--r-- | mm/page_alloc.c | 72 |
5 files changed, 61 insertions, 23 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index a512dd836931..03bbcb84d96e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -2029,7 +2029,9 @@ extern void shake_page(struct page *p, int access); | |||
2029 | extern atomic_long_t num_poisoned_pages; | 2029 | extern atomic_long_t num_poisoned_pages; |
2030 | extern int soft_offline_page(struct page *page, int flags); | 2030 | extern int soft_offline_page(struct page *page, int flags); |
2031 | 2031 | ||
2032 | extern void dump_page(struct page *page); | 2032 | extern void dump_page(struct page *page, char *reason); |
2033 | extern void dump_page_badflags(struct page *page, char *reason, | ||
2034 | unsigned long badflags); | ||
2033 | 2035 | ||
2034 | #if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_HUGETLBFS) | 2036 | #if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_HUGETLBFS) |
2035 | extern void clear_huge_page(struct page *page, | 2037 | extern void clear_huge_page(struct page *page, |
diff --git a/mm/balloon_compaction.c b/mm/balloon_compaction.c index 07dbc8ec46cf..6e45a5074bf0 100644 --- a/mm/balloon_compaction.c +++ b/mm/balloon_compaction.c | |||
@@ -267,7 +267,7 @@ void balloon_page_putback(struct page *page) | |||
267 | put_page(page); | 267 | put_page(page); |
268 | } else { | 268 | } else { |
269 | WARN_ON(1); | 269 | WARN_ON(1); |
270 | dump_page(page); | 270 | dump_page(page, "not movable balloon page"); |
271 | } | 271 | } |
272 | unlock_page(page); | 272 | unlock_page(page); |
273 | } | 273 | } |
@@ -287,7 +287,7 @@ int balloon_page_migrate(struct page *newpage, | |||
287 | BUG_ON(!trylock_page(newpage)); | 287 | BUG_ON(!trylock_page(newpage)); |
288 | 288 | ||
289 | if (WARN_ON(!__is_movable_balloon_page(page))) { | 289 | if (WARN_ON(!__is_movable_balloon_page(page))) { |
290 | dump_page(page); | 290 | dump_page(page, "not movable balloon page"); |
291 | unlock_page(newpage); | 291 | unlock_page(newpage); |
292 | return rc; | 292 | return rc; |
293 | } | 293 | } |
diff --git a/mm/memory.c b/mm/memory.c index 86487dfa5e59..71d70c082b98 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -671,7 +671,7 @@ static void print_bad_pte(struct vm_area_struct *vma, unsigned long addr, | |||
671 | current->comm, | 671 | current->comm, |
672 | (long long)pte_val(pte), (long long)pmd_val(*pmd)); | 672 | (long long)pte_val(pte), (long long)pmd_val(*pmd)); |
673 | if (page) | 673 | if (page) |
674 | dump_page(page); | 674 | dump_page(page, "bad pte"); |
675 | printk(KERN_ALERT | 675 | printk(KERN_ALERT |
676 | "addr:%p vm_flags:%08lx anon_vma:%p mapping:%p index:%lx\n", | 676 | "addr:%p vm_flags:%08lx anon_vma:%p mapping:%p index:%lx\n", |
677 | (void *)addr, vma->vm_flags, vma->anon_vma, mapping, index); | 677 | (void *)addr, vma->vm_flags, vma->anon_vma, mapping, index); |
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index cc2ab37220b7..a512a47241a4 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c | |||
@@ -1309,7 +1309,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn) | |||
1309 | #ifdef CONFIG_DEBUG_VM | 1309 | #ifdef CONFIG_DEBUG_VM |
1310 | printk(KERN_ALERT "removing pfn %lx from LRU failed\n", | 1310 | printk(KERN_ALERT "removing pfn %lx from LRU failed\n", |
1311 | pfn); | 1311 | pfn); |
1312 | dump_page(page); | 1312 | dump_page(page, "failed to remove from LRU"); |
1313 | #endif | 1313 | #endif |
1314 | put_page(page); | 1314 | put_page(page); |
1315 | /* Because we don't have big zone->lock. we should | 1315 | /* Because we don't have big zone->lock. we should |
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 | } | ||