linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
From: Wei Yang <richard.weiyang@gmail.com>
To: akpm@linux-foundation.org
Cc: david@redhat.com, lorenzo.stoakes@oracle.com, riel@surriel.com,
	vbabka@suse.cz, harry.yoo@oracle.com, jannh@google.com,
	baohua@kernel.org, linux-mm@kvack.org,
	Wei Yang <richard.weiyang@gmail.com>
Subject: [RFC Patch 2/5] anon_vma: add skeleton code for userland testing of anon_vma logic
Date: Tue, 29 Apr 2025 09:06:36 +0000	[thread overview]
Message-ID: <20250429090639.784-3-richard.weiyang@gmail.com> (raw)
In-Reply-To: <20250429090639.784-1-richard.weiyang@gmail.com>

Establish a new userland anon_vma unit testing implementation under
tools/testing.

Current is a skeleton with simple cases and provides possibility for new
tests.

Signed-off-by: Wei Yang <richard.weiyang@gmail.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Jann Horn <jannh@google.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Rik van Riel <riel@surriel.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Harry Yoo <harry.yoo@oracle.com>
---
 tools/include/linux/rwsem.h                |  10 +
 tools/include/linux/slab.h                 |   4 +
 tools/testing/anon_vma/.gitignore          |   3 +
 tools/testing/anon_vma/Makefile            |  25 ++
 tools/testing/anon_vma/anon_vma.c          | 438 +++++++++++++++++++++
 tools/testing/anon_vma/anon_vma_internal.h |  55 +++
 tools/testing/anon_vma/interval_tree.c     |  53 +++
 tools/testing/anon_vma/linux/atomic.h      |  18 +
 tools/testing/anon_vma/linux/fs.h          |   6 +
 tools/testing/anon_vma/linux/mm.h          |  44 +++
 tools/testing/anon_vma/linux/mm_types.h    |  57 +++
 tools/testing/anon_vma/linux/mmzone.h      |   6 +
 tools/testing/anon_vma/linux/rmap.h        |   8 +
 tools/testing/shared/linux/anon_vma.h      |   7 +
 14 files changed, 734 insertions(+)
 create mode 100644 tools/testing/anon_vma/.gitignore
 create mode 100644 tools/testing/anon_vma/Makefile
 create mode 100644 tools/testing/anon_vma/anon_vma.c
 create mode 100644 tools/testing/anon_vma/anon_vma_internal.h
 create mode 100644 tools/testing/anon_vma/interval_tree.c
 create mode 100644 tools/testing/anon_vma/linux/atomic.h
 create mode 100644 tools/testing/anon_vma/linux/fs.h
 create mode 100644 tools/testing/anon_vma/linux/mm.h
 create mode 100644 tools/testing/anon_vma/linux/mm_types.h
 create mode 100644 tools/testing/anon_vma/linux/mmzone.h
 create mode 100644 tools/testing/anon_vma/linux/rmap.h
 create mode 100644 tools/testing/shared/linux/anon_vma.h

diff --git a/tools/include/linux/rwsem.h b/tools/include/linux/rwsem.h
index f8bffd4a987c..a74e424e4cfe 100644
--- a/tools/include/linux/rwsem.h
+++ b/tools/include/linux/rwsem.h
@@ -38,6 +38,16 @@ static inline int up_write(struct rw_semaphore *sem)
 	return pthread_rwlock_unlock(&sem->lock);
 }
 
+static inline int down_read_trylock(struct rw_semaphore *sem)
+{
+	return pthread_rwlock_tryrdlock(&sem->lock);
+}
+
+static inline int down_write_trylock(struct rw_semaphore *sem)
+{
+	return pthread_rwlock_trywrlock(&sem->lock);
+}
+
 #define down_read_nested(sem, subclass)		down_read(sem)
 #define down_write_nested(sem, subclass)	down_write(sem)
 
diff --git a/tools/include/linux/slab.h b/tools/include/linux/slab.h
index c87051e2b26f..6ebdda7aadbf 100644
--- a/tools/include/linux/slab.h
+++ b/tools/include/linux/slab.h
@@ -41,6 +41,10 @@ struct kmem_cache *kmem_cache_create(const char *name, unsigned int size,
 			unsigned int align, unsigned int flags,
 			void (*ctor)(void *));
 
