gup: Turn fault_in_pages_{readable,writeable} into fault_in_{readable,writeable}
authorAndreas Gruenbacher <agruenba@redhat.com>
Mon, 2 Aug 2021 11:44:20 +0000 (13:44 +0200)
committerAndreas Gruenbacher <agruenba@redhat.com>
Mon, 18 Oct 2021 14:33:03 +0000 (16:33 +0200)
Turn fault_in_pages_{readable,writeable} into versions that return the
number of bytes not faulted in, similar to copy_to_user, instead of
returning a non-zero value when any of the requested pages couldn't be
faulted in.  This supports the existing users that require all pages to
be faulted in as well as new users that are happy if any pages can be
faulted in.

Rename the functions to fault_in_{readable,writeable} to make sure
this change doesn't silently break things.

Neither of these functions is entirely trivial and it doesn't seem
useful to inline them, so move them to mm/gup.c.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
arch/powerpc/kernel/kvm.c
arch/powerpc/kernel/signal_32.c
arch/powerpc/kernel/signal_64.c
arch/x86/kernel/fpu/signal.c
drivers/gpu/drm/armada/armada_gem.c
fs/btrfs/ioctl.c
include/linux/pagemap.h
lib/iov_iter.c
mm/filemap.c
mm/gup.c

index d89cf802d9aa771788b550cf9359571b77c5a4a8..6568823cf3063086f02f050d4e7a4f66a4a672c6 100644 (file)
@@ -669,7 +669,8 @@ static void __init kvm_use_magic_page(void)
        on_each_cpu(kvm_map_magic_page, &features, 1);
 
        /* Quick self-test to see if the mapping works */
-       if (fault_in_pages_readable((const char *)KVM_MAGIC_PAGE, sizeof(u32))) {
+       if (fault_in_readable((const char __user *)KVM_MAGIC_PAGE,
+                             sizeof(u32))) {
                kvm_patching_worked = false;
                return;
        }
index 0608581967f093187cf2970a46033ec9dedfa5f8..38c3eae40c145090cb8fa692da97d8356b494437 100644 (file)
@@ -1048,7 +1048,7 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
        if (new_ctx == NULL)
                return 0;
        if (!access_ok(new_ctx, ctx_size) ||
-           fault_in_pages_readable((u8 __user *)new_ctx, ctx_size))
+           fault_in_readable((char __user *)new_ctx, ctx_size))
                return -EFAULT;
 
        /*
@@ -1237,7 +1237,7 @@ SYSCALL_DEFINE3(debug_setcontext, struct ucontext __user *, ctx,
 #endif
 
        if (!access_ok(ctx, sizeof(*ctx)) ||
-           fault_in_pages_readable((u8 __user *)ctx, sizeof(*ctx)))
+           fault_in_readable((char __user *)ctx, sizeof(*ctx)))
                return -EFAULT;
 
        /*
index 1831bba0582e10dd0ab6ccafed3b1a6a045f1246..9f471b4a11e3951bb5e41114a613eb926919909c 100644 (file)
@@ -688,7 +688,7 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
        if (new_ctx == NULL)
                return 0;
        if (!access_ok(new_ctx, ctx_size) ||
-           fault_in_pages_readable((u8 __user *)new_ctx, ctx_size))
+           fault_in_readable((char __user *)new_ctx, ctx_size))
                return -EFAULT;
 
        /*
index fa17a27390ab0128a136592a2cc4dbde6ac93708..164c9643470413610dc8e280cd5bf39cdd89fb16 100644 (file)
@@ -205,7 +205,7 @@ retry:
        fpregs_unlock();
 
        if (ret) {
-               if (!fault_in_pages_writeable(buf_fx, fpu_user_xstate_size))
+               if (!fault_in_writeable(buf_fx, fpu_user_xstate_size))
                        goto retry;
                return -EFAULT;
        }
@@ -278,10 +278,9 @@ retry:
                if (ret != -EFAULT)
                        return -EINVAL;
 
-               ret = fault_in_pages_readable(buf, size);
-               if (!ret)
+               if (!fault_in_readable(buf, size))
                        goto retry;
-               return ret;
+               return -EFAULT;
        }
 
        /*
index 21909642ee4ca9ec3d4529afca3ff5289018dc08..8fbb25913327c9551e20744176adeea0b30eb029 100644 (file)
@@ -336,7 +336,7 @@ int armada_gem_pwrite_ioctl(struct drm_device *dev, void *data,
        struct drm_armada_gem_pwrite *args = data;
        struct armada_gem_object *dobj;
        char __user *ptr;
-       int ret;
+       int ret = 0;
 
        DRM_DEBUG_DRIVER("handle %u off %u size %u ptr 0x%llx\n",
                args->handle, args->offset, args->size, args->ptr);
@@ -349,9 +349,8 @@ int armada_gem_pwrite_ioctl(struct drm_device *dev, void *data,
        if (!access_ok(ptr, args->size))
                return -EFAULT;
 
-       ret = fault_in_pages_readable(ptr, args->size);
-       if (ret)
-               return ret;
+       if (fault_in_readable(ptr, args->size))
+               return -EFAULT;
 
        dobj = armada_gem_object_lookup(file, args->handle);
        if (dobj == NULL)
index cc61813213d83a726738205c5ad7144e2103fe57..c0739f0af6340781d0f26e3897429576d9adc069 100644 (file)
@@ -2261,9 +2261,8 @@ static noinline int search_ioctl(struct inode *inode,
        key.offset = sk->min_offset;
 
        while (1) {
-               ret = fault_in_pages_writeable(ubuf + sk_offset,
-                                              *buf_size - sk_offset);
-               if (ret)
+               ret = -EFAULT;
+               if (fault_in_writeable(ubuf + sk_offset, *buf_size - sk_offset))
                        break;
 
                ret = btrfs_search_forward(root, &key, path, sk->min_transid);
index 62db6b0176b95782666e5e1a7369dea10715ed6d..9fe94f7a4f7e3525809baec686294df0c30703cd 100644 (file)
@@ -733,61 +733,10 @@ int wait_on_page_private_2_killable(struct page *page);
 extern void add_page_wait_queue(struct page *page, wait_queue_entry_t *waiter);
 
 /*
- * Fault everything in given userspace address range in.
+ * Fault in userspace address range.
  */
