This allows passing around more parameters and states. No behavior change. CC: Andi Kleen Signed-off-by: Wu Fengguang --- mm/memory-failure.c | 108 +++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 43 deletions(-) --- linux-mm.orig/mm/memory-failure.c 2009-11-30 20:33:58.000000000 +0800 +++ linux-mm/mm/memory-failure.c 2009-11-30 20:35:49.000000000 +0800 @@ -313,20 +313,27 @@ static void collect_procs(struct page *p * Error handlers for various types of pages. */ -enum outcome { +enum hwpoison_result { FAILED, /* Error handling failed */ DELAYED, /* Will be handled later */ IGNORED, /* Error safely ignored */ RECOVERED, /* Successfully recovered */ }; -static const char *action_name[] = { +static const char *hwpoison_result_name[] = { [FAILED] = "Failed", [DELAYED] = "Delayed", [IGNORED] = "Ignored", [RECOVERED] = "Recovered", }; +struct hwpoison_control { + unsigned long pfn; + struct page *p; /* raw corrupted page */ + struct page *page; /* compound page head */ + int result; +}; + /* * XXX: It is possible that a page is isolated from LRU cache, * and then kept in swap cache or failed to remove from page cache. @@ -356,7 +363,7 @@ static int delete_from_lru_cache(struct * Do nothing, try to be lucky and not touch this instead. For a few cases we * could be more sophisticated. */ -static int me_kernel(struct page *p, unsigned long pfn) +static int me_kernel(struct hwpoison_control *hpc) { return DELAYED; } @@ -364,28 +371,30 @@ static int me_kernel(struct page *p, uns /* * Already poisoned page. */ -static int me_ignore(struct page *p, unsigned long pfn) +static int me_ignore(struct hwpoison_control *hpc) { + printk(KERN_ERR "MCE %#lx: Unknown page state\n", hpc->pfn); return IGNORED; } /* * Page in unknown state. Do nothing. */ -static int me_unknown(struct page *p, unsigned long pfn) +static int me_unknown(struct hwpoison_control *hpc) { - printk(KERN_ERR "MCE %#lx: Unknown page state\n", pfn); + printk(KERN_ERR "MCE %#lx: Unknown page state\n", hpc->pfn); return FAILED; } /* * Clean (or cleaned) page cache page. */ -static int me_pagecache_clean(struct page *p, unsigned long pfn) +static int me_pagecache_clean(struct hwpoison_control *hpc) { int err; int ret = FAILED; struct address_space *mapping; + struct page *p = hpc->page; delete_from_lru_cache(p); @@ -420,10 +429,11 @@ static int me_pagecache_clean(struct pag err = mapping->a_ops->error_remove_page(mapping, p); if (err != 0) { printk(KERN_INFO "MCE %#lx: Failed to punch page: %d\n", - pfn, err); + hpc->pfn, err); } else if (page_has_private(p) && !try_to_release_page(p, GFP_NOIO)) { - pr_debug("MCE %#lx: failed to release buffers\n", pfn); + pr_debug("MCE %#lx: failed to release buffers\n", + hpc->pfn); } else { ret = RECOVERED; } @@ -436,7 +446,7 @@ static int me_pagecache_clean(struct pag ret = RECOVERED; else printk(KERN_INFO "MCE %#lx: Failed to invalidate\n", - pfn); + hpc->pfn); } return ret; } @@ -446,11 +456,11 @@ static int me_pagecache_clean(struct pag * Issues: when the error hit a hole page the error is not properly * propagated. */ -static int me_pagecache_dirty(struct page *p, unsigned long pfn) +static int me_pagecache_dirty(struct hwpoison_control *hpc) { - struct address_space *mapping = page_mapping(p); + struct address_space *mapping = page_mapping(hpc->page); - SetPageError(p); + SetPageError(hpc->page); /* TBD: print more information about the file. */ if (mapping) { /* @@ -490,7 +500,7 @@ static int me_pagecache_dirty(struct pag mapping_set_error(mapping, EIO); } - return me_pagecache_clean(p, pfn); + return me_pagecache_clean(hpc); } /* @@ -512,8 +522,9 @@ static int me_pagecache_dirty(struct pag * Clean swap cache pages can be directly isolated. A later page fault will * bring in the known good data from disk. */ -static int me_swapcache_dirty(struct page *p, unsigned long pfn) +static int me_swapcache_dirty(struct hwpoison_control *hpc) { + struct page *p = hpc->page; ClearPageDirty(p); /* Trigger EIO in shmem: */ ClearPageUptodate(p); @@ -524,8 +535,10 @@ static int me_swapcache_dirty(struct pag return FAILED; } -static int me_swapcache_clean(struct page *p, unsigned long pfn) +static int me_swapcache_clean(struct hwpoison_control *hpc) { + struct page *p = hpc->page; + delete_from_swap_cache(p); if (!delete_from_lru_cache(p)) @@ -545,7 +558,7 @@ static int me_swapcache_clean(struct pag * Should handle free huge pages and dequeue them too, but this needs to * handle huge page accounting correctly. */ -static int me_huge_page(struct page *p, unsigned long pfn) +static int me_huge_page(struct hwpoison_control *hpc) { return FAILED; } @@ -581,7 +594,7 @@ static struct page_state { unsigned long mask; unsigned long res; char *msg; - int (*action)(struct page *p, unsigned long pfn); + int (*action)(struct hwpoison_control *hpc); } error_states[] = { { reserved, reserved, "reserved kernel", me_ignore }, @@ -619,30 +632,29 @@ static struct page_state { { 0, 0, "unknown page state", me_unknown }, }; -static void action_result(unsigned long pfn, char *msg, int result) +static void action_result(struct hwpoison_control *hpc, char *msg, int result) { - struct page *page = pfn_to_page(pfn); - + hpc->result = result; printk(KERN_ERR "MCE %#lx: %s%s page recovery: %s\n", - pfn, - PageDirty(page) ? "dirty " : "", - msg, action_name[result]); + hpc->pfn, + PageDirty(hpc->page) ? "dirty " : "", + msg, hwpoison_result_name[result]); } -static int page_action(struct page_state *ps, struct page *p, - unsigned long pfn) +static int page_action(struct page_state *ps, + struct hwpoison_control *hpc) { int result; int count; - result = ps->action(p, pfn); - action_result(pfn, ps->msg, result); + result = ps->action(hpc); + action_result(hpc, ps->msg, result); - count = page_count(p) - 1; + count = page_count(hpc->page) - 1; if (count != 0) printk(KERN_ERR "MCE %#lx: %s page still referenced by %d users\n", - pfn, ps->msg, count); + hpc->pfn, ps->msg, count); /* Could do more checks here if page looks ok */ /* @@ -658,11 +670,12 @@ static int page_action(struct page_state * Do all that is necessary to remove user space mappings. Unmap * the pages and send SIGBUS to the processes if the data was dirty. */ -static int hwpoison_user_mappings(struct page *p, unsigned long pfn, - int trapno) +static int hwpoison_user_mappings(struct hwpoison_control *hpc, int trapno) { enum ttu_flags ttu = TTU_UNMAP | TTU_IGNORE_MLOCK | TTU_IGNORE_ACCESS; struct address_space *mapping; + struct page *p = hpc->page; + unsigned long pfn = hpc->pfn; LIST_HEAD(tokill); int ret; int i; @@ -725,7 +738,8 @@ static int hwpoison_user_mappings(struct ret = try_to_unmap(p, ttu); if (ret == SWAP_SUCCESS) break; - pr_debug("MCE %#lx: try_to_unmap retry needed %d\n", pfn, ret); + pr_debug("MCE %#lx: try_to_unmap retry needed %d\n", + pfn, ret); } if (ret != SWAP_SUCCESS) @@ -749,8 +763,10 @@ static int hwpoison_user_mappings(struct int __memory_failure(unsigned long pfn, int trapno, int ref) { + struct hwpoison_control hpc; struct page_state *ps; struct page *p; + struct page *page; int res; if (!sysctl_memory_failure_recovery) @@ -763,9 +779,15 @@ int __memory_failure(unsigned long pfn, return -ENXIO; } - p = pfn_to_page(pfn); + p = pfn_to_page(pfn); + page = compound_head(p); + + hpc.pfn = pfn; + hpc.p = p; + hpc.page = page; + if (TestSetPageHWPoison(p)) { - action_result(pfn, "already hardware poisoned", IGNORED); + action_result(&hpc, "already hardware poisoned", IGNORED); return 0; } @@ -782,12 +804,12 @@ int __memory_failure(unsigned long pfn, * In fact it's dangerous to directly bump up page count from 0, * that may make page_freeze_refs()/page_unfreeze_refs() mismatch. */ - if (!ref && !get_page_unless_zero(compound_head(p))) { + if (!ref && !get_page_unless_zero(page)) { if (is_free_buddy_page(p)) { - action_result(pfn, "free buddy", DELAYED); + action_result(&hpc, "free buddy", DELAYED); return 0; } else { - action_result(pfn, "high order kernel", IGNORED); + action_result(&hpc, "high order kernel", IGNORED); return -EBUSY; } } @@ -803,7 +825,7 @@ int __memory_failure(unsigned long pfn, if (!PageLRU(p)) lru_add_drain_all(); if (!PageLRU(p)) { - action_result(pfn, "non LRU", IGNORED); + action_result(&hpc, "non LRU", IGNORED); put_page(p); return -EBUSY; } @@ -819,7 +841,7 @@ int __memory_failure(unsigned long pfn, * unpoison always clear PG_hwpoison inside page lock */ if (!PageHWPoison(p)) { - action_result(pfn, "unpoisoned", IGNORED); + action_result(&hpc, "unpoisoned", IGNORED); res = 0; goto out; } @@ -830,7 +852,7 @@ int __memory_failure(unsigned long pfn, * Now take care of user space mappings. * Abort on fail: __remove_from_page_cache() assumes unmapped page. */ - if (hwpoison_user_mappings(p, pfn, trapno) != SWAP_SUCCESS) { + if (hwpoison_user_mappings(&hpc, trapno) != SWAP_SUCCESS) { res = -EBUSY; goto out; } @@ -839,7 +861,7 @@ int __memory_failure(unsigned long pfn, * Torn down by someone else? */ if (PageLRU(p) && !PageSwapCache(p) && p->mapping == NULL) { - action_result(pfn, "already truncated LRU", IGNORED); + action_result(&hpc, "already truncated LRU", IGNORED); res = 0; goto out; } @@ -847,7 +869,7 @@ int __memory_failure(unsigned long pfn, res = -EBUSY; for (ps = error_states;; ps++) { if ((p->flags & ps->mask) == ps->res) { - res = page_action(ps, p, pfn); + res = page_action(ps, &hpc); break; } } -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/