Skip to contentSkip to conversation

Make core VMA operations internal and testable

PATCH Superseded Lorenzo Stoakes 5 participants 22 messages Jul 3, 2024 – Jul 4, 2024

Subsystems (10)

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

cover Make core VMA operations internal and testable

There are a number of "core" VMA manipulation functions implemented in mm/mmap.c, notably those concerning VMA merging, splitting, modifying, expanding and shrinking, which logically don't belong there.

More importantly this functionality represents an internal implementation detail of memory management and should not be exposed outside of mm/ itself.

This patch series isolates core VMA manipulation functionality into its own file, mm/vma.c, and provides an API to the rest of the mm code in mm/vma.h.

Importantly, it also carefully implements mm/vma_internal.h, which specifies which headers need to be imported by vma.c, leading to the very useful property that vma.c depends only on mm/vma.h and mm/vma_internal.h.

This means we can then re-implement vma_internal.h in userland, adding shims for kernel mechanisms as required, allowing us to unit test internal VMA functionality.

This testing is useful as opposed to an e.g. kunit implementation as this way we can avoid all external kernel side-effects while testing, run tests VERY quickly, and iterate on and debug problems quickly.

Excitingly this opens the door to, in the future, recreating precise problems observed in production in userland and very quickly debugging problems that might otherwise be very difficult to reproduce.

This patch series takes advantage of existing shim logic and full userland maple tree support contained in tools/testing/radix-tree/ and tools/include/linux/, separating out shared components of the radix tree implementation to provide this testing.

Kernel functionality is stubbed and shimmed as needed in tools/testing/vma/ which contains a fully functional userland vma_internal.h file and which imports mm/vma.c and mm/vma.h to be directly tested from userland.

A simple, skeleton testing implementation is provided in tools/testing/vma/vma.c as a proof-of-concept, asserting that simple VMA merge, modify (testing split), expand and shrink functionality work correctly.

v1:
* Fix test_simple_modify() to specify correct prev.
* Improve vma test Makefile so it picks up dependency changes correctly.
* Rename relocate_vma() to relocate_vma_down().
* Remove shift_arg_pages() and invoked relocate_vma_down() directly from setup_arg_pages().
* MAINTAINERS fixups.

RFC v2:
* Reword commit messages.
* Replace vma_expand() / vma_shrink() wrappers with relocate_vma().
* Make move_page_tables() internal too.
* Have internal.h import vma.h.
* Use header guards to more cleanly implement userland testing code.
* Rename main.c to vma.c.
* Update mm/vma_internal.h to have fewer superfluous comments.
* Rework testing logic so we count test failures, and output test results.
* Correct some SPDX license prefixes.
* Make VM_xxx_ON() debug asserts forward to xxx_ON() macros.
* Update VMA tests to correctly free memory, and re-enable ASAN leak detection.
https://lore.kernel.org/all/[email protected]/

RFC v1:
https://lore.kernel.org/all/[email protected]/

Lorenzo Stoakes (7):
userfaultfd: move core VMA manipulation logic to mm/userfaultfd.c
mm: move vma_modify() and helpers to internal header
mm: move vma_shrink(), vma_expand() to internal header
mm: move internal core VMA manipulation functions to own file
MAINTAINERS: Add entry for new VMA files
tools: separate out shared radix-tree components
tools: add skeleton code for userland testing of VMA logic

 MAINTAINERS                                   |   14 +
 fs/exec.c                                     |   81 +-
 fs/userfaultfd.c                              |  160 +-
 include/linux/atomic.h                        |    2 +-
 include/linux/mm.h                            |  112 +-
 include/linux/mmzone.h                        |    3 +-
 include/linux/userfaultfd_k.h                 |   19 +
 mm/Makefile                                   |    2 +-
 mm/internal.h                                 |  167 +-
 mm/mmap.c                                     | 2069 ++---------------
 mm/mmu_notifier.c                             |    2 +
 mm/userfaultfd.c                              |  168 ++
 mm/vma.c                                      | 1766 ++++++++++++++
 mm/vma.h                                      |  362 +++
 mm/vma_internal.h                             |   52 +
 tools/testing/radix-tree/Makefile             |   68 +-
 tools/testing/radix-tree/maple.c              |   14 +-
 tools/testing/radix-tree/xarray.c             |    9 +-
 tools/testing/shared/autoconf.h               |    2 +
 tools/testing/{radix-tree => shared}/bitmap.c |    0
 tools/testing/{radix-tree => shared}/linux.c  |    0
 .../{radix-tree => shared}/linux/bug.h        |    0
 .../{radix-tree => shared}/linux/cpu.h        |    0
 .../{radix-tree => shared}/linux/idr.h        |    0
 .../{radix-tree => shared}/linux/init.h       |    0
 .../{radix-tree => shared}/linux/kconfig.h    |    0
 .../{radix-tree => shared}/linux/kernel.h     |    0
 .../{radix-tree => shared}/linux/kmemleak.h   |    0
 .../{radix-tree => shared}/linux/local_lock.h |    0
 .../{radix-tree => shared}/linux/lockdep.h    |    0
 .../{radix-tree => shared}/linux/maple_tree.h |    0
 .../{radix-tree => shared}/linux/percpu.h     |    0
 .../{radix-tree => shared}/linux/preempt.h    |    0
 .../{radix-tree => shared}/linux/radix-tree.h |    0
 .../{radix-tree => shared}/linux/rcupdate.h   |    0
 .../{radix-tree => shared}/linux/xarray.h     |    0
 tools/testing/shared/maple-shared.h           |    9 +
 tools/testing/shared/maple-shim.c             |    7 +
 tools/testing/shared/shared.h                 |   34 +
 tools/testing/shared/shared.mk                |   68 +
 .../testing/shared/trace/events/maple_tree.h  |    5 +
 tools/testing/shared/xarray-shared.c          |    5 +
 tools/testing/shared/xarray-shared.h          |    4 +
 tools/testing/vma/.gitignore                  |    6 +
 tools/testing/vma/Makefile                    |   16 +
 tools/testing/vma/errors.txt                  |    0
 tools/testing/vma/generated/autoconf.h        |    2 +
 tools/testing/vma/linux/atomic.h              |   12 +
 tools/testing/vma/linux/mmzone.h              |   38 +
 tools/testing/vma/vma.c                       |  207 ++
 tools/testing/vma/vma_internal.h              |  882 +++++++
 51 files changed, 3914 insertions(+), 2453 deletions(-)
 create mode 100644 mm/vma.c
 create mode 100644 mm/vma.h
 create mode 100644 mm/vma_internal.h
 create mode 100644 tools/testing/shared/autoconf.h
 rename tools/testing/{radix-tree => shared}/bitmap.c (100%)
 rename tools/testing/{radix-tree => shared}/linux.c (100%)
 rename tools/testing/{radix-tree => shared}/linux/bug.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/cpu.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/idr.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/init.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/kconfig.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/kernel.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/kmemleak.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/local_lock.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/lockdep.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/maple_tree.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/percpu.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/preempt.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/radix-tree.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/rcupdate.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/xarray.h (100%)
 create mode 100644 tools/testing/shared/maple-shared.h
 create mode 100644 tools/testing/shared/maple-shim.c
 create mode 100644 tools/testing/shared/shared.h
 create mode 100644 tools/testing/shared/shared.mk
 create mode 100644 tools/testing/shared/trace/events/maple_tree.h
 create mode 100644 tools/testing/shared/xarray-shared.c
 create mode 100644 tools/testing/shared/xarray-shared.h
 create mode 100644 tools/testing/vma/.gitignore
 create mode 100644 tools/testing/vma/Makefile
 create mode 100644 tools/testing/vma/errors.txt
 create mode 100644 tools/testing/vma/generated/autoconf.h
 create mode 100644 tools/testing/vma/linux/atomic.h
 create mode 100644 tools/testing/vma/linux/mmzone.h
 create mode 100644 tools/testing/vma/vma.c
 create mode 100644 tools/testing/vma/vma_internal.h

--
2.45.2

#1 lore raw

PATCH 1/7 userfaultfd: move core VMA manipulation logic to mm/userfaultfd.c

This patch forms part of a patch series intending to separate out VMA logic and render it testable from userspace, which requires that core manipulation functions be exposed in an mm/-internal header file.

In order to do this, we must abstract APIs we wish to test, in this instance functions which ultimately invoke vma_modify().

This patch therefore moves all logic which ultimately invokes vma_modify() to mm/userfaultfd.c, trying to transfer code at a functional granularity where possible.

---
 fs/userfaultfd.c              | 160 +++-----------------------------
 include/linux/userfaultfd_k.h |  19 ++++
 mm/userfaultfd.c              | 168 ++++++++++++++++++++++++++++++++++
 3 files changed, 198 insertions(+), 149 deletions(-)
fs/userfaultfd.c +11 -149
index 27a3e9285fbf..b3ed7207df7e 100644
@@ -104,21 +104,6 @@ bool userfaultfd_wp_unpopulated(struct vm_area_struct *vma)
 	return ctx->features & UFFD_FEATURE_WP_UNPOPULATED;
 }
 
-static void userfaultfd_set_vm_flags(struct vm_area_struct *vma,
-				     vm_flags_t flags)
-{
-	const bool uffd_wp_changed = (vma->vm_flags ^ flags) & VM_UFFD_WP;
-
-	vm_flags_reset(vma, flags);
-	/*
-	 * For shared mappings, we want to enable writenotify while
-	 * userfaultfd-wp is enabled (see vma_wants_writenotify()). We'll simply
-	 * recalculate vma->vm_page_prot whenever userfaultfd-wp changes.
-	 */
-	if ((vma->vm_flags & VM_SHARED) && uffd_wp_changed)
-		vma_set_page_prot(vma);
-}
-
 static int userfaultfd_wake_function(wait_queue_entry_t *wq, unsigned mode,
 				     int wake_flags, void *key)
 {
@@ -615,22 +600,7 @@ static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,
 	spin_unlock_irq(&ctx->event_wqh.lock);
 
 	if (release_new_ctx) {
-		struct vm_area_struct *vma;
-		struct mm_struct *mm = release_new_ctx->mm;
-		VMA_ITERATOR(vmi, mm, 0);
-
-		/* the various vma->vm_userfaultfd_ctx still points to it */
-		mmap_write_lock(mm);
-		for_each_vma(vmi, vma) {
-			if (vma->vm_userfaultfd_ctx.ctx == release_new_ctx) {
-				vma_start_write(vma);
-				vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
-				userfaultfd_set_vm_flags(vma,
-							 vma->vm_flags & ~__VM_UFFD_FLAGS);
-			}
-		}
-		mmap_write_unlock(mm);
-
+		userfaultfd_release_new(release_new_ctx);
 		userfaultfd_ctx_put(release_new_ctx);
 	}
 
@@ -662,9 +632,7 @@ int dup_userfaultfd(struct vm_area_struct *vma, struct list_head *fcs)
 		return 0;
 
 	if (!(octx->features & UFFD_FEATURE_EVENT_FORK)) {
-		vma_start_write(vma);
-		vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
-		userfaultfd_set_vm_flags(vma, vma->vm_flags & ~__VM_UFFD_FLAGS);
+		userfaultfd_reset_ctx(vma);
 		return 0;
 	}
 
@@ -749,9 +717,7 @@ void mremap_userfaultfd_prep(struct vm_area_struct *vma,
 		up_write(&ctx->map_changing_lock);
 	} else {
 		/* Drop uffd context if remap feature not enabled */
-		vma_start_write(vma);
-		vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
-		userfaultfd_set_vm_flags(vma, vma->vm_flags & ~__VM_UFFD_FLAGS);
+		userfaultfd_reset_ctx(vma);
 	}
 }
 
@@ -870,53 +836,13 @@ static int userfaultfd_release(struct inode *inode, struct file *file)
 {
 	struct userfaultfd_ctx *ctx = file->private_data;
 	struct mm_struct *mm = ctx->mm;
-	struct vm_area_struct *vma, *prev;
 	/* len == 0 means wake all */
 	struct userfaultfd_wake_range range = { .len = 0, };
-	unsigned long new_flags;
-	VMA_ITERATOR(vmi, mm, 0);
 
 	WRITE_ONCE(ctx->released, true);
 
-	if (!mmget_not_zero(mm))
-		goto wakeup;
-
-	/*
-	 * Flush page faults out of all CPUs. NOTE: all page faults
-	 * must be retried without returning VM_FAULT_SIGBUS if
-	 * userfaultfd_ctx_get() succeeds but vma->vma_userfault_ctx
-	 * changes while handle_userfault released the mmap_lock. So
-	 * it's critical that released is set to true (above), before
-	 * taking the mmap_lock for writing.
-	 */
-	mmap_write_lock(mm);
-	prev = NULL;
-	for_each_vma(vmi, vma) {
-		cond_resched();
-		BUG_ON(!!vma->vm_userfaultfd_ctx.ctx ^
-		       !!(vma->vm_flags & __VM_UFFD_FLAGS));
-		if (vma->vm_userfaultfd_ctx.ctx != ctx) {
-			prev = vma;
-			continue;
-		}
-		/* Reset ptes for the whole vma range if wr-protected */
-		if (userfaultfd_wp(vma))
-			uffd_wp_range(vma, vma->vm_start,
-				      vma->vm_end - vma->vm_start, false);
-		new_flags = vma->vm_flags & ~__VM_UFFD_FLAGS;
-		vma = vma_modify_flags_uffd(&vmi, prev, vma, vma->vm_start,
-					    vma->vm_end, new_flags,
-					    NULL_VM_UFFD_CTX);
-
-		vma_start_write(vma);
-		userfaultfd_set_vm_flags(vma, new_flags);
-		vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
+	userfaultfd_release_all(mm, ctx);
 
-		prev = vma;
-	}
-	mmap_write_unlock(mm);
-	mmput(mm);
-wakeup:
 	/*
 	 * After no new page faults can wait on this fault_*wqh, flush
 	 * the last page faults that may have been already waiting on
@@ -1293,14 +1219,14 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
 				unsigned long arg)
 {
 	struct mm_struct *mm = ctx->mm;
-	struct vm_area_struct *vma, *prev, *cur;
+	struct vm_area_struct *vma, *cur;
 	int ret;
 	struct uffdio_register uffdio_register;
 	struct uffdio_register __user *user_uffdio_register;
-	unsigned long vm_flags, new_flags;
+	unsigned long vm_flags;
 	bool found;
 	bool basic_ioctls;
-	unsigned long start, end, vma_end;
+	unsigned long start, end;
 	struct vma_iterator vmi;
 	bool wp_async = userfaultfd_wp_async_ctx(ctx);
 
@@ -1428,57 +1354,8 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
 	} for_each_vma_range(vmi, cur, end);
 	BUG_ON(!found);
 
-	vma_iter_set(&vmi, start);
-	prev = vma_prev(&vmi);
-	if (vma->vm_start < start)
-		prev = vma;
-
-	ret = 0;
-	for_each_vma_range(vmi, vma, end) {
-		cond_resched();
-
-		BUG_ON(!vma_can_userfault(vma, vm_flags, wp_async));
-		BUG_ON(vma->vm_userfaultfd_ctx.ctx &&
-		       vma->vm_userfaultfd_ctx.ctx != ctx);
-		WARN_ON(!(vma->vm_flags & VM_MAYWRITE));
-
-		/*
-		 * Nothing to do: this vma is already registered into this
-		 * userfaultfd and with the right tracking mode too.
-		 */
-		if (vma->vm_userfaultfd_ctx.ctx == ctx &&
-		    (vma->vm_flags & vm_flags) == vm_flags)
-			goto skip;
-
-		if (vma->vm_start > start)
-			start = vma->vm_start;
-		vma_end = min(end, vma->vm_end);
-
-		new_flags = (vma->vm_flags & ~__VM_UFFD_FLAGS) | vm_flags;
-		vma = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,
-					    new_flags,
-					    (struct vm_userfaultfd_ctx){ctx});
-		if (IS_ERR(vma)) {
-			ret = PTR_ERR(vma);
-			break;
-		}
-
-		/*
-		 * In the vma_merge() successful mprotect-like case 8:
-		 * the next vma was merged into the current one and
-		 * the current one has not been updated yet.
-		 */
-		vma_start_write(vma);
-		userfaultfd_set_vm_flags(vma, new_flags);
-		vma->vm_userfaultfd_ctx.ctx = ctx;
-
-		if (is_vm_hugetlb_page(vma) && uffd_disable_huge_pmd_share(vma))
-			hugetlb_unshare_all_pmds(vma);
-
-	skip:
-		prev = vma;
-		start = vma->vm_end;
-	}
+	ret = userfaultfd_register_range(ctx, vma, vm_flags, start, end,
+					 wp_async);
 
 out_unlock:
 	mmap_write_unlock(mm);
