Re: [PATCH v4 16/38] mm/vma: add and use vma_[flags]_is_fixed_mapping
From: Suren Baghdasaryan
Date: Sun Oct 04 2026 - 13:58:28 EST
On Sat, Oct 3, 2026 at 6:38 PM Lorenzo Stoakes (ARM) <ljs@xxxxxxxxxx> wrote:
>
> 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 mappings not managed by core mm, 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.
>
> Also update the userland VMA tests to reflect the change.
>
> No functional change intended.
>
> Reviewed-by: Zi Yan <ziy@xxxxxxxxxx>
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@xxxxxxxxxx>
Reviewed-by: Suren Baghdasaryan <surenb@xxxxxxxxxx>
> ---
> include/linux/mm.h | 39 +++++++++++++++++++++++++++++++++++----
> mm/huge_memory.c | 11 +++++++----
> mm/mremap.c | 5 ++---
> tools/testing/vma/include/dup.h | 16 +++++++++++++++-
> 4 files changed, 59 insertions(+), 12 deletions(-)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 83953757735a..6b5f06cd174a 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
>
> @@ -1647,6 +1644,40 @@ static inline bool vma_is_mm_managed(const struct vm_area_struct *vma)
> return vma_flags_is_mm_managed(&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 mapping of a fixed range of memory established by a driver),
> + * 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?
> @@ -1668,7 +1699,7 @@ static inline bool vma_flags_can_merge(const vma_flags_t *flags)
> if (!vma_flags_is_mm_managed(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..87e8bf8029ab 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.
> + *
> + * Mappings not managed by core mm cannot be reliably reconstructed from
> + * page faults, and fixed mappings (including hugetlb) may well be
> + * mm-managed - 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))
> diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h
> index 89c645a6ad60..442ea9eb098d 100644
> --- a/tools/testing/vma/include/dup.h
> +++ b/tools/testing/vma/include/dup.h
> @@ -1676,6 +1676,20 @@ static inline bool vma_is_mm_managed(const struct vm_area_struct *vma)
> return vma_flags_is_mm_managed(&vma->flags);
> }
>
> +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);
> +}
> +
> +static inline bool vma_is_fixed_mapping(const struct vm_area_struct *vma)
> +{
> + return vma_flags_is_fixed_mapping(&vma->flags);
> +}
> +
> static inline bool vma_flags_can_merge(const vma_flags_t *flags)
> {
> /*
> @@ -1691,7 +1705,7 @@ static inline bool vma_flags_can_merge(const vma_flags_t *flags)
> if (!vma_flags_is_mm_managed(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;
>
> --
> 2.55.0
>