Commit f0b791a3 authored by Dave Hansen's avatar Dave Hansen Committed by Linus Torvalds

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: default avatarDave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: default avatarChristoph Lameter <cl@linux.com>
Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 12ab028b
...@@ -2029,7 +2029,9 @@ extern void shake_page(struct page *p, int access); ...@@ -2029,7 +2029,9 @@ extern void shake_page(struct page *p, int access);
extern atomic_long_t num_poisoned_pages; extern atomic_long_t num_poisoned_pages;
extern int soft_offline_page(struct page *page, int flags); extern int soft_offline_page(struct page *page, int flags);
extern void dump_page(struct page *page); extern void dump_page(struct page *page, char *reason);
extern void dump_page_badflags(struct page *page, char *reason,
unsigned long badflags);
#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_HUGETLBFS) #if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_HUGETLBFS)
extern void clear_huge_page(struct page *page, extern void clear_huge_page(struct page *page,
......
...@@ -267,7 +267,7 @@ void balloon_page_putback(struct page *page) ...@@ -267,7 +267,7 @@ void balloon_page_putback(struct page *page)
put_page(page); put_page(page);
} else { } else {
WARN_ON(1); WARN_ON(1);
dump_page(page); dump_page(page, "not movable balloon page");
} }
unlock_page(page); unlock_page(page);
} }
...@@ -287,7 +287,7 @@ int balloon_page_migrate(struct page *newpage, ...@@ -287,7 +287,7 @@ int balloon_page_migrate(struct page *newpage,
BUG_ON(!trylock_page(newpage)); BUG_ON(!trylock_page(newpage));
if (WARN_ON(!__is_movable_balloon_page(page))) { if (WARN_ON(!__is_movable_balloon_page(page))) {
dump_page(page); dump_page(page, "not movable balloon page");
unlock_page(newpage); unlock_page(newpage);
return rc; return rc;
} }
......
...@@ -671,7 +671,7 @@ static void print_bad_pte(struct vm_area_struct *vma, unsigned long addr, ...@@ -671,7 +671,7 @@ static void print_bad_pte(struct vm_area_struct *vma, unsigned long addr,
current->comm, current->comm,
(long long)pte_val(pte), (long long)pmd_val(*pmd)); (long long)pte_val(pte), (long long)pmd_val(*pmd));
if (page) if (page)
dump_page(page); dump_page(page, "bad pte");
printk(KERN_ALERT printk(KERN_ALERT
"addr:%p vm_flags:%08lx anon_vma:%p mapping:%p index:%lx\n", "addr:%p vm_flags:%08lx anon_vma:%p mapping:%p index:%lx\n",
(void *)addr, vma->vm_flags, vma->anon_vma, mapping, index); (void *)addr, vma->vm_flags, vma->anon_vma, mapping, index);
......
...@@ -1309,7 +1309,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn) ...@@ -1309,7 +1309,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
#ifdef CONFIG_DEBUG_VM #ifdef CONFIG_DEBUG_VM
printk(KERN_ALERT "removing pfn %lx from LRU failed\n", printk(KERN_ALERT "removing pfn %lx from LRU failed\n",
pfn); pfn);
dump_page(page); dump_page(page, "failed to remove from LRU");
#endif #endif
put_page(page); put_page(page);
/* Because we don't have big zone->lock. we should /* Because we don't have big zone->lock. we should
......
...@@ -295,7 +295,7 @@ static inline int bad_range(struct zone *zone, struct page *page) ...@@ -295,7 +295,7 @@ static inline int bad_range(struct zone *zone, struct page *page)
} }
#endif #endif
static void bad_page(struct page *page) static void bad_page(struct page *page, char *reason, unsigned long bad_flags)
{ {
static unsigned long resume; static unsigned long resume;
static unsigned long nr_shown; static unsigned long nr_shown;
...@@ -329,7 +329,7 @@ static void bad_page(struct page *page) ...@@ -329,7 +329,7 @@ static void bad_page(struct page *page)
printk(KERN_ALERT "BUG: Bad page state in process %s pfn:%05lx\n", printk(KERN_ALERT "BUG: Bad page state in process %s pfn:%05lx\n",
current->comm, page_to_pfn(page)); current->comm, page_to_pfn(page));
dump_page(page); dump_page_badflags(page, reason, bad_flags);
print_modules(); print_modules();
dump_stack(); dump_stack();
...@@ -383,7 +383,7 @@ static int destroy_compound_page(struct page *page, unsigned long order) ...@@ -383,7 +383,7 @@ static int destroy_compound_page(struct page *page, unsigned long order)
int bad = 0; int bad = 0;
if (unlikely(compound_order(page) != order)) { if (unlikely(compound_order(page) != order)) {
bad_page(page); bad_page(page, "wrong compound order", 0);
bad++; bad++;
} }
...@@ -392,8 +392,11 @@ static int destroy_compound_page(struct page *page, unsigned long order) ...@@ -392,8 +392,11 @@ static int destroy_compound_page(struct page *page, unsigned long order)
for (i = 1; i < nr_pages; i++) { for (i = 1; i < nr_pages; i++) {
struct page *p = page + i; struct page *p = page + i;
if (unlikely(!PageTail(p) || (p->first_page != page))) { if (unlikely(!PageTail(p))) {
bad_page(page); bad_page(page, "PageTail not set", 0);
bad++;
} else if (unlikely(p->first_page != page)) {
bad_page(page, "first_page not consistent", 0);
bad++; bad++;
} }
__ClearPageTail(p); __ClearPageTail(p);
...@@ -618,12 +621,23 @@ static inline void __free_one_page(struct page *page, ...@@ -618,12 +621,23 @@ static inline void __free_one_page(struct page *page,
static inline int free_pages_check(struct page *page) static inline int free_pages_check(struct page *page)
{ {
if (unlikely(page_mapcount(page) | char *bad_reason = NULL;
(page->mapping != NULL) | unsigned long bad_flags = 0;
(atomic_read(&page->_count) != 0) |
(page->flags & PAGE_FLAGS_CHECK_AT_FREE) | if (unlikely(page_mapcount(page)))
(mem_cgroup_bad_page_check(page)))) { bad_reason = "nonzero mapcount";
bad_page(page); if (unlikely(page->mapping != NULL))
bad_reason = "non-NULL mapping";
if (unlikely(atomic_read(&page->_count) != 0))
bad_reason = "nonzero _count";
if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_FREE)) {
bad_reason = "PAGE_FLAGS_CHECK_AT_FREE flag(s) set";
bad_flags = PAGE_FLAGS_CHECK_AT_FREE;
}
if (unlikely(mem_cgroup_bad_page_check(page)))
bad_reason = "cgroup check failed";
if (unlikely(bad_reason)) {
bad_page(page, bad_reason, bad_flags);
return 1; return 1;
} }
page_cpupid_reset_last(page); page_cpupid_reset_last(page);
...@@ -843,12 +857,23 @@ static inline void expand(struct zone *zone, struct page *page, ...@@ -843,12 +857,23 @@ static inline void expand(struct zone *zone, struct page *page,
*/ */
static inline int check_new_page(struct page *page) static inline int check_new_page(struct page *page)
{ {
if (unlikely(page_mapcount(page) | char *bad_reason = NULL;
(page->mapping != NULL) | unsigned long bad_flags = 0;
(atomic_read(&page->_count) != 0) |
(page->flags & PAGE_FLAGS_CHECK_AT_PREP) | if (unlikely(page_mapcount(page)))
(mem_cgroup_bad_page_check(page)))) { bad_reason = "nonzero mapcount";
bad_page(page); if (unlikely(page->mapping != NULL))
bad_reason = "non-NULL mapping";
if (unlikely(atomic_read(&page->_count) != 0))
bad_reason = "nonzero _count";
if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_PREP)) {
bad_reason = "PAGE_FLAGS_CHECK_AT_PREP flag set";
bad_flags = PAGE_FLAGS_CHECK_AT_PREP;
}
if (unlikely(mem_cgroup_bad_page_check(page)))
bad_reason = "cgroup check failed";
if (unlikely(bad_reason)) {
bad_page(page, bad_reason, bad_flags);
return 1; return 1;
} }
return 0; return 0;
...@@ -6494,12 +6519,23 @@ static void dump_page_flags(unsigned long flags) ...@@ -6494,12 +6519,23 @@ static void dump_page_flags(unsigned long flags)
printk(")\n"); printk(")\n");
} }
void dump_page(struct page *page) void dump_page_badflags(struct page *page, char *reason, unsigned long badflags)
{ {
printk(KERN_ALERT printk(KERN_ALERT
"page:%p count:%d mapcount:%d mapping:%p index:%#lx\n", "page:%p count:%d mapcount:%d mapping:%p index:%#lx\n",
page, atomic_read(&page->_count), page_mapcount(page), page, atomic_read(&page->_count), page_mapcount(page),
page->mapping, page->index); page->mapping, page->index);
dump_page_flags(page->flags); dump_page_flags(page->flags);
if (reason)
pr_alert("page dumped because: %s\n", reason);
if (page->flags & badflags) {
pr_alert("bad because of flags:\n");
dump_page_flags(page->flags & badflags);
}
mem_cgroup_print_bad_page(page); mem_cgroup_print_bad_page(page);
} }
void dump_page(struct page *page, char *reason)
{
dump_page_badflags(page, reason, 0);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment