Commit cc637b17 authored by Xie XiuQi's avatar Xie XiuQi Committed by Linus Torvalds

memory-failure: export page_type and action result

Export 'outcome' and 'action_page_type' to mm.h, so we could use
this emnus outside.

This patch is preparation for adding trace events for memory-failure
recovery action.
Signed-off-by: default avatarXie XiuQi <xiexiuqi@huawei.com>
Acked-by: default avatarNaoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Chen Gong <gong.chen@linux.intel.com>
Cc: Jim Davis <jim.epost@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Tony Luck <tony.luck@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent eb3c24f3
...@@ -2153,6 +2153,40 @@ extern void shake_page(struct page *p, int access); ...@@ -2153,6 +2153,40 @@ 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);
/*
* Error handlers for various types of pages.
*/
enum mf_outcome {
MF_IGNORED, /* Error: cannot be handled */
MF_FAILED, /* Error: handling failed */
MF_DELAYED, /* Will be handled later */
MF_RECOVERED, /* Successfully recovered */
};
enum mf_action_page_type {
MF_MSG_KERNEL,
MF_MSG_KERNEL_HIGH_ORDER,
MF_MSG_SLAB,
MF_MSG_DIFFERENT_COMPOUND,
MF_MSG_POISONED_HUGE,
MF_MSG_HUGE,
MF_MSG_FREE_HUGE,
MF_MSG_UNMAP_FAILED,
MF_MSG_DIRTY_SWAPCACHE,
MF_MSG_CLEAN_SWAPCACHE,
MF_MSG_DIRTY_MLOCKED_LRU,
MF_MSG_CLEAN_MLOCKED_LRU,
MF_MSG_DIRTY_UNEVICTABLE_LRU,
MF_MSG_CLEAN_UNEVICTABLE_LRU,
MF_MSG_DIRTY_LRU,
MF_MSG_CLEAN_LRU,
MF_MSG_TRUNCATED_LRU,
MF_MSG_BUDDY,
MF_MSG_BUDDY_2ND,
MF_MSG_UNKNOWN,
};
#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,
unsigned long addr, unsigned long addr,
......
...@@ -504,68 +504,34 @@ static void collect_procs(struct page *page, struct list_head *tokill, ...@@ -504,68 +504,34 @@ static void collect_procs(struct page *page, struct list_head *tokill,
kfree(tk); kfree(tk);
} }
/*
* Error handlers for various types of pages.
*/
enum outcome {
IGNORED, /* Error: cannot be handled */
FAILED, /* Error: handling failed */
DELAYED, /* Will be handled later */
RECOVERED, /* Successfully recovered */
};
static const char *action_name[] = { static const char *action_name[] = {
[IGNORED] = "Ignored", [MF_IGNORED] = "Ignored",
[FAILED] = "Failed", [MF_FAILED] = "Failed",
[DELAYED] = "Delayed", [MF_DELAYED] = "Delayed",
[RECOVERED] = "Recovered", [MF_RECOVERED] = "Recovered",
};
enum action_page_type {
MSG_KERNEL,
MSG_KERNEL_HIGH_ORDER,
MSG_SLAB,
MSG_DIFFERENT_COMPOUND,
MSG_POISONED_HUGE,
MSG_HUGE,
MSG_FREE_HUGE,
MSG_UNMAP_FAILED,
MSG_DIRTY_SWAPCACHE,
MSG_CLEAN_SWAPCACHE,
MSG_DIRTY_MLOCKED_LRU,
MSG_CLEAN_MLOCKED_LRU,
MSG_DIRTY_UNEVICTABLE_LRU,
MSG_CLEAN_UNEVICTABLE_LRU,
MSG_DIRTY_LRU,
MSG_CLEAN_LRU,
MSG_TRUNCATED_LRU,
MSG_BUDDY,
MSG_BUDDY_2ND,
MSG_UNKNOWN,
}; };
static const char * const action_page_types[] = { static const char * const action_page_types[] = {
[MSG_KERNEL] = "reserved kernel page", [MF_MSG_KERNEL] = "reserved kernel page",
[MSG_KERNEL_HIGH_ORDER] = "high-order kernel page", [MF_MSG_KERNEL_HIGH_ORDER] = "high-order kernel page",
[MSG_SLAB] = "kernel slab page", [MF_MSG_SLAB] = "kernel slab page",
[MSG_DIFFERENT_COMPOUND] = "different compound page after locking", [MF_MSG_DIFFERENT_COMPOUND] = "different compound page after locking",
[MSG_POISONED_HUGE] = "huge page already hardware poisoned", [MF_MSG_POISONED_HUGE] = "huge page already hardware poisoned",
[MSG_HUGE] = "huge page", [MF_MSG_HUGE] = "huge page",
[MSG_FREE_HUGE] = "free huge page", [MF_MSG_FREE_HUGE] = "free huge page",
[MSG_UNMAP_FAILED] = "unmapping failed page", [MF_MSG_UNMAP_FAILED] = "unmapping failed page",
[MSG_DIRTY_SWAPCACHE] = "dirty swapcache page", [MF_MSG_DIRTY_SWAPCACHE] = "dirty swapcache page",
[MSG_CLEAN_SWAPCACHE] = "clean swapcache page", [MF_MSG_CLEAN_SWAPCACHE] = "clean swapcache page",
[MSG_DIRTY_MLOCKED_LRU] = "dirty mlocked LRU page", [MF_MSG_DIRTY_MLOCKED_LRU] = "dirty mlocked LRU page",
[MSG_CLEAN_MLOCKED_LRU] = "clean mlocked LRU page", [MF_MSG_CLEAN_MLOCKED_LRU] = "clean mlocked LRU page",
[MSG_DIRTY_UNEVICTABLE_LRU] = "dirty unevictable LRU page", [MF_MSG_DIRTY_UNEVICTABLE_LRU] = "dirty unevictable LRU page",
[MSG_CLEAN_UNEVICTABLE_LRU] = "clean unevictable LRU page", [MF_MSG_CLEAN_UNEVICTABLE_LRU] = "clean unevictable LRU page",
[MSG_DIRTY_LRU] = "dirty LRU page", [MF_MSG_DIRTY_LRU] = "dirty LRU page",
[MSG_CLEAN_LRU] = "clean LRU page", [MF_MSG_CLEAN_LRU] = "clean LRU page",
[MSG_TRUNCATED_LRU] = "already truncated LRU page", [MF_MSG_TRUNCATED_LRU] = "already truncated LRU page",
[MSG_BUDDY] = "free buddy page", [MF_MSG_BUDDY] = "free buddy page",
[MSG_BUDDY_2ND] = "free buddy page (2nd try)", [MF_MSG_BUDDY_2ND] = "free buddy page (2nd try)",
[MSG_UNKNOWN] = "unknown page", [MF_MSG_UNKNOWN] = "unknown page",
}; };
/* /*
...@@ -599,7 +565,7 @@ static int delete_from_lru_cache(struct page *p) ...@@ -599,7 +565,7 @@ static int delete_from_lru_cache(struct page *p)
*/ */
static int me_kernel(struct page *p, unsigned long pfn) static int me_kernel(struct page *p, unsigned long pfn)
{ {
return IGNORED; return MF_IGNORED;
} }
/* /*
...@@ -608,7 +574,7 @@ static int me_kernel(struct page *p, unsigned long pfn) ...@@ -608,7 +574,7 @@ static int me_kernel(struct page *p, unsigned long pfn)
static int me_unknown(struct page *p, unsigned long pfn) static int me_unknown(struct page *p, unsigned long pfn)
{ {
printk(KERN_ERR "MCE %#lx: Unknown page state\n", pfn); printk(KERN_ERR "MCE %#lx: Unknown page state\n", pfn);
return FAILED; return MF_FAILED;
} }
/* /*
...@@ -617,7 +583,7 @@ static int me_unknown(struct page *p, unsigned long pfn) ...@@ -617,7 +583,7 @@ static int me_unknown(struct page *p, unsigned long pfn)
static int me_pagecache_clean(struct page *p, unsigned long pfn) static int me_pagecache_clean(struct page *p, unsigned long pfn)
{ {
int err; int err;
int ret = FAILED; int ret = MF_FAILED;
struct address_space *mapping; struct address_space *mapping;
delete_from_lru_cache(p); delete_from_lru_cache(p);
...@@ -627,7 +593,7 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn) ...@@ -627,7 +593,7 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn)
* should be the one m_f() holds. * should be the one m_f() holds.
*/ */
if (PageAnon(p)) if (PageAnon(p))
return RECOVERED; return MF_RECOVERED;
/* /*
* Now truncate the page in the page cache. This is really * Now truncate the page in the page cache. This is really
...@@ -641,7 +607,7 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn) ...@@ -641,7 +607,7 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn)
/* /*
* Page has been teared down in the meanwhile * Page has been teared down in the meanwhile
*/ */
return FAILED; return MF_FAILED;
} }
/* /*
...@@ -658,7 +624,7 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn) ...@@ -658,7 +624,7 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn)
!try_to_release_page(p, GFP_NOIO)) { !try_to_release_page(p, GFP_NOIO)) {
pr_info("MCE %#lx: failed to release buffers\n", pfn); pr_info("MCE %#lx: failed to release buffers\n", pfn);
} else { } else {
ret = RECOVERED; ret = MF_RECOVERED;
} }
} else { } else {
/* /*
...@@ -666,7 +632,7 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn) ...@@ -666,7 +632,7 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn)
* This fails on dirty or anything with private pages * This fails on dirty or anything with private pages
*/ */
if (invalidate_inode_page(p)) if (invalidate_inode_page(p))
ret = RECOVERED; ret = MF_RECOVERED;
else else
printk(KERN_INFO "MCE %#lx: Failed to invalidate\n", printk(KERN_INFO "MCE %#lx: Failed to invalidate\n",
pfn); pfn);
...@@ -752,9 +718,9 @@ static int me_swapcache_dirty(struct page *p, unsigned long pfn) ...@@ -752,9 +718,9 @@ static int me_swapcache_dirty(struct page *p, unsigned long pfn)
ClearPageUptodate(p); ClearPageUptodate(p);
if (!delete_from_lru_cache(p)) if (!delete_from_lru_cache(p))
return DELAYED; return MF_DELAYED;
else else
return FAILED; return MF_FAILED;
} }
static int me_swapcache_clean(struct page *p, unsigned long pfn) static int me_swapcache_clean(struct page *p, unsigned long pfn)
...@@ -762,9 +728,9 @@ static int me_swapcache_clean(struct page *p, unsigned long pfn) ...@@ -762,9 +728,9 @@ static int me_swapcache_clean(struct page *p, unsigned long pfn)
delete_from_swap_cache(p); delete_from_swap_cache(p);
if (!delete_from_lru_cache(p)) if (!delete_from_lru_cache(p))
return RECOVERED; return MF_RECOVERED;
else else
return FAILED; return MF_FAILED;
} }
/* /*
...@@ -794,9 +760,9 @@ static int me_huge_page(struct page *p, unsigned long pfn) ...@@ -794,9 +760,9 @@ static int me_huge_page(struct page *p, unsigned long pfn)
if (!(page_mapping(hpage) || PageAnon(hpage))) { if (!(page_mapping(hpage) || PageAnon(hpage))) {
res = dequeue_hwpoisoned_huge_page(hpage); res = dequeue_hwpoisoned_huge_page(hpage);
if (!res) if (!res)
return RECOVERED; return MF_RECOVERED;
} }
return DELAYED; return MF_DELAYED;
} }
/* /*
...@@ -828,10 +794,10 @@ static int me_huge_page(struct page *p, unsigned long pfn) ...@@ -828,10 +794,10 @@ static int me_huge_page(struct page *p, unsigned long pfn)
static struct page_state { static struct page_state {
unsigned long mask; unsigned long mask;
unsigned long res; unsigned long res;
enum action_page_type type; enum mf_action_page_type type;
int (*action)(struct page *p, unsigned long pfn); int (*action)(struct page *p, unsigned long pfn);
} error_states[] = { } error_states[] = {
{ reserved, reserved, MSG_KERNEL, me_kernel }, { reserved, reserved, MF_MSG_KERNEL, me_kernel },
/* /*
* free pages are specially detected outside this table: * free pages are specially detected outside this table:
* PG_buddy pages only make a small fraction of all free pages. * PG_buddy pages only make a small fraction of all free pages.
...@@ -842,31 +808,31 @@ static struct page_state { ...@@ -842,31 +808,31 @@ static struct page_state {
* currently unused objects without touching them. But just * currently unused objects without touching them. But just
* treat it as standard kernel for now. * treat it as standard kernel for now.
*/ */
{ slab, slab, MSG_SLAB, me_kernel }, { slab, slab, MF_MSG_SLAB, me_kernel },
#ifdef CONFIG_PAGEFLAGS_EXTENDED #ifdef CONFIG_PAGEFLAGS_EXTENDED
{ head, head, MSG_HUGE, me_huge_page }, { head, head, MF_MSG_HUGE, me_huge_page },
{ tail, tail, MSG_HUGE, me_huge_page }, { tail, tail, MF_MSG_HUGE, me_huge_page },
#else #else
{ compound, compound, MSG_HUGE, me_huge_page }, { compound, compound, MF_MSG_HUGE, me_huge_page },
#endif #endif
{ sc|dirty, sc|dirty, MSG_DIRTY_SWAPCACHE, me_swapcache_dirty }, { sc|dirty, sc|dirty, MF_MSG_DIRTY_SWAPCACHE, me_swapcache_dirty },
{ sc|dirty, sc, MSG_CLEAN_SWAPCACHE, me_swapcache_clean }, { sc|dirty, sc, MF_MSG_CLEAN_SWAPCACHE, me_swapcache_clean },
{ mlock|dirty, mlock|dirty, MSG_DIRTY_MLOCKED_LRU, me_pagecache_dirty }, { mlock|dirty, mlock|dirty, MF_MSG_DIRTY_MLOCKED_LRU, me_pagecache_dirty },
{ mlock|dirty, mlock, MSG_CLEAN_MLOCKED_LRU, me_pagecache_clean }, { mlock|dirty, mlock, MF_MSG_CLEAN_MLOCKED_LRU, me_pagecache_clean },
{ unevict|dirty, unevict|dirty, MSG_DIRTY_UNEVICTABLE_LRU, me_pagecache_dirty }, { unevict|dirty, unevict|dirty, MF_MSG_DIRTY_UNEVICTABLE_LRU, me_pagecache_dirty },
{ unevict|dirty, unevict, MSG_CLEAN_UNEVICTABLE_LRU, me_pagecache_clean }, { unevict|dirty, unevict, MF_MSG_CLEAN_UNEVICTABLE_LRU, me_pagecache_clean },
{ lru|dirty, lru|dirty, MSG_DIRTY_LRU, me_pagecache_dirty }, { lru|dirty, lru|dirty, MF_MSG_DIRTY_LRU, me_pagecache_dirty },
{ lru|dirty, lru, MSG_CLEAN_LRU, me_pagecache_clean }, { lru|dirty, lru, MF_MSG_CLEAN_LRU, me_pagecache_clean },
/* /*
* Catchall entry: must be at end. * Catchall entry: must be at end.
*/ */
{ 0, 0, MSG_UNKNOWN, me_unknown }, { 0, 0, MF_MSG_UNKNOWN, me_unknown },
}; };
#undef dirty #undef dirty
...@@ -886,7 +852,7 @@ static struct page_state { ...@@ -886,7 +852,7 @@ static struct page_state {
* "Dirty/Clean" indication is not 100% accurate due to the possibility of * "Dirty/Clean" indication is not 100% accurate due to the possibility of
* setting PG_dirty outside page lock. See also comment above set_page_dirty(). * setting PG_dirty outside page lock. See also comment above set_page_dirty().
*/ */
static void action_result(unsigned long pfn, enum action_page_type type, int result) static void action_result(unsigned long pfn, enum mf_action_page_type type, int result)
{ {
pr_err("MCE %#lx: recovery action for %s: %s\n", pr_err("MCE %#lx: recovery action for %s: %s\n",
pfn, action_page_types[type], action_name[result]); pfn, action_page_types[type], action_name[result]);
...@@ -901,13 +867,13 @@ static int page_action(struct page_state *ps, struct page *p, ...@@ -901,13 +867,13 @@ static int page_action(struct page_state *ps, struct page *p,
result = ps->action(p, pfn); result = ps->action(p, pfn);
count = page_count(p) - 1; count = page_count(p) - 1;
if (ps->action == me_swapcache_dirty && result == DELAYED) if (ps->action == me_swapcache_dirty && result == MF_DELAYED)
count--; count--;
if (count != 0) { if (count != 0) {
printk(KERN_ERR printk(KERN_ERR
"MCE %#lx: %s still referenced by %d users\n", "MCE %#lx: %s still referenced by %d users\n",
pfn, action_page_types[ps->type], count); pfn, action_page_types[ps->type], count);
result = FAILED; result = MF_FAILED;
} }
action_result(pfn, ps->type, result); action_result(pfn, ps->type, result);
...@@ -916,7 +882,7 @@ static int page_action(struct page_state *ps, struct page *p, ...@@ -916,7 +882,7 @@ static int page_action(struct page_state *ps, struct page *p,
* Could adjust zone counters here to correct for the missing page. * Could adjust zone counters here to correct for the missing page.
*/ */
return (result == RECOVERED || result == DELAYED) ? 0 : -EBUSY; return (result == MF_RECOVERED || result == MF_DELAYED) ? 0 : -EBUSY;
} }
/** /**
...@@ -1136,7 +1102,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags) ...@@ -1136,7 +1102,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
*/ */
if (!(flags & MF_COUNT_INCREASED) && !get_hwpoison_page(p)) { if (!(flags & MF_COUNT_INCREASED) && !get_hwpoison_page(p)) {
if (is_free_buddy_page(p)) { if (is_free_buddy_page(p)) {
action_result(pfn, MSG_BUDDY, DELAYED); action_result(pfn, MF_MSG_BUDDY, MF_DELAYED);
return 0; return 0;
} else if (PageHuge(hpage)) { } else if (PageHuge(hpage)) {
/* /*
...@@ -1153,12 +1119,12 @@ int memory_failure(unsigned long pfn, int trapno, int flags) ...@@ -1153,12 +1119,12 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
} }
set_page_hwpoison_huge_page(hpage); set_page_hwpoison_huge_page(hpage);
res = dequeue_hwpoisoned_huge_page(hpage); res = dequeue_hwpoisoned_huge_page(hpage);
action_result(pfn, MSG_FREE_HUGE, action_result(pfn, MF_MSG_FREE_HUGE,
res ? IGNORED : DELAYED); res ? MF_IGNORED : MF_DELAYED);
unlock_page(hpage); unlock_page(hpage);
return res; return res;
} else { } else {
action_result(pfn, MSG_KERNEL_HIGH_ORDER, IGNORED); action_result(pfn, MF_MSG_KERNEL_HIGH_ORDER, MF_IGNORED);
return -EBUSY; return -EBUSY;
} }
} }
...@@ -1203,10 +1169,10 @@ int memory_failure(unsigned long pfn, int trapno, int flags) ...@@ -1203,10 +1169,10 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
*/ */
if (is_free_buddy_page(p)) { if (is_free_buddy_page(p)) {
if (flags & MF_COUNT_INCREASED) if (flags & MF_COUNT_INCREASED)
action_result(pfn, MSG_BUDDY, DELAYED); action_result(pfn, MF_MSG_BUDDY, MF_DELAYED);
else else
action_result(pfn, MSG_BUDDY_2ND, action_result(pfn, MF_MSG_BUDDY_2ND,
DELAYED); MF_DELAYED);
return 0; return 0;
} }
} }
...@@ -1219,7 +1185,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags) ...@@ -1219,7 +1185,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
* If this happens just bail out. * If this happens just bail out.
*/ */
if (PageCompound(p) && compound_head(p) != orig_head) { if (PageCompound(p) && compound_head(p) != orig_head) {
action_result(pfn, MSG_DIFFERENT_COMPOUND, IGNORED); action_result(pfn, MF_MSG_DIFFERENT_COMPOUND, MF_IGNORED);
res = -EBUSY; res = -EBUSY;
goto out; goto out;
} }
...@@ -1259,7 +1225,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags) ...@@ -1259,7 +1225,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
* on the head page to show that the hugepage is hwpoisoned * on the head page to show that the hugepage is hwpoisoned
*/ */
if (PageHuge(p) && PageTail(p) && TestSetPageHWPoison(hpage)) { if (PageHuge(p) && PageTail(p) && TestSetPageHWPoison(hpage)) {
action_result(pfn, MSG_POISONED_HUGE, IGNORED); action_result(pfn, MF_MSG_POISONED_HUGE, MF_IGNORED);
unlock_page(hpage); unlock_page(hpage);
put_page(hpage); put_page(hpage);
return 0; return 0;
...@@ -1288,7 +1254,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags) ...@@ -1288,7 +1254,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
*/ */
if (hwpoison_user_mappings(p, pfn, trapno, flags, &hpage) if (hwpoison_user_mappings(p, pfn, trapno, flags, &hpage)
!= SWAP_SUCCESS) { != SWAP_SUCCESS) {
action_result(pfn, MSG_UNMAP_FAILED, IGNORED); action_result(pfn, MF_MSG_UNMAP_FAILED, MF_IGNORED);
res = -EBUSY; res = -EBUSY;
goto out; goto out;
} }
...@@ -1297,7 +1263,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags) ...@@ -1297,7 +1263,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
* Torn down by someone else? * Torn down by someone else?
*/ */
if (PageLRU(p) && !PageSwapCache(p) && p->mapping == NULL) { if (PageLRU(p) && !PageSwapCache(p) && p->mapping == NULL) {
action_result(pfn, MSG_TRUNCATED_LRU, IGNORED); action_result(pfn, MF_MSG_TRUNCATED_LRU, MF_IGNORED);
res = -EBUSY; res = -EBUSY;
goto out; goto out;
} }
......
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