|
@@ -856,14 +856,14 @@ static int page_action(struct page_state *ps, struct page *p,
|
|
|
* 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, int flags)
|
|
|
+ int trapno, int flags, struct page **hpagep)
|
|
|
{
|
|
|
enum ttu_flags ttu = TTU_UNMAP | TTU_IGNORE_MLOCK | TTU_IGNORE_ACCESS;
|
|
|
struct address_space *mapping;
|
|
|
LIST_HEAD(tokill);
|
|
|
int ret;
|
|
|
int kill = 1, forcekill;
|
|
|
- struct page *hpage = compound_head(p);
|
|
|
+ struct page *hpage = *hpagep;
|
|
|
struct page *ppage;
|
|
|
|
|
|
if (PageReserved(p) || PageSlab(p))
|
|
@@ -942,11 +942,14 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn,
|
|
|
* We pinned the head page for hwpoison handling,
|
|
|
* now we split the thp and we are interested in
|
|
|
* the hwpoisoned raw page, so move the refcount
|
|
|
- * to it.
|
|
|
+ * to it. Similarly, page lock is shifted.
|
|
|
*/
|
|
|
if (hpage != p) {
|
|
|
put_page(hpage);
|
|
|
get_page(p);
|
|
|
+ lock_page(p);
|
|
|
+ unlock_page(hpage);
|
|
|
+ *hpagep = p;
|
|
|
}
|
|
|
/* THP is split, so ppage should be the real poisoned page. */
|
|
|
ppage = p;
|
|
@@ -964,17 +967,11 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn,
|
|
|
if (kill)
|
|
|
collect_procs(ppage, &tokill);
|
|
|
|
|
|
- if (hpage != ppage)
|
|
|
- lock_page(ppage);
|
|
|
-
|
|
|
ret = try_to_unmap(ppage, ttu);
|
|
|
if (ret != SWAP_SUCCESS)
|
|
|
printk(KERN_ERR "MCE %#lx: failed to unmap page (mapcount=%d)\n",
|
|
|
pfn, page_mapcount(ppage));
|
|
|
|
|
|
- if (hpage != ppage)
|
|
|
- unlock_page(ppage);
|
|
|
-
|
|
|
/*
|
|
|
* Now that the dirty bit has been propagated to the
|
|
|
* struct page and all unmaps done we can decide if
|
|
@@ -1193,8 +1190,12 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
|
|
|
/*
|
|
|
* Now take care of user space mappings.
|
|
|
* Abort on fail: __delete_from_page_cache() assumes unmapped page.
|
|
|
+ *
|
|
|
+ * When the raw error page is thp tail page, hpage points to the raw
|
|
|
+ * page after thp split.
|
|
|
*/
|
|
|
- if (hwpoison_user_mappings(p, pfn, trapno, flags) != SWAP_SUCCESS) {
|
|
|
+ if (hwpoison_user_mappings(p, pfn, trapno, flags, &hpage)
|
|
|
+ != SWAP_SUCCESS) {
|
|
|
printk(KERN_ERR "MCE %#lx: cannot unmap page, give up\n", pfn);
|
|
|
res = -EBUSY;
|
|
|
goto out;
|