[PATCH 00/39] mm: make VMA flag semantics explicit, eliminate VM_SPECIAL
From: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>
Date: 2026-09-08 20:02:28
Also in:
bpf, dri-devel, fuse-devel, kvm, kvm-riscv, kvmarm, linux-arch, linux-doc, linux-fbdev, linux-fsdevel, linux-mm, linux-rdma, linux-s390, linux-scsi, linux-sound, linux-trace-kernel, linux-usb, linuxppc-dev, lkml, selinux, sparclinux
The VM_SPECIAL / VMA_SPECIAL_FLAGS mask conflates several unrelated
properties:
* Is this kernel-owned, whether MMIO, kernel-allocated pages, or ordinary
pages a driver maps itself?
* Can it be expanded or merged?
* Is this a 'weird' case like mlock where migration might race and we
'have' to set invalid flags to notify?
* Is it another 'weird' case where we just want to stop GUP from touching
it?
Driver writers have often been confused about this, and who can blame them?
It also interacts badly with the eternal edgecase known as hugetlb - which
sets VMA_DONTEXPAND_BIT but doesn't also want to be treated like a
'special' flag.
Another issue is that we cannot make sensible assumptions about flag
use. It's not possible to assume VMA_IO_BIT means iommu because drivers
abuse it and mlock abuses it.
Special is also an overloaded term in mm. VDSO and VVAR mappings are also
called 'special' but they're special in a... special way.
Sometimes things are called special that are a subset of
VMA_SPECIAL_FLAGS (VMA_PFNMAP_BIT and VMA_MIXEDMAP_BIT for instance when it
comes to zapping or vm_normal_folio()).
There's a specific kind of special for THP too, which considers
PFN map, mixed map 'special' but DAX not.
It's all rather a mess.
This series brings some order to things by both limiting what drivers can
do with VMA flags and switching to using predicates that describe
behaviour, not arbitrary flags.
It establishes the invariant that only kernel-owned mappings may set
VMA_IO_BIT or clear VMA_MAYWRITE_BIT in an mmap hook, enforcing this by
validating VMA state after every mmap and mmap_prepare hook.
It updates usbmon and sg to mmap_prepare in order to do so, adding a new
mmap action for mapping discontiguous kernel pages, and has hfi1 and the
ALSA PCM status page map their pages eagerly instead.
It also establishes the invariant that VMA_MIXEDMAP_BIT be set when mapping
kernel memory, something that is usually the case but happens not to be for
some users - specifically defio, cmt_speech, uprobes and the bpf arena, all
of which are updated to do the right thing.
It replaces VM_SPECIAL and arbitrary flag tests with predicates that say
what is actually being tested:
vma_is_kernel_owned() Does a driver or kernel code manage a VMA's
life cycle?
vma_is_fixed_mapping() Is the VMA not permitted to be expanded or
merged?
vma_is_persistent() Do bytes written to the VMA stay written, and
bytes read stay the same unless userland changes
them?
vma_can_merge() Can the VMA be merged with a compatible
neighbour?
vma_can_gup() Can GUP obtain pages from the VMA, i.e. is it
neither a PFN map nor memory-mapped I/O?
Remaining raw VMA_IO_BIT, VMA_PFNMAP_BIT and VMA_MIXEDMAP_BIT tests
scattered across mm are also converted to predicates where it makes sense
to do so.
And also the opportunity is taken to eliminate THP's vma_is_special_huge()
which was an existing source of confusion.
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Lorenzo Stoakes (ARM) (39):
mm/vma: predicate setting mmap_prepare VMA fields on new vma alloc
mm/vma: introduce and use vma_[flags_]can_merge()
mm: consistently validate VMA state after mmap[_prepare] hooks
mm/vma: ensure mmap_prepare doesn't set actions on a mergeable vma
mm: make map_kernel_pages_[prepare,complete] internal and unexported
mm/vma: tidy up map kernel pages enum values
mm: add mmap action for discontiguous kernel page mapping
docs: filesystems: update mmap_prepare docs for discontig kernel pgs
drivers/usb/mon: update to use mmap_prepare + map kernel pages
infiniband: update hfi1 to use remap_vmalloc_range()
selinux: reject writable opens of policy file, drop mmap shared/write check
ALSA: pcm: use vm_insert_page() to map PCM status page
bpf: arena: mark arena_map_mmap() mappings VM_MIXEDMAP
mm/vma: add vma[_flags]_is_kernel_owned() predicates
mm/vma: only allow mmap to clear VMA_MAYWRITE_BIT if kernel-owned
mm/vma: add and use vma_[flags]_is_fixed_mapping
scsi: sg: convert mmap hook to mmap_prepare and rework
fbdev: defio: assert FBINFO_VIRTFB, drop VM_IO, add VM_MIXEDMAP
HSI: cmt_speech: convert mmap hook to mmap_prepare, refactor
mm/gup: error out early on !VMA_MAYREAD_BIT VMAs
uprobes: remove VM_IO, set VM_MIXEDMAP for mapped kernel pages
mm/mlock: clear VMA_LOCKED_MASK over mmap callback
mm/mlock: eliminate weird VMA_IO_BIT abuse and simplify
mm/vma: enforce that only kernel-owned mappings may set VMA_IO_BIT
mm: remove VMA_IO_BIT check in vma[_flags]_is_kernel_owned()
mm: remove hugetlb_inline.h
mm: rename is_vm_hugetlb_page() to vma_is_hugetlb()
mm: drop some redundant checks around hugetlb VMAs
mm/madvise: update is_valid_guard_vma() to use vma_can_merge()
mm/vma: introduce vma[_flags]_is_persistent()
mm/uffd: use predicates for userfaultfd checks
mm/madvise: use predicates for madvise(..., MADV_DOFORK)
mm: eliminate VMA_SPECIAL_FLAGS usage when hugetlb explicitly tested
mm: eliminate VMA_SPECIAL_FLAGS check in lru_gen_look_around()
mm: avoid use of VMA_SPECIAL_FLAGS in migrate_vma_setup()
mm: eliminate VM_SPECIAL, VMA_SPECIAL_FLAGS
fuse: dax: do not set VM_MIXEDMAP
mm/huge_memory: remove vma_is_special_huge()
mm/vma: introduce and use vma[_flags]_can_gup()
Documentation/filesystems/mmap_prepare.rst | 81 +++++++++
arch/arm64/kvm/mmu.c | 4 +-
arch/powerpc/mm/book3s64/radix_tlb.c | 6 +-
arch/powerpc/mm/nohash/e500_hugetlbpage.c | 2 +-
arch/powerpc/mm/nohash/tlb.c | 2 +-
arch/riscv/kvm/mmu.c | 2 +-
arch/riscv/mm/tlbflush.c | 2 +-
arch/s390/mm/gmap_helpers.c | 6 +-
arch/sparc/mm/init_64.c | 2 +-
arch/x86/kernel/uprobes.c | 2 +-
drivers/gpu/drm/drm_gpusvm.c | 5 +-
drivers/hsi/clients/cmt_speech.c | 33 +---
drivers/infiniband/hw/hfi1/file_ops.c | 79 +++------
drivers/scsi/sg.c | 115 ++++++-------
drivers/usb/mon/mon_bin.c | 82 +++++----
drivers/video/fbdev/core/fb_defio.c | 6 +-
drivers/video/fbdev/ssd1307fb.c | 2 +
fs/coredump.c | 6 +-
fs/fuse/dax.c | 2 +-
fs/hugetlbfs/inode.c | 2 +-
fs/proc/task_mmu.c | 8 +-
include/asm-generic/tlb.h | 4 +-
include/linux/hugetlb.h | 5 +-
include/linux/hugetlb_inline.h | 28 ---
include/linux/mm.h | 266 +++++++++++++++++++++++++++--
include/linux/mm_types.h | 50 +++++-
include/linux/pagemap.h | 1 -
include/linux/rmap.h | 2 +-
include/linux/userfaultfd_k.h | 1 -
kernel/bpf/arena.c | 3 +-
kernel/events/core.c | 2 +-
kernel/events/uprobes.c | 4 +-
kernel/sched/fair.c | 3 +-
mm/folio.c | 2 +-
mm/gup.c | 15 +-
mm/hmm.c | 3 +-
mm/huge_memory.c | 31 ++--
mm/hugetlb.c | 14 +-
mm/internal.h | 71 ++++----
mm/ksm.c | 4 +-
mm/madvise.c | 28 +--
mm/memory.c | 142 ++++++++++++---
mm/mempolicy.c | 5 +-
mm/migrate_device.c | 12 +-
mm/mlock.c | 51 +++---
mm/mmap.c | 2 +-
mm/mmu_gather.c | 2 +-
mm/mprotect.c | 5 +-
mm/mremap.c | 11 +-
mm/page_vma_mapped.c | 4 +-
mm/pagewalk.c | 2 +-
mm/rmap.c | 4 +-
mm/swapfile.c | 2 +-
mm/userfaultfd.c | 45 +++--
mm/util.c | 22 ++-
mm/vma.c | 161 +++++++++++++----
mm/vma.h | 24 ++-
mm/vma_internal.h | 1 -
mm/vmscan.c | 9 +-
security/selinux/selinuxfs.c | 11 +-
sound/core/pcm_native.c | 37 ++--
tools/testing/vma/include/dup.h | 60 +++++--
tools/testing/vma/include/stubs.h | 2 +-
tools/testing/vma/tests/merge.c | 10 +-
64 files changed, 1071 insertions(+), 539 deletions(-)
---
base-commit: d118502628f8b673be9023db8bdf878f64a7ed45
change-id: 20260721-b4-mmap-prepare-vma-flag-sanify-2100425df5aa
Best regards,
--
Lorenzo Stoakes (ARM) [off-list ref]