[PATCH 16/39] mm/vma: add and use vma_[flags]_is_fixed_mapping
From: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>
Date: 2026-09-08 20:10:04
Also in:
bpf, dri-devel, fuse-devel, kvm, kvm-riscv, kvmarm, linux-arch, linux-doc, linux-fbdev, linux-fsdevel, linux-mm, linux-perf-users, linux-rdma, linux-s390, linux-scsi, linux-sound, linux-usb, linuxppc-dev, lkml, selinux, sparclinux
Subsystem:
memory management, memory management - core, memory management - thp (transparent huge page), memory mapping, the rest · Maintainers:
Andrew Morton, David Hildenbrand, Liam R. Howlett, Lorenzo Stoakes, Linus Torvalds
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) <ljs@kernel.org>
---
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 45c59474c853..bca955941212 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 dd66c6ad5af1..befffadd978e 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