forked from mirrors/linux
		
	mm/gup: remove vmas parameter from get_user_pages_remote()
The only instances of get_user_pages_remote() invocations which used the vmas parameter were for a single page which can instead simply look up the VMA directly. In particular:- - __update_ref_ctr() looked up the VMA but did nothing with it so we simply remove it. - __access_remote_vm() was already using vma_lookup() when the original lookup failed so by doing the lookup directly this also de-duplicates the code. We are able to perform these VMA operations as we already hold the mmap_lock in order to be able to call get_user_pages_remote(). As part of this work we add get_user_page_vma_remote() which abstracts the VMA lookup, error handling and decrementing the page reference count should the VMA lookup fail. This forms part of a broader set of patches intended to eliminate the vmas parameter altogether. [akpm@linux-foundation.org: avoid passing NULL to PTR_ERR] Link: https://lkml.kernel.org/r/d20128c849ecdbf4dd01cc828fcec32127ed939a.1684350871.git.lstoakes@gmail.com Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> (for arm64) Acked-by: David Hildenbrand <david@redhat.com> Reviewed-by: Janosch Frank <frankja@linux.ibm.com> (for s390) Reviewed-by: Christoph Hellwig <hch@lst.de> Cc: Christian König <christian.koenig@amd.com> Cc: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Jarkko Sakkinen <jarkko@kernel.org> Cc: Jason Gunthorpe <jgg@nvidia.com> Cc: Jens Axboe <axboe@kernel.dk> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Sakari Ailus <sakari.ailus@linux.intel.com> Cc: Sean Christopherson <seanjc@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									0b295316b3
								
							
						
					
					
						commit
						ca5e863233
					
				
					 10 changed files with 64 additions and 43 deletions
				
			
		|  | @ -416,10 +416,9 @@ long get_mte_ctrl(struct task_struct *task) | |||
| static int __access_remote_tags(struct mm_struct *mm, unsigned long addr, | ||||
| 				struct iovec *kiov, unsigned int gup_flags) | ||||
| { | ||||
| 	struct vm_area_struct *vma; | ||||
| 	void __user *buf = kiov->iov_base; | ||||
| 	size_t len = kiov->iov_len; | ||||
| 	int ret; | ||||
| 	int err = 0; | ||||
| 	int write = gup_flags & FOLL_WRITE; | ||||
| 
 | ||||
| 	if (!access_ok(buf, len)) | ||||
|  | @ -429,14 +428,16 @@ static int __access_remote_tags(struct mm_struct *mm, unsigned long addr, | |||
| 		return -EIO; | ||||
| 
 | ||||
| 	while (len) { | ||||
| 		struct vm_area_struct *vma; | ||||
| 		unsigned long tags, offset; | ||||
| 		void *maddr; | ||||
| 		struct page *page = NULL; | ||||
| 		struct page *page = get_user_page_vma_remote(mm, addr, | ||||
| 							     gup_flags, &vma); | ||||
| 
 | ||||
| 		ret = get_user_pages_remote(mm, addr, 1, gup_flags, &page, | ||||
| 					    &vma, NULL); | ||||
| 		if (ret <= 0) | ||||
| 		if (IS_ERR_OR_NULL(page)) { | ||||
| 			err = page == NULL ? -EIO : PTR_ERR(page); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Only copy tags if the page has been mapped as PROT_MTE | ||||
|  | @ -446,7 +447,7 @@ static int __access_remote_tags(struct mm_struct *mm, unsigned long addr, | |||
| 		 * was never mapped with PROT_MTE. | ||||
| 		 */ | ||||
| 		if (!(vma->vm_flags & VM_MTE)) { | ||||
| 			ret = -EOPNOTSUPP; | ||||
| 			err = -EOPNOTSUPP; | ||||
| 			put_page(page); | ||||
| 			break; | ||||
| 		} | ||||
|  | @ -479,7 +480,7 @@ static int __access_remote_tags(struct mm_struct *mm, unsigned long addr, | |||
| 	kiov->iov_len = buf - kiov->iov_base; | ||||
| 	if (!kiov->iov_len) { | ||||
| 		/* check for error accessing the tracee's address space */ | ||||
| 		if (ret <= 0) | ||||
| 		if (err) | ||||
| 			return -EIO; | ||||
| 		else | ||||
| 			return -EFAULT; | ||||
|  |  | |||
|  | @ -2777,7 +2777,7 @@ static struct page *get_map_page(struct kvm *kvm, u64 uaddr) | |||
| 
 | ||||
| 	mmap_read_lock(kvm->mm); | ||||
| 	get_user_pages_remote(kvm->mm, uaddr, 1, FOLL_WRITE, | ||||
| 			      &page, NULL, NULL); | ||||
| 			      &page, NULL); | ||||
| 	mmap_read_unlock(kvm->mm); | ||||
| 	return page; | ||||
| } | ||||
|  |  | |||
|  | @ -220,7 +220,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, | |||
| 	 */ | ||||
| 	mmap_read_lock(bprm->mm); | ||||
| 	ret = get_user_pages_remote(bprm->mm, pos, 1, gup_flags, | ||||
| 			&page, NULL, NULL); | ||||
| 			&page, NULL); | ||||
| 	mmap_read_unlock(bprm->mm); | ||||
| 	if (ret <= 0) | ||||
| 		return NULL; | ||||
|  |  | |||
|  | @ -2353,6 +2353,9 @@ static inline void unmap_shared_mapping_range(struct address_space *mapping, | |||
| 	unmap_mapping_range(mapping, holebegin, holelen, 0); | ||||
| } | ||||
| 
 | ||||
| static inline struct vm_area_struct *vma_lookup(struct mm_struct *mm, | ||||
| 						unsigned long addr); | ||||
| 
 | ||||
| extern int access_process_vm(struct task_struct *tsk, unsigned long addr, | ||||
| 		void *buf, int len, unsigned int gup_flags); | ||||
| extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, | ||||
|  | @ -2361,13 +2364,38 @@ extern int __access_remote_vm(struct mm_struct *mm, unsigned long addr, | |||
| 			      void *buf, int len, unsigned int gup_flags); | ||||
| 
 | ||||
| long get_user_pages_remote(struct mm_struct *mm, | ||||
| 			    unsigned long start, unsigned long nr_pages, | ||||
| 			    unsigned int gup_flags, struct page **pages, | ||||
| 			    struct vm_area_struct **vmas, int *locked); | ||||
| 			   unsigned long start, unsigned long nr_pages, | ||||
| 			   unsigned int gup_flags, struct page **pages, | ||||
| 			   int *locked); | ||||
| long pin_user_pages_remote(struct mm_struct *mm, | ||||
| 			   unsigned long start, unsigned long nr_pages, | ||||
| 			   unsigned int gup_flags, struct page **pages, | ||||
| 			   int *locked); | ||||
| 
 | ||||
| static inline struct page *get_user_page_vma_remote(struct mm_struct *mm, | ||||
| 						    unsigned long addr, | ||||
| 						    int gup_flags, | ||||
| 						    struct vm_area_struct **vmap) | ||||
| { | ||||
| 	struct page *page; | ||||
| 	struct vm_area_struct *vma; | ||||
| 	int got = get_user_pages_remote(mm, addr, 1, gup_flags, &page, NULL); | ||||
| 
 | ||||
| 	if (got < 0) | ||||
| 		return ERR_PTR(got); | ||||
| 	if (got == 0) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	vma = vma_lookup(mm, addr); | ||||
| 	if (WARN_ON_ONCE(!vma)) { | ||||
| 		put_page(page); | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 	} | ||||
| 
 | ||||
| 	*vmap = vma; | ||||
| 	return page; | ||||
| } | ||||
| 
 | ||||
| long get_user_pages(unsigned long start, unsigned long nr_pages, | ||||
| 		    unsigned int gup_flags, struct page **pages); | ||||
| long pin_user_pages(unsigned long start, unsigned long nr_pages, | ||||
|  |  | |||
|  | @ -365,7 +365,6 @@ __update_ref_ctr(struct mm_struct *mm, unsigned long vaddr, short d) | |||
| { | ||||
| 	void *kaddr; | ||||
| 	struct page *page; | ||||
| 	struct vm_area_struct *vma; | ||||
| 	int ret; | ||||
| 	short *ptr; | ||||
| 
 | ||||
|  | @ -373,7 +372,7 @@ __update_ref_ctr(struct mm_struct *mm, unsigned long vaddr, short d) | |||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	ret = get_user_pages_remote(mm, vaddr, 1, | ||||
| 			FOLL_WRITE, &page, &vma, NULL); | ||||
| 				    FOLL_WRITE, &page, NULL); | ||||
| 	if (unlikely(ret <= 0)) { | ||||
| 		/*
 | ||||
| 		 * We are asking for 1 page. If get_user_pages_remote() fails, | ||||
|  | @ -474,10 +473,9 @@ int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, | |||
| 	if (is_register) | ||||
| 		gup_flags |= FOLL_SPLIT_PMD; | ||||
| 	/* Read the page with vaddr into memory */ | ||||
| 	ret = get_user_pages_remote(mm, vaddr, 1, gup_flags, | ||||
| 				    &old_page, &vma, NULL); | ||||
| 	if (ret <= 0) | ||||
| 		return ret; | ||||
| 	old_page = get_user_page_vma_remote(mm, vaddr, gup_flags, &vma); | ||||
| 	if (IS_ERR_OR_NULL(old_page)) | ||||
| 		return old_page ? PTR_ERR(old_page) : 0; | ||||
| 
 | ||||
| 	ret = verify_opcode(old_page, vaddr, &opcode); | ||||
| 	if (ret <= 0) | ||||
|  | @ -2027,8 +2025,7 @@ static int is_trap_at_addr(struct mm_struct *mm, unsigned long vaddr) | |||
| 	 * but we treat this as a 'remote' access since it is | ||||
| 	 * essentially a kernel access to the memory. | ||||
| 	 */ | ||||
| 	result = get_user_pages_remote(mm, vaddr, 1, FOLL_FORCE, &page, | ||||
| 			NULL, NULL); | ||||
| 	result = get_user_pages_remote(mm, vaddr, 1, FOLL_FORCE, &page, NULL); | ||||
| 	if (result < 0) | ||||
| 		return result; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										12
									
								
								mm/gup.c
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								mm/gup.c
									
									
									
									
									
								
							|  | @ -2165,8 +2165,6 @@ static bool is_valid_gup_args(struct page **pages, struct vm_area_struct **vmas, | |||
|  * @pages:	array that receives pointers to the pages pinned. | ||||
|  *		Should be at least nr_pages long. Or NULL, if caller | ||||
|  *		only intends to ensure the pages are faulted in. | ||||
|  * @vmas:	array of pointers to vmas corresponding to each page. | ||||
|  *		Or NULL if the caller does not require them. | ||||
|  * @locked:	pointer to lock flag indicating whether lock is held and | ||||
|  *		subsequently whether VM_FAULT_RETRY functionality can be | ||||
|  *		utilised. Lock must initially be held. | ||||
|  | @ -2181,8 +2179,6 @@ static bool is_valid_gup_args(struct page **pages, struct vm_area_struct **vmas, | |||
|  * | ||||
|  * The caller is responsible for releasing returned @pages, via put_page(). | ||||
|  * | ||||
|  * @vmas are valid only as long as mmap_lock is held. | ||||
|  * | ||||
|  * Must be called with mmap_lock held for read or write. | ||||
|  * | ||||
|  * get_user_pages_remote walks a process's page tables and takes a reference | ||||
|  | @ -2219,15 +2215,15 @@ static bool is_valid_gup_args(struct page **pages, struct vm_area_struct **vmas, | |||
| long get_user_pages_remote(struct mm_struct *mm, | ||||
| 		unsigned long start, unsigned long nr_pages, | ||||
| 		unsigned int gup_flags, struct page **pages, | ||||
| 		struct vm_area_struct **vmas, int *locked) | ||||
| 		int *locked) | ||||
| { | ||||
| 	int local_locked = 1; | ||||
| 
 | ||||
| 	if (!is_valid_gup_args(pages, vmas, locked, &gup_flags, | ||||
| 	if (!is_valid_gup_args(pages, NULL, locked, &gup_flags, | ||||
| 			       FOLL_TOUCH | FOLL_REMOTE)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return __get_user_pages_locked(mm, start, nr_pages, pages, vmas, | ||||
| 	return __get_user_pages_locked(mm, start, nr_pages, pages, NULL, | ||||
| 				       locked ? locked : &local_locked, | ||||
| 				       gup_flags); | ||||
| } | ||||
|  | @ -2237,7 +2233,7 @@ EXPORT_SYMBOL(get_user_pages_remote); | |||
| long get_user_pages_remote(struct mm_struct *mm, | ||||
| 			   unsigned long start, unsigned long nr_pages, | ||||
| 			   unsigned int gup_flags, struct page **pages, | ||||
| 			   struct vm_area_struct **vmas, int *locked) | ||||
| 			   int *locked) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										20
									
								
								mm/memory.c
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								mm/memory.c
									
									
									
									
									
								
							|  | @ -5587,7 +5587,6 @@ EXPORT_SYMBOL_GPL(generic_access_phys); | |||
| int __access_remote_vm(struct mm_struct *mm, unsigned long addr, void *buf, | ||||
| 		       int len, unsigned int gup_flags) | ||||
| { | ||||
| 	struct vm_area_struct *vma; | ||||
| 	void *old_buf = buf; | ||||
| 	int write = gup_flags & FOLL_WRITE; | ||||
| 
 | ||||
|  | @ -5596,29 +5595,30 @@ int __access_remote_vm(struct mm_struct *mm, unsigned long addr, void *buf, | |||
| 
 | ||||
| 	/* ignore errors, just check how much was successfully transferred */ | ||||
| 	while (len) { | ||||
| 		int bytes, ret, offset; | ||||
| 		int bytes, offset; | ||||
| 		void *maddr; | ||||
| 		struct page *page = NULL; | ||||
| 		struct vm_area_struct *vma = NULL; | ||||
| 		struct page *page = get_user_page_vma_remote(mm, addr, | ||||
| 							     gup_flags, &vma); | ||||
| 
 | ||||
| 		ret = get_user_pages_remote(mm, addr, 1, | ||||
| 				gup_flags, &page, &vma, NULL); | ||||
| 		if (ret <= 0) { | ||||
| 		if (IS_ERR_OR_NULL(page)) { | ||||
| #ifndef CONFIG_HAVE_IOREMAP_PROT | ||||
| 			break; | ||||
| #else | ||||
| 			int res = 0; | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * Check if this is a VM_IO | VM_PFNMAP VMA, which | ||||
| 			 * we can access using slightly different code. | ||||
| 			 */ | ||||
| 			vma = vma_lookup(mm, addr); | ||||
| 			if (!vma) | ||||
| 				break; | ||||
| 			if (vma->vm_ops && vma->vm_ops->access) | ||||
| 				ret = vma->vm_ops->access(vma, addr, buf, | ||||
| 				res = vma->vm_ops->access(vma, addr, buf, | ||||
| 							  len, write); | ||||
| 			if (ret <= 0) | ||||
| 			if (res <= 0) | ||||
| 				break; | ||||
| 			bytes = ret; | ||||
| 			bytes = res; | ||||
| #endif | ||||
| 		} else { | ||||
| 			bytes = len; | ||||
|  |  | |||
|  | @ -2328,7 +2328,7 @@ int make_device_exclusive_range(struct mm_struct *mm, unsigned long start, | |||
| 
 | ||||
| 	npages = get_user_pages_remote(mm, start, npages, | ||||
| 				       FOLL_GET | FOLL_WRITE | FOLL_SPLIT_PMD, | ||||
| 				       pages, NULL, NULL); | ||||
| 				       pages, NULL); | ||||
| 	if (npages < 0) | ||||
| 		return npages; | ||||
| 
 | ||||
|  |  | |||
|  | @ -916,7 +916,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, | |||
| 	 */ | ||||
| 	mmap_read_lock(bprm->mm); | ||||
| 	ret = get_user_pages_remote(bprm->mm, pos, 1, | ||||
| 				    FOLL_FORCE, &page, NULL, NULL); | ||||
| 				    FOLL_FORCE, &page, NULL); | ||||
| 	mmap_read_unlock(bprm->mm); | ||||
| 	if (ret <= 0) | ||||
| 		return false; | ||||
|  |  | |||
|  | @ -61,8 +61,7 @@ static void async_pf_execute(struct work_struct *work) | |||
| 	 * access remotely. | ||||
| 	 */ | ||||
| 	mmap_read_lock(mm); | ||||
| 	get_user_pages_remote(mm, addr, 1, FOLL_WRITE, NULL, NULL, | ||||
| 			&locked); | ||||
| 	get_user_pages_remote(mm, addr, 1, FOLL_WRITE, NULL, &locked); | ||||
| 	if (locked) | ||||
| 		mmap_read_unlock(mm); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Lorenzo Stoakes
						Lorenzo Stoakes