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>
This commit is contained in:
Родитель
12ab028be0
Коммит
f0b791a34c
|
@ -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)
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
#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)
|
||||||
}
|
}
|
||||||
#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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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 @@ out:
|
||||||
|
|
||||||
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,
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче