diff options
Diffstat (limited to 'mm/memory.c')
| -rw-r--r-- | mm/memory.c | 14 | 
1 files changed, 12 insertions, 2 deletions
diff --git a/mm/memory.c b/mm/memory.c index 635451abc8f7..8132787ae4d5 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3404,8 +3404,18 @@ static int __handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,  	if (unlikely(pmd_none(*pmd)) &&  	    unlikely(__pte_alloc(mm, vma, pmd, address)))  		return VM_FAULT_OOM; -	/* if an huge pmd materialized from under us just retry later */ -	if (unlikely(pmd_trans_huge(*pmd) || pmd_devmap(*pmd))) +	/* +	 * If a huge pmd materialized under us just retry later.  Use +	 * pmd_trans_unstable() instead of pmd_trans_huge() to ensure the pmd +	 * didn't become pmd_trans_huge under us and then back to pmd_none, as +	 * a result of MADV_DONTNEED running immediately after a huge pmd fault +	 * in a different thread of this mm, in turn leading to a misleading +	 * pmd_trans_huge() retval.  All we have to ensure is that it is a +	 * regular pmd that we can walk with pte_offset_map() and we can do that +	 * through an atomic read in C, which is what pmd_trans_unstable() +	 * provides. +	 */ +	if (unlikely(pmd_trans_unstable(pmd) || pmd_devmap(*pmd)))  		return 0;  	/*  	 * A regular pmd is established and it can't morph into a huge pmd  | 
