* [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain
@ 2026-07-07 6:32 tao
2026-07-07 6:32 ` [RFC PATCH v2 01/13] mm: add CONFIG_ANON_VMA_FRACTAL tao
` (15 more replies)
0 siblings, 16 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:32 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Hi all,
Since v1 was relatively complex and difficult to extend, this version
adopts a simpler anon_vma implementation approach.
Replace the original anon_vma + anon_vma_chain multiple rbtree
structures with a depth-aware doubly linked list, and remove anon_vma_chain.
During rmap walks, vma_rmap_base(vma) is used to compute the page
address and look up the corresponding VMA from mm_mt:
vma_rmap_base(vma) = vma->vm_start - vma->vm_pgoff * PAGE_SIZE
page_address(vma, pgoff)
= vma->vm_start + (pgoff - vma->vm_pgoff) * PAGE_SIZE
= vma->vm_start - vma->vm_pgoff * PAGE_SIZE + pgoff * PAGE_SIZE
= vma_rmap_base(vma) + pgoff * PAGE_SIZE
Since vma_rmap_base(vma) remains invariant across VMA merge and split
operations, VMA updates only need to adjust the VMA count of the
corresponding anon_vma. The anon_vma no longer directly tracks VMAs,
which eliminates the need for anon_vma_chain.
Process fork behaves like a fractal expansion of both tasks and
anon_vmas. A dedicated fractal_list records forked anon_vmas, while
descendant anon_vmas are derived from fork depth during rmap walks.
For this reason, the new design is named ANON_VMA_FRACTAL.
The depth is initialized to 0.
When a child is added, the root depth is set to 1.
A remap child has a depth of parent->depth + 1,
while a fork child has a depth of parent->depth + 2.
For example:
1) A forks B and C:
(1A)
/ \
(3B)---(3C)
2) B remaps D:
(1A)
/ \
(3B) (3C)
/ /
(4D)-----o
3) B forks E and F:
(1A)--o
/ \
(3B) (3C)
/ /
(4D) /
/ /
(5E)------(5F)
4) C forks G:
(1A)------------o
/ \
(3B) (3C) o
/ / \ /
(4D) / \ /
/ / \ /
(5E)------(5F) (5G)
Only the root node A needs to traverse all descendants; non-root nodes
only traverse their own descendant nodes. For example:
- When rmap-ing A: first process A, then traverse all descendants
B/D/E/F/C/G. A does not need to be traversed again.
- When rmap-ing B: first process B, then traverse its descendants
D/E/F. Since C->depth <= B->depth, the rmap walk terminates at C.
The fractal_list remains stable across VMA merge and split operations.
It is updated only when anon_vmas are forked, remapped, or unlinked, and
is traversed during rmap walks.
On an Android device after boot, memory usage of vm_area_struct,
anon_vma, and anon_vma_chain is reduced by about 35 MB.
struct base(KB) patch(KB) patch_struct saved(KB)
--------------------------------------------------------------------
vm_area_struct 110760 103837 vm_area_struct 6887
anon_vma 19152 8576 anon_node 10576
anon_vma_chain 17560 0 NA 17560
NA 0 346 anon_semaphore -346
lat_proc shows fork performance improves by about 15% at P=10.
Changes since v1:
- anon_vma now stores mm and rmap_base for VMA lookup, and only tracks
the number of associated VMAs instead of directly tracking VMAs
- Use fractal_list + depth to maintain anon_vma topology and remove
anon_vma_chain completely
- Keep folio->mapping storing anon_vma
- Preserve existing APIs and external callers unchanged while
introducing ANON_RMAP_FOREACH_VMA() to unify anonymous rmap traversal
- Use vm_refcnt on leaf VMAs for rmap protection
- Optimize rwsem memory usage with shared semaphores
Patch layout:
1-2: add CONFIG_ANON_VMA_FRACTAL and basic helpers
3-7: implement anon_vma fractal infrastructure
8: implement anonymous folio rmap
9-10: replace anon_vma with anon_node
11: use vm_refcnt on leaf VMAs for rmap protection
12: optimize anon_vma memory usage with shared semaphores
v1:
https://lore.kernel.org/all/20260527110147.17815-1-tao.wangtao@honor.com/
tao (13):
mm: add CONFIG_ANON_VMA_FRACTAL
mm: implement helpers for ANON_VMA_FRACTAL
mm: implement __anon_node_prepare for ANON_VMA_FRACTAL
mm: implement anon_node_clone for ANON_VMA_FRACTAL
mm: implement anon_node_fork_with_prev for ANON_VMA_FRACTAL
mm: implement unlink_anon_nodes for ANON_VMA_FRACTAL
mm: handle rmap_base changes for ANON_VMA_FRACTAL
mm: implement anonymous folio rmap for ANON_VMA_FRACTAL
mm: prepare anon_node replacement for ANON_VMA_FRACTAL
mm: replace anon_vma with anon_node for ANON_VMA_FRACTAL
mm: optimize rmap for ANON_VMA_FRACTAL with PVL
mm: shared semaphores for ANON_VMA_FRACTAL
mm: Enable CONFIG_ANON_VMA_FRACTAL by default
include/linux/mm.h | 2 +
include/linux/mm_types.h | 2 +
include/linux/rmap.h | 56 ++++
include/linux/rwsem.h | 10 +
mm/Kconfig | 37 +++
mm/internal.h | 205 ++++++++++++
mm/ksm.c | 19 +-
mm/memory-failure.c | 8 +-
mm/mmap.c | 7 +-
mm/rmap.c | 686 +++++++++++++++++++++++++++++++++++++--
mm/vma.c | 76 ++++-
mm/vma_init.c | 2 +
12 files changed, 1056 insertions(+), 54 deletions(-)
--
2.17.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* [RFC PATCH v2 01/13] mm: add CONFIG_ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
@ 2026-07-07 6:32 ` tao
2026-07-07 6:32 ` [RFC PATCH v2 02/13] mm: implement helpers for ANON_VMA_FRACTAL tao
` (14 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:32 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Use vma_rmap_base(vma) to compute page addresses during rmap and
look up the corresponding VMA in mm_mt. Since vma_rmap_base(vma)
remains invariant across VMA merge and split, VMA operations only
need to adjust the VMA count of the corresponding anon_vma,
eliminating anon_vma_chain.
Fork creates derived anon_vmas in a fractal-like structure. A list
tracks anon_vmas, while fork depth is used to locate descendant
anon_vmas during rmap traversal.
Signed-off-by: tao <tao.wangtao@honor.com>
---
include/linux/rmap.h | 48 ++++++++++++++++++++++++++++++++++++++++++++
mm/Kconfig | 28 ++++++++++++++++++++++++++
mm/internal.h | 28 ++++++++++++++++++++++++++
3 files changed, 104 insertions(+)
diff --git a/include/linux/rmap.h b/include/linux/rmap.h
index 8dc0871e5f00..75738f4ede1f 100644
--- a/include/linux/rmap.h
+++ b/include/linux/rmap.h
@@ -67,6 +67,54 @@ struct anon_vma {
struct rb_root_cached rb_root;
};
+#ifdef CONFIG_ANON_VMA_FRACTAL
+
+/* struct anon_node - Replace anon_vma for anonymous page reverse mapping. */
+struct anon_node {
+ struct anon_node *root; /* Root of this anon_node fractal tree */
+ struct rw_semaphore rwsem;
+ atomic_t vm_lock_ref; /* for vm_lock_anon_vma */
+ atomic_t refcount;
+
+ struct anon_node *parent; /* NULL if created by a page fault */
+
+ /*
+ * depth is initialized to 0. When a child is added, depth is set to 1.
+ * An rbc child has depth = parent->depth + 1,
+ * and fork child has depth = parent->depth + 2.
+ *
+ * A forks B and C; B remaps D and forks E and F; C forks G.
+ * The fractal_list is as follows:
+ * (1A)--------------o
+ * / \
+ * (3B) (3C) o
+ * / / \ /
+ * (4D) / \ /
+ * / / \ /
+ * (5E)----(5F) (5G)
+ */
+ unsigned int depth;
+ unsigned int nr_children; /* < PID_MAX_LIMIT + max_map_count. */
+ struct list_head fractal_list;
+
+ /* mm and rmap_base can be used to find the VMA a page belongs to. */
+ struct mm_struct *mm;
+ /* rmap_base with VMA count in the low PAGE_SHIFT bits. */
+ atomic_long_t rbc;
+};
+
+/* Create rbc child anon_node on VMA remap or when count exceeds MAX. */
+#define ANON_RMAP_BASE_COUNT_MASK (PAGE_SIZE - 1)
+#define ANON_RMAP_BASE_COUNT_MAX (ANON_RMAP_BASE_COUNT_MASK)
+
+/*
+ * Allow VMAs to share an anon_node only if the number of VMAs attached
+ * is below this limit; keep it small to reduce lock contention.
+ */
+#define ANON_NODE_SHARE_LIMIT (ANON_RMAP_BASE_COUNT_MAX / 1000)
+
+#endif
+
/*
* The copy-on-write semantics of fork mean that an anon_vma
* can become associated with multiple processes. Furthermore,
diff --git a/mm/Kconfig b/mm/Kconfig
index 9e0ca4824905..c4c6d3e52d72 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1451,6 +1451,34 @@ config LOCK_MM_AND_FIND_VMA
bool
depends on !STACK_GROWSUP
+config ANON_VMA_FRACTAL
+ bool "anon_vma fractal list"
+ def_bool n
+ depends on MMU
+ help
+ This option uses vma_rmap_base(vma) during rmap to compute the page
+ address and look up the corresponding VMA in mm_mt.
+
+ vma_rmap_base(vma) = vma->vm_start - vma->vm_pgoff * PAGE_SIZE
+
+ page_address(vma, pgoff)
+ = vma->vm_start + (pgoff - vma->vm_pgoff) * PAGE_SIZE
+ = vma->vm_start - vma->vm_pgoff * PAGE_SIZE + pgoff * PAGE_SIZE
+ = vma_rmap_base(vma) + pgoff * PAGE_SIZE
+
+ Since vma_rmap_base(vma) remains invariant across VMA merge and
+ split, VMA operations only need to adjust the VMA count of the
+ corresponding anon_vma, eliminating the need for anon_vma_chain.
+
+ Process fork behaves like a fractal expansion of both tasks and
+ anon_vmas. A list records forked anon_vmas, and descendant anon_vmas
+ are derived from fork depth for rmap handling.
+
+ The fractal list remains unchanged during VMA merge or split. It
+ is only updated when anon_vmas are created or removed, and traversed
+ during rmap walks. Since rmap_one operations may switch tasks,
+ semaphores are still required instead of spinlock_t protection.
+
config IOMMU_MM_DATA
bool
diff --git a/mm/internal.h b/mm/internal.h
index 181e79f1d6a2..a33ed1a30f09 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -202,6 +202,16 @@ void unmap_vmas(struct mmu_gather *tlb, struct unmap_desc *unmap);
#ifdef CONFIG_MMU
+static inline unsigned long rmap_base(unsigned long vm_start, pgoff_t pgoff)
+{
+ return vm_start - (pgoff << PAGE_SHIFT);
+}
+
+static inline unsigned long vma_rmap_base(const struct vm_area_struct *vma)
+{
+ return rmap_base(vma->vm_start, vma->vm_pgoff);
+}
+
static inline void get_anon_vma(struct anon_vma *anon_vma)
{
atomic_inc(&anon_vma->refcount);
@@ -245,6 +255,24 @@ static inline void anon_vma_unlock_read(struct anon_vma *anon_vma)
up_read(&anon_vma->root->rwsem);
}
+#ifdef CONFIG_ANON_VMA_FRACTAL
+
+static inline struct anon_node *anon_node_next_rbc_child(
+ struct anon_node *anon_nod, struct anon_node *node)
+{
+ node = list_next_entry(node, fractal_list);
+ return node->depth == anon_nod->depth + 1 ? node : NULL;
+}
+
+static inline struct anon_node *anon_node_next_descendant(
+ struct anon_node *anon_nod, struct anon_node *node)
+{
+ node = list_next_entry(node, fractal_list);
+ return node->depth > anon_nod->depth ? node : NULL;
+}
+
+#endif
+
struct anon_vma *folio_get_anon_vma(const struct folio *folio);
/* Operations which modify VMAs. */
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 02/13] mm: implement helpers for ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
2026-07-07 6:32 ` [RFC PATCH v2 01/13] mm: add CONFIG_ANON_VMA_FRACTAL tao
@ 2026-07-07 6:32 ` tao
2026-07-07 6:32 ` [RFC PATCH v2 03/13] mm: implement __anon_node_prepare " tao
` (13 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:32 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Implement basic helpers.
Signed-off-by: tao <tao.wangtao@honor.com>
---
mm/internal.h | 25 +++++++++++++++
mm/rmap.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 114 insertions(+)
diff --git a/mm/internal.h b/mm/internal.h
index a33ed1a30f09..c1b3961b9d3e 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -271,6 +271,31 @@ static inline struct anon_node *anon_node_next_descendant(
return node->depth > anon_nod->depth ? node : NULL;
}
+static inline void get_anon_node(struct anon_node *anon_nod)
+{
+ VM_BUG_ON(atomic_read(&anon_nod->refcount) == 0);
+ atomic_inc(&anon_nod->refcount);
+}
+
+void __put_anon_node(struct anon_node *anon_nod);
+
+static inline void put_anon_node(struct anon_node *anon_nod)
+{
+ VM_BUG_ON(atomic_read(&anon_nod->refcount) == 0);
+ if (atomic_dec_and_test(&anon_nod->refcount))
+ __put_anon_node(anon_nod);
+}
+
+static inline unsigned long anon_node_rmap_base(struct anon_node *anon_nod)
+{
+ return atomic_long_read(&anon_nod->rbc) & ~ANON_RMAP_BASE_COUNT_MASK;
+}
+
+static inline unsigned long anon_node_rmap_count(struct anon_node *anon_nod)
+{
+ return atomic_long_read(&anon_nod->rbc) & ANON_RMAP_BASE_COUNT_MAX;
+}
+
#endif
struct anon_vma *folio_get_anon_vma(const struct folio *folio);
diff --git a/mm/rmap.c b/mm/rmap.c
index 1c77d5dc06e9..fa9d82271005 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -558,6 +558,95 @@ void __init anon_vma_init(void)
SLAB_PANIC|SLAB_ACCOUNT);
}
+#ifdef CONFIG_ANON_VMA_FRACTAL
+
+static struct kmem_cache *anon_node_cachep;
+
+static inline struct rw_semaphore *anon_node_rmap_sem(struct anon_node *node)
+{
+ return &node->root->rwsem;
+}
+
+static inline atomic_t *anon_node_vm_lock_ref(struct anon_node *node)
+{
+ return &node->root->vm_lock_ref;
+}
+
+static void anon_node_ctor(void *data)
+{
+ struct anon_node *anon_nod = data;
+
+ init_rwsem(&anon_nod->rwsem);
+ atomic_set(&anon_nod->vm_lock_ref, 0);
+ atomic_set(&anon_nod->refcount, 0);
+}
+
+static inline void anon_vma_fractal_init(void)
+{
+ struct kmem_cache_args args = {
+ .use_freeptr_offset = true,
+ .freeptr_offset = offsetof(struct anon_node, fractal_list),
+ .ctor = anon_node_ctor,
+ };
+
+ anon_node_cachep = kmem_cache_create("anon_node",
+ sizeof(struct anon_node), &args,
+ SLAB_TYPESAFE_BY_RCU|SLAB_PANIC|SLAB_ACCOUNT);
+}
+
+static inline void anon_node_free(struct anon_node *anon_nod)
+{
+ struct rw_semaphore *rmap_sem = anon_node_rmap_sem(anon_nod);
+
+ VM_BUG_ON(atomic_read(&anon_nod->refcount));
+ VM_BUG_ON(!list_empty(&anon_nod->fractal_list));
+ VM_BUG_ON(anon_nod->nr_children);
+ VM_BUG_ON(anon_node_rmap_count(anon_nod));
+
+ might_sleep();
+ if (rwsem_is_locked(rmap_sem)) {
+ down_write(rmap_sem);
+ up_write(rmap_sem);
+ }
+
+ kmem_cache_free(anon_node_cachep, anon_nod);
+}
+
+void __put_anon_node(struct anon_node *anon_nod)
+{
+ struct anon_node *root = anon_nod->root;
+
+ anon_node_free(anon_nod);
+ if (root != anon_nod && atomic_dec_and_test(&root->refcount))
+ anon_node_free(root);
+}
+
+static inline struct anon_node *anon_node_alloc(struct vm_area_struct *vma,
+ struct anon_node *parent, bool is_fork)
+{
+ struct anon_node *anon_nod;
+
+ anon_nod = kmem_cache_alloc(anon_node_cachep, GFP_KERNEL);
+ if (!anon_nod)
+ return NULL;
+
+ anon_nod->root = parent ? parent->root : anon_nod;
+ anon_nod->parent = parent;
+ atomic_set(&anon_nod->refcount, 1);
+ anon_nod->depth = 0;
+ if (parent) {
+ get_anon_node(parent->root);
+ anon_nod->depth = parent->depth + (1 << is_fork);
+ }
+ anon_nod->nr_children = 0;
+ INIT_LIST_HEAD(&anon_nod->fractal_list);
+ anon_nod->mm = vma->vm_mm;
+ atomic_long_set(&anon_nod->rbc, vma_rmap_base(vma) + 1);
+ return anon_nod;
+}
+
+#endif
+
/*
* Getting a lock on a stable anon_vma from a page off the LRU is tricky!
*
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 03/13] mm: implement __anon_node_prepare for ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
2026-07-07 6:32 ` [RFC PATCH v2 01/13] mm: add CONFIG_ANON_VMA_FRACTAL tao
2026-07-07 6:32 ` [RFC PATCH v2 02/13] mm: implement helpers for ANON_VMA_FRACTAL tao
@ 2026-07-07 6:32 ` tao
2026-07-07 6:32 ` [RFC PATCH v2 04/13] mm: implement anon_node_clone " tao
` (12 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:32 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Add anon_node APIs corresponding to existing anon_vma operations:
- anon_node_clone() corresponds to anon_vma_clone()
- __anon_node_prepare() corresponds to __anon_vma_prepare()
- anon_node_fork_with_prev() corresponds to anon_vma_fork()
- unlink_anon_nodes() corresponds to unlink_anon_vmas()
Implement __anon_node_prepare() first to keep the patch small.
Signed-off-by: tao <tao.wangtao@honor.com>
---
mm/internal.h | 11 ++++++++++
mm/rmap.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 69 insertions(+)
diff --git a/mm/internal.h b/mm/internal.h
index c1b3961b9d3e..710baf02575b 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -314,6 +314,17 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma);
int __anon_vma_prepare(struct vm_area_struct *vma);
void unlink_anon_vmas(struct vm_area_struct *vma);
+#ifdef CONFIG_ANON_VMA_FRACTAL
+int anon_node_clone(struct vm_area_struct *vma, struct vm_area_struct *src,
+ enum vma_operation operation);
+int anon_node_fork_with_prev(struct vm_area_struct *vma,
+ struct vm_area_struct *pvma, struct vm_area_struct *vma_prev,
+ struct vm_area_struct *pvma_prev);
+int __anon_node_prepare(struct vm_area_struct *vma);
+void unlink_anon_nodes(struct vm_area_struct *vma);
+
+#endif
+
static inline int anon_vma_prepare(struct vm_area_struct *vma)
{
if (likely(vma->anon_vma))
diff --git a/mm/rmap.c b/mm/rmap.c
index fa9d82271005..b98ba6a83272 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -645,6 +645,64 @@ static inline struct anon_node *anon_node_alloc(struct vm_area_struct *vma,
return anon_nod;
}
+static bool try_track_rmap(struct anon_node *anon_nod, unsigned long rmap_base)
+{
+ const unsigned long rbc_max = rmap_base + ANON_RMAP_BASE_COUNT_MAX;
+
+ return rmap_base == anon_node_rmap_base(anon_nod) &&
+ atomic_long_add_unless(&anon_nod->rbc, 1, rbc_max);
+}
+
+static inline bool try_untrack_rmap(struct anon_node *anon_nod,
+ unsigned long rmap_base, bool *test_empty)
+{
+ const unsigned long rbc_max = rmap_base + ANON_RMAP_BASE_COUNT_MAX;
+ unsigned long rbc = atomic_long_read(&anon_nod->rbc);
+
+ if (rbc > rmap_base && rbc <= rbc_max) {
+ rbc = atomic_long_dec_return(&anon_nod->rbc);
+ *test_empty = (rbc == rmap_base);
+ return true;
+ }
+ return false;
+}
+
+/* attach an anon_node to the VMA. */
+int __anon_node_prepare(struct vm_area_struct *vma)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ unsigned long rmap_base = vma_rmap_base(vma);
+ struct anon_node *anon_nod;
+ struct anon_vma *anon_old = NULL, *anon_new;
+ bool test_empty = false;
+
+ mmap_assert_locked(mm);
+ might_sleep();
+
+ anon_nod = NULL; /* Fix find_mergeable_anon_vma(vma) later. */
+ if (anon_nod) {
+ anon_new = (struct anon_vma *)anon_nod;
+ if (try_track_rmap(anon_nod, rmap_base) &&
+ !try_cmpxchg(&vma->anon_vma, &anon_old, anon_new))
+ try_untrack_rmap(anon_nod, rmap_base, &test_empty);
+ if (vma->anon_vma)
+ return 0;
+ }
+
+ anon_nod = anon_node_alloc(vma, NULL, false);
+ if (unlikely(!anon_nod))
+ return -ENOMEM;
+
+ anon_new = (struct anon_vma *)anon_nod;
+ /* maybe prepared by another thread */
+ if (!try_cmpxchg(&vma->anon_vma, &anon_old, anon_new)) {
+ try_untrack_rmap(anon_nod, rmap_base, &test_empty);
+ put_anon_node(anon_nod);
+ }
+
+ return 0;
+}
+
#endif
/*
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 04/13] mm: implement anon_node_clone for ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (2 preceding siblings ...)
2026-07-07 6:32 ` [RFC PATCH v2 03/13] mm: implement __anon_node_prepare " tao
@ 2026-07-07 6:32 ` tao
2026-07-07 6:33 ` [RFC PATCH v2 05/13] mm: implement anon_node_fork_with_prev " tao
` (11 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:32 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Prefer using anon_node for tracking in anon_node_clone.
If the VMA is remapped or the VMA count exceeds
ANON_RMAP_BASE_COUNT_MAX, create a child anon_node.
Newly created anon_node are inserted at the tail of the rbc children.
Signed-off-by: tao <tao.wangtao@honor.com>
---
mm/rmap.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 85 insertions(+)
diff --git a/mm/rmap.c b/mm/rmap.c
index b98ba6a83272..74817876107a 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -703,6 +703,91 @@ int __anon_node_prepare(struct vm_area_struct *vma)
return 0;
}
+static inline struct anon_node *vma_anon_node(struct vm_area_struct *vma)
+{
+ return vma ? (struct anon_node *)vma->anon_vma : NULL;
+}
+
+static inline void vma_set_anon_node(struct vm_area_struct *vma,
+ struct anon_node *anon_nod, bool is_locked)
+{
+ struct rw_semaphore *rmap_sem = anon_node_rmap_sem(anon_nod);
+
+ if (is_locked) {
+ vma->anon_vma = (struct anon_vma *)anon_nod;
+ return;
+ }
+ down_write(rmap_sem);
+ vma->anon_vma = (struct anon_vma *)anon_nod;
+ up_write(rmap_sem);
+}
+
+static int anon_node_add_child(struct anon_node *anon_nod,
+ struct vm_area_struct *vma, bool is_fork)
+{
+ struct anon_node *child;
+ struct anon_node *root = anon_nod->root;
+ struct rw_semaphore *rmap_sem = anon_node_rmap_sem(root);
+ struct anon_node *rbc_ch = anon_nod, *next;
+
+ if (root->depth == 0)
+ root->depth = 1;
+ child = anon_node_alloc(vma, anon_nod, is_fork);
+ if (!child)
+ return -ENOMEM;
+
+ rmap_sem = anon_node_rmap_sem(root);
+ down_write(rmap_sem);
+ /* Add new child to the tail of rbc children. */
+ while ((next = anon_node_next_rbc_child(anon_nod, rbc_ch)) != NULL) {
+ rbc_ch = next;
+ }
+ list_add(&child->fractal_list, &rbc_ch->fractal_list);
+ ++anon_nod->nr_children;
+
+ if (is_fork)
+ vma_set_anon_node(vma, child, true);
+ up_write(rmap_sem);
+ return 0;
+}
+
+/* Returns 0 on success, non-zero on failure. */
+static int anon_node_track_rmap(struct anon_node *anon_nod,
+ struct vm_area_struct *vma)
+{
+ struct rw_semaphore *rmap_sem = anon_node_rmap_sem(anon_nod);
+ unsigned long rmap_base = vma_rmap_base(vma);
+ struct anon_node *rbc_ch = anon_nod;
+
+ if (try_track_rmap(anon_nod, rmap_base))
+ return 0;
+
+ down_read(rmap_sem);
+ while ((rbc_ch = anon_node_next_rbc_child(anon_nod, rbc_ch)) != NULL) {
+ if (try_track_rmap(rbc_ch, rmap_base))
+ break;
+ }
+ up_read(rmap_sem);
+ if (rbc_ch)
+ return 0;
+
+ return anon_node_add_child(anon_nod, vma, false);
+}
+
+/* vma clones src anon_node and increments the count. */
+int anon_node_clone(struct vm_area_struct *vma, struct vm_area_struct *src,
+ enum vma_operation operation)
+{
+ struct anon_node *anon_nod = vma_anon_node(src);
+
+ mmap_assert_write_locked(vma->vm_mm);
+ if (!anon_nod)
+ return 0;
+
+ VM_BUG_ON_VMA(vma->anon_vma != (void *)anon_nod, vma);
+ return anon_node_track_rmap(anon_nod, vma);
+}
+
#endif
/*
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 05/13] mm: implement anon_node_fork_with_prev for ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (3 preceding siblings ...)
2026-07-07 6:32 ` [RFC PATCH v2 04/13] mm: implement anon_node_clone " tao
@ 2026-07-07 6:33 ` tao
2026-07-07 6:33 ` [RFC PATCH v2 06/13] mm: implement unlink_anon_nodes " tao
` (10 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:33 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Fork handling introduces two additional parameters to help child VMAs
clone anon_node. The logic follows three cases:
1) Fast path: clone the anon_node with the previous VMA.
2) Reuse an empty anon_node from an ancestor.
3) Allocate a new anon_node and insert it into fractal_list.
Signed-off-by: tao <tao.wangtao@honor.com>
---
mm/rmap.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/mm/rmap.c b/mm/rmap.c
index 74817876107a..93fa04c5a007 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -667,6 +667,17 @@ static inline bool try_untrack_rmap(struct anon_node *anon_nod,
return false;
}
+static bool try_reuse_anon_node(struct anon_node *anon_nod,
+ unsigned long new_rbc)
+{
+ long old_rbase;
+
+ if (anon_node_rmap_count(anon_nod))
+ return false;
+ old_rbase = anon_node_rmap_base(anon_nod);
+ return atomic_long_try_cmpxchg(&anon_nod->rbc, &old_rbase, new_rbc);
+}
+
/* attach an anon_node to the VMA. */
int __anon_node_prepare(struct vm_area_struct *vma)
{
@@ -788,6 +799,67 @@ int anon_node_clone(struct vm_area_struct *vma, struct vm_area_struct *src,
return anon_node_track_rmap(anon_nod, vma);
}
+static struct anon_node *fork_reuse_ancestor(struct anon_node *parent,
+ struct vm_area_struct *vma)
+{
+ struct anon_node *root = parent->root;
+ struct anon_node *node;
+ unsigned long new_rbc = vma_rmap_base(vma) + 1;
+ /* At least N anon_nodes between parent and root: P -> a1 .. aN -> R */
+ const unsigned int ANON_DEPTH_REUSE_THRESHOLD = 1 + (5 << 1);
+
+ if (parent->depth < ANON_DEPTH_REUSE_THRESHOLD)
+ return NULL;
+
+ /* Release from leaf only; parent is stable, no lock needed. */
+ for (node = parent; node != root; node = node->parent) {
+ if (READ_ONCE(node->nr_children) > 1)
+ continue;
+ /* Replace with the new_rbc atomically. */
+ if (!try_reuse_anon_node(node, new_rbc))
+ continue;
+
+ node->mm = vma->vm_mm;
+ vma_set_anon_node(vma, node, false);
+ return node;
+ }
+
+ return NULL;
+}
+
+/* Clone an anon_node from prev or fork one from the parent pvma. */
+int anon_node_fork_with_prev(struct vm_area_struct *vma,
+ struct vm_area_struct *pvma, struct vm_area_struct *prev,
+ struct vm_area_struct *pvma_prev)
+{
+ struct anon_node *parent = vma_anon_node(pvma);
+ struct anon_node *anon_nod = vma_anon_node(prev);
+ unsigned long rmap_base = vma_rmap_base(vma);
+
+ if (!parent)
+ return 0;
+
+ /* Drop inherited anon_nod. */
+ vma->anon_vma = NULL;
+
+ /* 1) Fast path: clone the anon_node with the previous VMA. */
+ if (anon_nod && pvma_prev && parent == vma_anon_node(pvma_prev)) {
+ if (try_track_rmap(anon_nod, rmap_base)) {
+ vma_set_anon_node(vma, anon_nod, false);
+ return 0;
+ }
+ }
+
+ /* 2) Reuse an existing anon_node. */
+ anon_nod = fork_reuse_ancestor(parent, vma);
+ if (anon_nod)
+ return 0;
+
+ /* 3) Fork a new anon_node. */
+ return anon_node_add_child(parent, vma, true);
+}
+
+
#endif
/*
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 06/13] mm: implement unlink_anon_nodes for ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (4 preceding siblings ...)
2026-07-07 6:33 ` [RFC PATCH v2 05/13] mm: implement anon_node_fork_with_prev " tao
@ 2026-07-07 6:33 ` tao
2026-07-07 6:33 ` [RFC PATCH v2 07/13] mm: handle rmap_base changes " tao
` (9 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:33 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Detaching an anon_node may require traversing or removing it from the
fractal list, so the lock must be held.
Releasing an anon_node may propagate through the parent chain, while
the actual release can be performed outside the lock.
Signed-off-by: tao <tao.wangtao@honor.com>
---
mm/rmap.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/mm/rmap.c b/mm/rmap.c
index 93fa04c5a007..ea3557d3b56c 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -859,6 +859,94 @@ int anon_node_fork_with_prev(struct vm_area_struct *vma,
return anon_node_add_child(parent, vma, true);
}
+/* Returns true if untrack succeeds. */
+static bool anon_node_untrack_rmap_locked(struct anon_node *anon_nod,
+ struct vm_area_struct *vma, struct anon_node **release_node)
+{
+ struct anon_node *root = anon_nod->root;
+ struct anon_node *node = anon_nod;
+ struct anon_node *empty_nod = NULL;
+ bool test_empty = false;
+
+ do {
+ if (try_untrack_rmap(node, vma_rmap_base(vma), &test_empty))
+ break;
+ node = anon_node_next_rbc_child(anon_nod, node);
+ } while (node);
+
+ if (!test_empty) {
+ VM_BUG_ON_VMA(!node, vma);
+ return (bool)node;
+ }
+
+ if (node->nr_children == 0)
+ empty_nod = node;
+ else if (node == anon_nod) {
+ /* Replace an rbc child. */
+ node = anon_node_next_rbc_child(anon_nod, anon_nod);
+ if (node && try_reuse_anon_node(anon_nod,
+ atomic_long_read(&node->rbc))) {
+ atomic_long_set(&node->rbc, 0);
+ empty_nod = node;
+ }
+ }
+ /* may be released up the parent chain */
+ *release_node = empty_nod;
+ while (empty_nod && empty_nod != root) {
+ struct anon_node *parent = empty_nod->parent;
+
+ /* delete the empty_nod first, release later. */
+ list_del_init(&empty_nod->fractal_list);
+ empty_nod->mm = NULL;
+ if (--parent->nr_children == 0 && !anon_node_rmap_count(parent))
+ empty_nod = parent;
+ else {
+ empty_nod->parent = NULL;
+ break;
+ }
+ }
+ return true;
+}
+
+static void release_anon_nodes(struct anon_node *anon_nod)
+{
+ struct anon_node *root = anon_nod->root;
+
+ while (anon_nod) {
+ struct anon_node *parent = anon_nod->parent;
+
+ put_anon_node(anon_nod);
+ anon_nod = (anon_nod != root) ? parent : NULL;
+ }
+}
+
+/* Remove link between this VMA and its anon_node. */
+void unlink_anon_nodes(struct vm_area_struct *vma)
+{
+ struct anon_node *anon_nod = vma_anon_node(vma);
+ struct rw_semaphore *rmap_sem;
+ struct anon_node *release_node = NULL;
+
+ /* Always hold mmap lock, read-lock on unmap possibly. */
+ mmap_assert_locked(vma->vm_mm);
+
+ /* Unfaulted */
+ if (!anon_nod)
+ return;
+
+ rmap_sem = anon_node_rmap_sem(anon_nod->root);
+ down_write(rmap_sem);
+ anon_nod = vma_anon_node(vma); /* Recheck after locking. */
+ if (anon_nod) {
+ anon_node_untrack_rmap_locked(anon_nod, vma, &release_node);
+ vma->anon_vma = NULL;
+ }
+ up_write(rmap_sem);
+
+ if (release_node)
+ release_anon_nodes(release_node);
+}
+
#endif
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 07/13] mm: handle rmap_base changes for ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (5 preceding siblings ...)
2026-07-07 6:33 ` [RFC PATCH v2 06/13] mm: implement unlink_anon_nodes " tao
@ 2026-07-07 6:33 ` tao
2026-07-07 6:33 ` [RFC PATCH v2 08/13] mm: implement anonymous folio rmap " tao
` (8 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:33 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
relocate_vma_down() updates vm_start without changing vm_pgoff, which
causes the VMA rmap_base to change.
track_rmap may need memory allocation and requires both old and new
rmap_base, so this cannot be handled inside
anon_vma_interval_tree_pre_update_vma() and
anon_vma_interval_tree_post_update_vma().
Introduce vma_pre_update_rmap_base() and
vma_post_update_rmap_base(), and call them before vma_prepare()
and after vma_complete() in vma_expand(), commit_merge(), and
vma_shrink(), where rmap_base may change.
Signed-off-by: tao <tao.wangtao@honor.com>
---
mm/internal.h | 10 ++++++++-
mm/rmap.c | 60 ++++++++++++++++++++++++++++++++++++++++-----------
mm/vma.c | 20 +++++++++++++++--
3 files changed, 74 insertions(+), 16 deletions(-)
diff --git a/mm/internal.h b/mm/internal.h
index 710baf02575b..970f11b1af50 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -314,7 +314,12 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma);
int __anon_vma_prepare(struct vm_area_struct *vma);
void unlink_anon_vmas(struct vm_area_struct *vma);
-#ifdef CONFIG_ANON_VMA_FRACTAL
+#ifndef CONFIG_ANON_VMA_FRACTAL
+static inline int vma_pre_update_rmap_base(struct vm_area_struct *vma,
+ unsigned long diff) { return 0; }
+static inline void vma_post_update_rmap_base(struct vm_area_struct *vma,
+ unsigned long diff) {}
+#else
int anon_node_clone(struct vm_area_struct *vma, struct vm_area_struct *src,
enum vma_operation operation);
int anon_node_fork_with_prev(struct vm_area_struct *vma,
@@ -322,6 +327,9 @@ int anon_node_fork_with_prev(struct vm_area_struct *vma,
struct vm_area_struct *pvma_prev);
int __anon_node_prepare(struct vm_area_struct *vma);
void unlink_anon_nodes(struct vm_area_struct *vma);
+/* relocate_vma_down() changes vma_rmap_base. */
+int vma_pre_update_rmap_base(struct vm_area_struct *vma, unsigned long diff);
+void vma_post_update_rmap_base(struct vm_area_struct *vma, unsigned long diff);
#endif
diff --git a/mm/rmap.c b/mm/rmap.c
index ea3557d3b56c..fd22576b0f58 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -622,7 +622,7 @@ void __put_anon_node(struct anon_node *anon_nod)
}
static inline struct anon_node *anon_node_alloc(struct vm_area_struct *vma,
- struct anon_node *parent, bool is_fork)
+ struct anon_node *parent, unsigned long rmap_base, bool is_fork)
{
struct anon_node *anon_nod;
@@ -641,7 +641,7 @@ static inline struct anon_node *anon_node_alloc(struct vm_area_struct *vma,
anon_nod->nr_children = 0;
INIT_LIST_HEAD(&anon_nod->fractal_list);
anon_nod->mm = vma->vm_mm;
- atomic_long_set(&anon_nod->rbc, vma_rmap_base(vma) + 1);
+ atomic_long_set(&anon_nod->rbc, rmap_base + 1);
return anon_nod;
}
@@ -700,7 +700,7 @@ int __anon_node_prepare(struct vm_area_struct *vma)
return 0;
}
- anon_nod = anon_node_alloc(vma, NULL, false);
+ anon_nod = anon_node_alloc(vma, NULL, rmap_base, false);
if (unlikely(!anon_nod))
return -ENOMEM;
@@ -734,7 +734,7 @@ static inline void vma_set_anon_node(struct vm_area_struct *vma,
}
static int anon_node_add_child(struct anon_node *anon_nod,
- struct vm_area_struct *vma, bool is_fork)
+ struct vm_area_struct *vma, unsigned long rmap_base, bool is_fork)
{
struct anon_node *child;
struct anon_node *root = anon_nod->root;
@@ -743,7 +743,7 @@ static int anon_node_add_child(struct anon_node *anon_nod,
if (root->depth == 0)
root->depth = 1;
- child = anon_node_alloc(vma, anon_nod, is_fork);
+ child = anon_node_alloc(vma, anon_nod, rmap_base, is_fork);
if (!child)
return -ENOMEM;
@@ -764,10 +764,9 @@ static int anon_node_add_child(struct anon_node *anon_nod,
/* Returns 0 on success, non-zero on failure. */
static int anon_node_track_rmap(struct anon_node *anon_nod,
- struct vm_area_struct *vma)
+ struct vm_area_struct *vma, unsigned long rmap_base)
{
struct rw_semaphore *rmap_sem = anon_node_rmap_sem(anon_nod);
- unsigned long rmap_base = vma_rmap_base(vma);
struct anon_node *rbc_ch = anon_nod;
if (try_track_rmap(anon_nod, rmap_base))
@@ -782,7 +781,7 @@ static int anon_node_track_rmap(struct anon_node *anon_nod,
if (rbc_ch)
return 0;
- return anon_node_add_child(anon_nod, vma, false);
+ return anon_node_add_child(anon_nod, vma, rmap_base, false);
}
/* vma clones src anon_node and increments the count. */
@@ -796,7 +795,7 @@ int anon_node_clone(struct vm_area_struct *vma, struct vm_area_struct *src,
return 0;
VM_BUG_ON_VMA(vma->anon_vma != (void *)anon_nod, vma);
- return anon_node_track_rmap(anon_nod, vma);
+ return anon_node_track_rmap(anon_nod, vma, vma_rmap_base(vma));
}
static struct anon_node *fork_reuse_ancestor(struct anon_node *parent,
@@ -856,12 +855,13 @@ int anon_node_fork_with_prev(struct vm_area_struct *vma,
return 0;
/* 3) Fork a new anon_node. */
- return anon_node_add_child(parent, vma, true);
+ return anon_node_add_child(parent, vma, rmap_base, true);
}
/* Returns true if untrack succeeds. */
static bool anon_node_untrack_rmap_locked(struct anon_node *anon_nod,
- struct vm_area_struct *vma, struct anon_node **release_node)
+ struct vm_area_struct *vma, unsigned long rmap_base,
+ struct anon_node **release_node)
{
struct anon_node *root = anon_nod->root;
struct anon_node *node = anon_nod;
@@ -869,7 +869,7 @@ static bool anon_node_untrack_rmap_locked(struct anon_node *anon_nod,
bool test_empty = false;
do {
- if (try_untrack_rmap(node, vma_rmap_base(vma), &test_empty))
+ if (try_untrack_rmap(node, rmap_base, &test_empty))
break;
node = anon_node_next_rbc_child(anon_nod, node);
} while (node);
@@ -938,7 +938,8 @@ void unlink_anon_nodes(struct vm_area_struct *vma)
down_write(rmap_sem);
anon_nod = vma_anon_node(vma); /* Recheck after locking. */
if (anon_nod) {
- anon_node_untrack_rmap_locked(anon_nod, vma, &release_node);
+ anon_node_untrack_rmap_locked(anon_nod, vma,
+ vma_rmap_base(vma), &release_node);
vma->anon_vma = NULL;
}
up_write(rmap_sem);
@@ -947,6 +948,39 @@ void unlink_anon_nodes(struct vm_area_struct *vma)
release_anon_nodes(release_node);
}
+int vma_pre_update_rmap_base(struct vm_area_struct *vma, unsigned long diff)
+{
+ struct anon_node *anon_nod = vma_anon_node(vma);
+ int err;
+
+ vma_assert_write_locked(vma);
+ if (!anon_nod || !diff)
+ return 0;
+
+ err = anon_node_track_rmap(anon_nod, vma, vma_rmap_base(vma) + diff);
+ if (!err)
+ down_write(anon_node_rmap_sem(anon_nod));
+ return err;
+}
+
+void vma_post_update_rmap_base(struct vm_area_struct *vma, unsigned long diff)
+{
+ struct anon_node *anon_nod = vma_anon_node(vma);
+ struct rw_semaphore *rmap_sem;
+ struct anon_node *release_node = NULL;
+
+ vma_assert_write_locked(vma);
+ if (!anon_nod || !diff)
+ return;
+
+ rmap_sem = anon_node_rmap_sem(anon_nod);
+ rwsem_assert_held_write(rmap_sem);
+ anon_node_untrack_rmap_locked(anon_nod, vma,
+ vma_rmap_base(vma) - diff, &release_node);
+ up_write(rmap_sem);
+ if (release_node)
+ release_anon_nodes(release_node);
+}
#endif
diff --git a/mm/vma.c b/mm/vma.c
index 9eea2850818a..d2996369e30d 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -732,6 +732,7 @@ static int commit_merge(struct vma_merge_struct *vmg)
{
struct vm_area_struct *vma;
struct vma_prepare vp;
+ unsigned long rbase_diff;
if (vmg->__adjust_next_start) {
/* We manipulate middle and adjust next, which is the target. */
@@ -748,12 +749,16 @@ static int commit_merge(struct vma_merge_struct *vmg)
/*
* If vmg->give_up_on_oom is set, we're safe, because we don't actually
* manipulate any VMAs until we succeed at preallocation.
- *
- * Past this point, we will not return an error.
*/
if (vma_iter_prealloc(vmg->vmi, vma))
return -ENOMEM;
+ rbase_diff = rmap_base(vmg->start, vmg->pgoff) - vma_rmap_base(vma);
+ if (rbase_diff && vma_pre_update_rmap_base(vma, rbase_diff)) {
+ vma_iter_free(vmg->vmi);
+ return -ENOMEM;
+ }
+
vma_prepare(&vp);
/*
* THP pages may need to do additional splits if we increase
@@ -766,6 +771,8 @@ static int commit_merge(struct vma_merge_struct *vmg)
vma_iter_store_overwrite(vmg->vmi, vmg->target);
vma_complete(&vp, vmg->vmi, vma->vm_mm);
+ if (rbase_diff)
+ vma_post_update_rmap_base(vma, rbase_diff);
return 0;
}
@@ -1248,6 +1255,7 @@ int vma_shrink(struct vma_iterator *vmi, struct vm_area_struct *vma,
unsigned long start, unsigned long end, pgoff_t pgoff)
{
struct vma_prepare vp;
+ unsigned long rbase_diff;
WARN_ON((vma->vm_start != start) && (vma->vm_end != end));
@@ -1261,6 +1269,12 @@ int vma_shrink(struct vma_iterator *vmi, struct vm_area_struct *vma,
vma_start_write(vma);
+ rbase_diff = rmap_base(start, pgoff) - vma_rmap_base(vma);
+ if (rbase_diff && vma_pre_update_rmap_base(vma, rbase_diff)) {
+ vma_iter_free(vmi);
+ return -ENOMEM;
+ }
+
init_vma_prep(&vp, vma);
vma_prepare(&vp);
vma_adjust_trans_huge(vma, start, end, NULL);
@@ -1268,6 +1282,8 @@ int vma_shrink(struct vma_iterator *vmi, struct vm_area_struct *vma,
vma_iter_clear(vmi);
vma_set_range(vma, start, end, pgoff);
vma_complete(&vp, vmi, vma->vm_mm);
+ if (rbase_diff)
+ vma_post_update_rmap_base(vma, rbase_diff);
validate_mm(vma->vm_mm);
return 0;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 08/13] mm: implement anonymous folio rmap for ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (6 preceding siblings ...)
2026-07-07 6:33 ` [RFC PATCH v2 07/13] mm: handle rmap_base changes " tao
@ 2026-07-07 6:33 ` tao
2026-07-07 6:33 ` [RFC PATCH v2 09/13] mm: prepare anon_node replacement " tao
` (7 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:33 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Add the ANON_RMAP_FOREACH_VMA() macro to encapsulate rmap traversal and
hide implementation differences from external users.
During reverse mapping of an anonymous folio, traversal first handles the
folio's own anon_node, then iterates through its descendants. Since large
folios may span multiple VMAs, VMA updates are used to advance pgoff, and
pgoff is used to determine completion of the current anon_node traversal.
For KSM, the VMA is located directly using the supplied address.
Signed-off-by: tao <tao.wangtao@honor.com>
---
mm/internal.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
mm/rmap.c | 41 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 90 insertions(+)
diff --git a/mm/internal.h b/mm/internal.h
index 970f11b1af50..b38c7b4c2a8f 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -296,6 +296,55 @@ static inline unsigned long anon_node_rmap_count(struct anon_node *anon_nod)
return atomic_long_read(&anon_nod->rbc) & ANON_RMAP_BASE_COUNT_MAX;
}
+static inline unsigned long anon_node_rmap_address(struct anon_node *anon_nod,
+ unsigned long pgoff)
+{
+ if (!anon_node_rmap_count(anon_nod))
+ return 0;
+ return anon_node_rmap_base(anon_nod) + (pgoff << PAGE_SHIFT);
+}
+
+static inline struct vm_area_struct *anon_node_lookup_vma(
+ struct anon_node *anon_nod, unsigned long addr, unsigned long pgoff)
+{
+ struct vm_area_struct *vma;
+
+ if (!addr)
+ addr = anon_node_rmap_address(anon_nod, pgoff);
+ if (!anon_nod->mm || !addr)
+ return NULL;
+
+ vma = vma_lookup(anon_nod->mm, addr);
+ return (vma && vma->anon_vma == (void *)anon_nod) ? vma : NULL;
+}
+
+void vm_lock_anon_node(struct mm_struct *mm, struct anon_node *anon_nod);
+void vm_unlock_anon_node(struct anon_node *anon_nod);
+int anon_node_trylock_rmap(struct anon_node *anon_nod);
+void anon_node_lock_rmap(struct anon_node *anon_nod);
+void anon_node_unlock_rmap(struct anon_node *anon_nod);
+
+#define ANON_RMAP_FOREACH_VMA(anon_vma, addr, start, last, rmap_proc_vma) \
+do { \
+ struct vm_area_struct *vma = NULL; /* must be named vma */ \
+ struct anon_node *_anon_nod = (void *)(anon_vma); \
+ struct anon_node *_nod = _anon_nod; \
+ unsigned long _pgoff = (start); \
+ unsigned long _nr = 0, _total = 1; \
+ \
+ while (_nod) { \
+ vma = anon_node_lookup_vma(_nod, (addr), _pgoff); \
+ if (vma) \
+ (rmap_proc_vma); \
+ _pgoff = vma ? vma->vm_pgoff + vma_pages(vma) : _pgoff + 1; \
+ if (!(addr) && _pgoff <= (last)) \
+ continue; \
+ _pgoff = (start); \
+ BUG_ON(++_nr > (_total += _nod->nr_children)); \
+ _nod = anon_node_next_descendant(_anon_nod, _nod); \
+ } \
+} while (0)
+
#endif
struct anon_vma *folio_get_anon_vma(const struct folio *folio);
diff --git a/mm/rmap.c b/mm/rmap.c
index fd22576b0f58..1551e9d2121d 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -982,6 +982,47 @@ void vma_post_update_rmap_base(struct vm_area_struct *vma, unsigned long diff)
release_anon_nodes(release_node);
}
+#define ANON_VM_LOCK_REF VM_REFCNT_EXCLUDE_READERS_FLAG
+
+void vm_lock_anon_node(struct mm_struct *mm, struct anon_node *anon_nod)
+{
+ atomic_t *vm_lock_ref = anon_node_vm_lock_ref(anon_nod);
+ struct rw_semaphore *rmap_sem = anon_node_rmap_sem(anon_nod);
+
+ if (!(atomic_read(vm_lock_ref) & ANON_VM_LOCK_REF)) {
+ down_write_nest_lock(rmap_sem, &mm->mmap_lock);
+ BUG_ON(atomic_read(vm_lock_ref));
+ atomic_set(vm_lock_ref, ANON_VM_LOCK_REF);
+ }
+}
+
+void vm_unlock_anon_node(struct anon_node *anon_nod)
+{
+ atomic_t *vm_lock_ref = anon_node_vm_lock_ref(anon_nod);
+ struct rw_semaphore *rmap_sem = anon_node_rmap_sem(anon_nod);
+
+ if (atomic_read(vm_lock_ref) & ANON_VM_LOCK_REF) {
+ BUG_ON(atomic_read(vm_lock_ref) != ANON_VM_LOCK_REF);
+ atomic_set(vm_lock_ref, 0);
+ up_write(rmap_sem);
+ }
+}
+
+int anon_node_trylock_rmap(struct anon_node *anon_nod)
+{
+ return down_read_trylock(anon_node_rmap_sem(anon_nod));
+}
+
+void anon_node_lock_rmap(struct anon_node *anon_nod)
+{
+ down_read(anon_node_rmap_sem(anon_nod));
+}
+
+void anon_node_unlock_rmap(struct anon_node *anon_nod)
+{
+ up_read(anon_node_rmap_sem(anon_nod));
+}
+
#endif
/*
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 09/13] mm: prepare anon_node replacement for ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (7 preceding siblings ...)
2026-07-07 6:33 ` [RFC PATCH v2 08/13] mm: implement anonymous folio rmap " tao
@ 2026-07-07 6:33 ` tao
2026-07-07 6:33 ` [RFC PATCH v2 10/13] mm: replace anon_vma with anon_node " tao
` (6 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:33 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Since this replacement involves large code changes, introduce macros
in vma.c first to isolate code differences and split the changes into
smaller patches.
Signed-off-by: tao <tao.wangtao@honor.com>
---
mm/internal.h | 16 ++++++++++++++++
mm/rmap.c | 2 +-
mm/vma.c | 52 +++++++++++++++++++++++++++++++++++++++++----------
3 files changed, 59 insertions(+), 11 deletions(-)
diff --git a/mm/internal.h b/mm/internal.h
index b38c7b4c2a8f..6f0ff492a89c 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -296,6 +296,22 @@ static inline unsigned long anon_node_rmap_count(struct anon_node *anon_nod)
return atomic_long_read(&anon_nod->rbc) & ANON_RMAP_BASE_COUNT_MAX;
}
+static inline bool anon_node_can_share(struct anon_node *anon_nod)
+{
+ return anon_nod->depth == 0 &&
+ anon_node_rmap_count(anon_nod) <= ANON_NODE_SHARE_LIMIT;
+}
+
+static inline void anon_node_verify(struct anon_node *node)
+{
+ struct anon_node *rbc_ch = node;
+
+ WARN_ON_ONCE(anon_node_rmap_count(node) == 0 && node->nr_children == 0);
+ while ((rbc_ch = anon_node_next_rbc_child(node, rbc_ch)) != NULL) {
+ WARN_ON_ONCE(anon_node_rmap_count(rbc_ch) == 0);
+ }
+}
+
static inline unsigned long anon_node_rmap_address(struct anon_node *anon_nod,
unsigned long pgoff)
{
diff --git a/mm/rmap.c b/mm/rmap.c
index 1551e9d2121d..12c6a1bdff3f 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -690,7 +690,7 @@ int __anon_node_prepare(struct vm_area_struct *vma)
mmap_assert_locked(mm);
might_sleep();
- anon_nod = NULL; /* Fix find_mergeable_anon_vma(vma) later. */
+ anon_nod = (struct anon_node *)find_mergeable_anon_vma(vma);
if (anon_nod) {
anon_new = (struct anon_vma *)anon_nod;
if (try_track_rmap(anon_nod, rmap_base) &&
diff --git a/mm/vma.c b/mm/vma.c
index d2996369e30d..02178112daa0 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -269,19 +269,23 @@ static void __remove_shared_vm_struct(struct vm_area_struct *vma,
static void
anon_vma_interval_tree_pre_update_vma(struct vm_area_struct *vma)
{
+#ifndef CONFIG_ANON_VMA_FRACTAL
struct anon_vma_chain *avc;
list_for_each_entry(avc, &vma->anon_vma_chain, same_vma)
anon_vma_interval_tree_remove(avc, &avc->anon_vma->rb_root);
+#endif
}
static void
anon_vma_interval_tree_post_update_vma(struct vm_area_struct *vma)
{
+#ifndef CONFIG_ANON_VMA_FRACTAL
struct anon_vma_chain *avc;
list_for_each_entry(avc, &vma->anon_vma_chain, same_vma)
anon_vma_interval_tree_insert(avc, &avc->anon_vma->rb_root);
+#endif
}
/*
@@ -651,10 +655,6 @@ void validate_mm(struct mm_struct *mm)
mt_validate(&mm->mm_mt);
for_each_vma(vmi, vma) {
-#ifdef CONFIG_DEBUG_VM_RB
- struct anon_vma *anon_vma = vma->anon_vma;
- struct anon_vma_chain *avc;
-#endif
unsigned long vmi_start, vmi_end;
bool warn = 0;
@@ -675,12 +675,19 @@ void validate_mm(struct mm_struct *mm)
vma_iter_dump_tree(&vmi);
}
-#ifdef CONFIG_DEBUG_VM_RB
- if (anon_vma) {
+#if CONFIG_DEBUG_VM_RB
+ if (vma->anon_vma) {
+#ifdef CONFIG_ANON_VMA_FRACTAL
+ anon_node_verify((void *)vma->anon_vma);
+#else
+ struct anon_vma *anon_vma = vma->anon_vma;
+ struct anon_vma_chain *avc;
+
anon_vma_lock_read(anon_vma);
list_for_each_entry(avc, &vma->anon_vma_chain, same_vma)
anon_vma_interval_tree_verify(avc);
anon_vma_unlock_read(anon_vma);
+#endif
}
#endif
/* Check for a infinite loop */
@@ -2027,8 +2034,13 @@ static struct anon_vma *reusable_anon_vma(struct vm_area_struct *old,
if (anon_vma_compatible(a, b)) {
struct anon_vma *anon_vma = READ_ONCE(old->anon_vma);
+#ifdef CONFIG_ANON_VMA_FRACTAL
+ if (anon_vma && anon_node_can_share((void *)anon_vma))
+ return anon_vma;
+#else
if (anon_vma && list_is_singular(&old->anon_vma_chain))
return anon_vma;
+#endif
}
return NULL;
}
@@ -2157,6 +2169,9 @@ static DEFINE_MUTEX(mm_all_locks_mutex);
static void vm_lock_anon_vma(struct mm_struct *mm, struct anon_vma *anon_vma)
{
+#ifdef CONFIG_ANON_VMA_FRACTAL
+ vm_lock_anon_node(mm, (void *)anon_vma);
+#else
if (!test_bit(0, (unsigned long *) &anon_vma->root->rb_root.rb_root.rb_node)) {
/*
* The LSB of head.next can't change from under us
@@ -2176,6 +2191,7 @@ static void vm_lock_anon_vma(struct mm_struct *mm, struct anon_vma *anon_vma)
&anon_vma->root->rb_root.rb_root.rb_node))
BUG();
}
+#endif
}
static void vm_lock_mapping(struct mm_struct *mm, struct address_space *mapping)
@@ -2237,7 +2253,6 @@ static void vm_lock_mapping(struct mm_struct *mm, struct address_space *mapping)
int mm_take_all_locks(struct mm_struct *mm)
{
struct vm_area_struct *vma;
- struct anon_vma_chain *avc;
VMA_ITERATOR(vmi, mm, 0);
mmap_assert_write_locked(mm);
@@ -2278,9 +2293,16 @@ int mm_take_all_locks(struct mm_struct *mm)
for_each_vma(vmi, vma) {
if (signal_pending(current))
goto out_unlock;
- if (vma->anon_vma)
+ if (vma->anon_vma) {
+#ifdef CONFIG_ANON_VMA_FRACTAL
+ vm_lock_anon_vma(mm, vma->anon_vma);
+#else
+ struct anon_vma_chain *avc;
+
list_for_each_entry(avc, &vma->anon_vma_chain, same_vma)
vm_lock_anon_vma(mm, avc->anon_vma);
+#endif
+ }
}
return 0;
@@ -2292,6 +2314,9 @@ int mm_take_all_locks(struct mm_struct *mm)
static void vm_unlock_anon_vma(struct anon_vma *anon_vma)
{
+#ifdef CONFIG_ANON_VMA_FRACTAL
+ vm_unlock_anon_node((void *)anon_vma);
+#else
if (test_bit(0, (unsigned long *) &anon_vma->root->rb_root.rb_root.rb_node)) {
/*
* The LSB of head.next can't change to 0 from under
@@ -2310,6 +2335,7 @@ static void vm_unlock_anon_vma(struct anon_vma *anon_vma)
BUG();
anon_vma_unlock_write(anon_vma);
}
+#endif
}
static void vm_unlock_mapping(struct address_space *mapping)
@@ -2333,16 +2359,22 @@ static void vm_unlock_mapping(struct address_space *mapping)
void mm_drop_all_locks(struct mm_struct *mm)
{
struct vm_area_struct *vma;
- struct anon_vma_chain *avc;
VMA_ITERATOR(vmi, mm, 0);
mmap_assert_write_locked(mm);
BUG_ON(!mutex_is_locked(&mm_all_locks_mutex));
for_each_vma(vmi, vma) {
- if (vma->anon_vma)
+ if (vma->anon_vma) {
+#ifdef CONFIG_ANON_VMA_FRACTAL
+ vm_unlock_anon_vma(vma->anon_vma);
+#else
+ struct anon_vma_chain *avc;
+
list_for_each_entry(avc, &vma->anon_vma_chain, same_vma)
vm_unlock_anon_vma(avc->anon_vma);
+#endif
+ }
if (vma->vm_file && vma->vm_file->f_mapping)
vm_unlock_mapping(vma->vm_file->f_mapping);
}
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 10/13] mm: replace anon_vma with anon_node for ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (8 preceding siblings ...)
2026-07-07 6:33 ` [RFC PATCH v2 09/13] mm: prepare anon_node replacement " tao
@ 2026-07-07 6:33 ` tao
2026-07-07 6:33 ` [RFC PATCH v2 11/13] mm: optimize rmap for ANON_VMA_FRACTAL with PVL tao
` (5 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:33 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Replace anon_vma with anon_node while keeping existing APIs to
minimize code changes.
Remove anon_vma_chain from VMA to save 16 bytes on 64-bit.
Use ANON_RMAP_FOREACH_VMA for anonymous reverse mapping walks in
ksm.c, memory-failure.c, and rmap.c.
Keep CONFIG_ANON_VMA_FRACTAL enabling in a separate patch for
easier control.
Signed-off-by: tao <tao.wangtao@honor.com>
---
include/linux/mm.h | 2 ++
include/linux/mm_types.h | 2 ++
include/linux/rmap.h | 5 +++-
mm/internal.h | 58 +++++++++++++++++++++++++++++++++++-----
mm/ksm.c | 19 ++++---------
mm/memory-failure.c | 8 ++----
mm/mmap.c | 7 ++---
mm/rmap.c | 43 +++++++++++++++--------------
mm/vma.c | 4 +++
mm/vma_init.c | 2 ++
10 files changed, 100 insertions(+), 50 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 485df9c2dbdd..3fc2f92040b0 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -970,7 +970,9 @@ static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm)
memset(vma, 0, sizeof(*vma));
vma->vm_mm = mm;
vma->vm_ops = &vma_dummy_vm_ops;
+#ifndef CONFIG_ANON_VMA_FRACTAL
INIT_LIST_HEAD(&vma->anon_vma_chain);
+#endif
vma_lock_init(vma, false);
}
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index b18c2b2e7d2c..b98090e6cfdd 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -970,8 +970,10 @@ struct vm_area_struct {
* can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack
* or brk vma (with NULL file) can only be in an anon_vma list.
*/
+#ifndef CONFIG_ANON_VMA_FRACTAL
struct list_head anon_vma_chain; /* Serialized by mmap_lock &
* page_table_lock */
+#endif
struct anon_vma *anon_vma; /* Serialized by page_table_lock */
/* Function pointers to deal with this struct. */
diff --git a/include/linux/rmap.h b/include/linux/rmap.h
index 75738f4ede1f..81ad29f3c392 100644
--- a/include/linux/rmap.h
+++ b/include/linux/rmap.h
@@ -15,6 +15,7 @@
#include <linux/memremap.h>
#include <linux/bit_spinlock.h>
+#ifndef CONFIG_ANON_VMA_FRACTAL
/*
* The anon_vma heads a list of private "related" vmas, to scan if
* an anonymous page pointing to this anon_vma needs to be unmapped:
@@ -67,7 +68,9 @@ struct anon_vma {
struct rb_root_cached rb_root;
};
-#ifdef CONFIG_ANON_VMA_FRACTAL
+#else
+
+#define anon_node anon_vma
/* struct anon_node - Replace anon_vma for anonymous page reverse mapping. */
struct anon_node {
diff --git a/mm/internal.h b/mm/internal.h
index 6f0ff492a89c..205f1b5226aa 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -212,6 +212,7 @@ static inline unsigned long vma_rmap_base(const struct vm_area_struct *vma)
return rmap_base(vma->vm_start, vma->vm_pgoff);
}
+#ifndef CONFIG_ANON_VMA_FRACTAL
static inline void get_anon_vma(struct anon_vma *anon_vma)
{
atomic_inc(&anon_vma->refcount);
@@ -255,7 +256,20 @@ static inline void anon_vma_unlock_read(struct anon_vma *anon_vma)
up_read(&anon_vma->root->rwsem);
}
-#ifdef CONFIG_ANON_VMA_FRACTAL
+/* Provide unified anon_vma VMA iteration interfaces. */
+#define ANON_RMAP_FOREACH_VMA(anon_vma, addr, start, last, rmap_proc_vma) \
+do { \
+ struct vm_area_struct *vma; /* must be named vma */ \
+ struct rb_root_cached *_rb_root = &(anon_vma)->rb_root; \
+ struct anon_vma_chain *_avc; \
+ \
+ anon_vma_interval_tree_foreach(_avc, _rb_root, start, last) { \
+ vma = _avc->vma; \
+ (rmap_proc_vma); \
+ } \
+} while (0)
+
+#else
static inline struct anon_node *anon_node_next_rbc_child(
struct anon_node *anon_nod, struct anon_node *node)
@@ -361,6 +375,36 @@ do { \
} \
} while (0)
+static inline void get_anon_vma(struct anon_vma *anon_vma)
+{
+ get_anon_node((void *)anon_vma);
+}
+
+static inline void put_anon_vma(struct anon_vma *anon_vma)
+{
+ put_anon_node((void *)anon_vma);
+}
+
+/* Huge pages are protected by folio_lock(), these APIs no longer needed. */
+static inline void anon_vma_lock_write(struct anon_vma *anon_vma) {}
+static inline int anon_vma_trylock_write(struct anon_vma *anon_vma) { return 1; }
+static inline void anon_vma_unlock_write(struct anon_vma *anon_vma) {}
+
+/* These are still used as rmap lock APIs. */
+static inline void anon_vma_lock_read(struct anon_vma *anon_vma)
+{
+ anon_node_lock_rmap((void *)anon_vma);
+}
+
+static inline int anon_vma_trylock_read(struct anon_vma *anon_vma)
+{
+ return anon_node_trylock_rmap((void *)anon_vma);
+}
+
+static inline void anon_vma_unlock_read(struct anon_vma *anon_vma)
+{
+ anon_node_unlock_rmap((void *)anon_vma);
+}
#endif
struct anon_vma *folio_get_anon_vma(const struct folio *folio);
@@ -380,18 +424,20 @@ int __anon_vma_prepare(struct vm_area_struct *vma);
void unlink_anon_vmas(struct vm_area_struct *vma);
#ifndef CONFIG_ANON_VMA_FRACTAL
+static inline int anon_vma_fork_with_prev(struct vm_area_struct *vma,
+ struct vm_area_struct *pvma, struct vm_area_struct *vma_prev,
+ struct vm_area_struct *pvma_prev)
+{
+ return anon_vma_fork(vma, pvma);
+}
static inline int vma_pre_update_rmap_base(struct vm_area_struct *vma,
unsigned long diff) { return 0; }
static inline void vma_post_update_rmap_base(struct vm_area_struct *vma,
unsigned long diff) {}
#else
-int anon_node_clone(struct vm_area_struct *vma, struct vm_area_struct *src,
- enum vma_operation operation);
-int anon_node_fork_with_prev(struct vm_area_struct *vma,
+int anon_vma_fork_with_prev(struct vm_area_struct *vma,
struct vm_area_struct *pvma, struct vm_area_struct *vma_prev,
struct vm_area_struct *pvma_prev);
-int __anon_node_prepare(struct vm_area_struct *vma);
-void unlink_anon_nodes(struct vm_area_struct *vma);
/* relocate_vma_down() changes vma_rmap_base. */
int vma_pre_update_rmap_base(struct vm_area_struct *vma, unsigned long diff);
void vma_post_update_rmap_base(struct vm_area_struct *vma, unsigned long diff);
diff --git a/mm/ksm.c b/mm/ksm.c
index 7d5b76478f0b..176b4da494ea 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -3174,8 +3174,6 @@ void rmap_walk_ksm(struct folio *folio, struct rmap_walk_control *rwc)
/* Ignore the stable/unstable/sqnr flags */
const unsigned long addr = rmap_item->address & PAGE_MASK;
struct anon_vma *anon_vma = rmap_item->anon_vma;
- struct anon_vma_chain *vmac;
- struct vm_area_struct *vma;
cond_resched();
if (!anon_vma_trylock_read(anon_vma)) {
@@ -3186,11 +3184,8 @@ void rmap_walk_ksm(struct folio *folio, struct rmap_walk_control *rwc)
anon_vma_lock_read(anon_vma);
}
- anon_vma_interval_tree_foreach(vmac, &anon_vma->rb_root,
- 0, ULONG_MAX) {
-
+ ANON_RMAP_FOREACH_VMA(anon_vma, addr, 0, ULONG_MAX, ({
cond_resched();
- vma = vmac->vma;
if (addr < vma->vm_start || addr >= vma->vm_end)
continue;
@@ -3214,7 +3209,7 @@ void rmap_walk_ksm(struct folio *folio, struct rmap_walk_control *rwc)
anon_vma_unlock_read(anon_vma);
return;
}
- }
+ }));
anon_vma_unlock_read(anon_vma);
}
if (!search_new_forks++)
@@ -3230,7 +3225,6 @@ void collect_procs_ksm(const struct folio *folio, const struct page *page,
{
struct ksm_stable_node *stable_node;
struct ksm_rmap_item *rmap_item;
- struct vm_area_struct *vma;
struct task_struct *tsk;
stable_node = folio_stable_node(folio);
@@ -3242,22 +3236,19 @@ void collect_procs_ksm(const struct folio *folio, const struct page *page,
anon_vma_lock_read(av);
rcu_read_lock();
for_each_process(tsk) {
- struct anon_vma_chain *vmac;
unsigned long addr;
struct task_struct *t =
task_early_kill(tsk, force_early);
if (!t)
continue;
- anon_vma_interval_tree_foreach(vmac, &av->rb_root, 0,
- ULONG_MAX)
- {
- vma = vmac->vma;
+ ANON_RMAP_FOREACH_VMA(av, rmap_item->address,
+ 0, ULONG_MAX, ({
if (vma->vm_mm == t->mm) {
addr = rmap_item->address & PAGE_MASK;
add_to_kill_ksm(t, page, vma, to_kill,
addr);
}
- }
+ }));
}
rcu_read_unlock();
anon_vma_unlock_read(av);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 51508a55c405..1f44e5f76a42 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -545,21 +545,17 @@ static void collect_procs_anon(const struct folio *folio,
pgoff = page_pgoff(folio, page);
rcu_read_lock();
for_each_process(tsk) {
- struct vm_area_struct *vma;
- struct anon_vma_chain *vmac;
struct task_struct *t = task_early_kill(tsk, force_early);
unsigned long addr;
if (!t)
continue;
- anon_vma_interval_tree_foreach(vmac, &av->rb_root,
- pgoff, pgoff) {
- vma = vmac->vma;
+ ANON_RMAP_FOREACH_VMA(av, 0, pgoff, pgoff, ({
if (vma->vm_mm != t->mm)
continue;
addr = page_mapped_in_vma(page, vma);
add_to_kill_anon_file(t, page, vma, to_kill, addr);
- }
+ }));
}
rcu_read_unlock();
anon_vma_unlock_read(av);
diff --git a/mm/mmap.c b/mm/mmap.c
index 2311ae7c2ff4..c283e0de819a 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1730,7 +1730,8 @@ bool mmap_read_lock_maybe_expand(struct mm_struct *mm,
__latent_entropy int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
{
- struct vm_area_struct *mpnt, *tmp;
+ struct vm_area_struct *mpnt = NULL, *tmp = NULL;
+ struct vm_area_struct *prev = NULL, *tmp_prev = NULL;
int retval;
unsigned long charge = 0;
LIST_HEAD(uf);
@@ -1759,7 +1760,7 @@ __latent_entropy int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
goto out;
mt_clear_in_rcu(vmi.mas.tree);
- for_each_vma(vmi, mpnt) {
+ for ( ; (mpnt = vma_next(&vmi)) != NULL; prev = mpnt, tmp_prev = tmp) {
struct file *file;
retval = vma_start_write_killable(mpnt);
@@ -1800,7 +1801,7 @@ __latent_entropy int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
* copy page for current vma.
*/
tmp->anon_vma = NULL;
- } else if (anon_vma_fork(tmp, mpnt))
+ } else if (anon_vma_fork_with_prev(tmp, mpnt, tmp_prev, prev))
goto fail_nomem_anon_vma_fork;
vm_flags_clear(tmp, VM_LOCKED_MASK);
/*
diff --git a/mm/rmap.c b/mm/rmap.c
index 12c6a1bdff3f..11239838b722 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -84,6 +84,7 @@
#include "internal.h"
#include "swap.h"
+#ifndef CONFIG_ANON_VMA_FRACTAL
static struct kmem_cache *anon_vma_cachep;
static struct kmem_cache *anon_vma_chain_cachep;
@@ -558,7 +559,16 @@ void __init anon_vma_init(void)
SLAB_PANIC|SLAB_ACCOUNT);
}
-#ifdef CONFIG_ANON_VMA_FRACTAL
+void __put_anon_vma(struct anon_vma *anon_vma)
+{
+ struct anon_vma *root = anon_vma->root;
+
+ anon_vma_free(anon_vma);
+ if (root != anon_vma && atomic_dec_and_test(&root->refcount))
+ anon_vma_free(root);
+}
+
+#else
static struct kmem_cache *anon_node_cachep;
@@ -581,7 +591,7 @@ static void anon_node_ctor(void *data)
atomic_set(&anon_nod->refcount, 0);
}
-static inline void anon_vma_fractal_init(void)
+void __init anon_vma_init(void)
{
struct kmem_cache_args args = {
.use_freeptr_offset = true,
@@ -621,6 +631,11 @@ void __put_anon_node(struct anon_node *anon_nod)
anon_node_free(root);
}
+static inline void __put_anon_vma(struct anon_vma *anon_vma)
+{
+ __put_anon_node((struct anon_node *)(anon_vma));
+}
+
static inline struct anon_node *anon_node_alloc(struct vm_area_struct *vma,
struct anon_node *parent, unsigned long rmap_base, bool is_fork)
{
@@ -679,7 +694,7 @@ static bool try_reuse_anon_node(struct anon_node *anon_nod,
}
/* attach an anon_node to the VMA. */
-int __anon_node_prepare(struct vm_area_struct *vma)
+int __anon_vma_prepare(struct vm_area_struct *vma)
{
struct mm_struct *mm = vma->vm_mm;
unsigned long rmap_base = vma_rmap_base(vma);
@@ -785,7 +800,7 @@ static int anon_node_track_rmap(struct anon_node *anon_nod,
}
/* vma clones src anon_node and increments the count. */
-int anon_node_clone(struct vm_area_struct *vma, struct vm_area_struct *src,
+int anon_vma_clone(struct vm_area_struct *vma, struct vm_area_struct *src,
enum vma_operation operation)
{
struct anon_node *anon_nod = vma_anon_node(src);
@@ -827,7 +842,7 @@ static struct anon_node *fork_reuse_ancestor(struct anon_node *parent,
}
/* Clone an anon_node from prev or fork one from the parent pvma. */
-int anon_node_fork_with_prev(struct vm_area_struct *vma,
+int anon_vma_fork_with_prev(struct vm_area_struct *vma,
struct vm_area_struct *pvma, struct vm_area_struct *prev,
struct vm_area_struct *pvma_prev)
{
@@ -921,7 +936,7 @@ static void release_anon_nodes(struct anon_node *anon_nod)
}
/* Remove link between this VMA and its anon_node. */
-void unlink_anon_nodes(struct vm_area_struct *vma)
+void unlink_anon_vmas(struct vm_area_struct *vma)
{
struct anon_node *anon_nod = vma_anon_node(vma);
struct rw_semaphore *rmap_sem;
@@ -3377,15 +3392,6 @@ struct page *make_device_exclusive(struct mm_struct *mm, unsigned long addr,
EXPORT_SYMBOL_GPL(make_device_exclusive);
#endif
-void __put_anon_vma(struct anon_vma *anon_vma)
-{
- struct anon_vma *root = anon_vma->root;
-
- anon_vma_free(anon_vma);
- if (root != anon_vma && atomic_dec_and_test(&root->refcount))
- anon_vma_free(root);
-}
-
static struct anon_vma *rmap_walk_anon_lock(const struct folio *folio,
struct rmap_walk_control *rwc)
{
@@ -3433,7 +3439,6 @@ static void rmap_walk_anon(struct folio *folio,
{
struct anon_vma *anon_vma;
pgoff_t pgoff_start, pgoff_end;
- struct anon_vma_chain *avc;
/*
* The folio lock ensures that folio->mapping can't be changed under us
@@ -3453,9 +3458,7 @@ static void rmap_walk_anon(struct folio *folio,
pgoff_start = folio_pgoff(folio);
pgoff_end = pgoff_start + folio_nr_pages(folio) - 1;
- anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root,
- pgoff_start, pgoff_end) {
- struct vm_area_struct *vma = avc->vma;
+ ANON_RMAP_FOREACH_VMA(anon_vma, 0, pgoff_start, pgoff_end, ({
unsigned long address = vma_address(vma, pgoff_start,
folio_nr_pages(folio));
@@ -3469,7 +3472,7 @@ static void rmap_walk_anon(struct folio *folio,
break;
if (rwc->done && rwc->done(folio))
break;
- }
+ }));
if (!locked)
anon_vma_unlock_read(anon_vma);
diff --git a/mm/vma.c b/mm/vma.c
index 02178112daa0..76294a7201ca 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -78,7 +78,11 @@ static bool vma_is_fork_child(struct vm_area_struct *vma)
* parents. This can improve scalability caused by the anon_vma root
* lock.
*/
+#ifndef CONFIG_ANON_VMA_FRACTAL
return vma && vma->anon_vma && !list_is_singular(&vma->anon_vma_chain);
+#else
+ return vma && vma->anon_vma && vma->anon_vma->depth > 1;
+#endif
}
static inline bool is_mergeable_vma(struct vma_merge_struct *vmg, bool merge_next)
diff --git a/mm/vma_init.c b/mm/vma_init.c
index 3c0b65950510..ffb7359ea47f 100644
--- a/mm/vma_init.c
+++ b/mm/vma_init.c
@@ -134,7 +134,9 @@ struct vm_area_struct *vm_area_dup(struct vm_area_struct *orig)
return NULL;
}
vma_lock_init(new, true);
+#ifndef CONFIG_ANON_VMA_FRACTAL
INIT_LIST_HEAD(&new->anon_vma_chain);
+#endif
vma_numab_state_init(new);
dup_anon_vma_name(orig, new);
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 11/13] mm: optimize rmap for ANON_VMA_FRACTAL with PVL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (9 preceding siblings ...)
2026-07-07 6:33 ` [RFC PATCH v2 10/13] mm: replace anon_vma with anon_node " tao
@ 2026-07-07 6:33 ` tao
2026-07-07 6:33 ` [RFC PATCH v2 12/13] mm: shared semaphores for ANON_VMA_FRACTAL tao
` (4 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:33 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Leaf anon_nodes have no children, and a given address can belong to at
most one VMA. In this case, PVL can protect the rmap.
Signed-off-by: tao <tao.wangtao@honor.com>
---
mm/internal.h | 22 ++++++++++++++++++
mm/rmap.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 83 insertions(+), 3 deletions(-)
diff --git a/mm/internal.h b/mm/internal.h
index 205f1b5226aa..f8dd2120b780 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -354,6 +354,23 @@ int anon_node_trylock_rmap(struct anon_node *anon_nod);
void anon_node_lock_rmap(struct anon_node *anon_nod);
void anon_node_unlock_rmap(struct anon_node *anon_nod);
+#define ANON_RMAP_LEAF_VMA 1
+#define ANON_RMAP_FLAGS (ANON_RMAP_LEAF_VMA)
+
+static inline bool anon_rmap_is_leaf_vma(void *anon_rmap)
+{
+#ifdef CONFIG_PER_VMA_LOCK
+ return ((unsigned long)anon_rmap & ANON_RMAP_FLAGS) == ANON_RMAP_LEAF_VMA;
+#else
+ return false;
+#endif
+}
+
+static inline struct vm_area_struct *anon_rmap_to_leaf_vma(void *anon_rmap)
+{
+ return (void *)((unsigned long)anon_rmap & ~ANON_RMAP_FLAGS);
+}
+
#define ANON_RMAP_FOREACH_VMA(anon_vma, addr, start, last, rmap_proc_vma) \
do { \
struct vm_area_struct *vma = NULL; /* must be named vma */ \
@@ -362,6 +379,11 @@ do { \
unsigned long _pgoff = (start); \
unsigned long _nr = 0, _total = 1; \
\
+ if (anon_rmap_is_leaf_vma(_anon_nod)) { \
+ vma = anon_rmap_to_leaf_vma(_anon_nod); \
+ (rmap_proc_vma); \
+ break; \
+ } \
while (_nod) { \
vma = anon_node_lookup_vma(_nod, (addr), _pgoff); \
if (vma) \
diff --git a/mm/rmap.c b/mm/rmap.c
index 11239838b722..d1099a2fef0a 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -572,6 +572,8 @@ void __put_anon_vma(struct anon_vma *anon_vma)
static struct kmem_cache *anon_node_cachep;
+
+
static inline struct rw_semaphore *anon_node_rmap_sem(struct anon_node *node)
{
return &node->root->rwsem;
@@ -1035,9 +1037,48 @@ void anon_node_lock_rmap(struct anon_node *anon_nod)
void anon_node_unlock_rmap(struct anon_node *anon_nod)
{
+#ifdef CONFIG_PER_VMA_LOCK
+ /* Corresponds to folio_lock_anon_vma_read(). */
+ if (anon_rmap_is_leaf_vma(anon_nod)) {
+ vma_refcount_put(anon_rmap_to_leaf_vma(anon_nod));
+ return;
+ }
+#endif
+
up_read(anon_node_rmap_sem(anon_nod));
}
+static struct vm_area_struct *trylock_leaf_vma_rmap(
+ struct anon_node *anon_nod, const struct folio *folio,
+ struct mm_struct **other_mm)
+{
+ unsigned long pgoff_start = folio_pgoff(folio);
+ unsigned long pgoff_end = pgoff_start + folio_nr_pages(folio) - 1;
+ unsigned long addr = anon_node_rmap_address(anon_nod, pgoff_start);
+ struct mm_struct *mm = anon_nod->mm;
+ struct vm_area_struct *vma;
+ int oldcnt;
+
+ if (anon_nod->nr_children)
+ return NULL;
+
+ if (!addr || !mm || !mmget_not_zero(mm))
+ return NULL;
+
+ vma = vma_lookup(mm, addr);
+ mmput_async(mm);
+ if (!vma || vma_last_pgoff(vma) < pgoff_end)
+ return NULL;
+
+ if (!__refcount_inc_not_zero_limited_acquire(&vma->vm_refcnt, &oldcnt,
+ VM_REFCNT_LIMIT)) {
+ return NULL;
+ }
+
+ *other_mm = (vma->vm_mm == mm) ? NULL : vma->vm_mm;
+ return vma;
+}
+
#endif
/*
@@ -1117,6 +1158,8 @@ struct anon_vma *folio_lock_anon_vma_read(const struct folio *folio,
{
struct anon_vma *anon_vma = NULL;
struct anon_vma *root_anon_vma;
+ struct vm_area_struct *leaf_vma = NULL;
+ struct mm_struct *other_mm = NULL;
unsigned long anon_mapping;
VM_WARN_ON_FOLIO(!folio_test_locked(folio), folio);
@@ -1130,6 +1173,15 @@ struct anon_vma *folio_lock_anon_vma_read(const struct folio *folio,
anon_vma = (struct anon_vma *) (anon_mapping - FOLIO_MAPPING_ANON);
root_anon_vma = READ_ONCE(anon_vma->root);
+#if defined(CONFIG_PER_VMA_LOCK) && defined(CONFIG_ANON_VMA_FRACTAL)
+ leaf_vma = trylock_leaf_vma_rmap(anon_vma, folio, &other_mm);
+ if (leaf_vma && !other_mm && folio_mapped(folio)) {
+ rcu_read_unlock();
+ return (void *)leaf_vma + ANON_RMAP_LEAF_VMA;
+ }
+ if (unlikely(other_mm))
+ mmgrab(other_mm); /* put vma after unlock. */
+#endif
if (down_read_trylock(&root_anon_vma->rwsem)) {
/*
* If the folio is still mapped, then this anon_vma is still
@@ -1156,9 +1208,8 @@ struct anon_vma *folio_lock_anon_vma_read(const struct folio *folio,
}
if (!folio_mapped(folio)) {
- rcu_read_unlock();
put_anon_vma(anon_vma);
- return NULL;
+ goto out;
}
/* we pinned the anon_vma, its safe to sleep */
@@ -1176,10 +1227,17 @@ struct anon_vma *folio_lock_anon_vma_read(const struct folio *folio,
anon_vma = NULL;
}
- return anon_vma;
+ goto out_unlocked;
out:
rcu_read_unlock();
+out_unlocked:
+#ifdef CONFIG_PER_VMA_LOCK
+ if (leaf_vma)
+ vma_refcount_put(leaf_vma);
+#endif
+ if (other_mm)
+ mmdrop(other_mm);
return anon_vma;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 12/13] mm: shared semaphores for ANON_VMA_FRACTAL
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (10 preceding siblings ...)
2026-07-07 6:33 ` [RFC PATCH v2 11/13] mm: optimize rmap for ANON_VMA_FRACTAL with PVL tao
@ 2026-07-07 6:33 ` tao
2026-07-07 6:33 ` [RFC PATCH v2 13/13] mm: Enable CONFIG_ANON_VMA_FRACTAL by default tao
` (3 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:33 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Use shared semaphores for anon_node to reduce memory overhead, as rmap
operations rarely run concurrently and fork/delete updates are
short-lived.
To reduce the fork impact of shared semaphores, reuse the root anon_node's
unused parent pointer to store a dedicated semaphore. Revalidate the lock
when acquiring it in rmap paths, since it may change.
Export rwsem_is_write_locked() in rwsem.h, and use it together with
rwsem_is_locked() to detect whether anon_node is in rmap, which can
reduce lock contention.
Signed-off-by: tao <tao.wangtao@honor.com>
---
include/linux/rmap.h | 7 ++-
include/linux/rwsem.h | 10 ++++
mm/Kconfig | 9 +++
mm/rmap.c | 134 +++++++++++++++++++++++++++++++++++++++---
4 files changed, 152 insertions(+), 8 deletions(-)
diff --git a/include/linux/rmap.h b/include/linux/rmap.h
index 81ad29f3c392..50919c66c368 100644
--- a/include/linux/rmap.h
+++ b/include/linux/rmap.h
@@ -75,11 +75,16 @@ struct anon_vma {
/* struct anon_node - Replace anon_vma for anonymous page reverse mapping. */
struct anon_node {
struct anon_node *root; /* Root of this anon_node fractal tree */
+#ifndef CONFIG_ANON_VMA_SHARED_SEMS
struct rw_semaphore rwsem;
atomic_t vm_lock_ref; /* for vm_lock_anon_vma */
+#endif
atomic_t refcount;
- struct anon_node *parent; /* NULL if created by a page fault */
+ union {
+ struct anon_node *parent; /* depth > 1 */
+ struct anon_semaphore *root_anon_sema; /* depth == 1 */
+ };
/*
* depth is initialized to 0. When a child is added, depth is set to 1.
diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h
index 6a1a7bae5f81..bc50a00a2c00 100644
--- a/include/linux/rwsem.h
+++ b/include/linux/rwsem.h
@@ -75,6 +75,11 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem)
return atomic_long_read(&sem->count) != RWSEM_UNLOCKED_VALUE;
}
+static inline int rwsem_is_write_locked(struct rw_semaphore *sem)
+{
+ return atomic_long_read(&sem->count) & RWSEM_WRITER_LOCKED;
+}
+
static inline void rwsem_assert_held_nolockdep(const struct rw_semaphore *sem)
__assumes_ctx_lock(sem)
{
@@ -181,6 +186,11 @@ static __always_inline int rwsem_is_locked(const struct rw_semaphore *sem)
return rw_base_is_locked(&sem->rwbase);
}
+static inline int rwsem_is_write_locked(struct rw_semaphore *sem)
+{
+ return rw_base_is_write_locked(&sem->rwbase);
+}
+
static __always_inline void rwsem_assert_held_nolockdep(const struct rw_semaphore *sem)
__assumes_ctx_lock(sem)
{
diff --git a/mm/Kconfig b/mm/Kconfig
index c4c6d3e52d72..1b909819035d 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1479,6 +1479,15 @@ config ANON_VMA_FRACTAL
during rmap walks. Since rmap_one operations may switch tasks,
semaphores are still required instead of spinlock_t protection.
+config ANON_VMA_SHARED_SEMS
+ bool "anon_vma shared semaphores"
+ def_bool n
+ depends on ANON_VMA_FRACTAL
+ help
+ As rmap operations rarely run concurrently, and fork/delete updates
+ are very short, a group of shared semaphores is used to reduce
+ memory overhead.
+
config IOMMU_MM_DATA
bool
diff --git a/mm/rmap.c b/mm/rmap.c
index d1099a2fef0a..3cb516b80eb2 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -559,6 +559,11 @@ void __init anon_vma_init(void)
SLAB_PANIC|SLAB_ACCOUNT);
}
+static inline struct rw_semaphore *anon_root_rmap_sem(struct anon_vma *root)
+{
+ return &root->rwsem;
+}
+
void __put_anon_vma(struct anon_vma *anon_vma)
{
struct anon_vma *root = anon_vma->root;
@@ -571,25 +576,82 @@ void __put_anon_vma(struct anon_vma *anon_vma)
#else
static struct kmem_cache *anon_node_cachep;
+static struct kmem_cache *anon_sema_cachep;
+
+/* Since rmap operations rarely run concurrently, use shared anon_root_sems. */
+#define ANON_NODE_RMAP_SEMS_BITS 12
+#define ANON_NODE_RMAP_SEMS_COUNT (1 << ANON_NODE_RMAP_SEMS_BITS)
+
+struct anon_semaphore {
+ struct rw_semaphore sem;
+ atomic_t vm_lock_ref; /* for vm_lock_anon_vma */
+};
+
+#ifdef CONFIG_ANON_VMA_SHARED_SEMS
+static struct anon_semaphore anon_node_root_semas[ANON_NODE_RMAP_SEMS_COUNT];
+static inline struct anon_semaphore *anon_root_anon_sema(struct anon_node *root)
+{
+ return (root->depth == 1 && root->root_anon_sema) ?
+ root->root_anon_sema :
+ &anon_node_root_semas[hash_ptr(root, ANON_NODE_RMAP_SEMS_BITS)];
+}
+#endif
+static inline void anon_sema_init(struct anon_semaphore *anon_sema)
+{
+ init_rwsem(&anon_sema->sem);
+ atomic_set(&anon_sema->vm_lock_ref, 0);
+}
+
+static inline void anon_root_sema_free(struct anon_semaphore *root_sema)
+{
+ VM_BUG_ON(rwsem_is_locked(&root_sema->sem));
+ kmem_cache_free(anon_sema_cachep, root_sema);
+}
+
+static inline struct anon_semaphore *anon_root_sema_alloc(void)
+{
+ struct anon_semaphore *root_sema;
+
+ root_sema = kmem_cache_alloc(anon_sema_cachep, GFP_KERNEL);
+ if (!root_sema)
+ return NULL;
+ anon_sema_init(root_sema);
+ return root_sema;
+}
+
+static inline struct rw_semaphore *anon_root_rmap_sem(struct anon_node *root)
+{
+#ifdef CONFIG_ANON_VMA_SHARED_SEMS
+ return &anon_root_anon_sema(root)->sem;
+#else
+ return &root->rwsem;
+#endif
+}
static inline struct rw_semaphore *anon_node_rmap_sem(struct anon_node *node)
{
- return &node->root->rwsem;
+ return anon_root_rmap_sem(node->root);
}
static inline atomic_t *anon_node_vm_lock_ref(struct anon_node *node)
{
+#ifdef CONFIG_ANON_VMA_SHARED_SEMS
+ return &anon_root_anon_sema(node->root)->vm_lock_ref;
+#else
return &node->root->vm_lock_ref;
+#endif
}
static void anon_node_ctor(void *data)
{
struct anon_node *anon_nod = data;
+#ifndef CONFIG_ANON_VMA_SHARED_SEMS
init_rwsem(&anon_nod->rwsem);
atomic_set(&anon_nod->vm_lock_ref, 0);
+#endif
atomic_set(&anon_nod->refcount, 0);
}
@@ -600,10 +662,16 @@ void __init anon_vma_init(void)
.freeptr_offset = offsetof(struct anon_node, fractal_list),
.ctor = anon_node_ctor,
};
+#ifdef CONFIG_ANON_VMA_SHARED_SEMS
+ int i;
+ for (i = 0; i < ANON_NODE_RMAP_SEMS_COUNT; i++)
+ anon_sema_init(&anon_node_root_semas[i]);
+#endif
anon_node_cachep = kmem_cache_create("anon_node",
sizeof(struct anon_node), &args,
SLAB_TYPESAFE_BY_RCU|SLAB_PANIC|SLAB_ACCOUNT);
+ anon_sema_cachep = KMEM_CACHE(anon_semaphore, SLAB_PANIC|SLAB_ACCOUNT);
}
static inline void anon_node_free(struct anon_node *anon_nod)
@@ -621,6 +689,12 @@ static inline void anon_node_free(struct anon_node *anon_nod)
up_write(rmap_sem);
}
+#ifdef CONFIG_ANON_VMA_SHARED_SEMS
+ if (anon_nod->depth == 1 && anon_nod->root_anon_sema) {
+ anon_root_sema_free(anon_nod->root_anon_sema);
+ anon_nod->root_anon_sema = NULL;
+ }
+#endif
kmem_cache_free(anon_node_cachep, anon_nod);
}
@@ -736,12 +810,17 @@ static inline struct anon_node *vma_anon_node(struct vm_area_struct *vma)
return vma ? (struct anon_node *)vma->anon_vma : NULL;
}
+static inline bool no_rmap_reader(struct rw_semaphore *rmap_sem)
+{
+ return !rwsem_is_locked(rmap_sem) || rwsem_is_write_locked(rmap_sem);
+}
+
static inline void vma_set_anon_node(struct vm_area_struct *vma,
struct anon_node *anon_nod, bool is_locked)
{
struct rw_semaphore *rmap_sem = anon_node_rmap_sem(anon_nod);
- if (is_locked) {
+ if (is_locked || no_rmap_reader(rmap_sem)) {
vma->anon_vma = (struct anon_vma *)anon_nod;
return;
}
@@ -758,8 +837,22 @@ static int anon_node_add_child(struct anon_node *anon_nod,
struct rw_semaphore *rmap_sem = anon_node_rmap_sem(root);
struct anon_node *rbc_ch = anon_nod, *next;
- if (root->depth == 0)
+ if (root->depth == 0) {
+#ifdef CONFIG_ANON_VMA_SHARED_SEMS
+ struct anon_semaphore *root_sema = anon_root_sema_alloc();
+
+ if (!root_sema)
+ return -ENOMEM;
+ down_write(rmap_sem);
+ if (cmpxchg(&root->root_anon_sema, NULL, root_sema) != NULL)
+ anon_root_sema_free(root_sema);
+ root->depth = 1;
+ up_write(rmap_sem);
+#else
root->depth = 1;
+#endif
+ }
+
child = anon_node_alloc(vma, anon_nod, rmap_base, is_fork);
if (!child)
return -ENOMEM;
@@ -1025,14 +1118,34 @@ void vm_unlock_anon_node(struct anon_node *anon_nod)
}
}
+static struct rw_semaphore *anon_node_lock_rmap_and_verify(
+ struct anon_node *anon_nod, bool trylock)
+{
+ struct anon_node *root = anon_nod->root;
+ struct rw_semaphore *rmap_sem = anon_node_rmap_sem(root);
+
+ while (true) {
+ if (trylock) {
+ if (!down_read_trylock(rmap_sem))
+ return NULL;
+ } else {
+ down_read(rmap_sem);
+ }
+ if (rmap_sem == anon_node_rmap_sem(root))
+ return rmap_sem;
+ up_read(rmap_sem);
+ rmap_sem = anon_node_rmap_sem(root);
+ }
+}
+
int anon_node_trylock_rmap(struct anon_node *anon_nod)
{
- return down_read_trylock(anon_node_rmap_sem(anon_nod));
+ return (bool)anon_node_lock_rmap_and_verify(anon_nod, true);
}
void anon_node_lock_rmap(struct anon_node *anon_nod)
{
- down_read(anon_node_rmap_sem(anon_nod));
+ anon_node_lock_rmap_and_verify(anon_nod, false);
}
void anon_node_unlock_rmap(struct anon_node *anon_nod)
@@ -1158,6 +1271,7 @@ struct anon_vma *folio_lock_anon_vma_read(const struct folio *folio,
{
struct anon_vma *anon_vma = NULL;
struct anon_vma *root_anon_vma;
+ struct rw_semaphore *root_rwsem;
struct vm_area_struct *leaf_vma = NULL;
struct mm_struct *other_mm = NULL;
unsigned long anon_mapping;
@@ -1182,14 +1296,20 @@ struct anon_vma *folio_lock_anon_vma_read(const struct folio *folio,
if (unlikely(other_mm))
mmgrab(other_mm); /* put vma after unlock. */
#endif
- if (down_read_trylock(&root_anon_vma->rwsem)) {
+trylock_agian:
+ root_rwsem = anon_root_rmap_sem(root_anon_vma);
+ if (down_read_trylock(root_rwsem)) {
+ if (root_rwsem != anon_root_rmap_sem(root_anon_vma)) {
+ up_read(root_rwsem);
+ goto trylock_agian;
+ }
/*
* If the folio is still mapped, then this anon_vma is still
* its anon_vma, and holding the mutex ensures that it will
* not go away, see anon_vma_free().
*/
if (!folio_mapped(folio)) {
- up_read(&root_anon_vma->rwsem);
+ up_read(root_rwsem);
anon_vma = NULL;
}
goto out;
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH v2 13/13] mm: Enable CONFIG_ANON_VMA_FRACTAL by default
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (11 preceding siblings ...)
2026-07-07 6:33 ` [RFC PATCH v2 12/13] mm: shared semaphores for ANON_VMA_FRACTAL tao
@ 2026-07-07 6:33 ` tao
2026-07-07 7:01 ` [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain David Hildenbrand (Arm)
` (2 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: tao @ 2026-07-07 6:33 UTC (permalink / raw)
To: akpm, david, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng, tao
Everything is now in place to enable CONFIG_ANON_VMA_FRACTAL and
CONFIG_ANON_VMA_SHARED_SEMS for further testing.
Signed-off-by: tao <tao.wangtao@honor.com>
---
mm/Kconfig | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mm/Kconfig b/mm/Kconfig
index 1b909819035d..9d4b81bbd4e1 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1453,7 +1453,7 @@ config LOCK_MM_AND_FIND_VMA
config ANON_VMA_FRACTAL
bool "anon_vma fractal list"
- def_bool n
+ def_bool y
depends on MMU
help
This option uses vma_rmap_base(vma) during rmap to compute the page
@@ -1481,7 +1481,7 @@ config ANON_VMA_FRACTAL
config ANON_VMA_SHARED_SEMS
bool "anon_vma shared semaphores"
- def_bool n
+ def_bool y
depends on ANON_VMA_FRACTAL
help
As rmap operations rarely run concurrently, and fork/delete updates
--
2.17.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (12 preceding siblings ...)
2026-07-07 6:33 ` [RFC PATCH v2 13/13] mm: Enable CONFIG_ANON_VMA_FRACTAL by default tao
@ 2026-07-07 7:01 ` David Hildenbrand (Arm)
2026-07-07 8:21 ` wangtao
2026-07-07 15:27 ` Borislav Petkov
2026-07-07 21:32 ` [syzbot ci] " syzbot ci
15 siblings, 1 reply; 23+ messages in thread
From: David Hildenbrand (Arm) @ 2026-07-07 7:01 UTC (permalink / raw)
To: tao, akpm, catalin.marinas, will, tglx, mingo, bp, dave.hansen,
x86, willy, sj, kees, luizcap, zhangjiao2, kas, ljs
Cc: hpa, liam, vbabka, rppt, surenb, mhocko, jack, riel, harry, jannh,
jgg, jhubbard, peterx, ziy, baolin.wang, npache, ryan.roberts,
dev.jain, baohua, lance.yang, xu.xin16, chengming.zhou,
muchun.song, baoquan.he, nao.horiguchi, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, gourry, ying.huang, apopple,
pfalcato, linux-arm-kernel, linux-kernel, linux-fsdevel, linux-mm,
damon, shakeel.butt, ryncsn, jparsana, dvander, zhangji1,
wangzicheng
On 7/7/26 08:32, tao wrote:
> Hi all,
>
> Since v1 was relatively complex and difficult to extend, this version
> adopts a simpler anon_vma implementation approach.
I really hate to repeat myself, but you seemed to have missed my main point [1]
"Lorenzo has been hard at work exploring various design options (and I'm afraid
he might be one of the 3 people on this planet that understand anon_vma in full
detail), so I suggest we wait for a redesign proposal from him and see if that
is doable?"
[1] https://lore.kernel.org/all/70377626-500d-46f2-a3f5-a0d2914ce365@kernel.org/
--
Cheers,
David
^ permalink raw reply [flat|nested] 23+ messages in thread
* RE: [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain
2026-07-07 7:01 ` [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain David Hildenbrand (Arm)
@ 2026-07-07 8:21 ` wangtao
2026-07-07 8:31 ` David Hildenbrand (Arm)
2026-07-07 9:07 ` Pedro Falcato
0 siblings, 2 replies; 23+ messages in thread
From: wangtao @ 2026-07-07 8:21 UTC (permalink / raw)
To: David Hildenbrand (Arm), akpm@linux-foundation.org,
catalin.marinas@arm.com, will@kernel.org, tglx@kernel.org,
mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com,
x86@kernel.org, willy@infradead.org, sj@kernel.org,
kees@kernel.org, luizcap@redhat.com,
zhangjiao2@cmss.chinamobile.com, kas@kernel.org, ljs@kernel.org
Cc: hpa@zytor.com, liam@infradead.org, vbabka@kernel.org,
rppt@kernel.org, surenb@google.com, mhocko@suse.com, jack@suse.cz,
riel@surriel.com, harry@kernel.org, jannh@google.com,
jgg@ziepe.ca, jhubbard@nvidia.com, peterx@redhat.com,
ziy@nvidia.com, baolin.wang@linux.alibaba.com, npache@redhat.com,
ryan.roberts@arm.com, dev.jain@arm.com, baohua@kernel.org,
lance.yang@linux.dev, xu.xin16@zte.com.cn,
chengming.zhou@linux.dev, muchun.song@linux.dev,
baoquan.he@linux.dev, nao.horiguchi@gmail.com,
matthew.brost@intel.com, joshua.hahnjy@gmail.com,
rakie.kim@sk.com, byungchul@sk.com, gourry@gourry.net,
ying.huang@linux.alibaba.com, apopple@nvidia.com,
pfalcato@suse.de, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-mm@kvack.org, damon@lists.linux.dev, shakeel.butt@linux.dev,
ryncsn@gmail.com, jparsana@google.com, dvander@google.com,
zhangji, wangzicheng
>
> On 7/7/26 08:32, tao wrote:
> > Hi all,
> >
> > Since v1 was relatively complex and difficult to extend, this version
> > adopts a simpler anon_vma implementation approach.
>
> I really hate to repeat myself, but you seemed to have missed my main point
> [1]
>
I saw it, and I’ve waited for a while as well.
If there are technical issues, please point them out directly.
Or is this simply how technical discussions are usually conducted in the Linux community or the mm subsystem?
Thanks,
Tao
> "Lorenzo has been hard at work exploring various design options (and I'm
> afraid he might be one of the 3 people on this planet that understand
> anon_vma in full detail), so I suggest we wait for a redesign proposal from
> him and see if that is doable?"
>
> [1] https://lore.kernel.org/all/70377626-500d-46f2-a3f5-
> a0d2914ce365@kernel.org/
>
> --
> Cheers,
>
> David
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain
2026-07-07 8:21 ` wangtao
@ 2026-07-07 8:31 ` David Hildenbrand (Arm)
2026-07-07 9:20 ` wangtao
2026-07-07 9:07 ` Pedro Falcato
1 sibling, 1 reply; 23+ messages in thread
From: David Hildenbrand (Arm) @ 2026-07-07 8:31 UTC (permalink / raw)
To: wangtao, akpm@linux-foundation.org, catalin.marinas@arm.com,
will@kernel.org, tglx@kernel.org, mingo@redhat.com, bp@alien8.de,
dave.hansen@linux.intel.com, x86@kernel.org, willy@infradead.org,
sj@kernel.org, kees@kernel.org, luizcap@redhat.com,
zhangjiao2@cmss.chinamobile.com, kas@kernel.org, ljs@kernel.org
Cc: hpa@zytor.com, liam@infradead.org, vbabka@kernel.org,
rppt@kernel.org, surenb@google.com, mhocko@suse.com, jack@suse.cz,
riel@surriel.com, harry@kernel.org, jannh@google.com,
jgg@ziepe.ca, jhubbard@nvidia.com, peterx@redhat.com,
ziy@nvidia.com, baolin.wang@linux.alibaba.com, npache@redhat.com,
ryan.roberts@arm.com, dev.jain@arm.com, baohua@kernel.org,
lance.yang@linux.dev, xu.xin16@zte.com.cn,
chengming.zhou@linux.dev, muchun.song@linux.dev,
baoquan.he@linux.dev, nao.horiguchi@gmail.com,
matthew.brost@intel.com, joshua.hahnjy@gmail.com,
rakie.kim@sk.com, byungchul@sk.com, gourry@gourry.net,
ying.huang@linux.alibaba.com, apopple@nvidia.com,
pfalcato@suse.de, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-mm@kvack.org, damon@lists.linux.dev, shakeel.butt@linux.dev,
ryncsn@gmail.com, jparsana@google.com, dvander@google.com,
zhangji, wangzicheng
On 7/7/26 10:21, wangtao wrote:
>>
>> On 7/7/26 08:32, tao wrote:
>>> Hi all,
>>>
>>> Since v1 was relatively complex and difficult to extend, this version
>>> adopts a simpler anon_vma implementation approach.
>>
>> I really hate to repeat myself, but you seemed to have missed my main point
>> [1]
>>
> I saw it, and I’ve waited for a while as well.
Apparently you saw it but you didn't read my reply about slapping more
complexity on something complicated.
12 files changed, 1056 insertions(+), 54 deletions(-)
>
> If there are technical issues, please point them out directly.
> Or is this simply how technical discussions are usually conducted in the Linux community or the mm subsystem?
I suggest you go back and read my email.
--
Cheers,
David
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain
2026-07-07 8:21 ` wangtao
2026-07-07 8:31 ` David Hildenbrand (Arm)
@ 2026-07-07 9:07 ` Pedro Falcato
1 sibling, 0 replies; 23+ messages in thread
From: Pedro Falcato @ 2026-07-07 9:07 UTC (permalink / raw)
To: wangtao
Cc: David Hildenbrand (Arm), akpm@linux-foundation.org,
catalin.marinas@arm.com, will@kernel.org, tglx@kernel.org,
mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com,
x86@kernel.org, willy@infradead.org, sj@kernel.org,
kees@kernel.org, luizcap@redhat.com,
zhangjiao2@cmss.chinamobile.com, kas@kernel.org, ljs@kernel.org,
hpa@zytor.com, liam@infradead.org, vbabka@kernel.org,
rppt@kernel.org, surenb@google.com, mhocko@suse.com, jack@suse.cz,
riel@surriel.com, harry@kernel.org, jannh@google.com,
jgg@ziepe.ca, jhubbard@nvidia.com, peterx@redhat.com,
ziy@nvidia.com, baolin.wang@linux.alibaba.com, npache@redhat.com,
ryan.roberts@arm.com, dev.jain@arm.com, baohua@kernel.org,
lance.yang@linux.dev, xu.xin16@zte.com.cn,
chengming.zhou@linux.dev, muchun.song@linux.dev,
baoquan.he@linux.dev, nao.horiguchi@gmail.com,
matthew.brost@intel.com, joshua.hahnjy@gmail.com,
rakie.kim@sk.com, byungchul@sk.com, gourry@gourry.net,
ying.huang@linux.alibaba.com, apopple@nvidia.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-mm@kvack.org, damon@lists.linux.dev, shakeel.butt@linux.dev,
ryncsn@gmail.com, jparsana@google.com, dvander@google.com,
zhangji, wangzicheng
On Tue, Jul 07, 2026 at 08:21:24AM +0000, wangtao wrote:
> >
> > On 7/7/26 08:32, tao wrote:
> > > Hi all,
> > >
> > > Since v1 was relatively complex and difficult to extend, this version
> > > adopts a simpler anon_vma implementation approach.
> >
> > I really hate to repeat myself, but you seemed to have missed my main point
> > [1]
> >
> I saw it, and I’ve waited for a while as well.
what?
A good part of software engineering (and FOSS in general) is collaboration.
People have their own work responsibilities & personal lives. If you had
questions, you could've raised them on-list or directly to Lorenzo and
could've helped him drive it forward (which he is, and he's posting careful
precursor patches).
>
> If there are technical issues, please point them out directly.
> Or is this simply how technical discussions are usually conducted in the Linux community or the mm subsystem?
There is no technical discussion to be had. Lorenzo is doing a large,
careful rework of anon rmap (and rmap in general). You don't get to override
his work because you decided to ignore the "community outreach and
collaboration" step.
--
Pedro
^ permalink raw reply [flat|nested] 23+ messages in thread
* RE: [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain
2026-07-07 8:31 ` David Hildenbrand (Arm)
@ 2026-07-07 9:20 ` wangtao
2026-07-07 16:19 ` Gregory Price
2026-07-07 17:09 ` David Hildenbrand (Arm)
0 siblings, 2 replies; 23+ messages in thread
From: wangtao @ 2026-07-07 9:20 UTC (permalink / raw)
To: David Hildenbrand (Arm), akpm@linux-foundation.org,
catalin.marinas@arm.com, will@kernel.org, tglx@kernel.org,
mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com,
x86@kernel.org, willy@infradead.org, sj@kernel.org,
kees@kernel.org, luizcap@redhat.com,
zhangjiao2@cmss.chinamobile.com, kas@kernel.org, ljs@kernel.org
Cc: hpa@zytor.com, liam@infradead.org, vbabka@kernel.org,
rppt@kernel.org, surenb@google.com, mhocko@suse.com, jack@suse.cz,
riel@surriel.com, harry@kernel.org, jannh@google.com,
jgg@ziepe.ca, jhubbard@nvidia.com, peterx@redhat.com,
ziy@nvidia.com, baolin.wang@linux.alibaba.com, npache@redhat.com,
ryan.roberts@arm.com, dev.jain@arm.com, baohua@kernel.org,
lance.yang@linux.dev, xu.xin16@zte.com.cn,
chengming.zhou@linux.dev, muchun.song@linux.dev,
baoquan.he@linux.dev, nao.horiguchi@gmail.com,
matthew.brost@intel.com, joshua.hahnjy@gmail.com,
rakie.kim@sk.com, byungchul@sk.com, gourry@gourry.net,
ying.huang@linux.alibaba.com, apopple@nvidia.com,
pfalcato@suse.de, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-mm@kvack.org, damon@lists.linux.dev, shakeel.butt@linux.dev,
ryncsn@gmail.com, jparsana@google.com, dvander@google.com,
zhangji, wangzicheng
> >> On 7/7/26 08:32, tao wrote:
> >>> Hi all,
> >>>
> >>> Since v1 was relatively complex and difficult to extend, this
> >>> version adopts a simpler anon_vma implementation approach.
> >>
> >> I really hate to repeat myself, but you seemed to have missed my main
> >> point [1]
> >>
> > I saw it, and I’ve waited for a while as well.
>
> Apparently you saw it but you didn't read my reply about slapping more
> complexity on something complicated.
This v2 is a complete rewrite replacing the original anon_vma and anonymous-page
reverse-mapping traversal.
The new anon_vma hierarchy is represented using a simple doubly linked list with depth information,
replacing the complex topology of red-black trees maintained by each anon_vma in the original design,
and also removing anon_vma_chain.
Excluding patch 11, which optimizes leaf VMA rmap with PVL, and patch 12, which optimizes memory
usage through shared semaphores, the base version contains roughly 800 lines of code, including the
compatibility part replacing the original anon_vma implementation.
>
> 12 files changed, 1056 insertions(+), 54 deletions(-)
>
> >
> > If there are technical issues, please point them out directly.
> > Or is this simply how technical discussions are usually conducted in the
> Linux community or the mm subsystem?
>
> I suggest you go back and read my email.
At first, I thought I was the only one who found anon_vma hard to understand. After reading your earlier email,
I realized that many people also consider it difficult to follow. So I tried a different approach and implemented
anon_vma rmap using a simpler doubly linked list plus depth mechanism.
Thanks,
Tao
> --
> Cheers,
>
> David
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (13 preceding siblings ...)
2026-07-07 7:01 ` [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain David Hildenbrand (Arm)
@ 2026-07-07 15:27 ` Borislav Petkov
2026-07-07 21:32 ` [syzbot ci] " syzbot ci
15 siblings, 0 replies; 23+ messages in thread
From: Borislav Petkov @ 2026-07-07 15:27 UTC (permalink / raw)
To: tao
Cc: akpm, david, catalin.marinas, will, tglx, mingo, dave.hansen, x86,
willy, sj, kees, luizcap, zhangjiao2, kas, ljs, hpa, liam, vbabka,
rppt, surenb, mhocko, jack, riel, harry, jannh, jgg, jhubbard,
peterx, ziy, baolin.wang, npache, ryan.roberts, dev.jain, baohua,
lance.yang, xu.xin16, chengming.zhou, muchun.song, baoquan.he,
nao.horiguchi, matthew.brost, joshua.hahnjy, rakie.kim, byungchul,
gourry, ying.huang, apopple, pfalcato, linux-arm-kernel,
linux-kernel, linux-fsdevel, linux-mm, damon, shakeel.butt,
ryncsn, jparsana, dvander, zhangji1, wangzicheng
On Tue, Jul 07, 2026 at 02:32:55PM +0800, tao wrote:
> include/linux/mm.h | 2 +
> include/linux/mm_types.h | 2 +
> include/linux/rmap.h | 56 ++++
> include/linux/rwsem.h | 10 +
> mm/Kconfig | 37 +++
> mm/internal.h | 205 ++++++++++++
> mm/ksm.c | 19 +-
> mm/memory-failure.c | 8 +-
> mm/mmap.c | 7 +-
> mm/rmap.c | 686 +++++++++++++++++++++++++++++++++++++--
> mm/vma.c | 76 ++++-
> mm/vma_init.c | 2 +
Why am I on To: on this diffstat?
Please learn how to use ./scripts/get_maintainer.pl and do not spam unrelated
people with mail.
Thx.
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain
2026-07-07 9:20 ` wangtao
@ 2026-07-07 16:19 ` Gregory Price
2026-07-07 17:09 ` David Hildenbrand (Arm)
1 sibling, 0 replies; 23+ messages in thread
From: Gregory Price @ 2026-07-07 16:19 UTC (permalink / raw)
To: wangtao
Cc: David Hildenbrand (Arm), akpm@linux-foundation.org,
catalin.marinas@arm.com, will@kernel.org, tglx@kernel.org,
mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com,
x86@kernel.org, willy@infradead.org, sj@kernel.org,
kees@kernel.org, luizcap@redhat.com,
zhangjiao2@cmss.chinamobile.com, kas@kernel.org, ljs@kernel.org,
hpa@zytor.com, liam@infradead.org, vbabka@kernel.org,
rppt@kernel.org, surenb@google.com, mhocko@suse.com, jack@suse.cz,
riel@surriel.com, harry@kernel.org, jannh@google.com,
jgg@ziepe.ca, jhubbard@nvidia.com, peterx@redhat.com,
ziy@nvidia.com, baolin.wang@linux.alibaba.com, npache@redhat.com,
ryan.roberts@arm.com, dev.jain@arm.com, baohua@kernel.org,
lance.yang@linux.dev, xu.xin16@zte.com.cn,
chengming.zhou@linux.dev, muchun.song@linux.dev,
baoquan.he@linux.dev, nao.horiguchi@gmail.com,
matthew.brost@intel.com, joshua.hahnjy@gmail.com,
rakie.kim@sk.com, byungchul@sk.com, ying.huang@linux.alibaba.com,
apopple@nvidia.com, pfalcato@suse.de,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-mm@kvack.org, damon@lists.linux.dev, shakeel.butt@linux.dev,
ryncsn@gmail.com, jparsana@google.com, dvander@google.com,
zhangji, wangzicheng
On Tue, Jul 07, 2026 at 09:20:24AM +0000, wangtao wrote:
> > >
> > > If there are technical issues, please point them out directly.
> > > Or is this simply how technical discussions are usually conducted in the
> > Linux community or the mm subsystem?
> >
> > I suggest you go back and read my email.
>
> At first, I thought I was the only one who found anon_vma hard to understand. After reading your earlier email,
> I realized that many people also consider it difficult to follow. So I tried a different approach and implemented
> anon_vma rmap using a simpler doubly linked list plus depth mechanism.
>
The in-flight was discussed at LSF and then reported on by lwn
https://lwn.net/Articles/1072378/
https://git.kernel.org/pub/scm/linux/kernel/git/ljs/linux.git/log/?h=project/cow-context
If you have issues with the in-flight work and think there's a better
way, it would be good to engage on that front - rather than posting
competing work and expecting maintainers to have to context switch
between two solutions for an objectively hard to understand problem
(which carries significant risk of regressions).
No one is saying you can't engage, just trying to avoid doing the same
work twice for negative value.
~Gregory
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain
2026-07-07 9:20 ` wangtao
2026-07-07 16:19 ` Gregory Price
@ 2026-07-07 17:09 ` David Hildenbrand (Arm)
1 sibling, 0 replies; 23+ messages in thread
From: David Hildenbrand (Arm) @ 2026-07-07 17:09 UTC (permalink / raw)
To: wangtao, akpm@linux-foundation.org, catalin.marinas@arm.com,
will@kernel.org, tglx@kernel.org, mingo@redhat.com, bp@alien8.de,
dave.hansen@linux.intel.com, x86@kernel.org, willy@infradead.org,
sj@kernel.org, kees@kernel.org, luizcap@redhat.com,
zhangjiao2@cmss.chinamobile.com, kas@kernel.org, ljs@kernel.org
Cc: hpa@zytor.com, liam@infradead.org, vbabka@kernel.org,
rppt@kernel.org, surenb@google.com, mhocko@suse.com, jack@suse.cz,
riel@surriel.com, harry@kernel.org, jannh@google.com,
jgg@ziepe.ca, jhubbard@nvidia.com, peterx@redhat.com,
ziy@nvidia.com, baolin.wang@linux.alibaba.com, npache@redhat.com,
ryan.roberts@arm.com, dev.jain@arm.com, baohua@kernel.org,
lance.yang@linux.dev, xu.xin16@zte.com.cn,
chengming.zhou@linux.dev, muchun.song@linux.dev,
baoquan.he@linux.dev, nao.horiguchi@gmail.com,
matthew.brost@intel.com, joshua.hahnjy@gmail.com,
rakie.kim@sk.com, byungchul@sk.com, gourry@gourry.net,
ying.huang@linux.alibaba.com, apopple@nvidia.com,
pfalcato@suse.de, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-mm@kvack.org, damon@lists.linux.dev, shakeel.butt@linux.dev,
ryncsn@gmail.com, jparsana@google.com, dvander@google.com,
zhangji, wangzicheng
On 7/7/26 11:20, wangtao wrote:
>>> I saw it, and I’ve waited for a while as well.
>>
>> Apparently you saw it but you didn't read my reply about slapping more
>> complexity on something complicated.
> This v2 is a complete rewrite replacing the original anon_vma and anonymous-page
> reverse-mapping traversal.
Okay, it leaves the old code in place for now. Just as a general node without
going into details: that won't fly. We don't need another MGLRU.
>
> The new anon_vma hierarchy is represented using a simple doubly linked list with depth information,
> replacing the complex topology of red-black trees maintained by each anon_vma in the original design,
> and also removing anon_vma_chain.
Okay, but anon_vma fundamentally remains. Using a lipstick on a pig.
>
> Excluding patch 11, which optimizes leaf VMA rmap with PVL, and patch 12, which optimizes memory
> usage through shared semaphores, the base version contains roughly 800 lines of code, including the
> compatibility part replacing the original anon_vma implementation.
>
>>
>> 12 files changed, 1056 insertions(+), 54 deletions(-)
>>
>>>
>>> If there are technical issues, please point them out directly.
>>> Or is this simply how technical discussions are usually conducted in the
>> Linux community or the mm subsystem?
>>
>> I suggest you go back and read my email.
>
> At first, I thought I was the only one who found anon_vma hard to understand. After reading your earlier email,
> I realized that many people also consider it difficult to follow.
Right, that's how Lorenzo started his work, trying to replace/remove anon_vma
entirely.
So I tried a different approach and implemented
> anon_vma rmap using a simpler doubly linked list plus depth mechanism.
And this is what I don't understand. You were told that Lorenzo is working on
removing anon_vma, yet you decided to work on + send something that reworks
anon_vma?
Also, I am really curious about your expectation: not having a single patch
contributed to MM, even worse, not a single RB tag or anything, yet you rework
one of the most complicated part of MM repeatedly using other complicated
approaches, and immediately start demand technical discussions? After sending an
absolute horrible v1?
I really try to be a nice person, but really, am I dreaming?
Obviously, Lorenzo is still working om his approach, about which there were
discussions at conferences and LWN articles.
Stating "that you waited for a while" is ludicrous given that your old patch set
was posted around one month ago, and a complete redesign -- moving away from
anon_vma -- is not expected to be a short-term thing.
Meanwhile, Lorenzo is sending real cleanups [1] as preparation for bigger
changes. ... which is what we usually expect on such fundamental reworks.
I'm really left clueless here why we anyone should spend time trying to thing
through yet another data structure and yet another locking scheme, for something
we want to remove entirely?
Again, I really try to be a nice person, but something here just doesn't add up.
[1] https://lore.kernel.org/r/cover.1782735110.git.ljs@kernel.org
--
Cheers,
David
^ permalink raw reply [flat|nested] 23+ messages in thread
* [syzbot ci] Re: mm: rework anon_vma and remove anon_vma_chain
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
` (14 preceding siblings ...)
2026-07-07 15:27 ` Borislav Petkov
@ 2026-07-07 21:32 ` syzbot ci
15 siblings, 0 replies; 23+ messages in thread
From: syzbot ci @ 2026-07-07 21:32 UTC (permalink / raw)
To: akpm, apopple, baohua, baolin.wang, baoquan.he, bp, byungchul,
catalin.marinas, chengming.zhou, damon, dave.hansen, david,
dev.jain, dvander, gourry, harry, hpa, jack, jannh, jgg, jhubbard,
joshua.hahnjy, jparsana, kas, kees, lance.yang, liam,
linux-arm-kernel, linux-fsdevel, linux-kernel, linux-mm, ljs,
luizcap, matthew.brost, mhocko, mingo, muchun.song, nao.horiguchi,
npache, peterx, pfalcato, rakie.kim, riel, rppt, ryan.roberts,
ryncsn, shakeel.butt, sj, surenb, tao.wangtao
Cc: syzbot, syzkaller-bugs
syzbot ci has tested the following series
[v2] mm: rework anon_vma and remove anon_vma_chain
https://lore.kernel.org/all/20260707063308.29176-1-tao.wangtao@honor.com
* [RFC PATCH v2 01/13] mm: add CONFIG_ANON_VMA_FRACTAL
* [RFC PATCH v2 02/13] mm: implement helpers for ANON_VMA_FRACTAL
* [RFC PATCH v2 03/13] mm: implement __anon_node_prepare for ANON_VMA_FRACTAL
* [RFC PATCH v2 04/13] mm: implement anon_node_clone for ANON_VMA_FRACTAL
* [RFC PATCH v2 05/13] mm: implement anon_node_fork_with_prev for ANON_VMA_FRACTAL
* [RFC PATCH v2 06/13] mm: implement unlink_anon_nodes for ANON_VMA_FRACTAL
* [RFC PATCH v2 07/13] mm: handle rmap_base changes for ANON_VMA_FRACTAL
* [RFC PATCH v2 08/13] mm: implement anonymous folio rmap for ANON_VMA_FRACTAL
* [RFC PATCH v2 09/13] mm: prepare anon_node replacement for ANON_VMA_FRACTAL
* [RFC PATCH v2 10/13] mm: replace anon_vma with anon_node for ANON_VMA_FRACTAL
* [RFC PATCH v2 11/13] mm: optimize rmap for ANON_VMA_FRACTAL with PVL
* [RFC PATCH v2 12/13] mm: shared semaphores for ANON_VMA_FRACTAL
* [RFC PATCH v2 13/13] mm: Enable CONFIG_ANON_VMA_FRACTAL by default
and found the following issue:
WARNING: bad unlock balance in exc_page_fault
Full report is available here:
https://ci.syzbot.org/series/39466cf1-707b-420e-8f1e-11735bea6c66
***
WARNING: bad unlock balance in exc_page_fault
tree: mm-new
URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/akpm/mm.git
base: 8ff49efd1cf18086320f3b43d1bd483c0b9e7e58
arch: amd64
compiler: Debian clang version 22.1.6 (++20260514074242+fc4aad7b5db3-1~exp1~20260514074407.73), Debian LLD 22.1.6
config: https://ci.syzbot.org/builds/c3a229e4-b949-4889-b309-dbdd1fadd95f/config
syz repro: https://ci.syzbot.org/findings/29b52ad4-b463-41da-877c-4138356e3960/syz_repro
=====================================
WARNING: bad unlock balance detected!
syzkaller #0 Not tainted
-------------------------------------
sshd/5593 is trying to release lock (vm_lock) at:
[<ffffffff8bb5032a>] handle_page_fault arch/x86/mm/fault.c:1483 [inline]
[<ffffffff8bb5032a>] exc_page_fault+0x6a/0xc0 arch/x86/mm/fault.c:1536
but there are no more locks to release!
other info that might help us debug this:
no locks held by sshd/5593.
stack backtrace:
CPU: 1 UID: 0 PID: 5593 Comm: sshd Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_unlock_imbalance_bug+0xdc/0xf0 kernel/locking/lockdep.c:5298
__lock_release kernel/locking/lockdep.c:5527 [inline]
lock_release+0x1f1/0x3c0 kernel/locking/lockdep.c:5889
vma_refcount_put include/linux/mmap_lock.h:216 [inline]
vma_end_read include/linux/mmap_lock.h:264 [inline]
do_user_addr_fault+0xab0/0x1340 arch/x86/mm/fault.c:1345
handle_page_fault arch/x86/mm/fault.c:1483 [inline]
exc_page_fault+0x6a/0xc0 arch/x86/mm/fault.c:1536
asm_exc_page_fault+0x26/0x30 arch/x86/include/asm/idtentry.h:595
RIP: 0033:0x5583206de68b
Code: 04 25 28 00 00 00 48 89 44 24 08 31 c0 48 89 e2 e8 8c 5b ff ff 85 c0 78 13 31 c0 4d 85 e4 74 0c 48 8b 3c 24 48 89 de 4c 89 e1 <f3> a4 48 8b 54 24 08 64 48 2b 14 25 28 00 00 00 74 05 e8 5e 17 fc
RSP: 002b:00007fff02a37e60 EFLAGS: 00010206
RAX: 0000000000000000 RBX: 000055835a20d6fe RCX: 0000000000008000
RDX: 0000000000000000 RSI: 000055835a20d6fe RDI: 00007fac5106c010
RBP: 000055832073df00 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000008000
R13: 000000000000005e R14: 00000000000000b3 R15: 00007fff02a37f88
</TASK>
***
If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
Tested-by: syzbot@syzkaller.appspotmail.com
---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.
To test a patch for this bug, please reply with `#syz test`
(should be on a separate line).
The patch should be attached to the email.
Note: arguments like custom git repos and branches are not supported.
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-07-07 21:33 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
2026-07-07 6:32 ` [RFC PATCH v2 01/13] mm: add CONFIG_ANON_VMA_FRACTAL tao
2026-07-07 6:32 ` [RFC PATCH v2 02/13] mm: implement helpers for ANON_VMA_FRACTAL tao
2026-07-07 6:32 ` [RFC PATCH v2 03/13] mm: implement __anon_node_prepare " tao
2026-07-07 6:32 ` [RFC PATCH v2 04/13] mm: implement anon_node_clone " tao
2026-07-07 6:33 ` [RFC PATCH v2 05/13] mm: implement anon_node_fork_with_prev " tao
2026-07-07 6:33 ` [RFC PATCH v2 06/13] mm: implement unlink_anon_nodes " tao
2026-07-07 6:33 ` [RFC PATCH v2 07/13] mm: handle rmap_base changes " tao
2026-07-07 6:33 ` [RFC PATCH v2 08/13] mm: implement anonymous folio rmap " tao
2026-07-07 6:33 ` [RFC PATCH v2 09/13] mm: prepare anon_node replacement " tao
2026-07-07 6:33 ` [RFC PATCH v2 10/13] mm: replace anon_vma with anon_node " tao
2026-07-07 6:33 ` [RFC PATCH v2 11/13] mm: optimize rmap for ANON_VMA_FRACTAL with PVL tao
2026-07-07 6:33 ` [RFC PATCH v2 12/13] mm: shared semaphores for ANON_VMA_FRACTAL tao
2026-07-07 6:33 ` [RFC PATCH v2 13/13] mm: Enable CONFIG_ANON_VMA_FRACTAL by default tao
2026-07-07 7:01 ` [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain David Hildenbrand (Arm)
2026-07-07 8:21 ` wangtao
2026-07-07 8:31 ` David Hildenbrand (Arm)
2026-07-07 9:20 ` wangtao
2026-07-07 16:19 ` Gregory Price
2026-07-07 17:09 ` David Hildenbrand (Arm)
2026-07-07 9:07 ` Pedro Falcato
2026-07-07 15:27 ` Borislav Petkov
2026-07-07 21:32 ` [syzbot ci] " syzbot ci
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox