[PATCH v4 25/38] mm: remove VMA_IO_BIT check in vma[_flags]_is_mm_managed()
From: Lorenzo Stoakes (ARM)
Date: Sat Oct 03 2026 - 13:20:02 EST
We have now made it such that every driver which sets VMA_IO_BIT marks it
as not mm-managed.
However, vma_flags_is_mm_managed() currently treats VMA_IO_BIT as
disqualifying. This was a product of drivers previously marking a range as
not mm-managed by
setting VMA_IO_BIT alone.
Fix this by removing the VMA_IO_BIT check in vma_flags_is_mm_managed(),
and update mmap_validate_vma_flags() to use vma_flags_is_mm_managed()
rather than open-coding the VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT check.
This change means that vma[_flags]_can_merge() doesn't check VMA_IO_BIT any
longer (which is now redundant) as it calls vma_flags_is_mm_managed().
Now that the predicate means precisely VMA_PFNMAP_BIT or VMA_MIXEDMAP_BIT,
also use it at the other sites which open-code that pair, so the intent is
stated rather than the flags, with no functional change:
zap_special_vma_range() only zaps mappings which are not mm-managed, as
drivers use it
to tear down ranges they established themselves.
The mprotect() arch PFN modification check applies to mappings not managed
by core mm, which may map PFNs without struct pages.
NUMA balancing skips VM_MIXEDMAP mappings having already excluded VM_IO
and VM_PFNMAP mappings via vma_migratable(), so it skips exactly the
mappings not managed by core mm - say so.
Finally, update the VMA userland merge 'special' flag tests to no longer
assert that VMA_IO_BIT prevents merge as VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT
now suffices.
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@xxxxxxxxxx>
---
include/linux/mm.h | 3 +--
kernel/sched/fair.c | 2 +-
mm/memory.c | 7 ++++---
mm/mprotect.c | 3 +--
mm/vma.c | 2 +-
tools/testing/vma/include/dup.h | 3 +--
tools/testing/vma/tests/merge.c | 10 ++--------
7 files changed, 11 insertions(+), 19 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 6b5f06cd174a..3ddb463db895 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1627,8 +1627,7 @@ static inline bool vma_is_shared_maywrite(const struct vm_area_struct *vma)
*/
static inline bool vma_flags_is_mm_managed(const vma_flags_t *flags)
{
- return !vma_flags_test_any(flags, VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT,
- VMA_IO_BIT);
+ return !vma_flags_test_any(flags, VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT);
}
/**
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 8dff37059faf..a07a658cd99f 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -4212,7 +4212,7 @@ static void task_numa_work(struct callback_head *work)
for (; vma; vma = vma_next(&vmi)) {
if (!vma_migratable(vma) || !vma_policy_mof(vma) ||
- is_vm_hugetlb_page(vma) || (vma->vm_flags & VM_MIXEDMAP)) {
+ is_vm_hugetlb_page(vma) || !vma_is_mm_managed(vma)) {
trace_sched_skip_vma_numa(mm, vma, NUMAB_SKIP_UNSUITABLE);
continue;
}
diff --git a/mm/memory.c b/mm/memory.c
index 45b21bb04a18..6b91805ef88f 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2343,19 +2343,20 @@ void zap_vma_range(struct vm_area_struct *vma, unsigned long address,
}
/**
- * zap_special_vma_range - zap all page table entries in a special vma range
+ * zap_special_vma_range - zap all page table entries in a VMA which is not
+ * mm-managed
* @vma: the vma covering the range to zap
* @address: starting address of the range to zap
* @size: number of bytes to zap
*
* This function does nothing when the provided address range is not fully
- * contained in @vma, or when the @vma is not VM_PFNMAP or VM_MIXEDMAP.
+ * contained in @vma, or when @vma is mm-managed.
*/
void zap_special_vma_range(struct vm_area_struct *vma, unsigned long address,
unsigned long size)
{
if (!range_in_vma(vma, address, address + size) ||
- !(vma->vm_flags & (VM_PFNMAP | VM_MIXEDMAP)))
+ vma_is_mm_managed(vma))
return;
zap_vma_range(vma, address, size);
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 2888ee638d87..706c679d4281 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -783,8 +783,7 @@ mprotect_fixup(struct vma_iterator *vmi, struct mmu_gather *tlb,
* uncommon case, so doesn't need to be very optimized.
*/
if (arch_has_pfn_modify_check() &&
- vma_flags_test_any(&old_vma_flags, VMA_PFNMAP_BIT,
- VMA_MIXEDMAP_BIT) &&
+ !vma_flags_is_mm_managed(&old_vma_flags) &&
!vma_flags_test_any_mask(&new_vma_flags, VMA_ACCESS_FLAGS)) {
pgprot_t new_pgprot = vm_get_page_prot(newflags);
diff --git a/mm/vma.c b/mm/vma.c
index 43d32c3b8f6b..6008e3ce01ee 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -2793,7 +2793,7 @@ static int mmap_validate_vma_flags(const vma_flags_t *flags)
return -EINVAL;
#endif
- if (!vma_flags_test_any(flags, VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT)) {
+ if (vma_flags_is_mm_managed(flags)) {
/* mm-managed mappings may not set VMA_IO_BIT. */
if (WARN_ON_ONCE(vma_flags_test(flags, VMA_IO_BIT)))
return -EINVAL;
diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h
index 442ea9eb098d..94bf818ad0e1 100644
--- a/tools/testing/vma/include/dup.h
+++ b/tools/testing/vma/include/dup.h
@@ -1667,8 +1667,7 @@ static inline bool file_is_dev_zero(const struct file *file)
static inline bool vma_flags_is_mm_managed(const vma_flags_t *flags)
{
- return !vma_flags_test_any(flags, VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT,
- VMA_IO_BIT);
+ return !vma_flags_test_any(flags, VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT);
}
static inline bool vma_is_mm_managed(const struct vm_area_struct *vma)
diff --git a/tools/testing/vma/tests/merge.c b/tools/testing/vma/tests/merge.c
index acaab282939c..b26f1a66a170 100644
--- a/tools/testing/vma/tests/merge.c
+++ b/tools/testing/vma/tests/merge.c
@@ -496,17 +496,11 @@ static bool test_vma_merge_special_flags(void)
.mm = &mm,
.vmi = &vmi,
};
- vma_flag_t special_flags[] = { VMA_IO_BIT, VMA_DONTEXPAND_BIT,
+ vma_flag_t special_flags[] = { VMA_DONTEXPAND_BIT,
VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT };
- vma_flags_t all_special_flags = EMPTY_VMA_FLAGS;
int i;
struct vm_area_struct *vma_left, *vma;
- /* Make sure there aren't new VM_SPECIAL flags. */
- for (i = 0; i < ARRAY_SIZE(special_flags); i++)
- vma_flags_set(&all_special_flags, special_flags[i]);
- ASSERT_FLAGS_SAME_MASK(&all_special_flags, VMA_SPECIAL_FLAGS);
-
/*
* 01234
* AAA
@@ -520,7 +514,7 @@ static bool test_vma_merge_special_flags(void)
* 01234
* AAA*
*
- * This should merge if not for the VM_SPECIAL flag.
+ * This should merge if not for the 'special' flag.
*/
vmg_set_range(&vmg, 0x3000, 0x4000, 3, vma_flags);
for (i = 0; i < ARRAY_SIZE(special_flags); i++) {
--
2.55.0