diff options
Diffstat (limited to 'mm/memory-failure.c')
| -rw-r--r-- | mm/memory-failure.c | 27 | 
1 files changed, 22 insertions, 5 deletions
diff --git a/mm/memory-failure.c b/mm/memory-failure.c index e4683459ab2..8f13b26a5b9 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1021,6 +1021,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags)  	struct page *hpage;  	int res;  	unsigned int nr_pages; +	unsigned long page_flags;  	if (!sysctl_memory_failure_recovery)  		panic("Memory failure from trap %d on page %lx", trapno, pfn); @@ -1129,6 +1130,15 @@ int memory_failure(unsigned long pfn, int trapno, int flags)  	lock_page(hpage);  	/* +	 * We use page flags to determine what action should be taken, but +	 * the flags can be modified by the error containment action.  One +	 * example is an mlocked page, where PG_mlocked is cleared by +	 * page_remove_rmap() in try_to_unmap_one(). So to determine page status +	 * correctly, we save a copy of the page flags at this time. +	 */ +	page_flags = p->flags; + +	/*  	 * unpoison always clear PG_hwpoison inside page lock  	 */  	if (!PageHWPoison(p)) { @@ -1186,12 +1196,19 @@ int memory_failure(unsigned long pfn, int trapno, int flags)  	}  	res = -EBUSY; -	for (ps = error_states;; ps++) { -		if ((p->flags & ps->mask) == ps->res) { -			res = page_action(ps, p, pfn); +	/* +	 * The first check uses the current page flags which may not have any +	 * relevant information. The second check with the saved page flagss is +	 * carried out only if the first check can't determine the page status. +	 */ +	for (ps = error_states;; ps++) +		if ((p->flags & ps->mask) == ps->res)  			break; -		} -	} +	if (!ps->mask) +		for (ps = error_states;; ps++) +			if ((page_flags & ps->mask) == ps->res) +				break; +	res = page_action(ps, p, pfn);  out:  	unlock_page(hpage);  	return res;  |