-static inline int fault_in_pages_writeable(char __user *uaddr, size_t size)
-{
-       char __user *end = uaddr + size - 1;
-
-       if (unlikely(size == 0))
-               return 0;
-
-       if (unlikely(uaddr > end))
-               return -EFAULT;
-       /*
-        * Writing zeroes into userspace here is OK, because we know that if
-        * the zero gets there, we'll be overwriting it.
-        */
-       do {
-               if (unlikely(__put_user(0, uaddr) != 0))
-                       return -EFAULT;
-               uaddr += PAGE_SIZE;
-       } while (uaddr <= end);
-
-       /* Check whether the range spilled into the next page. */
-       if (((unsigned long)uaddr & PAGE_MASK) ==
-                       ((unsigned long)end & PAGE_MASK))
-               return __put_user(0, end);
-
-       return 0;
-}
-
-static inline int fault_in_pages_readable(const char __user *uaddr, size_t size)
-{
-       volatile char c;
-       const char __user *end = uaddr + size - 1;
-
-       if (unlikely(size == 0))
-               return 0;
-
-       if (unlikely(uaddr > end))
-               return -EFAULT;
-
-       do {
-               if (unlikely(__get_user(c, uaddr) != 0))
-                       return -EFAULT;
-               uaddr += PAGE_SIZE;
-       } while (uaddr <= end);
-
-       /* Check whether the range spilled into the next page. */
-       if (((unsigned long)uaddr & PAGE_MASK) ==
-                       ((unsigned long)end & PAGE_MASK)) {
-               return __get_user(c, end);
-       }
-
-       (void)c;
-       return 0;
-}
+size_t fault_in_writeable(char __user *uaddr, size_t size);
+size_t fault_in_readable(const char __user *uaddr, size_t size);
 
 int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
                                pgoff_t index, gfp_t gfp_mask);
index 60b5e6edfbaa77865bb7ec763293786b6e91b54f..c88908f0f13824bf188586808e830b71d34f0704 100644 (file)
@@ -191,7 +191,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b
        buf = iov->iov_base + skip;
        copy = min(bytes, iov->iov_len - skip);
 
