Commit ca5e8632 authored by Lorenzo Stoakes's avatar Lorenzo Stoakes Committed by Andrew Morton
Browse files

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: default avatarLorenzo Stoakes <lstoakes@gmail.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> (for arm64)
Acked-by: default avatarDavid Hildenbrand <david@redhat.com>
Reviewed-by: Janosch Frank <frankja@linux.ibm.com> (for s390)
Reviewed-by: default avatarChristoph 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: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 0b295316
Loading
Loading
Loading
Loading
+9 −8
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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;
}
+1 −1
Original line number Diff line number Diff line
@@ -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;
+31 −3
Original line number Diff line number Diff line
@@ -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,
@@ -2363,11 +2366,36 @@ extern int __access_remote_vm(struct mm_struct *mm, unsigned long addr,
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);
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,
+5 −8
Original line number Diff line number Diff line
@@ -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;

Loading