This determines whether a VMA cannot be expanded or merged because what
they mapped was determined to be a set size at mmap time.

This typically refers to kernel-owned mappings, however VMA_DONTEXPAND_BIT
is not reliably set alongside VMA_PFNMAP_BIT or VMA_MIXEDMAP_BIT, so we
must explicitly test for this for now.

We also explicitly test for VMA_PFNMAP_BIT as VMA_DONTEXPAND_BIT may not be
set for VMA_PFNMAP_BIT's despite the one implying the other.

Use this predicate in vma_flags_can_merge() and in check_prep_vma() in the
mremap logic testing to see if mremap() can expand the VMA. The criteria
for khugepaged and MADV_COLLAPSE eligibility in
__thp_vma_allowable_orders() are precisely those for mergeability, so use
vma_can_merge() there (with an expanded comment).

This obviates the need for the VM_NO_KHUGEPAGED mask, so remove it.

Hugetlb VMAs remain excluded from khugepaged as hugetlbfs always sets
VMA_DONTEXPAND_BIT.

No functional change intended.

Signed-off-by: Lorenzo Stoakes (ARM) <[email protected]>
---
 include/linux/mm.h | 39 +++++++++++++++++++++++++++++++++++----
 mm/huge_memory.c   | 11 +++++++----
 mm/mremap.c        |  5 ++---
 3 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index cab29d6e15c1..ca598e5f9715 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -600,9 +600,6 @@ enum {
 #define VMA_REMAP_FLAGS mk_vma_flags(VMA_IO_BIT, VMA_PFNMAP_BIT,       \
                                     VMA_DONTEXPAND_BIT, VMA_DONTDUMP_BIT)
 
-/* This mask prevents VMA from being scanned with khugepaged */
-#define VM_NO_KHUGEPAGED (VM_SPECIAL | VM_HUGETLB)
-
 /* This mask defines which mm->def_flags a process can inherit its parent */
 #define VM_INIT_DEF_MASK       VM_NOHUGEPAGE
 
@@ -1650,6 +1647,40 @@ static inline bool vma_is_kernel_owned(const struct 
vm_area_struct *vma)
        return vma_flags_is_kernel_owned(&vma->flags);
 }
 
+/**
+ * vma_flags_is_fixed_mapping() - Do the specified VMA flags indicate that this
+ * is a fixed mapping that cannot be expanded or merged?
+ * @flags: The VMA flags to test.
+ *
+ * Fixed mappings are those whose size is set at the point of mmap (for
+ * instance, a kernel-owned mapping of a fixed range of memory), and thus
+ * cannot be expanded or merged.
+ *
+ * Returns: true if the flags indicate a fixed mapping.
+ */
+static inline bool vma_flags_is_fixed_mapping(const vma_flags_t *flags)
+{
+       /*
+        * VMA_PFNMAP_BIT should imply VMA_DONTEXPAND_BIT, but some callers set
+        * only the former.
+        */
+       return vma_flags_test_any(flags, VMA_PFNMAP_BIT, VMA_DONTEXPAND_BIT);
+}
+
+/**
+ * vma_is_fixed_mapping() - Is this VMA a fixed mapping that cannot be
+ * expanded or merged?
+ * @vma: The VMA to test.
+ *
+ * See vma_flags_is_fixed_mapping() for a description of this property.
+ *
+ * Returns: true if the VMA maps a fixed mapping.
+ */
+static inline bool vma_is_fixed_mapping(const struct vm_area_struct *vma)
+{
+       return vma_flags_is_fixed_mapping(&vma->flags);
+}
+
 /**
  * vma_flags_can_merge() - Do the specified VMA flags permit the VMA to be
  * merged with another?
@@ -1671,7 +1702,7 @@ static inline bool vma_flags_can_merge(const vma_flags_t 
*flags)
        if (vma_flags_is_kernel_owned(flags))
                return false;
        /* VMA explicitly marked as being unmergeable. */
-       if (vma_flags_test(flags, VMA_DONTEXPAND_BIT))
+       if (vma_flags_is_fixed_mapping(flags))
                return false;
 
        return true;
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 4cd917f77f3f..4d0acd9a1099 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -212,11 +212,14 @@ unsigned long __thp_vma_allowable_orders(struct 
vm_area_struct *vma,
                return in_pf ? orders : 0;
 
        /*
-        * khugepaged special VMA and hugetlb VMA.
-        * Must be checked after dax since some dax mappings may have
-        * VM_MIXEDMAP set.
+        * khugepaged moves data from VMAs once collapsed, after they have been
+        * faulted in, relying on refaulting for file-backed memory.
+        *
+        * Kernel-owned mappings cannot be reliably reconstructed from page
+        * faults, and fixed mappings (including hugetlb) may not be marked as
+        * kernel-owned - precisely the mappings which cannot be merged.
         */
-       if (!in_pf && !smaps && (vm_flags & VM_NO_KHUGEPAGED))
+       if (!in_pf && !smaps && !vma_can_merge(vma))
                return 0;
 
        /*
diff --git a/mm/mremap.c b/mm/mremap.c
index 7c368440fafe..ed19b47c2caf 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -1788,8 +1788,7 @@ static int check_prep_vma(struct vma_remap_struct *vrm)
                return -EINVAL;
        }
 
-       if ((vrm->flags & MREMAP_DONTUNMAP) &&
-           vma_test_any(vma, VMA_DONTEXPAND_BIT, VMA_PFNMAP_BIT))
+       if ((vrm->flags & MREMAP_DONTUNMAP) && vma_is_fixed_mapping(vma))
                return -EINVAL;
 
        /*
@@ -1827,7 +1826,7 @@ static int check_prep_vma(struct vma_remap_struct *vrm)
        if (pgoff + (new_len >> PAGE_SHIFT) < pgoff)
                return -EINVAL;
 
-       if (vma_test_any(vma, VMA_DONTEXPAND_BIT, VMA_PFNMAP_BIT))
+       if (vma_is_fixed_mapping(vma))
                return -EFAULT;
 
        if (!mlock_future_ok(mm, vma_test(vma, VMA_LOCKED_BIT), vrm->delta))

-- 
2.55.0


Reply via email to