+#define KMEM_CACHE(__struct, __flags)					\
+	kmem_cache_create(#__struct, sizeof(struct __struct),		\
+		__alignof__(struct __struct), __flags, NULL)
+
 void kmem_cache_free_bulk(struct kmem_cache *cachep, size_t size, void **list);
 int kmem_cache_alloc_bulk(struct kmem_cache *cachep, gfp_t gfp, size_t size,
 			  void **list);
diff --git a/tools/testing/anon_vma/.gitignore b/tools/testing/anon_vma/.gitignore
new file mode 100644
index 000000000000..aa2885a029f9
--- /dev/null
+++ b/tools/testing/anon_vma/.gitignore
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+generated/
+anon_vma
diff --git a/tools/testing/anon_vma/Makefile b/tools/testing/anon_vma/Makefile
new file mode 100644
index 000000000000..120e2217e957
--- /dev/null
+++ b/tools/testing/anon_vma/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+.PHONY: default clean
+
+default: anon_vma
+
+include ../shared/shared.mk
+
+CFLAGS += -D CONFIG_PHYS_ADDR_T_64BIT
+ifeq ($(DEBUG), 1)
+	CFLAGS += -D ANON_VMA_INTERVAL_TREE_DEBUG -g
+endif
+
+OFILES = $(LIBS) linux.o anon_vma.o rbtree-shim.o \
+	 interval_tree-shim.o interval_tree.o
+TARGETS = anon_vma
+
+interval_tree.o: ../../../mm/interval_tree.c
+anon_vma.o: anon_vma.c anon_vma_internal.h ../../../mm/anon_vma.c \
+	 ../../../include/linux/anon_vma.h
+
+anon_vma: $(OFILES)
+
+clean:
+	$(RM) $(TARGETS) *.o generated/*
diff --git a/tools/testing/anon_vma/anon_vma.c b/tools/testing/anon_vma/anon_vma.c
new file mode 100644
index 000000000000..f3ca193857ec
--- /dev/null
+++ b/tools/testing/anon_vma/anon_vma.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/bitmap.h>
+
+#include "anon_vma_internal.h"
+#include "../../../mm/anon_vma.c"
+
+#define ASSERT_TRUE(_expr)						\
+	do {								\
+		if (!(_expr)) {						\
+			fprintf(stderr,					\
+				"Assert FAILED at %s:%d:%s(): %s is FALSE.\n", \
+				__FILE__, __LINE__, __func__, #_expr); \
+			return false;					\
+		}							\
+	} while (0)
+#define ASSERT_FALSE(_expr) ASSERT_TRUE(!(_expr))
+#define ASSERT_EQ(_val1, _val2) ASSERT_TRUE((_val1) == (_val2))
+#define ASSERT_NE(_val1, _val2) ASSERT_TRUE((_val1) != (_val2))
+
+static struct mm_struct dummy_mm = {
+	.page_table_lock = __SPIN_LOCK_UNLOCKED(dummy_mm.page_table_lock),
+};
+
+#define NUM_VMAS 50
+static int vmas_idx;
+static struct vm_area_struct *vmas[NUM_VMAS];
+static struct kmem_cache *vm_area_cachep;
+
+static void vma_ctor(void *data)
+{
+	struct vm_area_struct *vma;
+
+	vma = data;
+	INIT_LIST_HEAD(&vma->anon_vma_chain);
+	vma->vm_mm = &dummy_mm;
+	vma->anon_vma = NULL;
+}
+
+void vma_cache_init(void)
+{
+	/* vma's kmem_cache for test */
+	vm_area_cachep = kmem_cache_create("vm_area_struct",
+			sizeof(struct vm_area_struct), 0,
+			SLAB_PANIC|SLAB_ACCOUNT, vma_ctor);
+}
+
+struct vm_area_struct *alloc_vma(unsigned long start, unsigned long end, pgoff_t pgoff)
+{
+	if (vmas_idx >= NUM_VMAS)
+		return NULL;
+
+	vmas[vmas_idx] = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
+	vmas[vmas_idx]->index = vmas_idx;
+	vma_set_range(vmas[vmas_idx], start, end, pgoff);
+
+	return vmas[vmas_idx++];
+}
+
+void vm_area_free(struct vm_area_struct *vma)
+{
+	kmem_cache_free(vm_area_cachep, vma);
+}
+
+void cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < NUM_VMAS; i++) {
+		if (!vmas[i])
+			continue;
+
+		unlink_anon_vmas(vmas[i]);
+		vm_area_free(vmas[i]);
+		vmas[i] = NULL;
+	}
+
+	vmas_idx = 0;
+}
+
+static bool test_simple_fault(void)
+{
+	struct vm_area_struct *root_vma;
+	struct anon_vma_chain *avc;
+
+	root_vma = alloc_vma(0x3000, 0x5000, 3);
+	/* First fault on anonymous vma would create its anon_vma. */
+	__anon_vma_prepare(root_vma);
+	ASSERT_NE(NULL, root_vma->anon_vma);
+	dump_anon_vma_interval_tree(root_vma->anon_vma);
+
+	/*
+	 *  anon_vma           root_vma
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 */
+
+	anon_vma_interval_tree_foreach(avc, &root_vma->anon_vma->rb_root, 3, 4) {
+		/* Expect to find itself from anon_vma interval tree */
+		ASSERT_EQ(avc->vma, root_vma);
+	}
+
+	cleanup();
+
+	ASSERT_EQ(0, nr_allocated);
+	return true;
+}
+
+static bool test_simple_fork(void)
+{
+	struct vm_area_struct *root_vma, *child_vma;
+	struct anon_vma_chain *avc;
+	DECLARE_BITMAP(expected, 10);
+	DECLARE_BITMAP(found, 10);
+
+	bitmap_zero(expected, 10);
+	bitmap_zero(found, 10);
+
+	/*
+	 *  anon_vma           root_vma
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 */
+
+	root_vma = alloc_vma(0x3000, 0x5000, 3);
+	/* First fault on parent anonymous vma. */
+	__anon_vma_prepare(root_vma);
+	ASSERT_NE(NULL, root_vma->anon_vma);
+	bitmap_set(expected, root_vma->index, 1);
+
+	/*
+	 *  anon_vma           root_vma
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 *                \
+	 *                 \   child_vma
+	 *                  \  +-----------+
+	 *                   > |           |
+	 *                     +-----------+
+	 */
+
+	child_vma = alloc_vma(0x3000, 0x5000, 3);
+	/* Fork child will link it to parent and may create its own anon_vma. */
+	anon_vma_fork(child_vma, root_vma);
+	ASSERT_NE(NULL, child_vma->anon_vma);
+	bitmap_set(expected, child_vma->index, 1);
+	/* Parent/Root is root_vma->anon_vma */
+	ASSERT_EQ(child_vma->anon_vma->parent, root_vma->anon_vma);
+	ASSERT_EQ(child_vma->anon_vma->root, root_vma->anon_vma);
+	dump_anon_vma_interval_tree(root_vma->anon_vma);
+
+	anon_vma_interval_tree_foreach(avc, &root_vma->anon_vma->rb_root, 3, 4) {
+		bitmap_set(found, avc->vma->index, 1);
+	}
+
+	/* Expect to find all vma including the forked one. */
+	ASSERT_TRUE(bitmap_equal(expected, found, 10));
+
+	cleanup();
+
+	ASSERT_EQ(0, nr_allocated);
+	return true;
+}
+
+static bool test_fork_two(void)
+{
+	struct vm_area_struct *root_vma, *vma1, *vma2;
+	struct anon_vma_chain *avc;
+	DECLARE_BITMAP(expected, 10);
+	DECLARE_BITMAP(found, 10);
+
+	bitmap_zero(expected, 10);
+	bitmap_zero(found, 10);
+
+	/*
+	 *  anon_vma           root_vma
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 */
+
+	root_vma = alloc_vma(0x3000, 0x5000, 3);
+	/* First fault on parent anonymous vma. */
+	__anon_vma_prepare(root_vma);
+	ASSERT_NE(NULL, root_vma->anon_vma);
+	bitmap_set(expected, root_vma->index, 1);
+
+	/* First fork */
+	/*
+	 *  anon_vma           root_vma
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 *                \
+	 *                 \   vma1
+	 *                  \  +-----------+
+	 *                   > |           |
+	 *                     +-----------+
+	 */
+	vma1 = alloc_vma(0x3000, 0x5000, 3);
+	anon_vma_fork(vma1, root_vma);
+	ASSERT_NE(NULL, vma1->anon_vma);
+	bitmap_set(expected, vma1->index, 1);
+	/* Parent/Root is root_vma->anon_vma */
+	ASSERT_EQ(vma1->anon_vma->parent, root_vma->anon_vma);
+	ASSERT_EQ(vma1->anon_vma->root, root_vma->anon_vma);
+
+	/* Second fork */
+	/*
+	 *  anon_vma           root_vma
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 *               \
+	 *                \------------------+
+	 *                 \   vma1           \   vma2
+	 *                  \  +-----------+   \  +-----------+
+	 *                   > |           |    > |           |
+	 *                     +-----------+      +-----------+
+	 */
+	vma2 = alloc_vma(0x3000, 0x5000, 3);
+	anon_vma_fork(vma2, root_vma);
+	ASSERT_NE(NULL, vma2->anon_vma);
+	bitmap_set(expected, vma2->index, 1);
+	/* Parent/Root is root_vma->anon_vma */
+	ASSERT_EQ(vma2->anon_vma->parent, root_vma->anon_vma);
+	ASSERT_EQ(vma2->anon_vma->root, root_vma->anon_vma);
+	dump_anon_vma_interval_tree(root_vma->anon_vma);
+
+	anon_vma_interval_tree_foreach(avc, &root_vma->anon_vma->rb_root, 3, 4) {
+		bitmap_set(found, avc->vma->index, 1);
+	}
+
+	/* Expect to find all vma including the forked one. */
+	ASSERT_TRUE(bitmap_equal(expected, found, 10));
+
+	/*
+	 *  vma1->anon_vma     vma1
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 */
+	anon_vma_interval_tree_foreach(avc, &vma1->anon_vma->rb_root, 3, 4) {
+		/* Expect to find only itself from its anon_vma interval tree */
+		ASSERT_EQ(avc->vma, vma1);
+	}
+
+	/*
+	 *  vma2->anon_vma     vma2
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 */
+	anon_vma_interval_tree_foreach(avc, &vma2->anon_vma->rb_root, 3, 4) {
+		/* Expect to find only itself from its anon_vma interval tree */
+		ASSERT_EQ(avc->vma, vma2);
+	}
+
+	cleanup();
+
+	ASSERT_EQ(0, nr_allocated);
+	return true;
+}
+
+static bool test_fork_grand_child(void)
+{
+	struct vm_area_struct *root_vma, *grand_vma, *vma1, *vma2;
+	struct anon_vma_chain *avc;
+	struct anon_vma *root_anon_vma;
+	DECLARE_BITMAP(expected, 10);
+	DECLARE_BITMAP(found, 10);
+
+	bitmap_zero(expected, 10);
+	bitmap_zero(found, 10);
+
+	/*
+	 *  root_anon_vma      root_vma
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 */
+
+	root_vma = alloc_vma(0x3000, 0x5000, 3);
+	/* First fault on parent anonymous vma. */
+	__anon_vma_prepare(root_vma);
+	root_anon_vma = root_vma->anon_vma;
+	ASSERT_NE(NULL, root_anon_vma);
+	bitmap_set(expected, root_vma->index, 1);
+
+	/* First fork */
+	/*
+	 *  root_anon_vma      root_vma
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 *                \
+	 *                 \   vma1
+	 *                  \  +-----------+
+	 *                   > |           |
+	 *                     +-----------+
+	 */
+	vma1 = alloc_vma(0x3000, 0x5000, 3);
+	anon_vma_fork(vma1, root_vma);
+	ASSERT_NE(NULL, vma1->anon_vma);
+	bitmap_set(expected, vma1->index, 1);
+	/* Parent/Root is root_vma->anon_vma */
+	ASSERT_EQ(vma1->anon_vma->parent, root_vma->anon_vma);
+	ASSERT_EQ(vma1->anon_vma->root, root_vma->anon_vma);
+
+	/* Second fork */
+	/*
+	 *  root_anon_vma      root_vma
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 *               \
+	 *                \------------------+
+	 *                 \   vma1           \   vma2
+	 *                  \  +-----------+   \  +-----------+
+	 *                   > |           |    > |           |
+	 *                     +-----------+      +-----------+
+	 */
+	vma2 = alloc_vma(0x3000, 0x5000, 3);
+	anon_vma_fork(vma2, root_vma);
+	ASSERT_NE(NULL, vma2->anon_vma);
+	bitmap_set(expected, vma2->index, 1);
+	/* Parent/Root is root_vma->anon_vma */
+	ASSERT_EQ(vma2->anon_vma->parent, root_vma->anon_vma);
+	ASSERT_EQ(vma2->anon_vma->root, root_vma->anon_vma);
+	dump_anon_vma_interval_tree(root_vma->anon_vma);
+
+	/* Fork grand child from second child */
+	/*
+	 *  root_anon_vma      root_vma
+	 *  +-----------+      +-----------+
+	 *  |           | ---> |           |
+	 *  +-----------+      +-----------+
+	 *               \
+	 *                \------------------+
+	 *                |\   vma1           \   vma2
+	 *                | \  +-----------+   \  +-----------+
+	 *                |  > |           |    > |           |
+	 *                |    +-----------+      +-----------+
+	 *                \
+	 *                 \   grand_vma
+	 *                  \  +-----------+
+	 *                   > |           |
+	 *                     +-----------+
+	 */
+	grand_vma = alloc_vma(0x3000, 0x5000, 3);
+	anon_vma_fork(grand_vma, vma2);
+	ASSERT_NE(NULL, grand_vma->anon_vma);
+	bitmap_set(expected, grand_vma->index, 1);
+	/* Root is root_vma->anon_vma */
+	ASSERT_EQ(grand_vma->anon_vma->root, root_vma->anon_vma);
+	/* Parent is vma2->anon_vma */
+	ASSERT_EQ(grand_vma->anon_vma->parent, vma2->anon_vma);
+
+	/* Expect to find only vmas from second fork */
+	anon_vma_interval_tree_foreach(avc, &vma2->anon_vma->rb_root, 3, 4) {
+		ASSERT_TRUE(avc->vma == vma2 || avc->vma == grand_vma);
+	}
+
+	anon_vma_interval_tree_foreach(avc, &root_vma->anon_vma->rb_root, 3, 4) {
+		bitmap_set(found, avc->vma->index, 1);
+	}
+	/* Expect to find all vma including child and grand child. */
+	ASSERT_TRUE(bitmap_equal(expected, found, 10));
+
+	/* Root process exit or unmap root_vma. */
+	/*
+	 *  root_anon_vma
+	 *  +-----------+
+	 *  |           |
+	 *  +-----------+
+	 *               \
+	 *                \------------------+
+	 *                |\   vma1           \   vma2
+	 *                | \  +-----------+   \  +-----------+
+	 *                |  > |           |    > |           |
+	 *                |    +-----------+      +-----------+
+	 *                \
+	 *                 \   grand_vma
+	 *                  \  +-----------+
+	 *                   > |           |
+	 *                     +-----------+
+	 */
+	bitmap_clear(expected, root_vma->index, 1);
+	unlink_anon_vmas(root_vma);
+	ASSERT_EQ(0, root_anon_vma->num_active_vmas);
+
+	bitmap_zero(found, 10);
+	anon_vma_interval_tree_foreach(avc, &root_anon_vma->rb_root, 3, 4) {
+		bitmap_set(found, avc->vma->index, 1);
+	}
+	/* Expect to find all vmas even root_vma released. */
+	ASSERT_TRUE(bitmap_equal(expected, found, 10));
+
+	cleanup();
+
+	ASSERT_EQ(0, nr_allocated);
+	return true;
+}
+
+int main(void)
+{
+	int num_tests = 0, num_fail = 0;
+
+	vma_cache_init();
+	anon_vma_init();
+
+#define TEST(name)							\
+	do {								\
+		num_tests++;						\
+		if (!test_##name()) {					\
+			num_fail++;					\
+			fprintf(stderr, "Test " #name " FAILED\n");	\
+		}							\
+	} while (0)
+
+	TEST(simple_fault);
+	TEST(simple_fork);
+	TEST(fork_two);
+	TEST(fork_grand_child);
+
+#undef TEST
+
+	printf("%d tests run, %d passed, %d failed.\n",
+	       num_tests, num_tests - num_fail, num_fail);
+
+	return num_fail == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tools/testing/anon_vma/anon_vma_internal.h b/tools/testing/anon_vma/anon_vma_internal.h
new file mode 100644
index 000000000000..296c1df71f7c
--- /dev/null
+++ b/tools/testing/anon_vma/anon_vma_internal.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __MM_ANON_VMA_INTERNAL_H
+#define __MM_ANON_VMA_INTERNAL_H
+
+#define CONFIG_MMU
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm_types.h>
+#include <linux/mm.h>
+#include <linux/bug.h>
+
+#define SLAB_TYPESAFE_BY_RCU	0
+#define SLAB_ACCOUNT		0
+
+static inline void might_sleep(void)
+{
+}
+
+static inline void mmap_assert_locked(struct mm_struct *)
+{
+}
+
+/*
+ * rwsem_is_locked() is only used in anon_vma_free() to synchronize against
+ * folio_lock_anon_vma_read(), which is not used in current tests.
+ *
+ * So just return 0 for simplification.
+ */
+static inline int rwsem_is_locked(struct rw_semaphore *sem)
+{
+	return 0;
+}
+
+static inline struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *vma)
+{
+	return NULL;
+}
+
+#ifndef pgoff_t
+#define pgoff_t unsigned long
+#endif
+static inline void vma_set_range(struct vm_area_struct *vma,
+					  unsigned long start, unsigned long end,
+					  pgoff_t pgoff)
+{
+	vma->vm_start = start;
+	vma->vm_end = end;
+	vma->vm_pgoff = pgoff;
+}
+
+void dump_anon_vma_interval_tree(struct anon_vma *anon_vma);
+extern int nr_allocated;
+#endif // __MM_ANON_VMA_INTERNAL_H
diff --git a/tools/testing/anon_vma/interval_tree.c b/tools/testing/anon_vma/interval_tree.c
new file mode 100644
index 000000000000..154c9ccbf3e1
--- /dev/null
+++ b/tools/testing/anon_vma/interval_tree.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "../../../mm/interval_tree.c"
+
+#ifdef ANON_VMA_INTERVAL_TREE_DEBUG
+enum child_dir {
+	left_child,
+	right_child,
+	root_node
+};
+
+typedef void (*dump_node)(struct rb_node *node, int level);
+
+void dump_avc(struct rb_node *node, int level)
+{
+	struct anon_vma_chain *avc;
+
+	avc = rb_entry(node, struct anon_vma_chain, rb);
+
+	printf(" -%02d [%lu, %lu] %lu\n", avc->vma->index,
+		avc_start_pgoff(avc), avc_last_pgoff(avc),
+		avc->rb_subtree_last);
+}
+
+void dump_rb_tree(struct rb_node *node, int level,
+		  enum child_dir state, dump_node print)
+{
+	char prefix[40] = "                                        ";
+
+	if (!node)
+		return;
+
+	dump_rb_tree(node->rb_right, level+1, right_child, print);
+
+	if (state == left_child)
+		printf("%.*s|\n", min_t(int, level * 2 + 2, ARRAY_SIZE(prefix)), prefix);
+
+	printf("%02d%.*s", level, min_t(int, level * 2, ARRAY_SIZE(prefix)), prefix);
+	(*print)(node, level);
+
+	if (state == right_child)
+		printf("%.*s|\n", min_t(int, level * 2 + 2, ARRAY_SIZE(prefix)), prefix);
+
+	dump_rb_tree(node->rb_left, level+1, left_child, print);
+}
+
+void dump_anon_vma_interval_tree(struct anon_vma *anon_vma)
+{
+	dump_rb_tree(anon_vma->rb_root.rb_root.rb_node, 0, root_node, dump_avc);
+}
+#else
+void dump_anon_vma_interval_tree(struct anon_vma *anon_vma) { return; }
+#endif
diff --git a/tools/testing/anon_vma/linux/atomic.h b/tools/testing/anon_vma/linux/atomic.h
new file mode 100644
index 000000000000..b9f7c2e91f15
--- /dev/null
+++ b/tools/testing/anon_vma/linux/atomic.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __TOOLS_LINUX_ATOMIC_H
+#define __TOOLS_LINUX_ATOMIC_H
+
+#include <urcu/uatomic.h>
+
+#define atomic_t int32_t
+#define atomic_inc(x) uatomic_inc(x)
+#define atomic_read(x) uatomic_read(x)
+#define atomic_set(x, y) uatomic_set(x, y)
+
+static inline bool atomic_dec_and_test(atomic_t *v)
+{
+	return uatomic_sub_return(v, 1) == 0;
+}
+
+#endif	/* __TOOLS_LINUX_ATOMIC_H */
diff --git a/tools/testing/anon_vma/linux/fs.h b/tools/testing/anon_vma/linux/fs.h
new file mode 100644
index 000000000000..9e9addd3310c
--- /dev/null
+++ b/tools/testing/anon_vma/linux/fs.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __TOOLS_LINUX_FS_H
+#define __TOOLS_LINUX_FS_H
+
+#endif /* __TOOLS_LINUX_FS_H */
diff --git a/tools/testing/anon_vma/linux/mm.h b/tools/testing/anon_vma/linux/mm.h
new file mode 100644
index 000000000000..3bfe232f298a
--- /dev/null
+++ b/tools/testing/anon_vma/linux/mm.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __TOOLS_LINUX_MM_H
+#define __TOOLS_LINUX_MM_H
+
+#include <linux/kernel.h>
+#include <linux/mm_types.h>
+#include <linux/interval_tree_generic.h>
+#include <linux/types.h>
+#include "../../include/linux/mm.h"
+
+#define VM_WARN_ON(_expr) (WARN_ON(_expr))
+#define VM_BUG_ON(_expr) (BUG_ON(_expr))
+#define VM_BUG_ON_VMA(_expr, _vma) (BUG_ON(_expr))
+
+static inline unsigned long vma_pages(struct vm_area_struct *vma)
+{
+	return (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+}
+
+#include <linux/anon_vma.h>
+
+#define vma_interval_tree_foreach(vma, root, start, last)		\
+	for (vma = vma_interval_tree_iter_first(root, start, last);	\
+	     vma; vma = vma_interval_tree_iter_next(vma, start, last))
+
+void anon_vma_interval_tree_insert(struct anon_vma_chain *node,
+				   struct rb_root_cached *root);
+void anon_vma_interval_tree_remove(struct anon_vma_chain *node,
+				   struct rb_root_cached *root);
+struct anon_vma_chain *
+anon_vma_interval_tree_iter_first(struct rb_root_cached *root,
+				  unsigned long start, unsigned long last);
+struct anon_vma_chain *anon_vma_interval_tree_iter_next(
+	struct anon_vma_chain *node, unsigned long start, unsigned long last);
+#ifdef CONFIG_DEBUG_VM_RB
+void anon_vma_interval_tree_verify(struct anon_vma_chain *node);
+#endif
+
+#define anon_vma_interval_tree_foreach(avc, root, start, last)		 \
+	for (avc = anon_vma_interval_tree_iter_first(root, start, last); \
+	     avc; avc = anon_vma_interval_tree_iter_next(avc, start, last))
+
+#endif /* __TOOLS_LINUX_MM_H */
diff --git a/tools/testing/anon_vma/linux/mm_types.h b/tools/testing/anon_vma/linux/mm_types.h
new file mode 100644
index 000000000000..9cfbedecdc35
--- /dev/null
+++ b/tools/testing/anon_vma/linux/mm_types.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __TOOLS_LINUX_MM_TYPES_H
+#define __TOOLS_LINUX_MM_TYPES_H
+
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <linux/rwsem.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+
+struct mm_struct {
+		spinlock_t page_table_lock;
+};
+
+struct vm_area_struct {
+	/* For test purpose */
+	int	index;
+	/* VMA covers [vm_start; vm_end) addresses within mm */
+	unsigned long vm_start;
+	unsigned long vm_end;
+
+	/* The address space we belong to. */
+	struct mm_struct *vm_mm;
+
+	/*
+	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
+	 * list, after a COW of one of the file pages.	A MAP_SHARED vma
+	 * 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.
+	 */
+	struct list_head anon_vma_chain; /* Serialized by mmap_lock &
+					  * page_table_lock */
+	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */
+	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
+					   units */
+	/*
+	 * For areas with an address space and backing store,
+	 * linkage into the address_space->i_mmap interval tree.
+	 *
+	 */
+	struct {
+		struct rb_node rb;
+		unsigned long rb_subtree_last;
+	} shared;
+#ifdef CONFIG_ANON_VMA_NAME
+	/*
+	 * For private and shared anonymous mappings, a pointer to a null
+	 * terminated string containing the name given to the vma, or NULL if
+	 * unnamed. Serialized by mmap_lock. Use anon_vma_name to access.
+	 */
+	struct anon_vma_name *anon_name;
+#endif
+};
+
+#endif	/* __TOOLS_LINUX_MM_TYPES_H */
diff --git a/tools/testing/anon_vma/linux/mmzone.h b/tools/testing/anon_vma/linux/mmzone.h
new file mode 100644
index 000000000000..e6be2d214777
--- /dev/null
+++ b/tools/testing/anon_vma/linux/mmzone.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __TOOLS_LINUX_MMZONE_H
+#define __TOOLS_LINUX_MMZONE_H
+
+#endif /* __TOOLS_LINUX_MMZONE_H */
diff --git a/tools/testing/anon_vma/linux/rmap.h b/tools/testing/anon_vma/linux/rmap.h
new file mode 100644
index 000000000000..d29511ae0294
--- /dev/null
+++ b/tools/testing/anon_vma/linux/rmap.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __TOOLS_LINUX_RMAP_H
+#define __TOOLS_LINUX_RMAP_H
+
+#include <linux/anon_vma.h>
+
+#endif /* __TOOLS_LINUX_RMAP_H */
diff --git a/tools/testing/shared/linux/anon_vma.h b/tools/testing/shared/linux/anon_vma.h
new file mode 100644
index 000000000000..a5bac325402a
--- /dev/null
+++ b/tools/testing/shared/linux/anon_vma.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _TEST_ANON_VMA_H
+#define _TEST_ANON_VMA_H
+
+#include "../../../../include/linux/anon_vma.h"
+
+#endif /* _TEST_ANON_VMA_H */
-- 
2.34.1



  parent reply	other threads:[~2025-04-29  9:07 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-29  9:06 [RFC Patch 0/5] Make anon_vma operations testable Wei Yang
2025-04-29  9:06 ` [RFC Patch 1/5] mm: move anon_vma manipulation functions to own file Wei Yang
2025-04-29  9:06 ` Wei Yang [this message]
2025-05-01  1:31   ` [RFC Patch 2/5] anon_vma: add skeleton code for userland testing of anon_vma logic Wei Yang
2025-05-01  9:41     ` Lorenzo Stoakes
2025-05-01 14:45       ` Wei Yang
2025-04-29  9:06 ` [RFC Patch 3/5] anon_vma: add test for mergeable anon_vma Wei Yang
2025-04-29  9:06 ` [RFC Patch 4/5] anon_vma: add test for reusable anon_vma Wei Yang
2025-04-29  9:06 ` [RFC Patch 5/5] anon_vma: add test to assert no double-reuse Wei Yang
2025-04-29  9:31 ` [RFC Patch 0/5] Make anon_vma operations testable Lorenzo Stoakes
2025-04-29  9:38   ` David Hildenbrand
2025-04-29  9:41     ` Lorenzo Stoakes
2025-04-29 23:56       ` Wei Yang
2025-04-30  7:47         ` David Hildenbrand
2025-04-30 15:44           ` Wei Yang
2025-04-30 21:36             ` David Hildenbrand
2025-05-14  1:23           ` Wei Yang
2025-05-27  6:34             ` Wei Yang
2025-05-27 11:31               ` David Hildenbrand
2025-05-28  1:17                 ` Wei Yang
2025-05-30  2:11                 ` Wei Yang
2025-05-30  8:00                   ` David Hildenbrand
2025-05-30 14:05                     ` Wei Yang
2025-05-30 14:39                       ` David Hildenbrand
2025-05-30 23:23                         ` Wei Yang
2025-06-03 21:31                           ` David Hildenbrand
2025-04-29 23:15   ` Wei Yang
2025-04-30 14:38     ` Lorenzo Stoakes
2025-04-30 15:41       ` Wei Yang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250429090639.784-3-richard.weiyang@gmail.com \
    --to=richard.weiyang@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=baohua@kernel.org \
    --cc=david@redhat.com \
    --cc=harry.yoo@oracle.com \
    --cc=jannh@google.com \
    --cc=linux-mm@kvack.org \
    --cc=lorenzo.stoakes@oracle.com \
    --cc=riel@surriel.com \
    --cc=vbabka@suse.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox