diff options
Diffstat (limited to 'mm/mmap.c')
| -rw-r--r-- | mm/mmap.c | 125 | 
1 files changed, 65 insertions, 60 deletions
| diff --git a/mm/mmap.c b/mm/mmap.c index aa632ade2be7..c739d6db7193 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -41,6 +41,7 @@  #include <linux/notifier.h>  #include <linux/memory.h>  #include <linux/printk.h> +#include <linux/userfaultfd_k.h>  #include <asm/uaccess.h>  #include <asm/cacheflush.h> @@ -919,7 +920,8 @@ again:			remove_next = 1 + (end > next->vm_end);   * per-vma resources, so we don't attempt to merge those.   */  static inline int is_mergeable_vma(struct vm_area_struct *vma, -			struct file *file, unsigned long vm_flags) +				struct file *file, unsigned long vm_flags, +				struct vm_userfaultfd_ctx vm_userfaultfd_ctx)  {  	/*  	 * VM_SOFTDIRTY should not prevent from VMA merging, if we @@ -935,6 +937,8 @@ static inline int is_mergeable_vma(struct vm_area_struct *vma,  		return 0;  	if (vma->vm_ops && vma->vm_ops->close)  		return 0; +	if (!is_mergeable_vm_userfaultfd_ctx(vma, vm_userfaultfd_ctx)) +		return 0;  	return 1;  } @@ -965,9 +969,11 @@ static inline int is_mergeable_anon_vma(struct anon_vma *anon_vma1,   */  static int  can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, -	struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) +		     struct anon_vma *anon_vma, struct file *file, +		     pgoff_t vm_pgoff, +		     struct vm_userfaultfd_ctx vm_userfaultfd_ctx)  { -	if (is_mergeable_vma(vma, file, vm_flags) && +	if (is_mergeable_vma(vma, file, vm_flags, vm_userfaultfd_ctx) &&  	    is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) {  		if (vma->vm_pgoff == vm_pgoff)  			return 1; @@ -984,9 +990,11 @@ can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags,   */  static int  can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, -	struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) +		    struct anon_vma *anon_vma, struct file *file, +		    pgoff_t vm_pgoff, +		    struct vm_userfaultfd_ctx vm_userfaultfd_ctx)  { -	if (is_mergeable_vma(vma, file, vm_flags) && +	if (is_mergeable_vma(vma, file, vm_flags, vm_userfaultfd_ctx) &&  	    is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) {  		pgoff_t vm_pglen;  		vm_pglen = vma_pages(vma); @@ -1029,7 +1037,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,  			struct vm_area_struct *prev, unsigned long addr,  			unsigned long end, unsigned long vm_flags,  			struct anon_vma *anon_vma, struct file *file, -			pgoff_t pgoff, struct mempolicy *policy) +			pgoff_t pgoff, struct mempolicy *policy, +			struct vm_userfaultfd_ctx vm_userfaultfd_ctx)  {  	pgoff_t pglen = (end - addr) >> PAGE_SHIFT;  	struct vm_area_struct *area, *next; @@ -1056,14 +1065,17 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,  	if (prev && prev->vm_end == addr &&  			mpol_equal(vma_policy(prev), policy) &&  			can_vma_merge_after(prev, vm_flags, -						anon_vma, file, pgoff)) { +					    anon_vma, file, pgoff, +					    vm_userfaultfd_ctx)) {  		/*  		 * OK, it can.  Can we now merge in the successor as well?  		 */  		if (next && end == next->vm_start &&  				mpol_equal(policy, vma_policy(next)) &&  				can_vma_merge_before(next, vm_flags, -					anon_vma, file, pgoff+pglen) && +						     anon_vma, file, +						     pgoff+pglen, +						     vm_userfaultfd_ctx) &&  				is_mergeable_anon_vma(prev->anon_vma,  						      next->anon_vma, NULL)) {  							/* cases 1, 6 */ @@ -1084,7 +1096,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,  	if (next && end == next->vm_start &&  			mpol_equal(policy, vma_policy(next)) &&  			can_vma_merge_before(next, vm_flags, -					anon_vma, file, pgoff+pglen)) { +					     anon_vma, file, pgoff+pglen, +					     vm_userfaultfd_ctx)) {  		if (prev && addr < prev->vm_end)	/* case 4 */  			err = vma_adjust(prev, prev->vm_start,  				addr, prev->vm_pgoff, NULL); @@ -1247,14 +1260,12 @@ static inline int mlock_future_check(struct mm_struct *mm,  /*   * The caller must hold down_write(¤t->mm->mmap_sem).   */ - -unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, +unsigned long do_mmap(struct file *file, unsigned long addr,  			unsigned long len, unsigned long prot, -			unsigned long flags, unsigned long pgoff, -			unsigned long *populate) +			unsigned long flags, vm_flags_t vm_flags, +			unsigned long pgoff, unsigned long *populate)  {  	struct mm_struct *mm = current->mm; -	vm_flags_t vm_flags;  	*populate = 0; @@ -1268,7 +1279,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,  	 *  mounted, in which case we dont add PROT_EXEC.)  	 */  	if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC)) -		if (!(file && (file->f_path.mnt->mnt_flags & MNT_NOEXEC))) +		if (!(file && path_noexec(&file->f_path)))  			prot |= PROT_EXEC;  	if (!(flags & MAP_FIXED)) @@ -1298,7 +1309,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,  	 * to. we assume access permissions have been handled by the open  	 * of the memory object, so we don't do any here.  	 */ -	vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) | +	vm_flags |= calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) |  			mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;  	if (flags & MAP_LOCKED) @@ -1337,7 +1348,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,  		case MAP_PRIVATE:  			if (!(file->f_mode & FMODE_READ))  				return -EACCES; -			if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) { +			if (path_noexec(&file->f_path)) {  				if (vm_flags & VM_EXEC)  					return -EPERM;  				vm_flags &= ~VM_MAYEXEC; @@ -1570,8 +1581,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr,  	/*  	 * Can we just expand an old mapping?  	 */ -	vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, -			NULL); +	vma = vma_merge(mm, prev, addr, addr + len, vm_flags, +			NULL, file, pgoff, NULL, NULL_VM_UFFD_CTX);  	if (vma)  		goto out; @@ -2442,7 +2453,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,  	      unsigned long addr, int new_below)  {  	struct vm_area_struct *new; -	int err = -ENOMEM; +	int err;  	if (is_vm_hugetlb_page(vma) && (addr &  					~(huge_page_mask(hstate_vma(vma))))) @@ -2450,7 +2461,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,  	new = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);  	if (!new) -		goto out_err; +		return -ENOMEM;  	/* most fields are the same, copy all, and then fixup */  	*new = *vma; @@ -2498,7 +2509,6 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,  	mpol_put(vma_policy(new));   out_free_vma:  	kmem_cache_free(vm_area_cachep, new); - out_err:  	return err;  } @@ -2757,7 +2767,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)  	/* Can we just expand an old private anonymous mapping? */  	vma = vma_merge(mm, prev, addr, addr + len, flags, -					NULL, NULL, pgoff, NULL); +			NULL, NULL, pgoff, NULL, NULL_VM_UFFD_CTX);  	if (vma)  		goto out; @@ -2859,6 +2869,13 @@ int insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vma)  	struct vm_area_struct *prev;  	struct rb_node **rb_link, *rb_parent; +	if (find_vma_links(mm, vma->vm_start, vma->vm_end, +			   &prev, &rb_link, &rb_parent)) +		return -ENOMEM; +	if ((vma->vm_flags & VM_ACCOUNT) && +	     security_vm_enough_memory_mm(mm, vma_pages(vma))) +		return -ENOMEM; +  	/*  	 * The vm_pgoff of a purely anonymous vma should be irrelevant  	 * until its first write fault, when page's anon_vma and index @@ -2871,16 +2888,10 @@ int insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vma)  	 * using the existing file pgoff checks and manipulations.  	 * Similarly in do_mmap_pgoff and in do_brk.  	 */ -	if (!vma->vm_file) { +	if (vma_is_anonymous(vma)) {  		BUG_ON(vma->anon_vma);  		vma->vm_pgoff = vma->vm_start >> PAGE_SHIFT;  	} -	if (find_vma_links(mm, vma->vm_start, vma->vm_end, -			   &prev, &rb_link, &rb_parent)) -		return -ENOMEM; -	if ((vma->vm_flags & VM_ACCOUNT) && -	     security_vm_enough_memory_mm(mm, vma_pages(vma))) -		return -ENOMEM;  	vma_link(mm, vma, prev, rb_link, rb_parent);  	return 0; @@ -2905,7 +2916,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,  	 * If anonymous vma has not yet been faulted, update new pgoff  	 * to match new location, to increase its chance of merging.  	 */ -	if (unlikely(!vma->vm_file && !vma->anon_vma)) { +	if (unlikely(vma_is_anonymous(vma) && !vma->anon_vma)) {  		pgoff = addr >> PAGE_SHIFT;  		faulted_in_anon_vma = false;  	} @@ -2913,7 +2924,8 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,  	if (find_vma_links(mm, addr, addr + len, &prev, &rb_link, &rb_parent))  		return NULL;	/* should never get here */  	new_vma = vma_merge(mm, prev, addr, addr + len, vma->vm_flags, -			vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma)); +			    vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), +			    vma->vm_userfaultfd_ctx);  	if (new_vma) {  		/*  		 * Source vma may have been merged into new_vma @@ -2938,30 +2950,31 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,  		*need_rmap_locks = (new_vma->vm_pgoff <= vma->vm_pgoff);  	} else {  		new_vma = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL); -		if (new_vma) { -			*new_vma = *vma; -			new_vma->vm_start = addr; -			new_vma->vm_end = addr + len; -			new_vma->vm_pgoff = pgoff; -			if (vma_dup_policy(vma, new_vma)) -				goto out_free_vma; -			INIT_LIST_HEAD(&new_vma->anon_vma_chain); -			if (anon_vma_clone(new_vma, vma)) -				goto out_free_mempol; -			if (new_vma->vm_file) -				get_file(new_vma->vm_file); -			if (new_vma->vm_ops && new_vma->vm_ops->open) -				new_vma->vm_ops->open(new_vma); -			vma_link(mm, new_vma, prev, rb_link, rb_parent); -			*need_rmap_locks = false; -		} +		if (!new_vma) +			goto out; +		*new_vma = *vma; +		new_vma->vm_start = addr; +		new_vma->vm_end = addr + len; +		new_vma->vm_pgoff = pgoff; +		if (vma_dup_policy(vma, new_vma)) +			goto out_free_vma; +		INIT_LIST_HEAD(&new_vma->anon_vma_chain); +		if (anon_vma_clone(new_vma, vma)) +			goto out_free_mempol; +		if (new_vma->vm_file) +			get_file(new_vma->vm_file); +		if (new_vma->vm_ops && new_vma->vm_ops->open) +			new_vma->vm_ops->open(new_vma); +		vma_link(mm, new_vma, prev, rb_link, rb_parent); +		*need_rmap_locks = false;  	}  	return new_vma; - out_free_mempol: +out_free_mempol:  	mpol_put(vma_policy(new_vma)); - out_free_vma: +out_free_vma:  	kmem_cache_free(vm_area_cachep, new_vma); +out:  	return NULL;  } @@ -3013,21 +3026,13 @@ static int special_mapping_fault(struct vm_area_struct *vma,  	pgoff_t pgoff;  	struct page **pages; -	/* -	 * special mappings have no vm_file, and in that case, the mm -	 * uses vm_pgoff internally. So we have to subtract it from here. -	 * We are allowed to do this because we are the mm; do not copy -	 * this code into drivers! -	 */ -	pgoff = vmf->pgoff - vma->vm_pgoff; -  	if (vma->vm_ops == &legacy_special_mapping_vmops)  		pages = vma->vm_private_data;  	else  		pages = ((struct vm_special_mapping *)vma->vm_private_data)->  			pages; -	for (; pgoff && *pages; ++pages) +	for (pgoff = vmf->pgoff; pgoff && *pages; ++pages)  		pgoff--;  	if (*pages) { |