diff options
Diffstat (limited to 'mm/memory-failure.c')
| -rw-r--r-- | mm/memory-failure.c | 36 | 
1 files changed, 21 insertions, 15 deletions
diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 7ef849da8278..3151c87dff73 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -199,7 +199,6 @@ struct to_kill {  	struct task_struct *tsk;  	unsigned long addr;  	short size_shift; -	char addr_valid;  };  /* @@ -324,22 +323,27 @@ static void add_to_kill(struct task_struct *tsk, struct page *p,  		}  	}  	tk->addr = page_address_in_vma(p, vma); -	tk->addr_valid = 1;  	if (is_zone_device_page(p))  		tk->size_shift = dev_pagemap_mapping_shift(p, vma);  	else  		tk->size_shift = compound_order(compound_head(p)) + PAGE_SHIFT;  	/* -	 * In theory we don't have to kill when the page was -	 * munmaped. But it could be also a mremap. Since that's -	 * likely very rare kill anyways just out of paranoia, but use -	 * a SIGKILL because the error is not contained anymore. +	 * Send SIGKILL if "tk->addr == -EFAULT". Also, as +	 * "tk->size_shift" is always non-zero for !is_zone_device_page(), +	 * so "tk->size_shift == 0" effectively checks no mapping on +	 * ZONE_DEVICE. Indeed, when a devdax page is mmapped N times +	 * to a process' address space, it's possible not all N VMAs +	 * contain mappings for the page, but at least one VMA does. +	 * Only deliver SIGBUS with payload derived from the VMA that +	 * has a mapping for the page.  	 */ -	if (tk->addr == -EFAULT || tk->size_shift == 0) { +	if (tk->addr == -EFAULT) {  		pr_info("Memory failure: Unable to find user space address %lx in %s\n",  			page_to_pfn(p), tsk->comm); -		tk->addr_valid = 0; +	} else if (tk->size_shift == 0) { +		kfree(tk); +		return;  	}  	get_task_struct(tsk);  	tk->tsk = tsk; @@ -366,7 +370,7 @@ static void kill_procs(struct list_head *to_kill, int forcekill, bool fail,  			 * make sure the process doesn't catch the  			 * signal and then access the memory. Just kill it.  			 */ -			if (fail || tk->addr_valid == 0) { +			if (fail || tk->addr == -EFAULT) {  				pr_err("Memory failure: %#lx: forcibly killing %s:%d because of failure to unmap corrupted page\n",  				       pfn, tk->tsk->comm, tk->tsk->pid);  				do_send_sig_info(SIGKILL, SEND_SIG_PRIV, @@ -1253,17 +1257,19 @@ int memory_failure(unsigned long pfn, int flags)  	if (!sysctl_memory_failure_recovery)  		panic("Memory failure on page %lx", pfn); -	if (!pfn_valid(pfn)) { +	p = pfn_to_online_page(pfn); +	if (!p) { +		if (pfn_valid(pfn)) { +			pgmap = get_dev_pagemap(pfn, NULL); +			if (pgmap) +				return memory_failure_dev_pagemap(pfn, flags, +								  pgmap); +		}  		pr_err("Memory failure: %#lx: memory outside kernel control\n",  			pfn);  		return -ENXIO;  	} -	pgmap = get_dev_pagemap(pfn, NULL); -	if (pgmap) -		return memory_failure_dev_pagemap(pfn, flags, pgmap); - -	p = pfn_to_page(pfn);  	if (PageHuge(p))  		return memory_failure_hugetlb(pfn, flags);  	if (TestSetPageHWPoison(p)) {  | 
