Now we have the ability to specify a custom hook we can handle even very
customised behaviour.

As part of this change, we must also update remap_vmalloc_range_partial()
to optionally not update VMA flags. Other than then remap_vmalloc_range()
wrapper, vmcore is the only user of this function so we can simply go ahead
and add a parameter.

Signed-off-by: Lorenzo Stoakes <lorenzo.stoa...@oracle.com>
---
 arch/s390/kernel/crash_dump.c |  6 ++--
 fs/proc/vmcore.c              | 54 +++++++++++++++++++++++------------
 include/linux/vmalloc.h       | 10 +++----
 mm/vmalloc.c                  | 16 +++++++++--
 4 files changed, 57 insertions(+), 29 deletions(-)

diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c
index d4839de8ce9d..44d7902f7e41 100644
--- a/arch/s390/kernel/crash_dump.c
+++ b/arch/s390/kernel/crash_dump.c
@@ -186,7 +186,7 @@ static int remap_oldmem_pfn_range_kdump(struct 
vm_area_struct *vma,
 
        if (pfn < oldmem_data.size >> PAGE_SHIFT) {
                size_old = min(size, oldmem_data.size - (pfn << PAGE_SHIFT));
-               rc = remap_pfn_range(vma, from,
+               rc = remap_pfn_range_complete(vma, from,
                                     pfn + (oldmem_data.start >> PAGE_SHIFT),
                                     size_old, prot);
                if (rc || size == size_old)
@@ -195,7 +195,7 @@ static int remap_oldmem_pfn_range_kdump(struct 
vm_area_struct *vma,
                from += size_old;
                pfn += size_old >> PAGE_SHIFT;
        }
-       return remap_pfn_range(vma, from, pfn, size, prot);
+       return remap_pfn_range_complete(vma, from, pfn, size, prot);
 }
 
 /*
@@ -220,7 +220,7 @@ static int remap_oldmem_pfn_range_zfcpdump(struct 
vm_area_struct *vma,
                from += size_hsa;
                pfn += size_hsa >> PAGE_SHIFT;
        }
-       return remap_pfn_range(vma, from, pfn, size, prot);
+       return remap_pfn_range_complete(vma, from, pfn, size, prot);
 }
 
 /*
diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index f188bd900eb2..faf811ed9b15 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -254,7 +254,7 @@ int __weak remap_oldmem_pfn_range(struct vm_area_struct 
*vma,
                                  unsigned long size, pgprot_t prot)
 {
        prot = pgprot_encrypted(prot);
-       return remap_pfn_range(vma, from, pfn, size, prot);
+       return remap_pfn_range_complete(vma, from, pfn, size, prot);
 }
 
 /*
@@ -308,7 +308,7 @@ static int vmcoredd_mmap_dumps(struct vm_area_struct *vma, 
unsigned long dst,
                        tsz = min(offset + (u64)dump->size - start, (u64)size);
                        buf = dump->buf + start - offset;
                        if (remap_vmalloc_range_partial(vma, dst, buf, 0,
-                                                       tsz))
+                                                       tsz, /* set_vma= 
*/false))
                                return -EFAULT;
 
                        size -= tsz;
@@ -588,24 +588,15 @@ static int vmcore_remap_oldmem_pfn(struct vm_area_struct 
*vma,
        return ret;
 }
 
-static int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
+static int mmap_prepare_action_vmcore(struct vm_area_struct *vma)
 {
+       struct mmap_action action;
        size_t size = vma->vm_end - vma->vm_start;
        u64 start, end, len, tsz;
        struct vmcore_range *m;
 
        start = (u64)vma->vm_pgoff << PAGE_SHIFT;
        end = start + size;
-
-       if (size > vmcore_size || end > vmcore_size)
-               return -EINVAL;
-
-       if (vma->vm_flags & (VM_WRITE | VM_EXEC))
-               return -EPERM;
-
-       vm_flags_mod(vma, VM_MIXEDMAP, VM_MAYWRITE | VM_MAYEXEC);
-       vma->vm_ops = &vmcore_mmap_ops;
-
        len = 0;
 
        if (start < elfcorebuf_sz) {
@@ -613,8 +604,10 @@ static int mmap_vmcore(struct file *file, struct 
vm_area_struct *vma)
 
                tsz = min(elfcorebuf_sz - (size_t)start, size);
                pfn = __pa(elfcorebuf + start) >> PAGE_SHIFT;
-               if (remap_pfn_range(vma, vma->vm_start, pfn, tsz,
-                                   vma->vm_page_prot))
+
+               mmap_action_remap(&action, vma->vm_start, pfn, tsz,
+                                 vma->vm_page_prot);
+               if (mmap_action_complete(&action, vma))
                        return -EAGAIN;
                size -= tsz;
                start += tsz;
@@ -664,7 +657,7 @@ static int mmap_vmcore(struct file *file, struct 
vm_area_struct *vma)
                tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)start, size);
                kaddr = elfnotes_buf + start - elfcorebuf_sz - vmcoredd_orig_sz;
                if (remap_vmalloc_range_partial(vma, vma->vm_start + len,
-                                               kaddr, 0, tsz))
+                               kaddr, 0, tsz, /* set_vma =*/false))
                        goto fail;
 
                size -= tsz;
@@ -700,8 +693,33 @@ static int mmap_vmcore(struct file *file, struct 
vm_area_struct *vma)
        do_munmap(vma->vm_mm, vma->vm_start, len, NULL);
        return -EAGAIN;
 }
+
+static int mmap_prepare_vmcore(struct vm_area_desc *desc)
+{
+       size_t size = vma_desc_size(desc);
+       u64 start, end;
+
+       start = (u64)desc->pgoff << PAGE_SHIFT;
+       end = start + size;
+
+       if (size > vmcore_size || end > vmcore_size)
+               return -EINVAL;
+
+       if (desc->vm_flags & (VM_WRITE | VM_EXEC))
+               return -EPERM;
+
+       /* This is a unique case where we set both PFN map and mixed map flags. 
*/
+       desc->vm_flags |= VM_MIXEDMAP | VM_REMAP_FLAGS;
+       desc->vm_flags &= ~(VM_MAYWRITE | VM_MAYEXEC);
+       desc->vm_ops = &vmcore_mmap_ops;
+
+       desc->action.type = MMAP_CUSTOM_ACTION;
+       desc->action.custom.action_hook = mmap_prepare_action_vmcore;
+
+       return 0;
+}
 #else
-static int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
+static int mmap_prepare_vmcore(struct vm_area_desc *desc)
 {
        return -ENOSYS;
 }
@@ -712,7 +730,7 @@ static const struct proc_ops vmcore_proc_ops = {
        .proc_release   = release_vmcore,
        .proc_read_iter = read_vmcore,
        .proc_lseek     = default_llseek,
-       .proc_mmap      = mmap_vmcore,
+       .proc_mmap_prepare = mmap_prepare_vmcore,
 };
 
 static u64 get_vmcore_size(size_t elfsz, size_t elfnotesegsz,
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
index eb54b7b3202f..588810e571aa 100644
--- a/include/linux/vmalloc.h
+++ b/include/linux/vmalloc.h
@@ -215,12 +215,12 @@ extern void *vmap(struct page **pages, unsigned int count,
 void *vmap_pfn(unsigned long *pfns, unsigned int count, pgprot_t prot);
 extern void vunmap(const void *addr);
 
-extern int remap_vmalloc_range_partial(struct vm_area_struct *vma,
-                                      unsigned long uaddr, void *kaddr,
-                                      unsigned long pgoff, unsigned long size);
+int remap_vmalloc_range_partial(struct vm_area_struct *vma,
+               unsigned long uaddr, void *kaddr, unsigned long pgoff,
+               unsigned long size, bool set_vma);
 
-extern int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
-                                                       unsigned long pgoff);
+int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
+               unsigned long pgoff);
 
 int vmap_pages_range(unsigned long addr, unsigned long end, pgprot_t prot,
                     struct page **pages, unsigned int page_shift);
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 9fc86ddf1711..3dd9d5c441d8 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -4531,6 +4531,7 @@ long vread_iter(struct iov_iter *iter, const char *addr, 
size_t count)
  * @kaddr:             virtual address of vmalloc kernel memory
  * @pgoff:             offset from @kaddr to start at
  * @size:              size of map area
+ * @set_vma:           If true, update VMA flags
  *
  * Returns:    0 for success, -Exxx on failure
  *
@@ -4543,7 +4544,7 @@ long vread_iter(struct iov_iter *iter, const char *addr, 
size_t count)
  */
 int remap_vmalloc_range_partial(struct vm_area_struct *vma, unsigned long 
uaddr,
                                void *kaddr, unsigned long pgoff,
-                               unsigned long size)
+                               unsigned long size, bool set_vma)
 {
        struct vm_struct *area;
        unsigned long off;
@@ -4569,6 +4570,10 @@ int remap_vmalloc_range_partial(struct vm_area_struct 
*vma, unsigned long uaddr,
                return -EINVAL;
        kaddr += off;
 
+       /* If we shouldn't modify VMA flags, vm_insert_page() mustn't. */
+       if (!set_vma && !(vma->vm_flags & VM_MIXEDMAP))
+               return -EINVAL;
+
        do {
                struct page *page = vmalloc_to_page(kaddr);
                int ret;
@@ -4582,7 +4587,11 @@ int remap_vmalloc_range_partial(struct vm_area_struct 
*vma, unsigned long uaddr,
                size -= PAGE_SIZE;
        } while (size > 0);
 
-       vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
+       if (set_vma)
+               vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
+       else
+               VM_WARN_ON_ONCE((vma->vm_flags & (VM_DONTEXPAND | VM_DONTDUMP)) 
!=
+                               (VM_DONTEXPAND | VM_DONTDUMP));
 
        return 0;
 }
@@ -4606,7 +4615,8 @@ int remap_vmalloc_range(struct vm_area_struct *vma, void 
*addr,
 {
        return remap_vmalloc_range_partial(vma, vma->vm_start,
                                           addr, pgoff,
-                                          vma->vm_end - vma->vm_start);
+                                          vma->vm_end - vma->vm_start,
+                                          /* set_vma= */ true);
 }
 EXPORT_SYMBOL(remap_vmalloc_range);
 
-- 
2.51.0


Reply via email to