Merge tag 's390-5.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
[linux-2.6-microblaze.git] / mm / gup.c
index 8651309..b947179 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -10,6 +10,7 @@
 #include <linux/rmap.h>
 #include <linux/swap.h>
 #include <linux/swapops.h>
+#include <linux/secretmem.h>
 
 #include <linux/sched/signal.h>
 #include <linux/rwsem.h>
@@ -855,6 +856,9 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address,
        struct follow_page_context ctx = { NULL };
        struct page *page;
 
+       if (vma_is_secretmem(vma))
+               return NULL;
+
        page = follow_page_mask(vma, address, foll_flags, &ctx);
        if (ctx.pgmap)
                put_dev_pagemap(ctx.pgmap);
@@ -988,6 +992,9 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)
        if ((gup_flags & FOLL_LONGTERM) && vma_is_fsdax(vma))
                return -EOPNOTSUPP;
 
+       if (vma_is_secretmem(vma))
+               return -EFAULT;
+
        if (write) {
                if (!(vm_flags & VM_WRITE)) {
                        if (!(gup_flags & FOLL_FORCE))
@@ -1500,6 +1507,67 @@ long populate_vma_page_range(struct vm_area_struct *vma,
                                NULL, NULL, locked);
 }
 
+/*
+ * faultin_vma_page_range() - populate (prefault) page tables inside the
+ *                           given VMA range readable/writable
+ *
+ * This takes care of mlocking the pages, too, if VM_LOCKED is set.
+ *
+ * @vma: target vma
+ * @start: start address
+ * @end: end address
+ * @write: whether to prefault readable or writable
+ * @locked: whether the mmap_lock is still held
+ *
+ * Returns either number of processed pages in the vma, or a negative error
+ * code on error (see __get_user_pages()).
+ *
+ * vma->vm_mm->mmap_lock must be held. The range must be page-aligned and
+ * covered by the VMA.
+ *
+ * If @locked is NULL, it may be held for read or write and will be unperturbed.
+ *
+ * If @locked is non-NULL, it must held for read only and may be released.  If
+ * it's released, *@locked will be set to 0.
+ */
+long faultin_vma_page_range(struct vm_area_struct *vma, unsigned long start,
+                           unsigned long end, bool write, int *locked)
+{
+       struct mm_struct *mm = vma->vm_mm;
+       unsigned long nr_pages = (end - start) / PAGE_SIZE;
+       int gup_flags;
+
+       VM_BUG_ON(!PAGE_ALIGNED(start));
+       VM_BUG_ON(!PAGE_ALIGNED(end));
+       VM_BUG_ON_VMA(start < vma->vm_start, vma);
+       VM_BUG_ON_VMA(end > vma->vm_end, vma);
+       mmap_assert_locked(mm);
+
+       /*
+        * FOLL_TOUCH: Mark page accessed and thereby young; will also mark
+        *             the page dirty with FOLL_WRITE -- which doesn't make a
+        *             difference with !FOLL_FORCE, because the page is writable
+        *             in the page table.
+        * FOLL_HWPOISON: Return -EHWPOISON instead of -EFAULT when we hit
+        *                a poisoned page.
+        * FOLL_POPULATE: Always populate memory with VM_LOCKONFAULT.
+        * !FOLL_FORCE: Require proper access permissions.
+        */
+       gup_flags = FOLL_TOUCH | FOLL_POPULATE | FOLL_MLOCK | FOLL_HWPOISON;
+       if (write)
+               gup_flags |= FOLL_WRITE;
+
+       /*
+        * We want to report -EINVAL instead of -EFAULT for any permission
+        * problems or incompatible mappings.
+        */
+       if (check_vma_flags(vma, gup_flags))
+               return -EINVAL;
+
+       return __get_user_pages(mm, start, nr_pages, gup_flags,
+                               NULL, NULL, locked);
+}
+
 /*
  * __mm_populate - populate and/or mlock pages within a range of address space.
  *
@@ -2112,6 +2180,11 @@ static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
                if (!head)
                        goto pte_unmap;
 
+               if (unlikely(page_is_secretmem(page))) {
+                       put_compound_head(head, 1, flags);
+                       goto pte_unmap;
+               }
+
                if (unlikely(pte_val(pte) != pte_val(*ptep))) {
                        put_compound_head(head, 1, flags);
                        goto pte_unmap;