diff options
Diffstat (limited to 'mm/madvise.c')
| -rw-r--r-- | mm/madvise.c | 44 | 
1 files changed, 41 insertions, 3 deletions
diff --git a/mm/madvise.c b/mm/madvise.c index dc5927c812d3..7a2abf0127ae 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -513,7 +513,43 @@ static long madvise_dontneed(struct vm_area_struct *vma,  	if (!can_madv_dontneed_vma(vma))  		return -EINVAL; -	userfaultfd_remove(vma, prev, start, end); +	if (!userfaultfd_remove(vma, start, end)) { +		*prev = NULL; /* mmap_sem has been dropped, prev is stale */ + +		down_read(¤t->mm->mmap_sem); +		vma = find_vma(current->mm, start); +		if (!vma) +			return -ENOMEM; +		if (start < vma->vm_start) { +			/* +			 * This "vma" under revalidation is the one +			 * with the lowest vma->vm_start where start +			 * is also < vma->vm_end. If start < +			 * vma->vm_start it means an hole materialized +			 * in the user address space within the +			 * virtual range passed to MADV_DONTNEED. +			 */ +			return -ENOMEM; +		} +		if (!can_madv_dontneed_vma(vma)) +			return -EINVAL; +		if (end > vma->vm_end) { +			/* +			 * Don't fail if end > vma->vm_end. If the old +			 * vma was splitted while the mmap_sem was +			 * released the effect of the concurrent +			 * operation may not cause MADV_DONTNEED to +			 * have an undefined result. There may be an +			 * adjacent next vma that we'll walk +			 * next. userfaultfd_remove() will generate an +			 * UFFD_EVENT_REMOVE repetition on the +			 * end-vma->vm_end range, but the manager can +			 * handle a repetition fine. +			 */ +			end = vma->vm_end; +		} +		VM_WARN_ON(start >= end); +	}  	zap_page_range(vma, start, end - start);  	return 0;  } @@ -554,8 +590,10 @@ static long madvise_remove(struct vm_area_struct *vma,  	 * mmap_sem.  	 */  	get_file(f); -	userfaultfd_remove(vma, prev, start, end); -	up_read(¤t->mm->mmap_sem); +	if (userfaultfd_remove(vma, start, end)) { +		/* mmap_sem was not released by userfaultfd_remove() */ +		up_read(¤t->mm->mmap_sem); +	}  	error = vfs_fallocate(f,  				FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,  				offset, end - start);  | 
