mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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