[PATCH 06/15] mm: add CONFIG_VMA_REF and VMA helpers
From: tao
Date: Wed May 27 2026 - 07:28:07 EST
rcuref only manages the lifetime of a VMA and does not track its state.
Prepare for the upcoming ANON_VMA_LAZY support.
Signed-off-by: tao <tao.wangtao@xxxxxxxxx>
---
include/linux/mm.h | 38 ++++++++++++++++++++++++++++++++++++++
include/linux/mm_types.h | 4 ++++
mm/Kconfig | 8 ++++++++
mm/debug_vm_pgtable.c | 2 +-
mm/mmap.c | 4 ++--
mm/vma.c | 12 ++++++------
mm/vma_exec.c | 2 +-
mm/vma_init.c | 1 +
8 files changed, 61 insertions(+), 10 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index af23453e9dbd..e98bdb414e43 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -918,6 +918,43 @@ static inline void assert_fault_locked(const struct vm_fault *vmf)
}
#endif /* CONFIG_PER_VMA_LOCK */
+#ifdef CONFIG_VMA_REF
+static inline void vma_rcuref_init(struct vm_area_struct *vma)
+{
+ rcuref_init(&vma->vm_rcuref, 1);
+}
+
+static inline struct vm_area_struct *vma_get(struct vm_area_struct *vma)
+{
+ if (rcuref_get(&vma->vm_rcuref))
+ return vma;
+ return NULL;
+}
+
+static inline bool vma_put(struct vm_area_struct *vma)
+{
+ bool release = rcuref_put(&vma->vm_rcuref);
+
+ if (unlikely(release))
+ vm_area_free(vma);
+ return release;
+}
+#else
+static inline void vma_rcuref_init(struct vm_area_struct *vma) {}
+
+static inline struct vm_area_struct *vma_get(struct vm_area_struct *vma)
+{
+ VM_WARN_ON_ONCE(true); /* not allowed */
+ return NULL;
+}
+
+static inline bool vma_put(struct vm_area_struct *vma)
+{
+ vm_area_free(vma);
+ return true;
+}
+#endif /* CONFIG_VMA_REF */
+
static inline bool mm_flags_test(int flag, const struct mm_struct *mm)
{
return test_bit(flag, ACCESS_PRIVATE(&mm->flags, __mm_flags));
@@ -957,6 +994,7 @@ static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm)
vma->vm_ops = &vma_dummy_vm_ops;
INIT_LIST_HEAD(&vma->anon_vma_chain);
vma_lock_init(vma, false);
+ vma_rcuref_init(vma);
}
/* Use when VMA is not part of the VMA tree and needs no locking */
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index e7f5debac98e..a2bf17a42b55 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -6,6 +6,7 @@
#include <linux/auxvec.h>
#include <linux/kref.h>
+#include <linux/rcuref.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rbtree.h>
@@ -978,6 +979,9 @@ struct vm_area_struct {
* slowpath.
*/
unsigned int vm_lock_seq;
+#endif
+#ifdef CONFIG_ANON_VMA_LAZY
+ rcuref_t vm_rcuref; /* Ensures the VMA stays valid. */
#endif
/*
* A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
diff --git a/mm/Kconfig b/mm/Kconfig
index c16b5d9b3ce9..c039ce583924 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1419,13 +1419,21 @@ config ANON_VMA_LAZY
bool "Lazy allocation of anon_vma"
def_bool y
depends on ARCH_SUPPORTS_ANON_VMA_LAZY && MMU
+ select VMA_REF
help
For anonymous VMAs without children, avoid allocating anon_vma
and anon_vma_chain to reduce memory overhead.
+ ANON_VMA_LAZY records the VMA in folio->mapping, while VMA_REF
+ ensures that the recorded VMA remains valid.
+
Say Y to enable this optimization for anonymous VMAs without
children.
+config VMA_REF
+ def_bool n
+ depends on MMU
+
config IOMMU_MM_DATA
bool
diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
index 23dc3ee09561..cab8a4e71243 100644
--- a/mm/debug_vm_pgtable.c
+++ b/mm/debug_vm_pgtable.c
@@ -1036,7 +1036,7 @@ static void __init destroy_args(struct pgtable_debug_args *args)
/* Free vma and mm struct */
if (args->vma)
- vm_area_free(args->vma);
+ vma_put(args->vma);
if (args->mm)
mmput(args->mm);
diff --git a/mm/mmap.c b/mm/mmap.c
index 2ae733eb39f0..ccedebc87cd5 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1481,7 +1481,7 @@ static struct vm_area_struct *__install_special_mapping(
return vma;
out:
- vm_area_free(vma);
+ vma_put(vma);
return ERR_PTR(ret);
}
@@ -1922,7 +1922,7 @@ __latent_entropy int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
fail_nomem_anon_vma_fork:
mpol_put(vma_policy(tmp));
fail_nomem_policy:
- vm_area_free(tmp);
+ vma_put(tmp);
fail_nomem:
retval = -ENOMEM;
vm_unacct_memory(charge);
diff --git a/mm/vma.c b/mm/vma.c
index 3501617085b0..ed15968a5891 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -392,7 +392,7 @@ static void vma_complete(struct vma_prepare *vp, struct vma_iterator *vmi,
mpol_put(vma_policy(vp->remove));
if (!vp->remove2)
WARN_ON_ONCE(vp->vma->vm_end < vp->remove->vm_end);
- vm_area_free(vp->remove);
+ vma_put(vp->remove);
/*
* In mprotect's case 6 (see comments on vma_merge),
@@ -470,7 +470,7 @@ void remove_vma(struct vm_area_struct *vma)
if (vma->vm_file)
fput(vma->vm_file);
mpol_put(vma_policy(vma));
- vm_area_free(vma);
+ vma_put(vma);
}
/*
@@ -582,7 +582,7 @@ __split_vma(struct vma_iterator *vmi, struct vm_area_struct *vma,
out_free_vmi:
vma_iter_free(vmi);
out_free_vma:
- vm_area_free(new);
+ vma_put(new);
return err;
}
@@ -1950,7 +1950,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
out_free_mempol:
mpol_put(vma_policy(new_vma));
out_free_vma:
- vm_area_free(new_vma);
+ vma_put(new_vma);
out:
return NULL;
}
@@ -2596,7 +2596,7 @@ static int __mmap_new_vma(struct mmap_state *map, struct vm_area_struct **vmap,
free_iter_vma:
vma_iter_free(vmi);
free_vma:
- vm_area_free(vma);
+ vma_put(vma);
return error;
}
@@ -2946,7 +2946,7 @@ int do_brk_flags(struct vma_iterator *vmi, struct vm_area_struct *vma,
return 0;
mas_store_fail:
- vm_area_free(vma);
+ vma_put(vma);
unacct_fail:
vm_unacct_memory(len >> PAGE_SHIFT);
return -ENOMEM;
diff --git a/mm/vma_exec.c b/mm/vma_exec.c
index 5cee8b7efa0f..e7f388010488 100644
--- a/mm/vma_exec.c
+++ b/mm/vma_exec.c
@@ -160,6 +160,6 @@ int create_init_stack_vma(struct mm_struct *mm, struct vm_area_struct **vmap,
mmap_write_unlock(mm);
err_free:
*vmap = NULL;
- vm_area_free(vma);
+ vma_put(vma);
return err;
}
diff --git a/mm/vma_init.c b/mm/vma_init.c
index 3c0b65950510..1300d813d61b 100644
--- a/mm/vma_init.c
+++ b/mm/vma_init.c
@@ -137,6 +137,7 @@ struct vm_area_struct *vm_area_dup(struct vm_area_struct *orig)
INIT_LIST_HEAD(&new->anon_vma_chain);
vma_numab_state_init(new);
dup_anon_vma_name(orig, new);
+ vma_rcuref_init(new);
return new;
}
--
2.17.1