@@ -1519,7 +1396,6 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
 	struct vm_area_struct *vma, *prev, *cur;
 	int ret;
 	struct uffdio_range uffdio_unregister;
-	unsigned long new_flags;
 	bool found;
 	unsigned long start, end, vma_end;
 	const void __user *buf = (void __user *)arg;
@@ -1622,27 +1498,13 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
 			wake_userfault(vma->vm_userfaultfd_ctx.ctx, &range);
 		}
 
-		/* Reset ptes for the whole vma range if wr-protected */
-		if (userfaultfd_wp(vma))
-			uffd_wp_range(vma, start, vma_end - start, false);
-
-		new_flags = vma->vm_flags & ~__VM_UFFD_FLAGS;
-		vma = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,
-					    new_flags, NULL_VM_UFFD_CTX);
+		vma = userfaultfd_clear_vma(&vmi, prev, vma,
+					    start, vma_end);
 		if (IS_ERR(vma)) {
 			ret = PTR_ERR(vma);
 			break;
 		}
 
-		/*
-		 * In the vma_merge() successful mprotect-like case 8:
-		 * the next vma was merged into the current one and
-		 * the current one has not been updated yet.
-		 */
-		vma_start_write(vma);
-		userfaultfd_set_vm_flags(vma, new_flags);
-		vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
-
 	skip:
 		prev = vma;
 		start = vma->vm_end;
include/linux/userfaultfd_k.h +19 -0
index 05d59f74fc88..6355ed5bd34b 100644
@@ -264,6 +264,25 @@ extern void userfaultfd_unmap_complete(struct mm_struct *mm,
 extern bool userfaultfd_wp_unpopulated(struct vm_area_struct *vma);
 extern bool userfaultfd_wp_async(struct vm_area_struct *vma);
 
+extern void userfaultfd_reset_ctx(struct vm_area_struct *vma);
+
+extern struct vm_area_struct *userfaultfd_clear_vma(struct vma_iterator *vmi,
+						    struct vm_area_struct *prev,
+						    struct vm_area_struct *vma,
+						    unsigned long start,
+						    unsigned long end);
+
+int userfaultfd_register_range(struct userfaultfd_ctx *ctx,
+			       struct vm_area_struct *vma,
+			       unsigned long vm_flags,
+			       unsigned long start, unsigned long end,
+			       bool wp_async);
+
+extern void userfaultfd_release_new(struct userfaultfd_ctx *ctx);
+
+extern void userfaultfd_release_all(struct mm_struct *mm,
+				    struct userfaultfd_ctx *ctx);
+
 #else /* CONFIG_USERFAULTFD */
 
 /* mm helpers */
mm/userfaultfd.c +168 -0

171 lines not on this page · raw message

--
2.45.2

#2 lore raw

PATCH 2/7 mm: move vma_modify() and helpers to internal header

In reply to Lorenzo Stoakes

These are core VMA manipulation functions which invoke VMA splitting and merging and should not be directly accessed from outside of mm/.

---
 include/linux/mm.h | 60 ---------------------------------------------
 mm/internal.h      | 61 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 60 deletions(-)
include/linux/mm.h +0 -60
index 5f1075d19600..4d2b5538925b 100644
@@ -3285,66 +3285,6 @@ extern struct vm_area_struct *copy_vma(struct vm_area_struct **,
 	unsigned long addr, unsigned long len, pgoff_t pgoff,
 	bool *need_rmap_locks);
 extern void exit_mmap(struct mm_struct *);
-struct vm_area_struct *vma_modify(struct vma_iterator *vmi,
-				  struct vm_area_struct *prev,
-				  struct vm_area_struct *vma,
-				  unsigned long start, unsigned long end,
-				  unsigned long vm_flags,
-				  struct mempolicy *policy,
-				  struct vm_userfaultfd_ctx uffd_ctx,
-				  struct anon_vma_name *anon_name);
-
-/* We are about to modify the VMA's flags. */
-static inline struct vm_area_struct
-*vma_modify_flags(struct vma_iterator *vmi,
-		  struct vm_area_struct *prev,
-		  struct vm_area_struct *vma,
-		  unsigned long start, unsigned long end,
-		  unsigned long new_flags)
-{
-	return vma_modify(vmi, prev, vma, start, end, new_flags,
-			  vma_policy(vma), vma->vm_userfaultfd_ctx,
-			  anon_vma_name(vma));
-}
-
-/* We are about to modify the VMA's flags and/or anon_name. */
-static inline struct vm_area_struct
-*vma_modify_flags_name(struct vma_iterator *vmi,
-		       struct vm_area_struct *prev,
-		       struct vm_area_struct *vma,
-		       unsigned long start,
-		       unsigned long end,
-		       unsigned long new_flags,
-		       struct anon_vma_name *new_name)
-{
-	return vma_modify(vmi, prev, vma, start, end, new_flags,
-			  vma_policy(vma), vma->vm_userfaultfd_ctx, new_name);
-}
-
-/* We are about to modify the VMA's memory policy. */
-static inline struct vm_area_struct
-*vma_modify_policy(struct vma_iterator *vmi,
-		   struct vm_area_struct *prev,
-		   struct vm_area_struct *vma,
-		   unsigned long start, unsigned long end,
-		   struct mempolicy *new_pol)
-{
-	return vma_modify(vmi, prev, vma, start, end, vma->vm_flags,
-			  new_pol, vma->vm_userfaultfd_ctx, anon_vma_name(vma));
-}
-
-/* We are about to modify the VMA's flags and/or uffd context. */
-static inline struct vm_area_struct
-*vma_modify_flags_uffd(struct vma_iterator *vmi,
-		       struct vm_area_struct *prev,
-		       struct vm_area_struct *vma,
-		       unsigned long start, unsigned long end,
-		       unsigned long new_flags,
-		       struct vm_userfaultfd_ctx new_ctx)
-{
-	return vma_modify(vmi, prev, vma, start, end, new_flags,
-			  vma_policy(vma), new_ctx, anon_vma_name(vma));
-}
 
 static inline int check_data_rlimit(unsigned long rlim,
 				    unsigned long new,
mm/internal.h +61 -0

67 lines not on this page · raw message

--
2.45.2

#3 lore raw

PATCH 3/7 mm: move vma_shrink(), vma_expand() to internal header

In reply to Lorenzo Stoakes

The vma_shrink() and vma_expand() functions are internal VMA manipulation functions which we ought to abstract for use outside of memory management code.

To achieve this, we replace shift_arg_pages() in fs/exec.c with an invocation of a new relocate_vma_down() function implemented in mm/mmap.c, which enables us to also move move_page_tables() and vma_iter_prev_range() to internal.h.

The purpose of doing this is to isolate key VMA manipulation functions in order that we can both abstract them and later render them easily testable.

---
 fs/exec.c          | 81 ++++------------------------------------------
 include/linux/mm.h | 17 +---------
 mm/internal.h      | 18 +++++++++++
 mm/mmap.c          | 81 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 106 insertions(+), 91 deletions(-)
fs/exec.c +6 -75
index 40073142288f..8596d325250c 100644
@@ -680,80 +680,6 @@ static int copy_strings_kernel(int argc, const char *const *argv,
 
 #ifdef CONFIG_MMU
 
-/*
- * During bprm_mm_init(), we create a temporary stack at STACK_TOP_MAX.  Once
- * the binfmt code determines where the new stack should reside, we shift it to
- * its final location.  The process proceeds as follows:
- *
- * 1) Use shift to calculate the new vma endpoints.
- * 2) Extend vma to cover both the old and new ranges.  This ensures the
- *    arguments passed to subsequent functions are consistent.
- * 3) Move vma's page tables to the new range.
- * 4) Free up any cleared pgd range.
- * 5) Shrink the vma to cover only the new range.
- */
-static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift)
-{
-	struct mm_struct *mm = vma->vm_mm;
-	unsigned long old_start = vma->vm_start;
-	unsigned long old_end = vma->vm_end;
-	unsigned long length = old_end - old_start;
-	unsigned long new_start = old_start - shift;
-	unsigned long new_end = old_end - shift;
-	VMA_ITERATOR(vmi, mm, new_start);
-	struct vm_area_struct *next;
-	struct mmu_gather tlb;
-
-	BUG_ON(new_start > new_end);
-
-	/*
-	 * ensure there are no vmas between where we want to go
-	 * and where we are
-	 */
-	if (vma != vma_next(&vmi))
-		return -EFAULT;
-
-	vma_iter_prev_range(&vmi);
-	/*
-	 * cover the whole range: [new_start, old_end)
-	 */
-	if (vma_expand(&vmi, vma, new_start, old_end, vma->vm_pgoff, NULL))
-		return -ENOMEM;
-
-	/*
-	 * move the page tables downwards, on failure we rely on
-	 * process cleanup to remove whatever mess we made.
-	 */
-	if (length != move_page_tables(vma, old_start,
-				       vma, new_start, length, false, true))
-		return -ENOMEM;
-
-	lru_add_drain();
-	tlb_gather_mmu(&tlb, mm);
-	next = vma_next(&vmi);
-	if (new_end > old_start) {
-		/*
-		 * when the old and new regions overlap clear from new_end.
-		 */
-		free_pgd_range(&tlb, new_end, old_end, new_end,
-			next ? next->vm_start : USER_PGTABLES_CEILING);
-	} else {
-		/*
-		 * otherwise, clean from old_start; this is done to not touch
-		 * the address space in [new_end, old_start) some architectures
-		 * have constraints on va-space that make this illegal (IA64) -
-		 * for the others its just a little faster.
-		 */
-		free_pgd_range(&tlb, old_start, old_end, new_end,
-			next ? next->vm_start : USER_PGTABLES_CEILING);
-	}
-	tlb_finish_mmu(&tlb);
-
-	vma_prev(&vmi);
-	/* Shrink the vma to just the new range */
-	return vma_shrink(&vmi, vma, new_start, new_end, vma->vm_pgoff);
-}
-
 /*
  * Finalizes the stack vm_area_struct. The flags and permissions are updated,
  * the stack is optionally relocated, and some extra space is added.
@@ -846,7 +772,12 @@ int setup_arg_pages(struct linux_binprm *bprm,
 
 	/* Move stack pages down in memory. */
 	if (stack_shift) {
-		ret = shift_arg_pages(vma, stack_shift);
+		/*
+		 * During bprm_mm_init(), we create a temporary stack at STACK_TOP_MAX.  Once
+		 * the binfmt code determines where the new stack should reside, we shift it to
+		 * its final location.
+		 */
+		ret = relocate_vma_down(vma, stack_shift);
 		if (ret)
 			goto out_unlock;
 	}
include/linux/mm.h +1 -16
index 4d2b5538925b..418aca7e37a6 100644
@@ -998,12 +998,6 @@ static inline struct vm_area_struct *vma_prev(struct vma_iterator *vmi)
 	return mas_prev(&vmi->mas, 0);
 }
 
-static inline
-struct vm_area_struct *vma_iter_prev_range(struct vma_iterator *vmi)
-{
-	return mas_prev_range(&vmi->mas, 0);
-}
-
 static inline unsigned long vma_iter_addr(struct vma_iterator *vmi)
 {
 	return vmi->mas.index;
@@ -2523,11 +2517,6 @@ int set_page_dirty_lock(struct page *page);
 
 int get_cmdline(struct task_struct *task, char *buffer, int buflen);
 
-extern unsigned long move_page_tables(struct vm_area_struct *vma,
-		unsigned long old_addr, struct vm_area_struct *new_vma,
-		unsigned long new_addr, unsigned long len,
-		bool need_rmap_locks, bool for_stack);
-
 /*
  * Flags used by change_protection().  For now we make it a bitmap so
  * that we can pass in multiple flags just like parameters.  However
@@ -3273,11 +3262,6 @@ void anon_vma_interval_tree_verify(struct anon_vma_chain *node);
 
 /* mmap.c */
 extern int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin);
-extern int vma_expand(struct vma_iterator *vmi, struct vm_area_struct *vma,
-		      unsigned long start, unsigned long end, pgoff_t pgoff,
-		      struct vm_area_struct *next);
-extern int vma_shrink(struct vma_iterator *vmi, struct vm_area_struct *vma,
-		       unsigned long start, unsigned long end, pgoff_t pgoff);
 extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *);
 extern int insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
 extern void unlink_file_vma(struct vm_area_struct *);
@@ -3285,6 +3269,7 @@ extern struct vm_area_struct *copy_vma(struct vm_area_struct **,
 	unsigned long addr, unsigned long len, pgoff_t pgoff,
 	bool *need_rmap_locks);
 extern void exit_mmap(struct mm_struct *);
+extern int relocate_vma_down(struct vm_area_struct *vma, unsigned long shift);
 
 static inline int check_data_rlimit(unsigned long rlim,
 				    unsigned long new,
mm/internal.h +18 -0
index 81564ce0f9e2..a4d0e98ccb97 100644
@@ -1305,6 +1305,12 @@ static inline struct vm_area_struct
 			  vma_policy(vma), new_ctx, anon_vma_name(vma));
 }
 
+int vma_expand(struct vma_iterator *vmi, struct vm_area_struct *vma,
+	      unsigned long start, unsigned long end, pgoff_t pgoff,
+	      struct vm_area_struct *next);
+int vma_shrink(struct vma_iterator *vmi, struct vm_area_struct *vma,
+	       unsigned long start, unsigned long end, pgoff_t pgoff);
+
 enum {
 	/* mark page accessed */
 	FOLL_TOUCH = 1 << 16,
@@ -1528,6 +1534,12 @@ static inline int vma_iter_store_gfp(struct vma_iterator *vmi,
 	return 0;
 }
 
+static inline
+struct vm_area_struct *vma_iter_prev_range(struct vma_iterator *vmi)
+{
+	return mas_prev_range(&vmi->mas, 0);
+}
+
 /*
  * VMA lock generalization
  */
@@ -1639,4 +1651,10 @@ void unlink_file_vma_batch_init(struct unlink_vma_file_batch *);
 void unlink_file_vma_batch_add(struct unlink_vma_file_batch *, struct vm_area_struct *);
 void unlink_file_vma_batch_final(struct unlink_vma_file_batch *);
 
+/* mremap.c */
+unsigned long move_page_tables(struct vm_area_struct *vma,
+	unsigned long old_addr, struct vm_area_struct *new_vma,
+	unsigned long new_addr, unsigned long len,
+	bool need_rmap_locks, bool for_stack);
+
 #endif	/* __MM_INTERNAL_H */
mm/mmap.c +81 -0

84 lines not on this page · raw message

--
2.45.2

#4 lore raw

PATCH 4/7 mm: move internal core VMA manipulation functions to own file

In reply to Lorenzo Stoakes

This patch introduces vma.c and moves internal core VMA manipulation functions to this file from mmap.c.

This allows us to isolate VMA functionality in a single place such that we can create userspace testing code that invokes this functionality in an environment where we can implement simple unit tests of core functionality.

This patch ensures that core VMA functionality is explicitly marked as such by its presence in mm/vma.h.

It also places the header includes required by vma.c in vma_internal.h, which is simply imported by vma.c. This makes the VMA functionality testable, as userland testing code can simply stub out functionality as required.

---
 include/linux/mm.h |   35 -
 mm/Makefile        |    2 +-
 mm/internal.h      |  236 +-----
 mm/mmap.c          | 1980 +++-----------------------------------------
 mm/mmu_notifier.c  |    2 +
 mm/vma.c           | 1766 +++++++++++++++++++++++++++++++++++++++
 mm/vma.h           |  362 ++++++++
 mm/vma_internal.h  |   52 ++
 8 files changed, 2292 insertions(+), 2143 deletions(-)
 create mode 100644 mm/vma.c
 create mode 100644 mm/vma.h
 create mode 100644 mm/vma_internal.h
include/linux/mm.h +0 -35
index 418aca7e37a6..69bcf48219fa 100644
@@ -998,21 +998,6 @@ static inline struct vm_area_struct *vma_prev(struct vma_iterator *vmi)
 	return mas_prev(&vmi->mas, 0);
 }
 
-static inline unsigned long vma_iter_addr(struct vma_iterator *vmi)
-{
-	return vmi->mas.index;
-}
-
-static inline unsigned long vma_iter_end(struct vma_iterator *vmi)
-{
-	return vmi->mas.last + 1;
-}
-static inline int vma_iter_bulk_alloc(struct vma_iterator *vmi,
-				      unsigned long count)
-{
-	return mas_expected_entries(&vmi->mas, count);
-}
-
 static inline int vma_iter_clear_gfp(struct vma_iterator *vmi,
 			unsigned long start, unsigned long end, gfp_t gfp)
 {
@@ -2537,21 +2522,6 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen);
 #define  MM_CP_UFFD_WP_ALL                 (MM_CP_UFFD_WP | \
 					    MM_CP_UFFD_WP_RESOLVE)
 
-bool vma_needs_dirty_tracking(struct vm_area_struct *vma);
-bool vma_wants_writenotify(struct vm_area_struct *vma, pgprot_t vm_page_prot);
-static inline bool vma_wants_manual_pte_write_upgrade(struct vm_area_struct *vma)
-{
-	/*
-	 * We want to check manually if we can change individual PTEs writable
-	 * if we can't do that automatically for all PTEs in a mapping. For
-	 * private mappings, that's always the case when we have write
-	 * permissions as we properly have to handle COW.
-	 */
-	if (vma->vm_flags & VM_SHARED)
-		return vma_wants_writenotify(vma, vma->vm_page_prot);
-	return !!(vma->vm_flags & VM_WRITE);
-
-}
 bool can_change_pte_writable(struct vm_area_struct *vma, unsigned long addr,
 			     pte_t pte);
 extern long change_protection(struct mmu_gather *tlb,
@@ -3262,12 +3232,7 @@ void anon_vma_interval_tree_verify(struct anon_vma_chain *node);
 
 /* mmap.c */
 extern int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin);
-extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *);
 extern int insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
-extern void unlink_file_vma(struct vm_area_struct *);
-extern struct vm_area_struct *copy_vma(struct vm_area_struct **,
-	unsigned long addr, unsigned long len, pgoff_t pgoff,
-	bool *need_rmap_locks);
 extern void exit_mmap(struct mm_struct *);
 extern int relocate_vma_down(struct vm_area_struct *vma, unsigned long shift);
 
mm/Makefile +1 -1
index d2915f8c9dc0..140a22654dde 100644
@@ -37,7 +37,7 @@ mmu-y			:= nommu.o
 mmu-$(CONFIG_MMU)	:= highmem.o memory.o mincore.o \
 			   mlock.o mmap.o mmu_gather.o mprotect.o mremap.o \
 			   msync.o page_vma_mapped.o pagewalk.o \
-			   pgtable-generic.o rmap.o vmalloc.o
+			   pgtable-generic.o rmap.o vmalloc.o vma.o
 
 
 ifdef CONFIG_CROSS_MEMORY_ATTACH
mm/internal.h +5 -231

273 lines not on this page · raw message

mm/mmap.c +104 -1876

2,057 lines not on this page · raw message

mm/mmu_notifier.c +2 -0
index 8982e6139d07..fc18fe274505 100644
@@ -19,6 +19,8 @@
 #include <linux/sched/mm.h>
 #include <linux/slab.h>
 
+#include "vma.h"
+
 /* global SRCU for all MMs */
 DEFINE_STATIC_SRCU(srcu);
 
mm/vma.c new file +1766 -0

600 lines not on this page · raw message

mm/vma.h new file +362 -0

362 lines not on this page · raw message

mm/vma_internal.h new file +52 -0

52 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

--
2.45.2

Part of the diff is not shown here. View the raw message.

#5 lore raw

PATCH 5/7 MAINTAINERS: Add entry for new VMA files

In reply to Lorenzo Stoakes

The vma files contain logic split from mmap.c for the most part and are all relevant to VMA logic, so maintain the same reviewers for both.

---
 MAINTAINERS | 13 +++++++++++++
 1 file changed, 13 insertions(+)
MAINTAINERS +13 -0
index 098d214f78d9..ff3e113ed081 100644
@@ -23971,6 +23971,19 @@ F:	include/uapi/linux/vsockmon.h
 F:	net/vmw_vsock/
 F:	tools/testing/vsock/
 
+VMA
+M:	Andrew Morton <[email protected]>
+R:	Liam R. Howlett <[email protected]>
+R:	Vlastimil Babka <[email protected]>
+R:	Lorenzo Stoakes <[email protected]>
+L:	[email protected]
+S:	Maintained
+W:	https://www.linux-mm.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
+F:	mm/vma.c
+F:	mm/vma.h
+F:	mm/vma_internal.h
+
 VMALLOC
 M:	Andrew Morton <[email protected]>
 R:	Uladzislau Rezki <[email protected]>

--
2.45.2

#6 lore raw

Re: [PATCH 5/7] MAINTAINERS: Add entry for new VMA files

Quoted 2 lines · Lorenzo Stoakes

The vma files contain logic split from mmap.c for the most part and are all relevant to VMA logic, so maintain the same reviewers for both.

But it's still related to mmap.c a lot, so why not just rename that existing "MEMORY MAPPING" appropriately (how? haha) and expand it with the new files?

Quoted 31 lines
---
 MAINTAINERS | 13 +++++++++++++
 1 file changed, 13 insertions(+)
MAINTAINERS +13 -1
index 098d214f78d9..ff3e113ed081 100644
@@ -23971,6 +23971,19 @@ F:	include/uapi/linux/vsockmon.h
 F:	net/vmw_vsock/
 F:	tools/testing/vsock/
 
+VMA
+M:	Andrew Morton <[email protected]>
+R:	Liam R. Howlett <[email protected]>
+R:	Vlastimil Babka <[email protected]>
+R:	Lorenzo Stoakes <[email protected]>
+L:	[email protected]
+S:	Maintained
+W:	https://www.linux-mm.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
+F:	mm/vma.c
+F:	mm/vma.h
+F:	mm/vma_internal.h
+
 VMALLOC
 M:	Andrew Morton <[email protected]>
 R:	Uladzislau Rezki <[email protected]>
--

2.45.2

#7 lore raw

Re: [PATCH 5/7] MAINTAINERS: Add entry for new VMA files

Quoted 6 lines · Vlastimil Babka
Quoted 2 lines · Lorenzo Stoakes

The vma files contain logic split from mmap.c for the most part and are all relevant to VMA logic, so maintain the same reviewers for both.

But it's still related to mmap.c a lot, so why not just rename that existing "MEMORY MAPPING" appropriately (how? haha) and expand it with the new files?

Yeah it's debatable, but I think going forward we may wish to to further separate the concept of memory mapping logic and VMA manipulation in which case it makes sense to keep them separated :)

Quoted 33 lines
Quoted 32 lines
---
 MAINTAINERS | 13 +++++++++++++
 1 file changed, 13 insertions(+)
MAINTAINERS +13 -1
index 098d214f78d9..ff3e113ed081 100644
@@ -23971,6 +23971,19 @@ F:	include/uapi/linux/vsockmon.h
 F:	net/vmw_vsock/
 F:	tools/testing/vsock/
 
+VMA
+M:	Andrew Morton <[email protected]>
+R:	Liam R. Howlett <[email protected]>
+R:	Vlastimil Babka <[email protected]>
+R:	Lorenzo Stoakes <[email protected]>
+L:	[email protected]
+S:	Maintained
+W:	https://www.linux-mm.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
+F:	mm/vma.c
+F:	mm/vma.h
+F:	mm/vma_internal.h
+
 VMALLOC
 M:	Andrew Morton <[email protected]>
 R:	Uladzislau Rezki <[email protected]>
--

2.45.2

#8 lore raw

PATCH 6/7 tools: separate out shared radix-tree components

In reply to Lorenzo Stoakes

The core components contained within the radix-tree tests which provide shims for kernel headers and access to the maple tree are useful for testing other things, so separate them out and make the radix tree tests dependent on the shared components.

This lays the groundwork for us to add VMA tests of the newly introduced vma.c file.

---
 tools/testing/radix-tree/Makefile             | 68 +++----------------
 tools/testing/radix-tree/maple.c              | 14 +---
 tools/testing/radix-tree/xarray.c             |  9 +--
 tools/testing/shared/autoconf.h               |  2 +
 tools/testing/{radix-tree => shared}/bitmap.c |  0
 tools/testing/{radix-tree => shared}/linux.c  |  0
 .../{radix-tree => shared}/linux/bug.h        |  0
 .../{radix-tree => shared}/linux/cpu.h        |  0
 .../{radix-tree => shared}/linux/idr.h        |  0
 .../{radix-tree => shared}/linux/init.h       |  0
 .../{radix-tree => shared}/linux/kconfig.h    |  0
 .../{radix-tree => shared}/linux/kernel.h     |  0
 .../{radix-tree => shared}/linux/kmemleak.h   |  0
 .../{radix-tree => shared}/linux/local_lock.h |  0
 .../{radix-tree => shared}/linux/lockdep.h    |  0
 .../{radix-tree => shared}/linux/maple_tree.h |  0
 .../{radix-tree => shared}/linux/percpu.h     |  0
 .../{radix-tree => shared}/linux/preempt.h    |  0
 .../{radix-tree => shared}/linux/radix-tree.h |  0
 .../{radix-tree => shared}/linux/rcupdate.h   |  0
 .../{radix-tree => shared}/linux/xarray.h     |  0
 tools/testing/shared/maple-shared.h           |  9 +++
 tools/testing/shared/maple-shim.c             |  7 ++
 tools/testing/shared/shared.h                 | 34 ++++++++++
 tools/testing/shared/shared.mk                | 68 +++++++++++++++++++
 .../testing/shared/trace/events/maple_tree.h  |  5 ++
 tools/testing/shared/xarray-shared.c          |  5 ++
 tools/testing/shared/xarray-shared.h          |  4 ++
 28 files changed, 147 insertions(+), 78 deletions(-)
 create mode 100644 tools/testing/shared/autoconf.h
 rename tools/testing/{radix-tree => shared}/bitmap.c (100%)
 rename tools/testing/{radix-tree => shared}/linux.c (100%)
 rename tools/testing/{radix-tree => shared}/linux/bug.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/cpu.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/idr.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/init.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/kconfig.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/kernel.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/kmemleak.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/local_lock.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/lockdep.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/maple_tree.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/percpu.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/preempt.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/radix-tree.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/rcupdate.h (100%)
 rename tools/testing/{radix-tree => shared}/linux/xarray.h (100%)
 create mode 100644 tools/testing/shared/maple-shared.h
 create mode 100644 tools/testing/shared/maple-shim.c
 create mode 100644 tools/testing/shared/shared.h
 create mode 100644 tools/testing/shared/shared.mk
 create mode 100644 tools/testing/shared/trace/events/maple_tree.h
 create mode 100644 tools/testing/shared/xarray-shared.c
 create mode 100644 tools/testing/shared/xarray-shared.h
tools/testing/radix-tree/Makefile +11 -57
index 7527f738b4a1..29d607063749 100644
@@ -1,29 +1,16 @@
 # SPDX-License-Identifier: GPL-2.0
 
-CFLAGS += -I. -I../../include -I../../../lib -g -Og -Wall \
-	  -D_LGPL_SOURCE -fsanitize=address -fsanitize=undefined
-LDFLAGS += -fsanitize=address -fsanitize=undefined
-LDLIBS+= -lpthread -lurcu
-TARGETS = main idr-test multiorder xarray maple
-CORE_OFILES := xarray.o radix-tree.o idr.o linux.o test.o find_bit.o bitmap.o \
-			 slab.o maple.o
-OFILES = main.o $(CORE_OFILES) regression1.o regression2.o regression3.o \
-	 regression4.o tag_check.o multiorder.o idr-test.o iteration_check.o \
-	 iteration_check_2.o benchmark.o
+.PHONY: default
 
-ifndef SHIFT
-	SHIFT=3
-endif
+default: main
 
-ifeq ($(BUILD), 32)
-	CFLAGS += -m32
-	LDFLAGS += -m32
-LONG_BIT := 32
-endif
+include ../shared/shared.mk
 
-ifndef LONG_BIT
-LONG_BIT := $(shell getconf LONG_BIT)
-endif
+TARGETS = main idr-test multiorder xarray maple
+CORE_OFILES = $(SHARED_OFILES) xarray.o maple.o test.o
+OFILES = main.o $(CORE_OFILES) regression1.o regression2.o \
+	 regression3.o regression4.o tag_check.o multiorder.o idr-test.o \
+	iteration_check.o iteration_check_2.o benchmark.o
 
 targets: generated/map-shift.h generated/bit-length.h $(TARGETS)
 
@@ -32,46 +19,13 @@ main:	$(OFILES)
 idr-test.o: ../../../lib/test_ida.c
 idr-test: idr-test.o $(CORE_OFILES)
 
-xarray: $(CORE_OFILES)
+xarray: $(CORE_OFILES) xarray.o
 
-maple: $(CORE_OFILES)
+maple: $(CORE_OFILES) maple.o
 
 multiorder: multiorder.o $(CORE_OFILES)
 
 clean:
 	$(RM) $(TARGETS) *.o radix-tree.c idr.c generated/map-shift.h generated/bit-length.h
 
-vpath %.c ../../lib
-
-$(OFILES): Makefile *.h */*.h generated/map-shift.h generated/bit-length.h \
-	../../include/linux/*.h \
-	../../include/asm/*.h \
-	../../../include/linux/xarray.h \
-	../../../include/linux/maple_tree.h \
-	../../../include/linux/radix-tree.h \
-	../../../lib/radix-tree.h \
-	../../../include/linux/idr.h
-
-radix-tree.c: ../../../lib/radix-tree.c
-	sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@
-
-idr.c: ../../../lib/idr.c
-	sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@
-
-xarray.o: ../../../lib/xarray.c ../../../lib/test_xarray.c
-
-maple.o: ../../../lib/maple_tree.c ../../../lib/test_maple_tree.c
-
-generated/map-shift.h:
-	@if ! grep -qws $(SHIFT) generated/map-shift.h; then		\
-		echo "#define XA_CHUNK_SHIFT $(SHIFT)" >		\
-				generated/map-shift.h;			\
-	fi
-
-generated/bit-length.h: FORCE
-	@if ! grep -qws CONFIG_$(LONG_BIT)BIT generated/bit-length.h; then   \
-		echo "Generating $@";                                        \
-		echo "#define CONFIG_$(LONG_BIT)BIT 1" > $@;                 \
-	fi
-
-FORCE: ;
+$(OFILES): $(SHARED_DEPS) *.h */*.h
tools/testing/radix-tree/maple.c +1 -13
index f1caf4bcf937..5b53ecf22fc4 100644
@@ -8,20 +8,8 @@
  * difficult to handle in kernel tests.
  */
 
-#define CONFIG_DEBUG_MAPLE_TREE
-#define CONFIG_MAPLE_SEARCH
-#define MAPLE_32BIT (MAPLE_NODE_SLOTS > 31)
+#include "maple-shared.h"
 #include "test.h"
-#include <stdlib.h>
-#include <time.h>
-#include "linux/init.h"
-
-#define module_init(x)
-#define module_exit(x)
-#define MODULE_AUTHOR(x)
-#define MODULE_LICENSE(x)
-#define dump_stack()	assert(0)
-
 #include "../../../lib/maple_tree.c"
 #include "../../../lib/test_maple_tree.c"
 
tools/testing/radix-tree/xarray.c +1 -8
index f20e12cbbfd4..253208a8541b 100644
@@ -4,16 +4,9 @@
  * Copyright (c) 2018 Matthew Wilcox <[email protected]>
  */
 
-#define XA_DEBUG
+#include "xarray-shared.h"
 #include "test.h"
 
-#define module_init(x)
-#define module_exit(x)
-#define MODULE_AUTHOR(x)
-#define MODULE_LICENSE(x)
-#define dump_stack()	assert(0)
-
-#include "../../../lib/xarray.c"
 #undef XA_DEBUG
 #include "../../../lib/test_xarray.c"
 
tools/testing/shared/autoconf.h new file +2 -0
new file mode 100644
index 000000000000..92dc474c349b
@@ -0,0 +1,2 @@
+#include "bit-length.h"
+#define CONFIG_XARRAY_MULTI 1
tools/testing/shared/bitmap.c renamed from tools/testing/radix-tree/bitmap.c +0 -0
similarity index 100%
rename from tools/testing/radix-tree/bitmap.c
rename to tools/testing/shared/bitmap.c
tools/testing/shared/linux.c renamed from tools/testing/radix-tree/linux.c +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux.c
rename to tools/testing/shared/linux.c
tools/testing/shared/linux/bug.h renamed from tools/testing/radix-tree/linux/bug.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/bug.h
rename to tools/testing/shared/linux/bug.h
tools/testing/shared/linux/cpu.h renamed from tools/testing/radix-tree/linux/cpu.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/cpu.h
rename to tools/testing/shared/linux/cpu.h
tools/testing/shared/linux/idr.h renamed from tools/testing/radix-tree/linux/idr.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/idr.h
rename to tools/testing/shared/linux/idr.h
tools/testing/shared/linux/init.h renamed from tools/testing/radix-tree/linux/init.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/init.h
rename to tools/testing/shared/linux/init.h
tools/testing/shared/linux/kconfig.h renamed from tools/testing/radix-tree/linux/kconfig.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/kconfig.h
rename to tools/testing/shared/linux/kconfig.h
tools/testing/shared/linux/kernel.h renamed from tools/testing/radix-tree/linux/kernel.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/kernel.h
rename to tools/testing/shared/linux/kernel.h
tools/testing/shared/linux/kmemleak.h renamed from tools/testing/radix-tree/linux/kmemleak.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/kmemleak.h
rename to tools/testing/shared/linux/kmemleak.h
tools/testing/shared/linux/local_lock.h renamed from tools/testing/radix-tree/linux/local_lock.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/local_lock.h
rename to tools/testing/shared/linux/local_lock.h
tools/testing/shared/linux/lockdep.h renamed from tools/testing/radix-tree/linux/lockdep.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/lockdep.h
rename to tools/testing/shared/linux/lockdep.h
tools/testing/shared/linux/maple_tree.h renamed from tools/testing/radix-tree/linux/maple_tree.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/maple_tree.h
rename to tools/testing/shared/linux/maple_tree.h
tools/testing/shared/linux/percpu.h renamed from tools/testing/radix-tree/linux/percpu.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/percpu.h
rename to tools/testing/shared/linux/percpu.h
tools/testing/shared/linux/preempt.h renamed from tools/testing/radix-tree/linux/preempt.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/preempt.h
rename to tools/testing/shared/linux/preempt.h
tools/testing/shared/linux/radix-tree.h renamed from tools/testing/radix-tree/linux/radix-tree.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/radix-tree.h
rename to tools/testing/shared/linux/radix-tree.h
tools/testing/shared/linux/rcupdate.h renamed from tools/testing/radix-tree/linux/rcupdate.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/rcupdate.h
rename to tools/testing/shared/linux/rcupdate.h
tools/testing/shared/linux/xarray.h renamed from tools/testing/radix-tree/linux/xarray.h +0 -0
similarity index 100%
rename from tools/testing/radix-tree/linux/xarray.h
rename to tools/testing/shared/linux/xarray.h
tools/testing/shared/maple-shared.h new file +9 -0
new file mode 100644
index 000000000000..3d847edd149d
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#define CONFIG_DEBUG_MAPLE_TREE
+#define CONFIG_MAPLE_SEARCH
+#define MAPLE_32BIT (MAPLE_NODE_SLOTS > 31)
+#include "shared.h"
+#include <stdlib.h>
+#include <time.h>
+#include "linux/init.h"
tools/testing/shared/maple-shim.c new file +7 -0
new file mode 100644
index 000000000000..640df76f483e
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* Very simple shim around the maple tree. */
+
+#include "maple-shared.h"
+
+#include "../../../lib/maple_tree.c"
tools/testing/shared/shared.h new file +34 -0
new file mode 100644
index 000000000000..495602e60b65
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/types.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+
+#include <linux/gfp.h>
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+
+#ifndef module_init
+#define module_init(x)
+#endif
+
+#ifndef module_exit
+#define module_exit(x)
+#endif
+
+#ifndef MODULE_AUTHOR
+#define MODULE_AUTHOR(x)
+#endif
+
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(x)
+#endif
+
+#ifndef MODULE_DESCRIPTION
+#define MODULE_DESCRIPTION(x)
+#endif
+
+#ifndef dump_stack
+#define dump_stack()	assert(0)
+#endif
tools/testing/shared/shared.mk new file +68 -0

68 lines not on this page · raw message

tools/testing/shared/trace/events/maple_tree.h new file +5 -0
new file mode 100644
index 000000000000..97d0e1ddcf08
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#define trace_ma_op(a, b) do {} while (0)
+#define trace_ma_read(a, b) do {} while (0)
+#define trace_ma_write(a, b, c, d) do {} while (0)
tools/testing/shared/xarray-shared.c new file +5 -0
new file mode 100644
index 000000000000..e90901958dcd
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "xarray-shared.h"
+
+#include "../../../lib/xarray.c"
tools/testing/shared/xarray-shared.h new file +4 -0
new file mode 100644
index 000000000000..ac2d16ff53ae
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#define XA_DEBUG
+#include "shared.h"

--
2.45.2

#9 lore raw

PATCH 7/7 tools: add skeleton code for userland testing of VMA logic

In reply to Lorenzo Stoakes

Establish a new userland VMA unit testing implementation under tools/testing which utilises existing logic providing maple tree support in userland utilising the now-shared code previously exclusive to radix tree testing.

This provides fundamental VMA operations whose API is defined in mm/vma.h, while stubbing out superfluous functionality.

This exists as a proof-of-concept, with the test implementation functional and sufficient to allow userland compilation of vma.c, but containing only cursory tests to demonstrate basic functionality.

---
 MAINTAINERS                            |   1 +
 include/linux/atomic.h                 |   2 +-
 include/linux/mmzone.h                 |   3 +-
 tools/testing/vma/.gitignore           |   6 +
 tools/testing/vma/Makefile             |  16 +
 tools/testing/vma/errors.txt           |   0
 tools/testing/vma/generated/autoconf.h |   2 +
 tools/testing/vma/linux/atomic.h       |  12 +
 tools/testing/vma/linux/mmzone.h       |  38 ++
 tools/testing/vma/vma.c                | 207 ++++++
 tools/testing/vma/vma_internal.h       | 882 +++++++++++++++++++++++++
 11 files changed, 1167 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/vma/.gitignore
 create mode 100644 tools/testing/vma/Makefile
 create mode 100644 tools/testing/vma/errors.txt
 create mode 100644 tools/testing/vma/generated/autoconf.h
 create mode 100644 tools/testing/vma/linux/atomic.h
 create mode 100644 tools/testing/vma/linux/mmzone.h
 create mode 100644 tools/testing/vma/vma.c
 create mode 100644 tools/testing/vma/vma_internal.h
MAINTAINERS +1 -0
index ff3e113ed081..c21099d0a123 100644
@@ -23983,6 +23983,7 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
 F:	mm/vma.c
 F:	mm/vma.h
 F:	mm/vma_internal.h
+F:	tools/testing/vma/
 
 VMALLOC
 M:	Andrew Morton <[email protected]>
include/linux/atomic.h +1 -1
index 8dd57c3a99e9..badfba2fd10f 100644
@@ -81,4 +81,4 @@
 #include <linux/atomic/atomic-long.h>
 #include <linux/atomic/atomic-instrumented.h>
 
-#endif /* _LINUX_ATOMIC_H */
+#endif	/* _LINUX_ATOMIC_H */
include/linux/mmzone.h +2 -1
index 41458892bc8a..30a22e57fa50 100644
@@ -1,4 +1,5 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
 #ifndef _LINUX_MMZONE_H
 #define _LINUX_MMZONE_H
 
tools/testing/vma/.gitignore new file +6 -0
new file mode 100644
index 000000000000..d915f7d7fb1a
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+generated/bit-length.h
+generated/map-shift.h
+idr.c
+radix-tree.c
+vma
tools/testing/vma/Makefile new file +16 -0
new file mode 100644
index 000000000000..70e728f2eee3
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+.PHONY: default
+
+default: vma
+
+include ../shared/shared.mk
+
+OFILES = $(SHARED_OFILES) vma.o maple-shim.o
+TARGETS = vma
+
+vma:	$(OFILES) vma_internal.h ../../../mm/vma.c ../../../mm/vma.h
+	$(CC) $(CFLAGS) -o $@ $(OFILES) $(LDLIBS)
+
+clean:
+	$(RM) $(TARGETS) *.o radix-tree.c idr.c generated/map-shift.h generated/bit-length.h
tools/testing/vma/errors.txt new file +0 -0
new file mode 100644
index 000000000000..e69de29bb2d1
tools/testing/vma/generated/autoconf.h new file +2 -0
new file mode 100644
index 000000000000..92dc474c349b
@@ -0,0 +1,2 @@
+#include "bit-length.h"
+#define CONFIG_XARRAY_MULTI 1
tools/testing/vma/linux/atomic.h new file +12 -0
new file mode 100644
index 000000000000..e01f66f98982
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _LINUX_ATOMIC_H
+#define _LINUX_ATOMIC_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) do {} while (0)
+#define U8_MAX UCHAR_MAX
+
+#endif	/* _LINUX_ATOMIC_H */
tools/testing/vma/linux/mmzone.h new file +38 -0
new file mode 100644
index 000000000000..e6a96c686610
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LINUX_MMZONE_H
+#define _LINUX_MMZONE_H
+
+#include <linux/atomic.h>
+
+struct pglist_data *first_online_pgdat(void);
+struct pglist_data *next_online_pgdat(struct pglist_data *pgdat);
+
+#define for_each_online_pgdat(pgdat)			\
+	for (pgdat = first_online_pgdat();		\
+	     pgdat;					\
+	     pgdat = next_online_pgdat(pgdat))
+
+enum zone_type {
+	__MAX_NR_ZONES
+};
+
+#define MAX_NR_ZONES __MAX_NR_ZONES
+#define MAX_PAGE_ORDER 10
+#define MAX_ORDER_NR_PAGES (1 << MAX_PAGE_ORDER)
+
+#define pageblock_order		MAX_PAGE_ORDER
+#define pageblock_nr_pages	BIT(pageblock_order)
+#define pageblock_align(pfn)	ALIGN((pfn), pageblock_nr_pages)
+#define pageblock_start_pfn(pfn)	ALIGN_DOWN((pfn), pageblock_nr_pages)
+
+struct zone {
+	atomic_long_t		managed_pages;
+};
+
+typedef struct pglist_data {
+	struct zone node_zones[MAX_NR_ZONES];
+
+} pg_data_t;
+
+#endif /* _LINUX_MMZONE_H */
tools/testing/vma/vma.c new file +207 -0

207 lines not on this page · raw message

tools/testing/vma/vma_internal.h new file +882 -0

882 lines not on this page · raw message

--
2.45.2

#10 lore raw

Re: [PATCH 7/7] tools: add skeleton code for userland testing of VMA logic

Hi Lorenzo,

Quoted 11 lines · On Wed, 3 Jul 2024 12:57:38 +0100 Lorenzo Stoakes…

Establish a new userland VMA unit testing implementation under tools/testing which utilises existing logic providing maple tree support in userland utilising the now-shared code previously exclusive to radix tree testing.

This provides fundamental VMA operations whose API is defined in mm/vma.h, while stubbing out superfluous functionality.

This exists as a proof-of-concept, with the test implementation functional and sufficient to allow userland compilation of vma.c, but containing only cursory tests to demonstrate basic functionality.

Overall, looks good to me. Appreciate this work. Nonetheless, I have some trivial questions and comments below.

Quoted 6 lines
---
 MAINTAINERS                            |   1 +
 include/linux/atomic.h                 |   2 +-
 include/linux/mmzone.h                 |   3 +-

I doubt if changes to above two files are intentional. Please read below comments.

Quoted 4 lines
 tools/testing/vma/.gitignore           |   6 +
 tools/testing/vma/Makefile             |  16 +
 tools/testing/vma/errors.txt           |   0
 tools/testing/vma/generated/autoconf.h |   2 +

I'm also unsure if above two files are intentionally added. Please read below comments.

Quoted 19 lines
 tools/testing/vma/linux/atomic.h       |  12 +
 tools/testing/vma/linux/mmzone.h       |  38 ++
 tools/testing/vma/vma.c                | 207 ++++++
 tools/testing/vma/vma_internal.h       | 882 +++++++++++++++++++++++++
 11 files changed, 1167 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/vma/.gitignore
 create mode 100644 tools/testing/vma/Makefile
 create mode 100644 tools/testing/vma/errors.txt
 create mode 100644 tools/testing/vma/generated/autoconf.h
 create mode 100644 tools/testing/vma/linux/atomic.h
 create mode 100644 tools/testing/vma/linux/mmzone.h
 create mode 100644 tools/testing/vma/vma.c
 create mode 100644 tools/testing/vma/vma_internal.h
MAINTAINERS +0 -0
index ff3e113ed081..c21099d0a123 100644

MAINTAINERS T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm · line 23986

Quoted 4 lines
MAINTAINERS +1 -0
@@ -23983,6 +23983,7 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
 F:	mm/vma.c
 F:	mm/vma.h
 F:	mm/vma_internal.h
+F:	tools/testing/vma/

Thank you for addressing my comment on the previous version :)

Btw, what do you think about moving the previous MAINTAINERS touching patch to the end of this patch series and making this change together at once?

include/linux/atomic.h line 84

Quoted 13 lines
MAINTAINERS +0 -0
 VMALLOC
 M:	Andrew Morton <[email protected]>
include/linux/atomic.h +1 -1
index 8dd57c3a99e9..badfba2fd10f 100644
@@ -81,4 +81,4 @@
 #include <linux/atomic/atomic-long.h>
 #include <linux/atomic/atomic-instrumented.h>
 
-#endif /* _LINUX_ATOMIC_H */
+#endif	/* _LINUX_ATOMIC_H */

Maybe unintended change?

include/linux/mmzone.h line 5

Quoted 11 lines
include/linux/mmzone.h +2 -1
index 41458892bc8a..30a22e57fa50 100644
@@ -1,4 +1,5 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
 #ifndef _LINUX_MMZONE_H
 #define _LINUX_MMZONE_H
 

To my understanding, the test adds tools/testing/vma/linux/mmzone.h and uses it instead of this file. If I'm not missing something here, above license change may not really needed?

tools/testing/vma/.gitignore line 3

Quoted 9 lines
tools/testing/vma/.gitignore new file +3 -0
new file mode 100644
index 000000000000..d915f7d7fb1a
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+generated/bit-length.h
+generated/map-shift.h

I guess we should also have 'generated/autoconf.h' here? Please read below comment for the file, too.

Quoted 15 lines
tools/testing/vma/.gitignore new file +3 -0
+idr.c
+radix-tree.c
+vma
tools/testing/vma/Makefile new file +6 -0
new file mode 100644
index 000000000000..70e728f2eee3
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+.PHONY: default
+
+default: vma
+

tools/testing/vma/Makefile line 16

Quoted 10 lines
tools/testing/vma/Makefile new file +10 -0
+include ../shared/shared.mk
+
+OFILES = $(SHARED_OFILES) vma.o maple-shim.o
+TARGETS = vma
+
+vma:	$(OFILES) vma_internal.h ../../../mm/vma.c ../../../mm/vma.h
+	$(CC) $(CFLAGS) -o $@ $(OFILES) $(LDLIBS)
+
+clean:
+	$(RM) $(TARGETS) *.o radix-tree.c idr.c generated/map-shift.h generated/bit-length.h

If my assumption about generated/autoconf.h file is not wrong, I think we should also remove the file here, too. 'git' wouldn't care, but I think removing generated/ directory with files under it would be clearer for working space management.

tools/testing/vma/errors.txt

Quoted 3 lines
tools/testing/vma/errors.txt new file +0 -0
new file mode 100644
index 000000000000..e69de29bb2d1

I'm not seeing who is really using this empty file. Is this file intentionally added?

tools/testing/vma/generated/autoconf.h line 2

Quoted 8 lines
tools/testing/vma/generated/autoconf.h new file +2 -0
new file mode 100644
index 000000000000..92dc474c349b
@@ -0,0 +1,2 @@
+#include "bit-length.h"
+#define CONFIG_XARRAY_MULTI 1

Seems this file is automatically generated by ../shared/shared.mk. If I'm not wrong, I think removing this and adding changes I suggested to .gitignore and Makefile would be needed?

Since share.mk just copies the file while setting -I flag so that tools/testing/vma/vma.c can include files from share/ directory, maybe another option is simply including the file from the share/ directory without copying it here.

Also, the previous patch (tools: separate out shared radix-tree components) that adds this file at tools/testing/shared/ would need to add SPDX License identifier?

Quoted 24 lines
tools/testing/vma/linux/atomic.h new file +12 -0
new file mode 100644
index 000000000000..e01f66f98982
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _LINUX_ATOMIC_H
+#define _LINUX_ATOMIC_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) do {} while (0)
+#define U8_MAX UCHAR_MAX
+
+#endif	/* _LINUX_ATOMIC_H */
tools/testing/vma/linux/mmzone.h new file +0 -0
new file mode 100644
index 000000000000..e6a96c686610

tools/testing/vma/linux/mmzone.h line 1

Quoted 1 line
tools/testing/vma/linux/mmzone.h new file +1 -0
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */

I'm not very familiar with the license stuffs, but based on the changes to other files including that to include/linux/mmazone.h above, I was thinking this file would also need to update the license to GP-2.0-or-later. Should this be updated so?

[...]

Quoted 58 lines
tools/testing/vma/vma.c new file +52 -0
new file mode 100644
index 000000000000..1f32bc4d60c2
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "maple-shared.h"
+#include "vma_internal.h"
+
+/*
+ * Directly import the VMA implementation here. Our vma_internal.h wrapper
+ * provides userland-equivalent functionality for everything vma.c uses.
+ */
+#include "../../../mm/vma.c"
+
+const struct vm_operations_struct vma_dummy_vm_ops;
+
+#define ASSERT_TRUE(_expr)						\
+	do {								\
+		if (!(_expr)) {						\
+			fprintf(stderr,					\
+				"Assert FAILED at %s:%d:%s(): %s is FALSE.\n", \
+				__FILE__, __LINE__, __FUNCTION__, #_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 vm_area_struct *alloc_vma(struct mm_struct *mm,
+					unsigned long start,
+					unsigned long end,
+					pgoff_t pgoff,
+					vm_flags_t flags)
+{
+	struct vm_area_struct *ret = vm_area_alloc(mm);
+
+	if (ret == NULL)
+		return NULL;
+
+	ret->vm_start = start;
+	ret->vm_end = end;
+	ret->vm_pgoff = pgoff;
+	ret->__vm_flags = flags;
+
+	return ret;
+}
+
+static bool test_simple_merge(void)
+{
+	struct vm_area_struct *vma;

tools/testing/vma/vma.c line 62

Quoted 10 lines
tools/testing/vma/vma.c new file +10 -0
+	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
+	struct mm_struct mm = {};
+	struct vm_area_struct *vma_left = alloc_vma(&mm, 0, 0x1000, 0, flags);
+	struct vm_area_struct *vma_middle = alloc_vma(&mm, 0x1000, 0x2000, 1, flags);
+	struct vm_area_struct *vma_right = alloc_vma(&mm, 0x2000, 0x3000, 2, flags);
+	VMA_ITERATOR(vmi, &mm, 0x1000);
+
+	ASSERT_FALSE(vma_link(&mm, vma_left));
+	ASSERT_FALSE(vma_link(&mm, vma_middle));
+	ASSERT_FALSE(vma_link(&mm, vma_right));

So, vma_link() returns the error if failed, or zero, and therefore above assertions check if the function calls success as expected? It maybe too straighforward to people who familiar with the code, but I think adding some comment explaining the intent of the test would be nice for new comers.

IMHO, 'ASSERT_EQ(vma_link(...), 0)' may be easier to read.

Also, in case of assertion failures, the assertion prints the error and return false, to indicate the failure of the test, right? Then, would the memory allocated before, e.g., that for vma_{left,middle,right} above be leaked? I know this is just a test program in the user-space, but... If this is intentional, I think clarifying it somewhere would be nice.

Quoted 34 lines
tools/testing/vma/vma.c new file +34 -0
+
+	vma = vma_merge_new_vma(&vmi, vma_left, vma_middle, 0x1000,
+				0x2000, 1);
+	ASSERT_NE(vma, NULL);
+
+	ASSERT_EQ(vma->vm_start, 0);
+	ASSERT_EQ(vma->vm_end, 0x3000);
+	ASSERT_EQ(vma->vm_pgoff, 0);
+	ASSERT_EQ(vma->vm_flags, flags);
+
+	vm_area_free(vma);
+	mtree_destroy(&mm.mm_mt);
+
+	return true;
+}
+
+static bool test_simple_modify(void)
+{
+	struct vm_area_struct *vma;
+	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
+	struct mm_struct mm = {};
+	struct vm_area_struct *init_vma = alloc_vma(&mm, 0, 0x3000, 0, flags);
+	VMA_ITERATOR(vmi, &mm, 0x1000);
+
+	ASSERT_FALSE(vma_link(&mm, init_vma));
+
+	/*
+	 * The flags will not be changed, the vma_modify_flags() function
+	 * performs the merge/split only.
+	 */
+	vma = vma_modify_flags(&vmi, init_vma, init_vma,
+			       0x1000, 0x2000, VM_READ | VM_MAYREAD);
+	ASSERT_NE(vma, NULL);
+	/* We modify the provided VMA, and on split allocate new VMAs. */

tools/testing/vma/vma.c line 106

Quoted 10 lines
tools/testing/vma/vma.c new file +10 -0
+	ASSERT_EQ(vma, init_vma);
+
+	ASSERT_EQ(vma->vm_start, 0x1000);
+	ASSERT_EQ(vma->vm_end, 0x2000);
+	ASSERT_EQ(vma->vm_pgoff, 1);
+
+	/*
+	 * Now walk through the three split VMAs and make sure they are as
+	 * expected.
+	 */

I like these kind comments :)

Quoted 90 lines
tools/testing/vma/vma.c new file +90 -0
+
+	vma_iter_set(&vmi, 0);
+	vma = vma_iter_load(&vmi);
+
+	ASSERT_EQ(vma->vm_start, 0);
+	ASSERT_EQ(vma->vm_end, 0x1000);
+	ASSERT_EQ(vma->vm_pgoff, 0);
+
+	vm_area_free(vma);
+	vma_iter_clear(&vmi);
+
+	vma = vma_next(&vmi);
+
+	ASSERT_EQ(vma->vm_start, 0x1000);
+	ASSERT_EQ(vma->vm_end, 0x2000);
+	ASSERT_EQ(vma->vm_pgoff, 1);
+
+	vm_area_free(vma);
+	vma_iter_clear(&vmi);
+
+	vma = vma_next(&vmi);
+
+	ASSERT_EQ(vma->vm_start, 0x2000);
+	ASSERT_EQ(vma->vm_end, 0x3000);
+	ASSERT_EQ(vma->vm_pgoff, 2);
+
+	vm_area_free(vma);
+	mtree_destroy(&mm.mm_mt);
+
+	return true;
+}
+
+static bool test_simple_expand(void)
+{
+	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
+	struct mm_struct mm = {};
+	struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x1000, 0, flags);
+	VMA_ITERATOR(vmi, &mm, 0);
+
+	ASSERT_FALSE(vma_link(&mm, vma));
+
+	ASSERT_FALSE(vma_expand(&vmi, vma, 0, 0x3000, 0, NULL));
+
+	ASSERT_EQ(vma->vm_start, 0);
+	ASSERT_EQ(vma->vm_end, 0x3000);
+	ASSERT_EQ(vma->vm_pgoff, 0);
+
+	vm_area_free(vma);
+	mtree_destroy(&mm.mm_mt);
+
+	return true;
+}
+
+static bool test_simple_shrink(void)
+{
+	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
+	struct mm_struct mm = {};
+	struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x3000, 0, flags);
+	VMA_ITERATOR(vmi, &mm, 0);
+
+	ASSERT_FALSE(vma_link(&mm, vma));
+
+	ASSERT_FALSE(vma_shrink(&vmi, vma, 0, 0x1000, 0));
+
+	ASSERT_EQ(vma->vm_start, 0);
+	ASSERT_EQ(vma->vm_end, 0x1000);
+	ASSERT_EQ(vma->vm_pgoff, 0);
+
+	vm_area_free(vma);
+	mtree_destroy(&mm.mm_mt);
+
+	return true;
+}
+
+int main(void)
+{
+	int num_tests = 0, num_fail = 0;
+
+	maple_tree_init();
+
+#define TEST(name)							\
+	do {								\
+		num_tests++;						\
+		if (!test_##name()) {					\
+			num_fail++;					\
+			fprintf(stderr, "Test " #name " FAILED\n");	\
+		}							\
+	} while (0)
+
+	TEST(simple_merge);

tools/testing/vma/vma.c line 206

Quoted 10 lines
tools/testing/vma/vma.c new file +10 -0
+	TEST(simple_modify);
+	TEST(simple_expand);
+	TEST(simple_shrink);
+
+#undef TEST
+
+	printf("%d tests run, %d passed, %d failed.\n",
+	       num_tests, num_tests - num_fail, num_fail);
+
+	return EXIT_SUCCESS;

What do you think about making the return value indicates if the overall test has pass or failed, for easy integration with other test frameworks or scripts in future?

[...]

I didn't read all of this patch series in detail yet (I'm not sure if I'll have time to do that, so please don't wait for me), but looks nice work overall to me. Thank you for your efforts on this.

Thanks,
SJ

#11 lore raw

Re: [PATCH 7/7] tools: add skeleton code for userland testing of VMA logic

Quoted 12 lines · SeongJae Park

Hi Lorenzo,

Quoted 8 lines · On Wed, 3 Jul 2024 12:57:38 +0100 Lorenzo Stoakes…

Establish a new userland VMA unit testing implementation under tools/testing which utilises existing logic providing maple tree support in userland utilising the now-shared code previously exclusive to radix tree testing.

This provides fundamental VMA operations whose API is defined in mm/vma.h, while stubbing out superfluous functionality.

Quoted 6 lines
Quoted 3 lines

This exists as a proof-of-concept, with the test implementation functional and sufficient to allow userland compilation of vma.c, but containing only cursory tests to demonstrate basic functionality.

Overall, looks good to me. Appreciate this work. Nonetheless, I have some trivial questions and comments below.

Thanks, appreciate the review!

Quoted 45 lines
Quoted 7 lines
---
 MAINTAINERS                            |   1 +
 include/linux/atomic.h                 |   2 +-
 include/linux/mmzone.h                 |   3 +-

I doubt if changes to above two files are intentional. Please read below comments.

Quoted 4 lines
 tools/testing/vma/.gitignore           |   6 +
 tools/testing/vma/Makefile             |  16 +
 tools/testing/vma/errors.txt           |   0
 tools/testing/vma/generated/autoconf.h |   2 +

I'm also unsure if above two files are intentionally added. Please read below comments.

Quoted 19 lines
 tools/testing/vma/linux/atomic.h       |  12 +
 tools/testing/vma/linux/mmzone.h       |  38 ++
 tools/testing/vma/vma.c                | 207 ++++++
 tools/testing/vma/vma_internal.h       | 882 +++++++++++++++++++++++++
 11 files changed, 1167 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/vma/.gitignore
 create mode 100644 tools/testing/vma/Makefile
 create mode 100644 tools/testing/vma/errors.txt
 create mode 100644 tools/testing/vma/generated/autoconf.h
 create mode 100644 tools/testing/vma/linux/atomic.h
 create mode 100644 tools/testing/vma/linux/mmzone.h
 create mode 100644 tools/testing/vma/vma.c
 create mode 100644 tools/testing/vma/vma_internal.h
MAINTAINERS +0 -0
index ff3e113ed081..c21099d0a123 100644

MAINTAINERS T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm · line 23986

Quoted 4 lines
MAINTAINERS +1 -0
@@ -23983,6 +23983,7 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
 F:	mm/vma.c
 F:	mm/vma.h
 F:	mm/vma_internal.h
+F:	tools/testing/vma/

Thank you for addressing my comment on the previous version :)

Quoted 2 lines

Btw, what do you think about moving the previous MAINTAINERS touching patch to the end of this patch series and making this change together at once?

Yeah I was thinking about separating that out actually, not hugely critical I don't think, but if I end up respinning I can do that.

Quoted 12 lines
Quoted 12 lines
MAINTAINERS +0 -0
 VMALLOC
 M:	Andrew Morton <[email protected]>
include/linux/atomic.h +0 -0
index 8dd57c3a99e9..badfba2fd10f 100644
@@ -81,4 +81,4 @@
 #include <linux/atomic/atomic-long.h>
 #include <linux/atomic/atomic-instrumented.h>
 
Quoted 4 lines

include/linux/atomic.h line 84

Quoted 2 lines
include/linux/atomic.h +1 -1
-#endif /* _LINUX_ATOMIC_H */
+#endif	/* _LINUX_ATOMIC_H */

Maybe unintended change?

Ugh, sorry my bad. Again, I don't think this is so big as to need a respin in itself, but if larger stuff comes up I will fix if you don't think this is too big a deal?

Quoted 61 lines

include/linux/mmzone.h line 5

Quoted 12 lines
include/linux/mmzone.h +2 -1
index 41458892bc8a..30a22e57fa50 100644
@@ -1,4 +1,5 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
 #ifndef _LINUX_MMZONE_H
 #define _LINUX_MMZONE_H
 

To my understanding, the test adds tools/testing/vma/linux/mmzone.h and uses it instead of this file. If I'm not missing something here, above license change may not really needed?

tools/testing/vma/.gitignore line 3

Quoted 9 lines
tools/testing/vma/.gitignore new file +3 -0
new file mode 100644
index 000000000000..d915f7d7fb1a
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+generated/bit-length.h
+generated/map-shift.h

I guess we should also have 'generated/autoconf.h' here? Please read below comment for the file, too.

Quoted 15 lines
tools/testing/vma/.gitignore new file +3 -0
+idr.c
+radix-tree.c
+vma
tools/testing/vma/Makefile new file +6 -0
new file mode 100644
index 000000000000..70e728f2eee3
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+.PHONY: default
+
+default: vma
+

tools/testing/vma/Makefile line 16

Quoted 10 lines
tools/testing/vma/Makefile new file +10 -0
+include ../shared/shared.mk
+
+OFILES = $(SHARED_OFILES) vma.o maple-shim.o
+TARGETS = vma
+
+vma:	$(OFILES) vma_internal.h ../../../mm/vma.c ../../../mm/vma.h
+	$(CC) $(CFLAGS) -o $@ $(OFILES) $(LDLIBS)
+
+clean:
+	$(RM) $(TARGETS) *.o radix-tree.c idr.c generated/map-shift.h generated/bit-length.h

If my assumption about generated/autoconf.h file is not wrong, I think we should also remove the file here, too. 'git' wouldn't care, but I think removing generated/ directory with files under it would be clearer for working space management.

Quoted 6 lines

tools/testing/vma/errors.txt

Quoted 3 lines
tools/testing/vma/errors.txt new file +0 -0
new file mode 100644
index 000000000000..e69de29bb2d1

I'm not seeing who is really using this empty file. Is this file intentionally added?

Ughhhh no, this was a pure accident! I guess we can ask Andrew to drop this part of the patch if no further respin is needed? May do a fix-patch actually.

Obviously will remove on next respin otherwise.

Thanks for that, great spot!

Quoted 13 lines

tools/testing/vma/generated/autoconf.h line 2

Quoted 9 lines
tools/testing/vma/generated/autoconf.h new file +2 -0
new file mode 100644
index 000000000000..92dc474c349b
@@ -0,0 +1,2 @@
+#include "bit-length.h"
+#define CONFIG_XARRAY_MULTI 1

Seems this file is automatically generated by ../shared/shared.mk. If I'm not wrong, I think removing this and adding changes I suggested to .gitignore and Makefile would be needed?

Can do the same with this :) good spot.

Quoted 9 lines

Since share.mk just copies the file while setting -I flag so that tools/testing/vma/vma.c can include files from share/ directory, maybe another option is simply including the file from the share/ directory without copying it here.

Also, the previous patch (tools: separate out shared radix-tree components) that adds this file at tools/testing/shared/ would need to add SPDX License identifier?

This file already existed in the radix tree code, I just moved it.

Quoted 23 lines
Quoted 23 lines
tools/testing/vma/linux/atomic.h new file +12 -0
new file mode 100644
index 000000000000..e01f66f98982
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _LINUX_ATOMIC_H
+#define _LINUX_ATOMIC_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) do {} while (0)
+#define U8_MAX UCHAR_MAX
+
+#endif	/* _LINUX_ATOMIC_H */
tools/testing/vma/linux/mmzone.h new file +0 -1
new file mode 100644
index 000000000000..e6a96c686610
--- /dev/null
Quoted 8 lines

tools/testing/vma/linux/mmzone.h line 1

Quoted 3 lines

+++ b/tools/testing/vma/linux/mmzone.h

tools/testing/vma/linux/mmzone.h new file +1 -0
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */

I'm not very familiar with the license stuffs, but based on the changes to other files including that to include/linux/mmazone.h above, I was thinking this file would also need to update the license to GP-2.0-or-later. Should this be updated so?

This was copied from tools/testing/memblock/linux/mmzone.h directly as-is. I didn't think it was worth reworking memblock testing to share this (again, this is meant to be a skeleton rather than a complete rework of how testing is done :) but we needed the header.

Whenever you bounce code around there's always a risk of somebody noticing something previously broken which would not really make sense for you to address as part of your change, I think this is one of those circumstances.

If considered critical for licensing of course I can change, but that does make me wonder whether that'd be better as a whole-repo change for all such instances?

Quoted 76 lines

[...]

Quoted 58 lines
tools/testing/vma/vma.c new file +52 -0
new file mode 100644
index 000000000000..1f32bc4d60c2
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "maple-shared.h"
+#include "vma_internal.h"
+
+/*
+ * Directly import the VMA implementation here. Our vma_internal.h wrapper
+ * provides userland-equivalent functionality for everything vma.c uses.
+ */
+#include "../../../mm/vma.c"
+
+const struct vm_operations_struct vma_dummy_vm_ops;
+
+#define ASSERT_TRUE(_expr)						\
+	do {								\
+		if (!(_expr)) {						\
+			fprintf(stderr,					\
+				"Assert FAILED at %s:%d:%s(): %s is FALSE.\n", \
+				__FILE__, __LINE__, __FUNCTION__, #_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 vm_area_struct *alloc_vma(struct mm_struct *mm,
+					unsigned long start,
+					unsigned long end,
+					pgoff_t pgoff,
+					vm_flags_t flags)
+{
+	struct vm_area_struct *ret = vm_area_alloc(mm);
+
+	if (ret == NULL)
+		return NULL;
+
+	ret->vm_start = start;
+	ret->vm_end = end;
+	ret->vm_pgoff = pgoff;
+	ret->__vm_flags = flags;
+
+	return ret;
+}
+
+static bool test_simple_merge(void)
+{
+	struct vm_area_struct *vma;

tools/testing/vma/vma.c line 62

Quoted 10 lines
tools/testing/vma/vma.c new file +10 -0
+	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
+	struct mm_struct mm = {};
+	struct vm_area_struct *vma_left = alloc_vma(&mm, 0, 0x1000, 0, flags);
+	struct vm_area_struct *vma_middle = alloc_vma(&mm, 0x1000, 0x2000, 1, flags);
+	struct vm_area_struct *vma_right = alloc_vma(&mm, 0x2000, 0x3000, 2, flags);
+	VMA_ITERATOR(vmi, &mm, 0x1000);
+
+	ASSERT_FALSE(vma_link(&mm, vma_left));
+	ASSERT_FALSE(vma_link(&mm, vma_middle));
+	ASSERT_FALSE(vma_link(&mm, vma_right));

So, vma_link() returns the error if failed, or zero, and therefore above assertions check if the function calls success as expected? It maybe too straighforward to people who familiar with the code, but I think adding some comment explaining the intent of the test would be nice for new comers.

Quoted 1 line

IMHO, 'ASSERT_EQ(vma_link(...), 0)' may be easier to read.

Yeah I did weigh this up, but the standard kernel pattern for this would be:

if (vma_link(...)) {
	/* ... error handing ... */
}

So to me this is consistent. I do take your point though, it's debatable, but I think it's ok as-is unless you feel strongly about it?

Quoted 6 lines

Also, in case of assertion failures, the assertion prints the error and return false, to indicate the failure of the test, right? Then, would the memory allocated before, e.g., that for vma_{left,middle,right} above be leaked? I know this is just a test program in the user-space, but... If this is intentional, I think clarifying it somewhere would be nice.

Unwinding memory would make this code really horrible to implement, I don't think it's a big deal to leak userland memory in failed tests (the point of which is to, of course, to not encounter thousands and thousands of assert fails :).

I'm not sure it's really important to point this out too, it's obvious, and it's distracting to do so. And again, it's really just a wrapper implementation. As discussed elsewhere moving forward it'd make sense to implement some 'userland kunit' style shared libraries that take care of all of this for us.

Quoted 39 lines
Quoted 39 lines
tools/testing/vma/vma.c new file +38 -0
+
+	vma = vma_merge_new_vma(&vmi, vma_left, vma_middle, 0x1000,
+				0x2000, 1);
+	ASSERT_NE(vma, NULL);
+
+	ASSERT_EQ(vma->vm_start, 0);
+	ASSERT_EQ(vma->vm_end, 0x3000);
+	ASSERT_EQ(vma->vm_pgoff, 0);
+	ASSERT_EQ(vma->vm_flags, flags);
+
+	vm_area_free(vma);
+	mtree_destroy(&mm.mm_mt);
+
+	return true;
+}
+
+static bool test_simple_modify(void)
+{
+	struct vm_area_struct *vma;
+	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
+	struct mm_struct mm = {};
+	struct vm_area_struct *init_vma = alloc_vma(&mm, 0, 0x3000, 0, flags);
+	VMA_ITERATOR(vmi, &mm, 0x1000);
+
+	ASSERT_FALSE(vma_link(&mm, init_vma));
+
+	/*
+	 * The flags will not be changed, the vma_modify_flags() function
+	 * performs the merge/split only.
+	 */
+	vma = vma_modify_flags(&vmi, init_vma, init_vma,
+			       0x1000, 0x2000, VM_READ | VM_MAYREAD);
+	ASSERT_NE(vma, NULL);
+	/* We modify the provided VMA, and on split allocate new VMAs. */
+	ASSERT_EQ(vma, init_vma);
+
+	ASSERT_EQ(vma->vm_start, 0x1000);
+	ASSERT_EQ(vma->vm_end, 0x2000);
Quoted 8 lines

tools/testing/vma/vma.c line 106

Quoted 6 lines
tools/testing/vma/vma.c new file +6 -0
+	ASSERT_EQ(vma->vm_pgoff, 1);
+
+	/*
+	 * Now walk through the three split VMAs and make sure they are as
+	 * expected.
+	 */

I like these kind comments :)

Thanks :) I try to maintain a nice balance between not adding _too many_ explanatory comments but not having globs of code that are hard to follow without giving an idea what's going on.

Quoted 97 lines
Quoted 97 lines
tools/testing/vma/vma.c new file +96 -0
+
+	vma_iter_set(&vmi, 0);
+	vma = vma_iter_load(&vmi);
+
+	ASSERT_EQ(vma->vm_start, 0);
+	ASSERT_EQ(vma->vm_end, 0x1000);
+	ASSERT_EQ(vma->vm_pgoff, 0);
+
+	vm_area_free(vma);
+	vma_iter_clear(&vmi);
+
+	vma = vma_next(&vmi);
+
+	ASSERT_EQ(vma->vm_start, 0x1000);
+	ASSERT_EQ(vma->vm_end, 0x2000);
+	ASSERT_EQ(vma->vm_pgoff, 1);
+
+	vm_area_free(vma);
+	vma_iter_clear(&vmi);
+
+	vma = vma_next(&vmi);
+
+	ASSERT_EQ(vma->vm_start, 0x2000);
+	ASSERT_EQ(vma->vm_end, 0x3000);
+	ASSERT_EQ(vma->vm_pgoff, 2);
+
+	vm_area_free(vma);
+	mtree_destroy(&mm.mm_mt);
+
+	return true;
+}
+
+static bool test_simple_expand(void)
+{
+	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
+	struct mm_struct mm = {};
+	struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x1000, 0, flags);
+	VMA_ITERATOR(vmi, &mm, 0);
+
+	ASSERT_FALSE(vma_link(&mm, vma));
+
+	ASSERT_FALSE(vma_expand(&vmi, vma, 0, 0x3000, 0, NULL));
+
+	ASSERT_EQ(vma->vm_start, 0);
+	ASSERT_EQ(vma->vm_end, 0x3000);
+	ASSERT_EQ(vma->vm_pgoff, 0);
+
+	vm_area_free(vma);
+	mtree_destroy(&mm.mm_mt);
+
+	return true;
+}
+
+static bool test_simple_shrink(void)
+{
+	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
+	struct mm_struct mm = {};
+	struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x3000, 0, flags);
+	VMA_ITERATOR(vmi, &mm, 0);
+
+	ASSERT_FALSE(vma_link(&mm, vma));
+
+	ASSERT_FALSE(vma_shrink(&vmi, vma, 0, 0x1000, 0));
+
+	ASSERT_EQ(vma->vm_start, 0);
+	ASSERT_EQ(vma->vm_end, 0x1000);
+	ASSERT_EQ(vma->vm_pgoff, 0);
+
+	vm_area_free(vma);
+	mtree_destroy(&mm.mm_mt);
+
+	return true;
+}
+
+int main(void)
+{
+	int num_tests = 0, num_fail = 0;
+
+	maple_tree_init();
+
+#define TEST(name)							\
+	do {								\
+		num_tests++;						\
+		if (!test_##name()) {					\
+			num_fail++;					\
+			fprintf(stderr, "Test " #name " FAILED\n");	\
+		}							\
+	} while (0)
+
+	TEST(simple_merge);
+	TEST(simple_modify);
+	TEST(simple_expand);
+	TEST(simple_shrink);
+
+#undef TEST
+
Quoted 8 lines

tools/testing/vma/vma.c line 206

Quoted 4 lines
tools/testing/vma/vma.c new file +4 -0
+	printf("%d tests run, %d passed, %d failed.\n",
+	       num_tests, num_tests - num_fail, num_fail);
+
+	return EXIT_SUCCESS;

What do you think about making the return value indicates if the overall test has pass or failed, for easy integration with other test frameworks or scripts in future?

Yeah this is a good idea, will change on next respin.

Quoted 6 lines

[...]

I didn't read all of this patch series in detail yet (I'm not sure if I'll have time to do that, so please don't wait for me), but looks nice work overall to me. Thank you for your efforts on this.

Thanks!

Quoted 4 lines

Thanks,
SJ

#12 lore raw

Re: [PATCH 7/7] tools: add skeleton code for userland testing of VMA logic

In reply to Lorenzo Stoakes

On second thoughts, I think it's best we fix the various small issues SeongJae discovered, Andrew - could you apply the following fix-patch when things seem ok to move into mm-unstable please?

I will pull into any respin going forward also, if they are required.

----8<----
From 0f7b9e6ed72773f22c0e344030337faca657ed6b Mon Sep 17 00:00:00 2001
From: Lorenzo Stoakes <[email protected]>
Date: Thu, 4 Jul 2024 12:36:00 +0100
Subject: [PATCH] [PATCH] tools: review feedback on tools/testing/vma/

Appy various small fixups to tools/testing/vma/ as per SeongJae's review.

---
 include/linux/atomic.h                 | 2 +-
 include/linux/mmzone.h                 | 3 +--
 tools/testing/vma/.gitignore           | 1 +
 tools/testing/vma/Makefile             | 2 +-
 tools/testing/vma/errors.txt           | 0
 tools/testing/vma/generated/autoconf.h | 2 --
 tools/testing/vma/linux/mmzone.h       | 2 +-
 tools/testing/vma/vma.c                | 2 +-
 8 files changed, 6 insertions(+), 8 deletions(-)
 delete mode 100644 tools/testing/vma/errors.txt
 delete mode 100644 tools/testing/vma/generated/autoconf.h
include/linux/atomic.h +1 -1
index badfba2fd10f..8dd57c3a99e9 100644
@@ -81,4 +81,4 @@
 #include <linux/atomic/atomic-long.h>
 #include <linux/atomic/atomic-instrumented.h>
 
-#endif	/* _LINUX_ATOMIC_H */
+#endif /* _LINUX_ATOMIC_H */
include/linux/mmzone.h +1 -2
index 30a22e57fa50..41458892bc8a 100644
@@ -1,5 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
+/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _LINUX_MMZONE_H
 #define _LINUX_MMZONE_H
 
tools/testing/vma/.gitignore +1 -0
index d915f7d7fb1a..b003258eba79 100644
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 generated/bit-length.h
 generated/map-shift.h
+generated/autoconf.h
 idr.c
 radix-tree.c
 vma
tools/testing/vma/Makefile +1 -1
index 70e728f2eee3..bfc905d222cf 100644
@@ -13,4 +13,4 @@ vma:	$(OFILES) vma_internal.h ../../../mm/vma.c ../../../mm/vma.h
 	$(CC) $(CFLAGS) -o $@ $(OFILES) $(LDLIBS)
 
 clean:
-	$(RM) $(TARGETS) *.o radix-tree.c idr.c generated/map-shift.h generated/bit-length.h
+	$(RM) $(TARGETS) *.o radix-tree.c idr.c generated/map-shift.h generated/bit-length.h generated/autoconf.h
tools/testing/vma/errors.txt deleted +0 -0
deleted file mode 100644
index e69de29bb2d1..000000000000
tools/testing/vma/generated/autoconf.h deleted +0 -2
deleted file mode 100644
index 92dc474c349b..000000000000
@@ -1,2 +0,0 @@
-#include "bit-length.h"
-#define CONFIG_XARRAY_MULTI 1
tools/testing/vma/linux/mmzone.h +1 -1
index e6a96c686610..33cd1517f7a3 100644
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 #ifndef _LINUX_MMZONE_H
 #define _LINUX_MMZONE_H
tools/testing/vma/vma.c +1 -1
index 1f32bc4d60c2..48e033c60d87 100644
@@ -203,5 +203,5 @@ int main(void)
 	printf("%d tests run, %d passed, %d failed.\n",
 	       num_tests, num_tests - num_fail, num_fail);
 
-	return EXIT_SUCCESS;
+	return num_fail == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }

--
2.45.2

maintainer
#13 lore raw
In reply to Lorenzo Stoakes
Quoted 3 lines · On Wed, 3 Jul 2024 12:57:31 +0100 Lorenzo Stoakes…

Kernel functionality is stubbed and shimmed as needed in tools/testing/vma/ which contains a fully functional userland vma_internal.h file and which imports mm/vma.c and mm/vma.h to be directly tested from userland.

Cool stuff.

Now we need to make sure that anyone who messes with vma code has run the tests. And has added more testcases, if appropriate.

Does it make sense to execute this test under selftests/ in some fashion? Quite a few people appear to be running the selftest code regularly and it would be good to make them run this as well.

Quoted 1 line
 51 files changed, 3914 insertions(+), 2453 deletions(-)

eep. The best time for me to merge this is late in the -rc cycle so the large skew between mainline and mm.git doesn't spend months hampering ongoing development. But that merge time is right now.

#14 lore raw
Quoted 7 lines · Andrew Morton
Quoted 3 lines · On Wed, 3 Jul 2024 12:57:31 +0100 Lorenzo Stoakes…

Kernel functionality is stubbed and shimmed as needed in tools/testing/vma/ which contains a fully functional userland vma_internal.h file and which imports mm/vma.c and mm/vma.h to be directly tested from userland.

Cool stuff.

Thanks :)

Quoted 7 lines

Now we need to make sure that anyone who messes with vma code has run the tests. And has added more testcases, if appropriate.

Does it make sense to execute this test under selftests/ in some fashion? Quite a few people appear to be running the selftest code regularly and it would be good to make them run this as well.

I think it will be useful to do that, yes, but as the tests are currently a skeleton to both provide the stubbing out and to provide essentially an example of how you might test (though enough that it'd now be easy to add a _ton_ of tests), it's not quite ready to be run just yet.

Quoted 6 lines
Quoted 2 lines
 51 files changed, 3914 insertions(+), 2453 deletions(-)

eep. The best time for me to merge this is late in the -rc cycle so the large skew between mainline and mm.git doesn't spend months hampering ongoing development. But that merge time is right now.

Argh. Well, the numbers are scary, but it's _mostly_ moving code around with some pretty straightforward refactorings and adding a bunch of userland code that won't impact kernels at all.

So I'd argue this is less crazy in size than it might seem...

maintainer
#15 lore raw
Quoted 7 lines · On Wed, 3 Jul 2024 21:33:00 +0100 Lorenzo Stoakes <[email protected]>…
Quoted 6 lines
Quoted 2 lines
 51 files changed, 3914 insertions(+), 2453 deletions(-)

eep. The best time for me to merge this is late in the -rc cycle so the large skew between mainline and mm.git doesn't spend months hampering ongoing development. But that merge time is right now.

Quoted 5 lines

Argh. Well, the numbers are scary, but it's _mostly_ moving code around with some pretty straightforward refactorings and adding a bunch of userland code that won't impact kernels at all.

So I'd argue this is less crazy in size than it might seem...

OK, let's leave it a couple of days for some feedback then decide. It's still a couple of weeks until we go upstream.

#16 lore raw
In reply to Lorenzo Stoakes
Quoted 15 lines · On Wed, 3 Jul 2024 21:33:00 +0100 Lorenzo Stoakes <[email protected]>…
Quoted 7 lines · Andrew Morton
Quoted 3 lines · On Wed, 3 Jul 2024 12:57:31 +0100 Lorenzo Stoakes…

Kernel functionality is stubbed and shimmed as needed in tools/testing/vma/ which contains a fully functional userland vma_internal.h file and which imports mm/vma.c and mm/vma.h to be directly tested from userland.

Cool stuff.

Thanks :)

Quoted 4 lines

Now we need to make sure that anyone who messes with vma code has run the tests. And has added more testcases, if appropriate.

Quoted 8 lines
Quoted 3 lines

Does it make sense to execute this test under selftests/ in some fashion? Quite a few people appear to be running the selftest code regularly and it would be good to make them run this as well.

I think it will be useful to do that, yes, but as the tests are currently a skeleton to both provide the stubbing out and to provide essentially an example of how you might test (though enough that it'd now be easy to add a _ton_ of tests), it's not quite ready to be run just yet.

If we will eventually move the files under selftests/, why dont' we place the files there from the beginning? Is there a strict rule saying files that not really involved with running tests or not ready cannot be added there? If so, could adding the files after the tests are ready to be run be an option? Cc-ing Shuah since I think she might have a comment.

Also, I haven't had enough time to read the patches in detail but just the cover letter a little bit. My humble impression from that is that this might better to eventually be kunit tests. I know there was a discussion with Kees on RFC v1 [1] which you kindly explained why you decide to implement this in user space. To my understanding, at least some of the problems are not real problems. For two things as examples,

1. I understand that you concern the test speed [2]. I think Kunit could be slower than the dedicated user space tests, but to my experience, it's not that bad when using the default UML-based execution.

2. My next humble undrestanding is that you want to test functions that you don't want to export [2,3] to kernel modules. To my understanding it's not limited on Kunit. I'm testing such DAMON functions using KUnit by including test code in the c file but protecting it via a config. For an example, please refer to DAMON_KUNIT_TEST.

I understand above are only small parts of the reason for your decision, and some of those would really unsupported by Kunit. In the case, I think adding this user space tests as is is good. Nonetheless, I think it would be good to hear some comments from Kunit developers. IMHO, letting them know the limitations will hopefully help setting their future TODO items. Cc-ing Brendan, David and Rae for that.

To recap, I have no strong opinions about this patch, but I think knowing how Selftests and KUnit developers think could be helpful.

[1] https://lore.kernel.org/202406270957.C0E5E8057@keescook
[2] https://lore.kernel.org/5zuowniex4sxy6l7erbsg5fiirf4d4f5fbpz2upay2igiwa2xk@vuezoh2wbqf4
[3] https://lore.kernel.org/[email protected]

Thanks,
SJ

[...]

#17 lore raw
Quoted 26 lines · SeongJae Park
Quoted 15 lines · On Wed, 3 Jul 2024 21:33:00 +0100 Lorenzo Stoakes <[email protected]>…
Quoted 7 lines · Andrew Morton
Quoted 3 lines · On Wed, 3 Jul 2024 12:57:31 +0100 Lorenzo Stoakes…

Kernel functionality is stubbed and shimmed as needed in tools/testing/vma/ which contains a fully functional userland vma_internal.h file and which imports mm/vma.c and mm/vma.h to be directly tested from userland.

Cool stuff.

Thanks :)

Quoted 4 lines

Now we need to make sure that anyone who messes with vma code has run the tests. And has added more testcases, if appropriate.

Quoted 8 lines
Quoted 3 lines

Does it make sense to execute this test under selftests/ in some fashion? Quite a few people appear to be running the selftest code regularly and it would be good to make them run this as well.

I think it will be useful to do that, yes, but as the tests are currently a skeleton to both provide the stubbing out and to provide essentially an example of how you might test (though enough that it'd now be easy to add a _ton_ of tests), it's not quite ready to be run just yet.

Quoted 5 lines

If we will eventually move the files under selftests/, why dont' we place the files there from the beginning? Is there a strict rule saying files that not really involved with running tests or not ready cannot be added there? If so, could adding the files after the tests are ready to be run be an option? Cc-ing Shuah since I think she might have a comment.

We already have tests under tools/testing which seems like a good place to put things. It's arguably not 'self' testing but a specific isolation mechanism.

It'd be a whole lot of churn including totally moving all of the radix tree tests to self test and then totally changing how mm self tests are built (existing code just runs userland code that uses system calls) for... what gain? I don't agree with this at all.

The self tests differ from this and other tests using the userland-stubbed kernel approach in that they test system call invocation and assert expectations.

My point to Andrew was that we could potentially automatically run these tests as part of a self-test run as they are so quick, at least in the future, if that made sense.

Quoted 7 lines

Also, I haven't had enough time to read the patches in detail but just the cover letter a little bit. My humble impression from that is that this might better to eventually be kunit tests. I know there was a discussion with Kees on RFC v1 [1] which you kindly explained why you decide to implement this in user space. To my understanding, at least some of the problems are not real problems. For two things as examples,

They are real problems. And I totally disagree that these should be kunit tests. I'm surprised you didn't find my and Liam's arguments compelling?

I suggest you try actually running tools/testing/vma/vma and putting a break point in gdb in vma_merge(), able to observe all state in great detail with no interrupts and see for yourself.

Quoted 4 lines

1. I understand that you concern the test speed [2]. I think Kunit could be slower than the dedicated user space tests, but to my experience, it's not that bad when using the default UML-based execution.

I'm sorry but running VMA code in the smallest possible form in userland is very clearly faster and you are missing the key point that we can _isolate_ anything we _don't need_.

There's no setup/teardown whatsoever, no clever tricks needed, we get to keep entirely internal interfaces internal and clean. It's compelling.

You are running the code as fast as you possibly can and that allows for lots of interesting things like being able to fuzz at scale, being able to run thousands of cases with basically zero setup/teardown or limits, etc. etc.

Also, it's basically impossible to explicitly _unit_ test vma merge and vma split and friends without invoking kernel stuff like TLB handling, MMU notifier, huge page handling, process setup/teardown, mm setup/teardown, rlimits, anon vma name handling, uprobes, memory policy handling, interval tree handling, lock contention, THP behaviour, etc. etc. etc.

With this test we can purely _unit_ test these fundamental operations, AND have the ability to for example in future - dump maple tree state from a buggy kernel situation that would result in a panic for instance - and recreate it immediately for debug.

We also then have the ability to have strong guarantees about the behaviour of these operations at a fundamental level.

If we want _system_ tests that bring in other kernel components then it makes more sense to use kunit/selftests. But this offers something else.

Also keep in mind this is a _skeleton_ test designed to prove the point that this works. We can rework this as we wish later, it's necessary to include it to demonstrate the purpose of the refactoring bits of the series.

I really don't want this series to get dragged into too much back + forth meanwhile blocking a super conflict-inviting refactoring that is actually valuable in itself.

I think it's more valuable to get the test skeleton in place and to perform follow up series to adjust if people have philosophical differences.

Quoted 6 lines

2. My next humble undrestanding is that you want to test functions that you don't want to export [2,3] to kernel modules. To my understanding it's not limited on Kunit. I'm testing such DAMON functions using KUnit by including test code in the c file but protecting it via a config. For an example, please refer to DAMON_KUNIT_TEST.

Right there are ways around this, but you lose all of the isolation/performance advantages, and then you end up dirtying the mm/ directory with test code which ends being more or less doing the same thing I'm doing here only in the kernel rather than stubbing?

Quoted 7 lines

I understand above are only small parts of the reason for your decision, and some of those would really unsupported by Kunit. In the case, I think adding this user space tests as is is good. Nonetheless, I think it would be good to hear some comments from Kunit developers. IMHO, letting them know the limitations will hopefully help setting their future TODO items. Cc-ing Brendan, David and Rae for that.

As I said above, I really do not want this series to get stuck on a back-and-forth about test philosophy. We already have tests like the _skeleton_ ones I added, we can change this later, and it's going to make the refactoring part of this more likely to experience conflicts.

Quoted 3 lines

To recap, I have no strong opinions about this patch, but I think knowing how Selftests and KUnit developers think could be helpful.

With respect it strikes me that you have rather strong feelings on this. But again I make the plea that we don't hold this up on the basis of a debate about this vs. other options re: testing.

Kees was agreeable with this approach so I don't think we should really see too much objection to this.

Quoted 11 lines

[1] https://lore.kernel.org/202406270957.C0E5E8057@keescook
[2] https://lore.kernel.org/5zuowniex4sxy6l7erbsg5fiirf4d4f5fbpz2upay2igiwa2xk@vuezoh2wbqf4
[3] https://lore.kernel.org/[email protected]

Thanks,
SJ

[...]

#18 lore raw
Quoted 32 lines · On Thu, 4 Jul 2024 00:24:15 +0100 Lorenzo Stoakes <[email protected]>…
Quoted 31 lines · SeongJae Park
Quoted 15 lines · On Wed, 3 Jul 2024 21:33:00 +0100 Lorenzo Stoakes <[email protected]>…
Quoted 7 lines · Andrew Morton
Quoted 3 lines · On Wed, 3 Jul 2024 12:57:31 +0100 Lorenzo Stoakes…

Kernel functionality is stubbed and shimmed as needed in tools/testing/vma/ which contains a fully functional userland vma_internal.h file and which imports mm/vma.c and mm/vma.h to be directly tested from userland.

Cool stuff.

Thanks :)

