Re: [PATCH v2 02/10] tools: add VMA merge tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



* Lorenzo Stoakes <lorenzo.stoakes@xxxxxxxxxx> [240823 16:07]:
> Add a variety of VMA merge unit tests to assert that the behaviour of VMA
> merge is correct at an abstract level and VMAs are merged or not merged as
> expected.
> 
> These are intentionally added _before_ we start refactoring vma_merge() in
> order that we can continually assert correctness throughout the rest of the
> series.
> 
> In order to reduce churn going forward, we backport the vma_merge_struct
> data type to the test code which we introduce and use in a future commit,
> and add wrappers around the merge new and existing VMA cases.
> 
> Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@xxxxxxxxxx>

Reviewed-by: Liam R. Howlett <Liam.Howlett@xxxxxxxxxx>

> ---
>  tools/testing/vma/vma.c          | 1218 +++++++++++++++++++++++++++++-
>  tools/testing/vma/vma_internal.h |   45 +-
>  2 files changed, 1253 insertions(+), 10 deletions(-)
> 
> diff --git a/tools/testing/vma/vma.c b/tools/testing/vma/vma.c
> index 48e033c60d87..9b272633ca9e 100644
> --- a/tools/testing/vma/vma.c
> +++ b/tools/testing/vma/vma.c
> @@ -7,13 +7,43 @@
>  #include "maple-shared.h"
>  #include "vma_internal.h"
>  
> +/* Include so header guard set. */
> +#include "../../../mm/vma.h"
> +
> +static bool fail_prealloc;
> +
> +/* Then override vma_iter_prealloc() so we can choose to fail it. */
> +#define vma_iter_prealloc(vmi, vma)					\
> +	(fail_prealloc ? -ENOMEM : mas_preallocate(&(vmi)->mas, (vma), GFP_KERNEL))
> +
>  /*
>   * Directly import the VMA implementation here. Our vma_internal.h wrapper
>   * provides userland-equivalent functionality for everything vma.c uses.
>   */
>  #include "../../../mm/vma.c"
>  
> +/*
> + * Temporarily forward-ported from a future in which vmg's are used for merging.
> + */
> +struct vma_merge_struct {
> +	struct mm_struct *mm;
> +	struct vma_iterator *vmi;
> +	pgoff_t pgoff;
> +	struct vm_area_struct *prev;
> +	struct vm_area_struct *next; /* Modified by vma_merge(). */
> +	struct vm_area_struct *vma; /* Either a new VMA or the one being modified. */
> +	unsigned long start;
> +	unsigned long end;
> +	unsigned long flags;
> +	struct file *file;
> +	struct anon_vma *anon_vma;
> +	struct mempolicy *policy;
> +	struct vm_userfaultfd_ctx uffd_ctx;
> +	struct anon_vma_name *anon_name;
> +};
> +
>  const struct vm_operations_struct vma_dummy_vm_ops;
> +static struct anon_vma dummy_anon_vma;
>  
>  #define ASSERT_TRUE(_expr)						\
>  	do {								\
> @@ -28,6 +58,14 @@ const struct vm_operations_struct vma_dummy_vm_ops;
>  #define ASSERT_EQ(_val1, _val2) ASSERT_TRUE((_val1) == (_val2))
>  #define ASSERT_NE(_val1, _val2) ASSERT_TRUE((_val1) != (_val2))
>  
> +static struct task_struct __current;
> +
> +struct task_struct *get_current(void)
> +{
> +	return &__current;
> +}
> +
> +/* Helper function to simply allocate a VMA. */
>  static struct vm_area_struct *alloc_vma(struct mm_struct *mm,
>  					unsigned long start,
>  					unsigned long end,
> @@ -47,22 +85,201 @@ static struct vm_area_struct *alloc_vma(struct mm_struct *mm,
>  	return ret;
>  }
>  
> +/* Helper function to allocate a VMA and link it to the tree. */
> +static struct vm_area_struct *alloc_and_link_vma(struct mm_struct *mm,
> +						 unsigned long start,
> +						 unsigned long end,
> +						 pgoff_t pgoff,
> +						 vm_flags_t flags)
> +{
> +	struct vm_area_struct *vma = alloc_vma(mm, start, end, pgoff, flags);
> +
> +	if (vma == NULL)
> +		return NULL;
> +
> +	if (vma_link(mm, vma)) {
> +		vm_area_free(vma);
> +		return NULL;
> +	}
> +
> +	/*
> +	 * Reset this counter which we use to track whether writes have
> +	 * begun. Linking to the tree will have caused this to be incremented,
> +	 * which means we will get a false positive otherwise.
> +	 */
> +	vma->vm_lock_seq = -1;
> +
> +	return vma;
> +}
> +
> +/* Helper function which provides a wrapper around a merge new VMA operation. */
> +static struct vm_area_struct *merge_new(struct vma_merge_struct *vmg)
> +{
> +	/* vma_merge() needs a VMA to determine mm, anon_vma, and file. */
> +	struct vm_area_struct dummy = {
> +		.vm_mm = vmg->mm,
> +		.vm_flags = vmg->flags,
> +		.anon_vma = vmg->anon_vma,
> +		.vm_file = vmg->file,
> +	};
> +
> +	/*
> +	 * For convenience, get prev and next VMAs. Which the new VMA operation
> +	 * requires.
> +	 */
> +	vmg->next = vma_next(vmg->vmi);
> +	vmg->prev = vma_prev(vmg->vmi);
> +
> +	vma_iter_set(vmg->vmi, vmg->start);
> +	return vma_merge_new_vma(vmg->vmi, vmg->prev, &dummy, vmg->start,
> +				 vmg->end, vmg->pgoff);
> +}
> +
> +/*
> + * Helper function which provides a wrapper around a merge existing VMA
> + * operation.
> + */
> +static struct vm_area_struct *merge_existing(struct vma_merge_struct *vmg)
> +{
> +	/* vma_merge() needs a VMA to determine mm, anon_vma, and file. */
> +	struct vm_area_struct dummy = {
> +		.vm_mm = vmg->mm,
> +		.vm_flags = vmg->flags,
> +		.anon_vma = vmg->anon_vma,
> +		.vm_file = vmg->file,
> +	};
> +
> +	return vma_merge(vmg->vmi, vmg->prev, &dummy, vmg->start, vmg->end,
> +			 vmg->flags, vmg->pgoff, vmg->policy, vmg->uffd_ctx,
> +			 vmg->anon_name);
> +}
> +
> +/*
> + * Helper function which provides a wrapper around the expansion of an existing
> + * VMA.
> + */
> +static int expand_existing(struct vma_merge_struct *vmg)
> +{
> +	return vma_expand(vmg->vmi, vmg->vma, vmg->start, vmg->end, vmg->pgoff,
> +			  vmg->next);
> +}
> +
> +/*
> + * Helper function to reset merge state the associated VMA iterator to a
> + * specified new range.
> + */
> +static void vmg_set_range(struct vma_merge_struct *vmg, unsigned long start,
> +			  unsigned long end, pgoff_t pgoff, vm_flags_t flags)
> +{
> +	vma_iter_set(vmg->vmi, start);
> +
> +	vmg->prev = NULL;
> +	vmg->next = NULL;
> +	vmg->vma = NULL;
> +
> +	vmg->start = start;
> +	vmg->end = end;
> +	vmg->pgoff = pgoff;
> +	vmg->flags = flags;
> +}
> +
> +/*
> + * Helper function to try to merge a new VMA.
> + *
> + * Update vmg and the iterator for it and try to merge, otherwise allocate a new
> + * VMA, link it to the maple tree and return it.
> + */
> +static struct vm_area_struct *try_merge_new_vma(struct mm_struct *mm,
> +						struct vma_merge_struct *vmg,
> +						unsigned long start, unsigned long end,
> +						pgoff_t pgoff, vm_flags_t flags,
> +						bool *was_merged)
> +{
> +	struct vm_area_struct *merged;
> +
> +	vmg_set_range(vmg, start, end, pgoff, flags);
> +
> +	merged = merge_new(vmg);
> +	if (merged) {
> +		*was_merged = true;
> +		return merged;
> +	}
> +
> +	*was_merged = false;
> +	return alloc_and_link_vma(mm, start, end, pgoff, flags);
> +}
> +
> +/*
> + * Helper function to reset the dummy anon_vma to indicate it has not been
> + * duplicated.
> + */
> +static void reset_dummy_anon_vma(void)
> +{
> +	dummy_anon_vma.was_cloned = false;
> +	dummy_anon_vma.was_unlinked = false;
> +}
> +
> +/*
> + * Helper function to remove all VMAs and destroy the maple tree associated with
> + * a virtual address space. Returns a count of VMAs in the tree.
> + */
> +static int cleanup_mm(struct mm_struct *mm, struct vma_iterator *vmi)
> +{
> +	struct vm_area_struct *vma;
> +	int count = 0;
> +
> +	fail_prealloc = false;
> +	reset_dummy_anon_vma();
> +
> +	vma_iter_set(vmi, 0);
> +	for_each_vma(*vmi, vma) {
> +		vm_area_free(vma);
> +		count++;
> +	}
> +
> +	mtree_destroy(&mm->mm_mt);
> +	mm->map_count = 0;
> +	return count;
> +}
> +
> +/* Helper function to determine if VMA has had vma_start_write() performed. */
> +static bool vma_write_started(struct vm_area_struct *vma)
> +{
> +	int seq = vma->vm_lock_seq;
> +
> +	/* We reset after each check. */
> +	vma->vm_lock_seq = -1;
> +
> +	/* The vma_start_write() stub simply increments this value. */
> +	return seq > -1;
> +}
> +
> +/* Helper function providing a dummy vm_ops->close() method.*/
> +static void dummy_close(struct vm_area_struct *)
> +{
> +}
> +
>  static bool test_simple_merge(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 *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);
> +	struct vma_merge_struct vmg = {
> +		.mm = &mm,
> +		.vmi = &vmi,
> +		.start = 0x1000,
> +		.end = 0x2000,
> +		.flags = flags,
> +		.pgoff = 1,
> +	};
>  
>  	ASSERT_FALSE(vma_link(&mm, vma_left));
> -	ASSERT_FALSE(vma_link(&mm, vma_middle));
>  	ASSERT_FALSE(vma_link(&mm, vma_right));
>  
> -	vma = vma_merge_new_vma(&vmi, vma_left, vma_middle, 0x1000,
> -				0x2000, 1);
> +	vma = merge_new(&vmg);
>  	ASSERT_NE(vma, NULL);
>  
>  	ASSERT_EQ(vma->vm_start, 0);
> @@ -142,10 +359,17 @@ static bool test_simple_expand(void)
>  	struct mm_struct mm = {};
>  	struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x1000, 0, flags);
>  	VMA_ITERATOR(vmi, &mm, 0);
> +	struct vma_merge_struct vmg = {
> +		.vmi = &vmi,
> +		.vma = vma,
> +		.start = 0,
> +		.end = 0x3000,
> +		.pgoff = 0,
> +	};
>  
>  	ASSERT_FALSE(vma_link(&mm, vma));
>  
> -	ASSERT_FALSE(vma_expand(&vmi, vma, 0, 0x3000, 0, NULL));
> +	ASSERT_FALSE(expand_existing(&vmg));
>  
>  	ASSERT_EQ(vma->vm_start, 0);
>  	ASSERT_EQ(vma->vm_end, 0x3000);
> @@ -178,6 +402,980 @@ static bool test_simple_shrink(void)
>  	return true;
>  }
>  
> +static bool test_merge_new(void)
> +{
> +	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> +	struct mm_struct mm = {};
> +	VMA_ITERATOR(vmi, &mm, 0);
> +	struct vma_merge_struct vmg = {
> +		.mm = &mm,
> +		.vmi = &vmi,
> +	};
> +	struct anon_vma_chain dummy_anon_vma_chain_a = {
> +		.anon_vma = &dummy_anon_vma,
> +	};
> +	struct anon_vma_chain dummy_anon_vma_chain_b = {
> +		.anon_vma = &dummy_anon_vma,
> +	};
> +	struct anon_vma_chain dummy_anon_vma_chain_c = {
> +		.anon_vma = &dummy_anon_vma,
> +	};
> +	struct anon_vma_chain dummy_anon_vma_chain_d = {
> +		.anon_vma = &dummy_anon_vma,
> +	};
> +	int count;
> +	struct vm_area_struct *vma, *vma_a, *vma_b, *vma_c, *vma_d;
> +	bool merged;
> +
> +	/*
> +	 * 0123456789abc
> +	 * AA B       CC
> +	 */
> +	vma_a = alloc_and_link_vma(&mm, 0, 0x2000, 0, flags);
> +	ASSERT_NE(vma_a, NULL);
> +	/* We give each VMA a single avc so we can test anon_vma duplication. */
> +	INIT_LIST_HEAD(&vma_a->anon_vma_chain);
> +	list_add(&dummy_anon_vma_chain_a.same_vma, &vma_a->anon_vma_chain);
> +
> +	vma_b = alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, flags);
> +	ASSERT_NE(vma_b, NULL);
> +	INIT_LIST_HEAD(&vma_b->anon_vma_chain);
> +	list_add(&dummy_anon_vma_chain_b.same_vma, &vma_b->anon_vma_chain);
> +
> +	vma_c = alloc_and_link_vma(&mm, 0xb000, 0xc000, 0xb, flags);
> +	ASSERT_NE(vma_c, NULL);
> +	INIT_LIST_HEAD(&vma_c->anon_vma_chain);
> +	list_add(&dummy_anon_vma_chain_c.same_vma, &vma_c->anon_vma_chain);
> +
> +	/*
> +	 * NO merge.
> +	 *
> +	 * 0123456789abc
> +	 * AA B   **  CC
> +	 */
> +	vma_d = try_merge_new_vma(&mm, &vmg, 0x7000, 0x9000, 7, flags, &merged);
> +	ASSERT_NE(vma_d, NULL);
> +	INIT_LIST_HEAD(&vma_d->anon_vma_chain);
> +	list_add(&dummy_anon_vma_chain_d.same_vma, &vma_d->anon_vma_chain);
> +	ASSERT_FALSE(merged);
> +	ASSERT_EQ(mm.map_count, 4);
> +
> +	/*
> +	 * Merge BOTH sides.
> +	 *
> +	 * 0123456789abc
> +	 * AA*B   DD  CC
> +	 */
> +	vma_b->anon_vma = &dummy_anon_vma;
> +	vma = try_merge_new_vma(&mm, &vmg, 0x2000, 0x3000, 2, flags, &merged);
> +	ASSERT_EQ(vma, vma_a);
> +	/* Merge with A, delete B. */
> +	ASSERT_TRUE(merged);
> +	ASSERT_EQ(vma->vm_start, 0);
> +	ASSERT_EQ(vma->vm_end, 0x4000);
> +	ASSERT_EQ(vma->vm_pgoff, 0);
> +	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_write_started(vma));
> +	ASSERT_EQ(mm.map_count, 3);
> +
> +	/*
> +	 * Merge to PREVIOUS VMA.
> +	 *
> +	 * 0123456789abc
> +	 * AAAA*  DD  CC
> +	 */
> +	vma = try_merge_new_vma(&mm, &vmg, 0x4000, 0x5000, 4, flags, &merged);
> +	ASSERT_EQ(vma, vma_a);
> +	/* Extend A. */
> +	ASSERT_TRUE(merged);
> +	ASSERT_EQ(vma->vm_start, 0);
> +	ASSERT_EQ(vma->vm_end, 0x5000);
> +	ASSERT_EQ(vma->vm_pgoff, 0);
> +	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_write_started(vma));
> +	ASSERT_EQ(mm.map_count, 3);
> +
> +	/*
> +	 * Merge to NEXT VMA.
> +	 *
> +	 * 0123456789abc
> +	 * AAAAA *DD  CC
> +	 */
> +	vma_d->anon_vma = &dummy_anon_vma;
> +	vma = try_merge_new_vma(&mm, &vmg, 0x6000, 0x7000, 6, flags, &merged);
> +	ASSERT_EQ(vma, vma_d);
> +	/* Prepend. */
> +	ASSERT_TRUE(merged);
> +	ASSERT_EQ(vma->vm_start, 0x6000);
> +	ASSERT_EQ(vma->vm_end, 0x9000);
> +	ASSERT_EQ(vma->vm_pgoff, 6);
> +	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_write_started(vma));
> +	ASSERT_EQ(mm.map_count, 3);
> +
> +	/*
> +	 * Merge BOTH sides.
> +	 *
> +	 * 0123456789abc
> +	 * AAAAA*DDD  CC
> +	 */
> +	vma = try_merge_new_vma(&mm, &vmg, 0x5000, 0x6000, 5, flags, &merged);
> +	ASSERT_EQ(vma, vma_a);
> +	/* Merge with A, delete D. */
> +	ASSERT_TRUE(merged);
> +	ASSERT_EQ(vma->vm_start, 0);
> +	ASSERT_EQ(vma->vm_end, 0x9000);
> +	ASSERT_EQ(vma->vm_pgoff, 0);
> +	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_write_started(vma));
> +	ASSERT_EQ(mm.map_count, 2);
> +
> +	/*
> +	 * Merge to NEXT VMA.
> +	 *
> +	 * 0123456789abc
> +	 * AAAAAAAAA *CC
> +	 */
> +	vma_c->anon_vma = &dummy_anon_vma;
> +	vma = try_merge_new_vma(&mm, &vmg, 0xa000, 0xb000, 0xa, flags, &merged);
> +	ASSERT_EQ(vma, vma_c);
> +	/* Prepend C. */
> +	ASSERT_TRUE(merged);
> +	ASSERT_EQ(vma->vm_start, 0xa000);
> +	ASSERT_EQ(vma->vm_end, 0xc000);
> +	ASSERT_EQ(vma->vm_pgoff, 0xa);
> +	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_write_started(vma));
> +	ASSERT_EQ(mm.map_count, 2);
> +
> +	/*
> +	 * Merge BOTH sides.
> +	 *
> +	 * 0123456789abc
> +	 * AAAAAAAAA*CCC
> +	 */
> +	vma = try_merge_new_vma(&mm, &vmg, 0x9000, 0xa000, 0x9, flags, &merged);
> +	ASSERT_EQ(vma, vma_a);
> +	/* Extend A and delete C. */
> +	ASSERT_TRUE(merged);
> +	ASSERT_EQ(vma->vm_start, 0);
> +	ASSERT_EQ(vma->vm_end, 0xc000);
> +	ASSERT_EQ(vma->vm_pgoff, 0);
> +	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_write_started(vma));
> +	ASSERT_EQ(mm.map_count, 1);
> +
> +	/*
> +	 * Final state.
> +	 *
> +	 * 0123456789abc
> +	 * AAAAAAAAAAAAA
> +	 */
> +
> +	count = 0;
> +	vma_iter_set(&vmi, 0);
> +	for_each_vma(vmi, vma) {
> +		ASSERT_NE(vma, NULL);
> +		ASSERT_EQ(vma->vm_start, 0);
> +		ASSERT_EQ(vma->vm_end, 0xc000);
> +		ASSERT_EQ(vma->vm_pgoff, 0);
> +		ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
> +
> +		vm_area_free(vma);
> +		count++;
> +	}
> +
> +	/* Should only have one VMA left (though freed) after all is done.*/
> +	ASSERT_EQ(count, 1);
> +
> +	mtree_destroy(&mm.mm_mt);
> +	return true;
> +}
> +
> +static bool test_vma_merge_special_flags(void)
> +{
> +	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> +	struct mm_struct mm = {};
> +	VMA_ITERATOR(vmi, &mm, 0);
> +	struct vma_merge_struct vmg = {
> +		.mm = &mm,
> +		.vmi = &vmi,
> +	};
> +	vm_flags_t special_flags[] = { VM_IO, VM_DONTEXPAND, VM_PFNMAP, VM_MIXEDMAP };
> +	vm_flags_t all_special_flags = 0;
> +	int i;
> +	struct vm_area_struct *vma_left, *vma;
> +
> +	/* Make sure there aren't new VM_SPECIAL flags. */
> +	for (i = 0; i < ARRAY_SIZE(special_flags); i++) {
> +		all_special_flags |= special_flags[i];
> +	}
> +	ASSERT_EQ(all_special_flags, VM_SPECIAL);
> +
> +	/*
> +	 * 01234
> +	 * AAA
> +	 */
> +	vma_left = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	ASSERT_NE(vma_left, NULL);
> +
> +	/* 1. Set up new VMA with special flag that would otherwise merge. */
> +
> +	/*
> +	 * 01234
> +	 * AAA*
> +	 *
> +	 * This should merge if not for the VM_SPECIAL flag.
> +	 */
> +	vmg_set_range(&vmg, 0x3000, 0x4000, 3, flags);
> +	for (i = 0; i < ARRAY_SIZE(special_flags); i++) {
> +		vm_flags_t special_flag = special_flags[i];
> +
> +		vma_left->__vm_flags = flags | special_flag;
> +		vmg.flags = flags | special_flag;
> +		vma = merge_new(&vmg);
> +		ASSERT_EQ(vma, NULL);
> +	}
> +
> +	/* 2. Modify VMA with special flag that would otherwise merge. */
> +
> +	/*
> +	 * 01234
> +	 * AAAB
> +	 *
> +	 * Create a VMA to modify.
> +	 */
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, flags);
> +	ASSERT_NE(vma, NULL);
> +	vmg.vma = vma;
> +
> +	for (i = 0; i < ARRAY_SIZE(special_flags); i++) {
> +		vm_flags_t special_flag = special_flags[i];
> +
> +		vma_left->__vm_flags = flags | special_flag;
> +		vmg.flags = flags | special_flag;
> +		vma = merge_existing(&vmg);
> +		ASSERT_EQ(vma, NULL);
> +	}
> +
> +	cleanup_mm(&mm, &vmi);
> +	return true;
> +}
> +
> +static bool test_vma_merge_with_close(void)
> +{
> +	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> +	struct mm_struct mm = {};
> +	VMA_ITERATOR(vmi, &mm, 0);
> +	struct vma_merge_struct vmg = {
> +		.mm = &mm,
> +		.vmi = &vmi,
> +	};
> +	const struct vm_operations_struct vm_ops = {
> +		.close = dummy_close,
> +	};
> +	struct vm_area_struct *vma_next =
> +		alloc_and_link_vma(&mm, 0x2000, 0x3000, 2, flags);
> +	struct vm_area_struct *vma;
> +
> +	/*
> +	 * When we merge VMAs we sometimes have to delete others as part of the
> +	 * operation.
> +	 *
> +	 * Considering the two possible adjacent VMAs to which a VMA can be
> +	 * merged:
> +	 *
> +	 * [ prev ][ vma ][ next ]
> +	 *
> +	 * In no case will we need to delete prev. If the operation is
> +	 * mergeable, then prev will be extended with one or both of vma and
> +	 * next deleted.
> +	 *
> +	 * As a result, during initial mergeability checks, only
> +	 * can_vma_merge_before() (which implies the VMA being merged with is
> +	 * 'next' as shown above) bothers to check to see whether the next VMA
> +	 * has a vm_ops->close() callback that will need to be called when
> +	 * removed.
> +	 *
> +	 * If it does, then we cannot merge as the resources that the close()
> +	 * operation potentially clears down are tied only to the existing VMA
> +	 * range and we have no way of extending those to the nearly merged one.
> +	 *
> +	 * We must consider two scenarios:
> +	 *
> +	 * A.
> +	 *
> +	 * vm_ops->close:     -       -    !NULL
> +	 *                 [ prev ][ vma ][ next ]
> +	 *
> +	 * Where prev may or may not be present/mergeable.
> +	 *
> +	 * This is picked up by a specific check in can_vma_merge_before().
> +	 *
> +	 * B.
> +	 *
> +	 * vm_ops->close:     -     !NULL
> +	 *                 [ prev ][ vma ]
> +	 *
> +	 * Where prev and vma are present and mergeable.
> +	 *
> +	 * This is picked up by a specific check in the modified VMA merge.
> +	 *
> +	 * IMPORTANT NOTE: We make the assumption that the following case:
> +	 *
> +	 *    -     !NULL   NULL
> +	 * [ prev ][ vma ][ next ]
> +	 *
> +	 * Cannot occur, because vma->vm_ops being the same implies the same
> +	 * vma->vm_file, and therefore this would mean that next->vm_ops->close
> +	 * would be set too, and thus scenario A would pick this up.
> +	 */
> +
> +	ASSERT_NE(vma_next, NULL);
> +
> +	/*
> +	 * SCENARIO A
> +	 *
> +	 * 0123
> +	 *  *N
> +	 */
> +
> +	/* Make the next VMA have a close() callback. */
> +	vma_next->vm_ops = &vm_ops;
> +
> +	/* Our proposed VMA has characteristics that would otherwise be merged. */
> +	vmg_set_range(&vmg, 0x1000, 0x2000, 1, flags);
> +
> +	/* The next VMA having a close() operator should cause the merge to fail.*/
> +	ASSERT_EQ(merge_new(&vmg), NULL);
> +
> +	/* Now create the VMA so we can merge via modified flags */
> +	vmg_set_range(&vmg, 0x1000, 0x2000, 1, flags);
> +	vma = alloc_and_link_vma(&mm, 0x1000, 0x2000, 1, flags);
> +	vmg.vma = vma;
> +
> +	/*
> +	 * The VMA being modified in a way that would otherwise merge should
> +	 * also fail.
> +	 */
> +	ASSERT_EQ(merge_existing(&vmg), NULL);
> +
> +	/* SCENARIO B
> +	 *
> +	 * 0123
> +	 * P*
> +	 *
> +	 * In order for this scenario to trigger, the VMA currently being
> +	 * modified must also have a .close().
> +	 */
> +
> +	/* Reset VMG state. */
> +	vmg_set_range(&vmg, 0x1000, 0x2000, 1, flags);
> +	/*
> +	 * Make next unmergeable, and don't let the scenario A check pick this
> +	 * up, we want to reproduce scenario B only.
> +	 */
> +	vma_next->vm_ops = NULL;
> +	vma_next->__vm_flags &= ~VM_MAYWRITE;
> +	/* Allocate prev. */
> +	vmg.prev = alloc_and_link_vma(&mm, 0, 0x1000, 0, flags);
> +	/* Assign a vm_ops->close() function to VMA explicitly. */
> +	vma->vm_ops = &vm_ops;
> +	vmg.vma = vma;
> +	/* Make sure merge does not occur. */
> +	ASSERT_EQ(merge_existing(&vmg), NULL);
> +
> +	cleanup_mm(&mm, &vmi);
> +	return true;
> +}
> +
> +static bool test_vma_merge_new_with_close(void)
> +{
> +	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> +	struct mm_struct mm = {};
> +	VMA_ITERATOR(vmi, &mm, 0);
> +	struct vma_merge_struct vmg = {
> +		.mm = &mm,
> +		.vmi = &vmi,
> +	};
> +	struct vm_area_struct *vma_prev = alloc_and_link_vma(&mm, 0, 0x2000, 0, flags);
> +	struct vm_area_struct *vma_next = alloc_and_link_vma(&mm, 0x5000, 0x7000, 5, flags);
> +	const struct vm_operations_struct vm_ops = {
> +		.close = dummy_close,
> +	};
> +	struct vm_area_struct *vma;
> +
> +	/*
> +	 * We should allow the partial merge of a proposed new VMA if the
> +	 * surrounding VMAs have vm_ops->close() hooks (but are otherwise
> +	 * compatible), e.g.:
> +	 *
> +	 *        New VMA
> +	 *    A  v-------v  B
> +	 * |-----|       |-----|
> +	 *  close         close
> +	 *
> +	 * Since the rule is to not DELETE a VMA with a close operation, this
> +	 * should be permitted, only rather than expanding A and deleting B, we
> +	 * should simply expand A and leave B intact, e.g.:
> +	 *
> +	 *        New VMA
> +	 *       A          B
> +	 * |------------||-----|
> +	 *  close         close
> +	 */
> +
> +	/* Have prev and next have a vm_ops->close() hook. */
> +	vma_prev->vm_ops = &vm_ops;
> +	vma_next->vm_ops = &vm_ops;
> +
> +	vmg_set_range(&vmg, 0x2000, 0x5000, 2, flags);
> +	vma = merge_new(&vmg);
> +	ASSERT_NE(vma, NULL);
> +	ASSERT_EQ(vma->vm_start, 0);
> +	ASSERT_EQ(vma->vm_end, 0x5000);
> +	ASSERT_EQ(vma->vm_pgoff, 0);
> +	ASSERT_EQ(vma->vm_ops, &vm_ops);
> +	ASSERT_TRUE(vma_write_started(vma));
> +	ASSERT_EQ(mm.map_count, 2);
> +
> +	cleanup_mm(&mm, &vmi);
> +	return true;
> +}
> +
> +static bool test_merge_existing(void)
> +{
> +	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> +	struct mm_struct mm = {};
> +	VMA_ITERATOR(vmi, &mm, 0);
> +	struct vm_area_struct *vma, *vma_prev, *vma_next;
> +	struct vma_merge_struct vmg = {
> +		.mm = &mm,
> +		.vmi = &vmi,
> +	};
> +
> +	/*
> +	 * Merge right case - partial span.
> +	 *
> +	 *    <->
> +	 * 0123456789
> +	 *   VVVVNNN
> +	 *            ->
> +	 * 0123456789
> +	 *   VNNNNNN
> +	 */
> +	vma = alloc_and_link_vma(&mm, 0x2000, 0x6000, 2, flags);
> +	vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, flags);
> +	vmg_set_range(&vmg, 0x3000, 0x6000, 3, flags);
> +	vmg.vma = vma;
> +	vmg.prev = vma;
> +	vma->anon_vma = &dummy_anon_vma;
> +	ASSERT_EQ(merge_existing(&vmg), vma_next);
> +	ASSERT_EQ(vma_next->vm_start, 0x3000);
> +	ASSERT_EQ(vma_next->vm_end, 0x9000);
> +	ASSERT_EQ(vma_next->vm_pgoff, 3);
> +	ASSERT_EQ(vma_next->anon_vma, &dummy_anon_vma);
> +	ASSERT_EQ(vma->vm_start, 0x2000);
> +	ASSERT_EQ(vma->vm_end, 0x3000);
> +	ASSERT_EQ(vma->vm_pgoff, 2);
> +	ASSERT_TRUE(vma_write_started(vma));
> +	ASSERT_TRUE(vma_write_started(vma_next));
> +	ASSERT_EQ(mm.map_count, 2);
> +
> +	/* Clear down and reset. */
> +	ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
> +
> +	/*
> +	 * Merge right case - full span.
> +	 *
> +	 *   <-->
> +	 * 0123456789
> +	 *   VVVVNNN
> +	 *            ->
> +	 * 0123456789
> +	 *   NNNNNNN
> +	 */
> +	vma = alloc_and_link_vma(&mm, 0x2000, 0x6000, 2, flags);
> +	vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, flags);
> +	vmg_set_range(&vmg, 0x2000, 0x6000, 2, flags);
> +	vmg.vma = vma;
> +	vma->anon_vma = &dummy_anon_vma;
> +	ASSERT_EQ(merge_existing(&vmg), vma_next);
> +	ASSERT_EQ(vma_next->vm_start, 0x2000);
> +	ASSERT_EQ(vma_next->vm_end, 0x9000);
> +	ASSERT_EQ(vma_next->vm_pgoff, 2);
> +	ASSERT_EQ(vma_next->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_write_started(vma_next));
> +	ASSERT_EQ(mm.map_count, 1);
> +
> +	/* Clear down and reset. We should have deleted vma. */
> +	ASSERT_EQ(cleanup_mm(&mm, &vmi), 1);
> +
> +	/*
> +	 * Merge left case - partial span.
> +	 *
> +	 *    <->
> +	 * 0123456789
> +	 * PPPVVVV
> +	 *            ->
> +	 * 0123456789
> +	 * PPPPPPV
> +	 */
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, flags);
> +	vmg_set_range(&vmg, 0x3000, 0x6000, 3, flags);
> +	vmg.prev = vma_prev;
> +	vmg.vma = vma;
> +	vma->anon_vma = &dummy_anon_vma;
> +
> +	ASSERT_EQ(merge_existing(&vmg), vma_prev);
> +	ASSERT_EQ(vma_prev->vm_start, 0);
> +	ASSERT_EQ(vma_prev->vm_end, 0x6000);
> +	ASSERT_EQ(vma_prev->vm_pgoff, 0);
> +	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
> +	ASSERT_EQ(vma->vm_start, 0x6000);
> +	ASSERT_EQ(vma->vm_end, 0x7000);
> +	ASSERT_EQ(vma->vm_pgoff, 6);
> +	ASSERT_TRUE(vma_write_started(vma_prev));
> +	ASSERT_TRUE(vma_write_started(vma));
> +	ASSERT_EQ(mm.map_count, 2);
> +
> +	/* Clear down and reset. */
> +	ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
> +
> +	/*
> +	 * Merge left case - full span.
> +	 *
> +	 *    <-->
> +	 * 0123456789
> +	 * PPPVVVV
> +	 *            ->
> +	 * 0123456789
> +	 * PPPPPPP
> +	 */
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, flags);
> +	vmg_set_range(&vmg, 0x3000, 0x7000, 3, flags);
> +	vmg.prev = vma_prev;
> +	vmg.vma = vma;
> +	vma->anon_vma = &dummy_anon_vma;
> +	ASSERT_EQ(merge_existing(&vmg), vma_prev);
> +	ASSERT_EQ(vma_prev->vm_start, 0);
> +	ASSERT_EQ(vma_prev->vm_end, 0x7000);
> +	ASSERT_EQ(vma_prev->vm_pgoff, 0);
> +	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_write_started(vma_prev));
> +	ASSERT_EQ(mm.map_count, 1);
> +
> +	/* Clear down and reset. We should have deleted vma. */
> +	ASSERT_EQ(cleanup_mm(&mm, &vmi), 1);
> +
> +	/*
> +	 * Merge both case.
> +	 *
> +	 *    <-->
> +	 * 0123456789
> +	 * PPPVVVVNNN
> +	 *             ->
> +	 * 0123456789
> +	 * PPPPPPPPPP
> +	 */
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, flags);
> +	vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, flags);
> +	vmg_set_range(&vmg, 0x3000, 0x7000, 3, flags);
> +	vmg.prev = vma_prev;
> +	vmg.vma = vma;
> +	vma->anon_vma = &dummy_anon_vma;
> +	ASSERT_EQ(merge_existing(&vmg), vma_prev);
> +	ASSERT_EQ(vma_prev->vm_start, 0);
> +	ASSERT_EQ(vma_prev->vm_end, 0x9000);
> +	ASSERT_EQ(vma_prev->vm_pgoff, 0);
> +	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_write_started(vma_prev));
> +	ASSERT_EQ(mm.map_count, 1);
> +
> +	/* Clear down and reset. We should have deleted prev and next. */
> +	ASSERT_EQ(cleanup_mm(&mm, &vmi), 1);
> +
> +	/*
> +	 * Non-merge ranges. the modified VMA merge operation assumes that the
> +	 * caller always specifies ranges within the input VMA so we need only
> +	 * examine these cases.
> +	 *
> +	 *     -
> +	 *      -
> +	 *       -
> +	 *     <->
> +	 *     <>
> +	 *      <>
> +	 * 0123456789a
> +	 * PPPVVVVVNNN
> +	 */
> +
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x8000, 3, flags);
> +	vma_next = alloc_and_link_vma(&mm, 0x8000, 0xa000, 8, flags);
> +
> +	vmg_set_range(&vmg, 0x4000, 0x5000, 4, flags);
> +	vmg.prev = vma;
> +	vmg.vma = vma;
> +	ASSERT_EQ(merge_existing(&vmg), NULL);
> +
> +	vmg_set_range(&vmg, 0x5000, 0x6000, 5, flags);
> +	vmg.prev = vma;
> +	vmg.vma = vma;
> +	ASSERT_EQ(merge_existing(&vmg), NULL);
> +
> +	vmg_set_range(&vmg, 0x6000, 0x7000, 6, flags);
> +	vmg.prev = vma;
> +	vmg.vma = vma;
> +	ASSERT_EQ(merge_existing(&vmg), NULL);
> +
> +	vmg_set_range(&vmg, 0x4000, 0x7000, 4, flags);
> +	vmg.prev = vma;
> +	vmg.vma = vma;
> +	ASSERT_EQ(merge_existing(&vmg), NULL);
> +
> +	vmg_set_range(&vmg, 0x4000, 0x6000, 4, flags);
> +	vmg.prev = vma;
> +	vmg.vma = vma;
> +	ASSERT_EQ(merge_existing(&vmg), NULL);
> +
> +	vmg_set_range(&vmg, 0x5000, 0x6000, 5, flags);
> +	vmg.prev = vma;
> +	vmg.vma = vma;
> +	ASSERT_EQ(merge_existing(&vmg), NULL);
> +
> +	ASSERT_EQ(cleanup_mm(&mm, &vmi), 3);
> +
> +	return true;
> +}
> +
> +static bool test_anon_vma_non_mergeable(void)
> +{
> +	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> +	struct mm_struct mm = {};
> +	VMA_ITERATOR(vmi, &mm, 0);
> +	struct vm_area_struct *vma, *vma_prev, *vma_next;
> +	struct vma_merge_struct vmg = {
> +		.mm = &mm,
> +		.vmi = &vmi,
> +	};
> +	struct anon_vma_chain dummy_anon_vma_chain1 = {
> +		.anon_vma = &dummy_anon_vma,
> +	};
> +	struct anon_vma_chain dummy_anon_vma_chain2 = {
> +		.anon_vma = &dummy_anon_vma,
> +	};
> +
> +	/*
> +	 * In the case of modified VMA merge, merging both left and right VMAs
> +	 * but where prev and next have incompatible anon_vma objects, we revert
> +	 * to a merge of prev and VMA:
> +	 *
> +	 *    <-->
> +	 * 0123456789
> +	 * PPPVVVVNNN
> +	 *            ->
> +	 * 0123456789
> +	 * PPPPPPPNNN
> +	 */
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, flags);
> +	vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, flags);
> +
> +	/*
> +	 * Give both prev and next single anon_vma_chain fields, so they will
> +	 * merge with the NULL vmg->anon_vma.
> +	 *
> +	 * However, when prev is compared to next, the merge should fail.
> +	 */
> +
> +	INIT_LIST_HEAD(&vma_prev->anon_vma_chain);
> +	list_add(&dummy_anon_vma_chain1.same_vma, &vma_prev->anon_vma_chain);
> +	ASSERT_TRUE(list_is_singular(&vma_prev->anon_vma_chain));
> +	vma_prev->anon_vma = &dummy_anon_vma;
> +	ASSERT_TRUE(is_mergeable_anon_vma(NULL, vma_prev->anon_vma, vma_prev));
> +
> +	INIT_LIST_HEAD(&vma_next->anon_vma_chain);
> +	list_add(&dummy_anon_vma_chain2.same_vma, &vma_next->anon_vma_chain);
> +	ASSERT_TRUE(list_is_singular(&vma_next->anon_vma_chain));
> +	vma_next->anon_vma = (struct anon_vma *)2;
> +	ASSERT_TRUE(is_mergeable_anon_vma(NULL, vma_next->anon_vma, vma_next));
> +
> +	ASSERT_FALSE(is_mergeable_anon_vma(vma_prev->anon_vma, vma_next->anon_vma, NULL));
> +
> +	vmg_set_range(&vmg, 0x3000, 0x7000, 3, flags);
> +	vmg.prev = vma_prev;
> +	vmg.vma = vma;
> +
> +	ASSERT_EQ(merge_existing(&vmg), vma_prev);
> +	ASSERT_EQ(vma_prev->vm_start, 0);
> +	ASSERT_EQ(vma_prev->vm_end, 0x7000);
> +	ASSERT_EQ(vma_prev->vm_pgoff, 0);
> +	ASSERT_TRUE(vma_write_started(vma_prev));
> +	ASSERT_FALSE(vma_write_started(vma_next));
> +
> +	/* Clear down and reset. */
> +	ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
> +
> +	/*
> +	 * Now consider the new VMA case. This is equivalent, only adding a new
> +	 * VMA in a gap between prev and next.
> +	 *
> +	 *    <-->
> +	 * 0123456789
> +	 * PPP****NNN
> +	 *            ->
> +	 * 0123456789
> +	 * PPPPPPPNNN
> +	 */
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, flags);
> +
> +	INIT_LIST_HEAD(&vma_prev->anon_vma_chain);
> +	list_add(&dummy_anon_vma_chain1.same_vma, &vma_prev->anon_vma_chain);
> +	vma_prev->anon_vma = (struct anon_vma *)1;
> +
> +	INIT_LIST_HEAD(&vma_next->anon_vma_chain);
> +	list_add(&dummy_anon_vma_chain2.same_vma, &vma_next->anon_vma_chain);
> +	vma_next->anon_vma = (struct anon_vma *)2;
> +
> +	vmg_set_range(&vmg, 0x3000, 0x7000, 3, flags);
> +	vmg.prev = vma_prev;
> +
> +	ASSERT_EQ(merge_new(&vmg), vma_prev);
> +	ASSERT_EQ(vma_prev->vm_start, 0);
> +	ASSERT_EQ(vma_prev->vm_end, 0x7000);
> +	ASSERT_EQ(vma_prev->vm_pgoff, 0);
> +	ASSERT_TRUE(vma_write_started(vma_prev));
> +	ASSERT_FALSE(vma_write_started(vma_next));
> +
> +	/* Final cleanup. */
> +	ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
> +
> +	return true;
> +}
> +
> +static bool test_dup_anon_vma(void)
> +{
> +	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> +	struct mm_struct mm = {};
> +	VMA_ITERATOR(vmi, &mm, 0);
> +	struct vma_merge_struct vmg = {
> +		.mm = &mm,
> +		.vmi = &vmi,
> +	};
> +	struct anon_vma_chain dummy_anon_vma_chain = {
> +		.anon_vma = &dummy_anon_vma,
> +	};
> +	struct vm_area_struct *vma_prev, *vma_next, *vma;
> +
> +	reset_dummy_anon_vma();
> +
> +	/*
> +	 * Expanding a VMA delete the next one duplicates next's anon_vma and
> +	 * assigns it to the expanded VMA.
> +	 *
> +	 * This covers new VMA merging, as these operations amount to a VMA
> +	 * expand.
> +	 */
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma_next = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags);
> +	vma_next->anon_vma = &dummy_anon_vma;
> +
> +	vmg_set_range(&vmg, 0, 0x5000, 0, flags);
> +	vmg.vma = vma_prev;
> +	vmg.next = vma_next;
> +
> +	ASSERT_EQ(expand_existing(&vmg), 0);
> +
> +	/* Will have been cloned. */
> +	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_prev->anon_vma->was_cloned);
> +
> +	/* Cleanup ready for next run. */
> +	cleanup_mm(&mm, &vmi);
> +
> +	/*
> +	 * next has anon_vma, we assign to prev.
> +	 *
> +	 *         |<----->|
> +	 * |-------*********-------|
> +	 *   prev     vma     next
> +	 *  extend   delete  delete
> +	 */
> +
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags);
> +	vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, flags);
> +
> +	/* Initialise avc so mergeability check passes. */
> +	INIT_LIST_HEAD(&vma_next->anon_vma_chain);
> +	list_add(&dummy_anon_vma_chain.same_vma, &vma_next->anon_vma_chain);
> +
> +	vma_next->anon_vma = &dummy_anon_vma;
> +	vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags);
> +	vmg.prev = vma_prev;
> +	vmg.vma = vma;
> +
> +	ASSERT_EQ(merge_existing(&vmg), vma_prev);
> +
> +	ASSERT_EQ(vma_prev->vm_start, 0);
> +	ASSERT_EQ(vma_prev->vm_end, 0x8000);
> +
> +	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_prev->anon_vma->was_cloned);
> +
> +	cleanup_mm(&mm, &vmi);
> +
> +	/*
> +	 * vma has anon_vma, we assign to prev.
> +	 *
> +	 *         |<----->|
> +	 * |-------*********-------|
> +	 *   prev     vma     next
> +	 *  extend   delete  delete
> +	 */
> +
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags);
> +	vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, flags);
> +
> +	vma->anon_vma = &dummy_anon_vma;
> +	vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags);
> +	vmg.prev = vma_prev;
> +	vmg.vma = vma;
> +
> +	ASSERT_EQ(merge_existing(&vmg), vma_prev);
> +
> +	ASSERT_EQ(vma_prev->vm_start, 0);
> +	ASSERT_EQ(vma_prev->vm_end, 0x8000);
> +
> +	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_prev->anon_vma->was_cloned);
> +
> +	cleanup_mm(&mm, &vmi);
> +
> +	/*
> +	 * vma has anon_vma, we assign to prev.
> +	 *
> +	 *         |<----->|
> +	 * |-------*************
> +	 *   prev       vma
> +	 *  extend shrink/delete
> +	 */
> +
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x8000, 3, flags);
> +
> +	vma->anon_vma = &dummy_anon_vma;
> +	vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags);
> +	vmg.prev = vma_prev;
> +	vmg.vma = vma;
> +
> +	ASSERT_EQ(merge_existing(&vmg), vma_prev);
> +
> +	ASSERT_EQ(vma_prev->vm_start, 0);
> +	ASSERT_EQ(vma_prev->vm_end, 0x5000);
> +
> +	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_prev->anon_vma->was_cloned);
> +
> +	cleanup_mm(&mm, &vmi);
> +
> +	/*
> +	 * vma has anon_vma, we assign to next.
> +	 *
> +	 *     |<----->|
> +	 * *************-------|
> +	 *      vma       next
> +	 * shrink/delete extend
> +	 */
> +
> +	vma = alloc_and_link_vma(&mm, 0, 0x5000, 0, flags);
> +	vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, flags);
> +
> +	vma->anon_vma = &dummy_anon_vma;
> +	vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags);
> +	vmg.prev = vma;
> +	vmg.vma = vma;
> +
> +	ASSERT_EQ(merge_existing(&vmg), vma_next);
> +
> +	ASSERT_EQ(vma_next->vm_start, 0x3000);
> +	ASSERT_EQ(vma_next->vm_end, 0x8000);
> +
> +	ASSERT_EQ(vma_next->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(vma_next->anon_vma->was_cloned);
> +
> +	cleanup_mm(&mm, &vmi);
> +	return true;
> +}
> +
> +static bool test_vmi_prealloc_fail(void)
> +{
> +	unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> +	struct mm_struct mm = {};
> +	VMA_ITERATOR(vmi, &mm, 0);
> +	struct vma_merge_struct vmg = {
> +		.mm = &mm,
> +		.vmi = &vmi,
> +	};
> +	struct vm_area_struct *vma_prev, *vma;
> +
> +	/*
> +	 * We are merging vma into prev, with vma possessing an anon_vma, which
> +	 * will be duplicated. We cause the vmi preallocation to fail and assert
> +	 * the duplicated anon_vma is unlinked.
> +	 */
> +
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags);
> +	vma->anon_vma = &dummy_anon_vma;
> +
> +	vmg_set_range(&vmg, 0x3000, 0x5000, 3, flags);
> +	vmg.prev = vma_prev;
> +	vmg.vma = vma;
> +
> +	fail_prealloc = true;
> +
> +	/* This will cause the merge to fail. */
> +	ASSERT_EQ(merge_existing(&vmg), NULL);
> +	/* We will already have assigned the anon_vma. */
> +	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
> +	/* And it was both cloned and unlinked. */
> +	ASSERT_TRUE(dummy_anon_vma.was_cloned);
> +	ASSERT_TRUE(dummy_anon_vma.was_unlinked);
> +
> +	cleanup_mm(&mm, &vmi); /* Resets fail_prealloc too. */
> +
> +	/*
> +	 * We repeat the same operation for expanding a VMA, which is what new
> +	 * VMA merging ultimately uses too. This asserts that unlinking is
> +	 * performed in this case too.
> +	 */
> +
> +	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, flags);
> +	vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags);
> +	vma->anon_vma = &dummy_anon_vma;
> +
> +	vmg_set_range(&vmg, 0, 0x5000, 3, flags);
> +	vmg.vma = vma_prev;
> +	vmg.next = vma;
> +
> +	fail_prealloc = true;
> +	ASSERT_EQ(expand_existing(&vmg), -ENOMEM);
> +
> +	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
> +	ASSERT_TRUE(dummy_anon_vma.was_cloned);
> +	ASSERT_TRUE(dummy_anon_vma.was_unlinked);
> +
> +	cleanup_mm(&mm, &vmi);
> +	return true;
> +}
> +
>  int main(void)
>  {
>  	int num_tests = 0, num_fail = 0;
> @@ -193,11 +1391,21 @@ int main(void)
>  		}							\
>  	} while (0)
>  
> +	/* Very simple tests to kick the tyres. */
>  	TEST(simple_merge);
>  	TEST(simple_modify);
>  	TEST(simple_expand);
>  	TEST(simple_shrink);
>  
> +	TEST(merge_new);
> +	TEST(vma_merge_special_flags);
> +	TEST(vma_merge_with_close);
> +	TEST(vma_merge_new_with_close);
> +	TEST(merge_existing);
> +	TEST(anon_vma_non_mergeable);
> +	TEST(dup_anon_vma);
> +	TEST(vmi_prealloc_fail);
> +
>  #undef TEST
>  
>  	printf("%d tests run, %d passed, %d failed.\n",
> diff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h
> index 093560e5b2ac..a3c262c6eb73 100644
> --- a/tools/testing/vma/vma_internal.h
> +++ b/tools/testing/vma/vma_internal.h
> @@ -81,8 +81,6 @@
>  
>  #define AS_MM_ALL_LOCKS 2
>  
> -#define current NULL
> -
>  /* We hardcode this for now. */
>  #define sysctl_max_map_count 0x1000000UL
>  
> @@ -92,6 +90,12 @@ typedef struct pgprot { pgprotval_t pgprot; } pgprot_t;
>  typedef unsigned long vm_flags_t;
>  typedef __bitwise unsigned int vm_fault_t;
>  
> +/*
> + * The shared stubs do not implement this, it amounts to an fprintf(STDERR,...)
> + * either way :)
> + */
> +#define pr_warn_once pr_err
> +
>  typedef struct refcount_struct {
>  	atomic_t refs;
>  } refcount_t;
> @@ -100,9 +104,30 @@ struct kref {
>  	refcount_t refcount;
>  };
>  
> +/*
> + * Define the task command name length as enum, then it can be visible to
> + * BPF programs.
> + */
> +enum {
> +	TASK_COMM_LEN = 16,
> +};
> +
> +struct task_struct {
> +	char comm[TASK_COMM_LEN];
> +	pid_t pid;
> +	struct mm_struct *mm;
> +};
> +
> +struct task_struct *get_current(void);
> +#define current get_current()
> +
>  struct anon_vma {
>  	struct anon_vma *root;
>  	struct rb_root_cached rb_root;
> +
> +	/* Test fields. */
> +	bool was_cloned;
> +	bool was_unlinked;
>  };
>  
>  struct anon_vma_chain {
> @@ -682,13 +707,21 @@ static inline int vma_dup_policy(struct vm_area_struct *, struct vm_area_struct
>  	return 0;
>  }
>  
> -static inline int anon_vma_clone(struct vm_area_struct *, struct vm_area_struct *)
> +static inline int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src)
>  {
> +	/* For testing purposes. We indicate that an anon_vma has been cloned. */
> +	if (src->anon_vma != NULL) {
> +		dst->anon_vma = src->anon_vma;
> +		dst->anon_vma->was_cloned = true;
> +	}
> +
>  	return 0;
>  }
>  
> -static inline void vma_start_write(struct vm_area_struct *)
> +static inline void vma_start_write(struct vm_area_struct *vma)
>  {
> +	/* Used to indicate to tests that a write operation has begun. */
> +	vma->vm_lock_seq++;
>  }
>  
>  static inline void vma_adjust_trans_huge(struct vm_area_struct *vma,
> @@ -759,8 +792,10 @@ static inline void vma_assert_write_locked(struct vm_area_struct *)
>  {
>  }
>  
> -static inline void unlink_anon_vmas(struct vm_area_struct *)
> +static inline void unlink_anon_vmas(struct vm_area_struct *vma)
>  {
> +	/* For testing purposes, indicate that the anon_vma was unlinked. */
> +	vma->anon_vma->was_unlinked = true;
>  }
>  
>  static inline void anon_vma_unlock_write(struct anon_vma *)
> -- 
> 2.46.0
> 




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux