]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
mm/gup: remove vmas parameter from get_user_pages_remote()
authorLorenzo Stoakes <lstoakes@gmail.com>
Wed, 17 May 2023 19:25:39 +0000 (20:25 +0100)
committerAndrew Morton <akpm@linux-foundation.org>
Fri, 9 Jun 2023 23:25:26 +0000 (16:25 -0700)
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>
arch/arm64/kernel/mte.c
arch/s390/kvm/interrupt.c
fs/exec.c
include/linux/mm.h
kernel/events/uprobes.c
mm/gup.c
mm/memory.c
mm/rmap.c
security/tomoyo/domain.c
virt/kvm/async_pf.c

index 7e89968bd28244ced303586a35bb2e81dc1ffad6..4c5ef9b2006500698c0257bd7899362532613eea 100644 (file)
@@ -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;
index da6dac36e959f2e4720995bf02d14b3c5995b65e..9bd0a873f3b15f984bb06e0ceebcc6b7343665fc 100644 (file)
@@ -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;
 }
index a466e797c8e2e67577be128e1e95775b00970d95..25c65b64544b26c6ac126f0d3210a57e1d8a9ab7 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -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;
index cf17ffdf4fbf6cb269b4582d5cb9b15094428ee8..fcbfb961b49f4b9c612022b2982c51bdbeff273a 100644 (file)
@@ -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,
index 59887c69d54c11bd3555d862a0085b759e26685d..607d742caa61b750623e00a63d8b842e2c5ccbea 100644 (file)
@@ -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 @@ retry:
        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;
 
index edf0fe2695b04ee9b4af04967cb3b34ff19c9e1a..764bf0c208272541bbbe60eaae8f62fb0a04a843 100644 (file)
--- a/mm/gup.c
+++ b/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;
 }
index f69fbc2511984e224ab31f38a6315404b5d902b1..4dd09f930c61443f6ae5db1d69e999902dc5fce0 100644 (file)
@@ -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;
index 19392e090bec6a7ca771bb964bd51326aa77ddce..cd918cb9a4311b3bc16f882f8ddb562a8673d4d9 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -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;
 
index 31af29f669d24068875917f5adfad8948143594e..ac20c0bdff9dc1194d6e6c437be7fc26c007c264 100644 (file)
@@ -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;
index 9bfe1d6f6529a2ff67094b66688a697e4605f11b..e033c79d528e0040e88fdd02f6eec3c6d6c00213 100644 (file)
@@ -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);