* 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 >