-       if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_pages_writeable(buf, copy)) {
+       if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_writeable(buf, copy)) {
                kaddr = kmap_atomic(page);
                from = kaddr + offset;
 
@@ -275,7 +275,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t
        buf = iov->iov_base + skip;
        copy = min(bytes, iov->iov_len - skip);
 
-       if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_pages_readable(buf, copy)) {
+       if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_readable(buf, copy)) {
                kaddr = kmap_atomic(page);
                to = kaddr + offset;
 
@@ -446,13 +446,11 @@ int iov_iter_fault_in_readable(const struct iov_iter *i, size_t bytes)
                        bytes = i->count;
                for (p = i->iov, skip = i->iov_offset; bytes; p++, skip = 0) {
                        size_t len = min(bytes, p->iov_len - skip);
-                       int err;
 
                        if (unlikely(!len))
                                continue;
-                       err = fault_in_pages_readable(p->iov_base + skip, len);
-                       if (unlikely(err))
-                               return err;
+                       if (fault_in_readable(p->iov_base + skip, len))
+                               return -EFAULT;
                        bytes -= len;
                }
        }
index dae481293b5d95742bf440351d0a79e5f95e9384..ff34f4087f87aa38cf6ab28fcc5a21976d52137f 100644 (file)
@@ -90,7 +90,7 @@
  *      ->lock_page            (filemap_fault, access_process_vm)
  *
  *  ->i_rwsem                  (generic_perform_write)
- *    ->mmap_lock              (fault_in_pages_readable->do_page_fault)
+ *    ->mmap_lock              (fault_in_readable->do_page_fault)
  *
  *  bdi->wb.list_lock
  *    sb_lock                  (fs/fs-writeback.c)
index 886d6148d3d03afbce35cdc7352c928d00c003bc..a7efb027d6cfc5943011f457963977368b1a29de 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -1656,6 +1656,78 @@ finish_or_fault:
 }
 #endif /* !CONFIG_MMU */
 
+/**
+ * fault_in_writeable - fault in userspace address range for writing
+ * @uaddr: start of address range
+ * @size: size of address range
+ *
+ * Returns the number of bytes not faulted in (like copy_to_user() and
+ * copy_from_user()).
+ */
+size_t fault_in_writeable(char __user *uaddr, size_t size)
+{
+       char __user *start = uaddr, *end;
+
+       if (unlikely(size == 0))
+               return 0;
+       if (!PAGE_ALIGNED(uaddr)) {
+               if (unlikely(__put_user(0, uaddr) != 0))
+                       return size;
+               uaddr = (char __user *)PAGE_ALIGN((unsigned long)uaddr);
+       }
+       end = (char __user *)PAGE_ALIGN((unsigned long)start + size);
+       if (unlikely(end < start))
+               end = NULL;
+       while (uaddr != end) {
+               if (unlikely(__put_user(0, uaddr) != 0))
+                       goto out;
+               uaddr += PAGE_SIZE;
+       }
+
+out:
+       if (size > uaddr - start)
+               return size - (uaddr - start);
+       return 0;
+}
+EXPORT_SYMBOL(fault_in_writeable);
+
+/**
+ * fault_in_readable - fault in userspace address range for reading
+ * @uaddr: start of user address range
+ * @size: size of user address range
+ *
+ * Returns the number of bytes not faulted in (like copy_to_user() and
+ * copy_from_user()).
+ */
+size_t fault_in_readable(const char __user *uaddr, size_t size)
+{
+       const char __user *start = uaddr, *end;
+       volatile char c;
+
+       if (unlikely(size == 0))
+               return 0;
+       if (!PAGE_ALIGNED(uaddr)) {
+               if (unlikely(__get_user(c, uaddr) != 0))
+                       return size;
+               uaddr = (const char __user *)PAGE_ALIGN((unsigned long)uaddr);
+       }
+       end = (const char __user *)PAGE_ALIGN((unsigned long)start + size);
+       if (unlikely(end < start))
+               end = NULL;
+       while (uaddr != end) {
+               if (unlikely(__get_user(c, uaddr) != 0))
+                       goto out;
+               uaddr += PAGE_SIZE;
+       }
+
+out:
+       (void)c;
+       if (size > uaddr - start)
+               return size - (uaddr - start);
+       return 0;
+}
+EXPORT_SYMBOL(fault_in_readable);
+
 /**
  * get_dump_page() - pin user page in memory while writing it to core dump
  * @addr: user address