Quoted 4 lines

Now we need to make sure that anyone who messes with vma code has run the tests. And has added more testcases, if appropriate.

Quoted 8 lines
Quoted 3 lines

Does it make sense to execute this test under selftests/ in some fashion? Quite a few people appear to be running the selftest code regularly and it would be good to make them run this as well.

I think it will be useful to do that, yes, but as the tests are currently a skeleton to both provide the stubbing out and to provide essentially an example of how you might test (though enough that it'd now be easy to add a _ton_ of tests), it's not quite ready to be run just yet.

If we will eventually move the files under selftests/, why dont' we place the files there from the beginning? Is there a strict rule saying files that not really involved with running tests or not ready cannot be added there? If so, could adding the files after the tests are ready to be run be an option? Cc-ing Shuah since I think she might have a comment.

[...]

Quoted 3 lines

My point to Andrew was that we could potentially automatically run these tests as part of a self-test run as they are so quick, at least in the future, if that made sense.

Ok, I think I was misunderstanding your point on the reply to Andrew. I was thinking you will eventually move the tests to selftests, but not for now, only because it is not ready to run. I understand your points now.

Quoted 25 lines
Quoted 8 lines

Also, I haven't had enough time to read the patches in detail but just the cover letter a little bit. My humble impression from that is that this might better to eventually be kunit tests. I know there was a discussion with Kees on RFC v1 [1] which you kindly explained why you decide to implement this in user space. To my understanding, at least some of the problems are not real problems. For two things as examples,

They are real problems. And I totally disagree that these should be kunit tests. I'm surprised you didn't find my and Liam's arguments compelling?

I suggest you try actually running tools/testing/vma/vma and putting a break point in gdb in vma_merge(), able to observe all state in great detail with no interrupts and see for yourself.

Quoted 4 lines

1. I understand that you concern the test speed [2]. I think Kunit could be slower than the dedicated user space tests, but to my experience, it's not that bad when using the default UML-based execution.

I'm sorry but running VMA code in the smallest possible form in userland is very clearly faster and you are missing the key point that we can _isolate_ anything we _don't need_.

Quoted 7 lines

There's no setup/teardown whatsoever, no clever tricks needed, we get to keep entirely internal interfaces internal and clean. It's compelling.

You are running the code as fast as you possibly can and that allows for lots of interesting things like being able to fuzz at scale, being able to run thousands of cases with basically zero setup/teardown or limits, etc. etc.

I read this from the previous thread, and this is really cool. I was thinking it would be really nice if more kernel subsystems and features be able to do this kind of great testing with minimum duplicated efforts. That was one of the motivations of my previous reply.

Quoted 12 lines

Also, it's basically impossible to explicitly _unit_ test vma merge and vma split and friends without invoking kernel stuff like TLB handling, MMU notifier, huge page handling, process setup/teardown, mm setup/teardown, rlimits, anon vma name handling, uprobes, memory policy handling, interval tree handling, lock contention, THP behaviour, etc. etc. etc.

With this test we can purely _unit_ test these fundamental operations, AND have the ability to for example in future - dump maple tree state from a buggy kernel situation that would result in a panic for instance - and recreate it immediately for debug.

Quoted 5 lines

We also then have the ability to have strong guarantees about the behaviour of these operations at a fundamental level.

If we want _system_ tests that bring in other kernel components then it makes more sense to use kunit/selftests. But this offers something else.

As I also previously mentioned, I was assuming you made the decision to not use KUnit based on real limitations of KUnit you found. Thank you so much for this detailed explanations with nice examples.

[...]

Quoted 6 lines
Quoted 2 lines

To recap, I have no strong opinions about this patch, but I think knowing how Selftests and KUnit developers think could be helpful.

With respect it strikes me that you have rather strong feelings on this. But again I make the plea that we don't hold this up on the basis of a debate about this vs. other options re: testing.

No worry, I'm not willing to delay this work with unnecessary discussions. That's why I'm saying I have no strong opinion. I'm rather regret that I don't have enough time to get a credit on this great work by reading the details and provide my Reviewed-by:.

What I want to say is that it would be nice to ensure the developers of Kselftest and Kunit, who obviously have experiences on testing, get a chance to be involved in this discussion. I believe that would be nice since they might find something we're misunderstanding about Kselftest and/or Kunit. Also they might find some unknown limitations of Kselftest and/or Kunit that you found. I personally hope it is the latter case and it helps evolving KUnit, so that not only vma but also other kernel subsystems and features be able to enhance their test setups with minimum efforts.

Again, I don't think such discussions and possible future works sould be blockers of this work.

Quoted 3 lines

Kees was agreeable with this approach so I don't think we should really see too much objection to this.

You're right. Nonetheless, I found the mail is not Cc-ing KUnit developers, and then I thought giving KUnit developers more chances to be involved would be nice.

Thanks,
SJ

[...]

maintainer
#19 lore raw
In reply to Lorenzo Stoakes
Quoted 7 lines · On Thu, 4 Jul 2024 00:24:15 +0100 Lorenzo Stoakes <[email protected]>…

The self tests differ from this and other tests using the userland-stubbed kernel approach in that they test system call invocation and assert expectations.

My point to Andrew was that we could potentially automatically run these tests as part of a self-test run as they are so quick, at least in the future, if that made sense.

Yes, I was thinking we'd just add a selftest which does (simplified, of course)

	cd ../../vma
	make
	./whatever

simply to cause this new code to be invoked when someone runs the selftest suite.

#20 lore raw
In reply to SeongJae Park

Thanks, SJ.

While I'd love to have the VMA tests be KUnit tests (and there are several advantages, particularly for tooling and automation), I do think the more self-contained userspace tests are great in circumstances like this where the code is self-contained enough to make it possible. Ideally, we'd have some standards and helpers to make these consistent — kselftest and KUnit are both not quite perfect for this case — but I don't think we should hold up a useful set of changes so we can write a whole new framework.

(Personally, I think a userspace implementation of a subset of KUnit or a KUnit-like API would be useful, see below.)

Quoted 6 lines · SeongJae Park
Quoted 3 lines · On Wed, 3 Jul 2024 21:33:00 +0100 Lorenzo Stoakes <[email protected]>…
Quoted 2 lines · Andrew Morton

On Wed, 3 Jul 2024 12:57:31 +0100 Lorenzo Stoakes <[email protected]> wrote:

[... snip ...]

Quoted 10 lines

Also, I haven't had enough time to read the patches in detail but just the cover letter a little bit. My humble impression from that is that this might better to eventually be kunit tests. I know there was a discussion with Kees on RFC v1 [1] which you kindly explained why you decide to implement this in user space. To my understanding, at least some of the problems are not real problems. For two things as examples,

1. I understand that you concern the test speed [2]. I think Kunit could be slower than the dedicated user space tests, but to my experience, it's not that bad when using the default UML-based execution.

KUnit/UML can be quite fast, but I do agree that a totally isolated test will be faster.

Quoted 6 lines

2. My next humble undrestanding is that you want to test functions that you don't want to export [2,3] to kernel modules. To my understanding it's not limited on Kunit. I'm testing such DAMON functions using KUnit by including test code in the c file but protecting it via a config. For an example, please refer to DAMON_KUNIT_TEST.

Quoted 6 lines

I understand above are only small parts of the reason for your decision, and some of those would really unsupported by Kunit. In the case, I think adding this user space tests as is is good. Nonetheless, I think it would be good to hear some comments from Kunit developers. IMHO, letting them know the limitations will hopefully help setting their future TODO items. Cc-ing Brendan, David and Rae for that.

There are a few different ways of working around this, including the '#include the source' method, and conditionally exporting symbols to a separate namespace (e.g., using VISIBLE_IF_KUNIT and EXPORT_SYMBOL_IF_KUNIT()).

Obviously, it's always going to be slightly nasty, but I don't think KUnit will fundamentally be uglier than any other similar hack.

Quoted 5 lines

To recap, I have no strong opinions about this patch, but I think knowing how Selftests and KUnit developers think could be helpful.

More generally, we've seen quite a few cases where we want to compile a small chunk of kernel code and some tests as a userspace binary, for a few different reasons, including:
- Improved speed/debuggability from being a "normal" userspace binary
- The desire to test userspace code which lives in the kernel tree (e.g., the perf tool)
- Smaller reproducable test cases to give to other parties (e.g., compiler developers)

So I think there's definitely a case for having these sorts of tests, it'd just be nice to be as consistent as we can. There are a few existing patches out there (most recently [1]) which implement a subset of the KUnit API in userspace, which has the twin advantages of making test code more consistent overall, and allowing some tests to be available both as KUnit tests and separate userspace tests (so we get the best of both worlds). Having a standard 'userspace kunit' implementation is definitely something I've thought about before, so I'll probably play around with that when I get some time.

Otherwise, if Shuah's okay with it, having these userspace tests be selftests seems at the very least an appropriate stopgap measure, which gets us some tooling and CI. I've always thought of selftests as "testing the running kernel", rather than the tree under test, but as long as it's clear that this is happening, there's no technical reason to avoid it,.

Cheers,
-- David

[1]: https://lore.kernel.org/all/[email protected]/

#21 lore raw
Quoted 10 lines · David Gow

Thanks, SJ.

While I'd love to have the VMA tests be KUnit tests (and there are several advantages, particularly for tooling and automation), I do think the more self-contained userspace tests are great in circumstances like this where the code is self-contained enough to make it possible. Ideally, we'd have some standards and helpers to make these consistent — kselftest and KUnit are both not quite perfect for this case — but I don't think we should hold up a useful set of changes so we can write a whole new framework.

Thanks David!

Quoted 3 lines

(Personally, I think a userspace implementation of a subset of KUnit or a KUnit-like API would be useful, see below.)

Indeed, yes.

Quoted 18 lines · SeongJae Park
Quoted 6 lines · On Wed, 3 Jul 2024 21:33:00 +0100 Lorenzo Stoakes <[email protected]>…
Quoted 4 lines · Andrew Morton
Quoted 2 lines · On Wed, 3 Jul 2024 12:57:31 +0100 Lorenzo Stoakes…

[... snip ...]

Quoted 7 lines

Also, I haven't had enough time to read the patches in detail but just the cover letter a little bit. My humble impression from that is that this might better to eventually be kunit tests. I know there was a discussion with Kees on RFC v1 [1] which you kindly explained why you decide to implement this in user space. To my understanding, at least some of the problems are not real problems. For two things as examples,

Quoted 6 lines
Quoted 3 lines

1. I understand that you concern the test speed [2]. I think Kunit could be slower than the dedicated user space tests, but to my experience, it's not that bad when using the default UML-based execution.

KUnit/UML can be quite fast, but I do agree that a totally isolated test will be faster.

Sure absolutely, the key point here is the essentially zero setup/tear down and zero code is always faster than _some_ code so as we stub/mock components naturally we get speed as well as not having to be concerned about how we might set up fundamental objects like task/mm/vma.

Quoted 15 lines
Quoted 8 lines

2. My next humble undrestanding is that you want to test functions that you don't want to export [2,3] to kernel modules. To my understanding it's not limited on Kunit. I'm testing such DAMON functions using KUnit by including test code in the c file but protecting it via a config. For an example, please refer to DAMON_KUNIT_TEST.

Quoted 6 lines

I understand above are only small parts of the reason for your decision, and some of those would really unsupported by Kunit. In the case, I think adding this user space tests as is is good. Nonetheless, I think it would be good to hear some comments from Kunit developers. IMHO, letting them know the limitations will hopefully help setting their future TODO items. Cc-ing Brendan, David and Rae for that.

Quoted 7 lines

There are a few different ways of working around this, including the '#include the source' method, and conditionally exporting symbols to a separate namespace (e.g., using VISIBLE_IF_KUNIT and EXPORT_SYMBOL_IF_KUNIT()).

Obviously, it's always going to be slightly nasty, but I don't think KUnit will fundamentally be uglier than any other similar hack.

Indeed, I mean this patch set makes use of the 'include the source' method in userland.

To me, the more you think about it and how you might implement testing of fundamnetals like this the more you end up with a mocked out design as in this series, unavoidably.

And sadly I think no matter how you do it you have to put the ugly somewhere, in this instance it's in the stubbed-out vma_internal.h.

Quoted 16 lines
Quoted 6 lines

To recap, I have no strong opinions about this patch, but I think knowing how Selftests and KUnit developers think could be helpful.

More generally, we've seen quite a few cases where we want to compile a small chunk of kernel code and some tests as a userspace binary, for a few different reasons, including:
- Improved speed/debuggability from being a "normal" userspace binary
- The desire to test userspace code which lives in the kernel tree (e.g., the perf tool)
- Smaller reproducable test cases to give to other parties (e.g., compiler developers)

Quoted 10 lines

So I think there's definitely a case for having these sorts of tests, it'd just be nice to be as consistent as we can. There are a few existing patches out there (most recently [1]) which implement a subset of the KUnit API in userspace, which has the twin advantages of making test code more consistent overall, and allowing some tests to be available both as KUnit tests and separate userspace tests (so we get the best of both worlds). Having a standard 'userspace kunit' implementation is definitely something I've thought about before, so I'll probably play around with that when I get some time.

Well indeed, [1] is what this patch series uses, heavily, to be viable :)

I do absolutely agree going forward that some means of standardisation would be very useful.

Quoted 6 lines

Otherwise, if Shuah's okay with it, having these userspace tests be selftests seems at the very least an appropriate stopgap measure, which gets us some tooling and CI. I've always thought of selftests as "testing the running kernel", rather than the tree under test, but as long as it's clear that this is happening, there's no technical reason to avoid it,.

Yeah, this implementation is explicitly intended to be a skeleton to be built on, providing a minimum implementation with the most important component provided, i.e. the stubbed out code - in order to demonstrate why the refactoring bits of the patch sets were done (i.e. to answer 'why so much churn?') AND to provide the basis to easily move ahead and write serious tests.

I think it is still viable to add further tests to this as-is (I'd rather not add too much friction to this hugely valuable exercise - we are seriously lacking for fundamental VMA unit/regression tests), but moving forward I think it should also be very easy to adapt this code to use a consistent userland kunit implementation.

Quoted 5 lines

Cheers,
-- David

[1]: https://lore.kernel.org/all/[email protected]/