Commit 29bef05e authored by Lorenzo Stoakes's avatar Lorenzo Stoakes Committed by Andrew Morton
Browse files

tools/testing/vma: add VMA sticky userland tests

Modify existing merge new/existing userland VMA tests to assert that
sticky VMA flags behave as expected.

We do so by generating every possible permutation of VMAs being
manipulated being sticky/not sticky and asserting that VMA flags with this
property retain are retained upon merge.

Link: https://lkml.kernel.org/r/5e2c7244485867befd052f8afc8188be6a4be670.1763460113.git.lorenzo.stoakes@oracle.com


Signed-off-by: default avatarLorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Andrei Vagin <avagin@gmail.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Barry Song <baohua@kernel.org>
Cc: David Hildenbrand (Red Hat) <david@kernel.org>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Jann Horn <jannh@google.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Nico Pache <npache@redhat.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Zi Yan <ziy@nvidia.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 49e14dab
Loading
Loading
Loading
Loading
+79 −10
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ static struct anon_vma dummy_anon_vma;
#define ASSERT_EQ(_val1, _val2) ASSERT_TRUE((_val1) == (_val2))
#define ASSERT_NE(_val1, _val2) ASSERT_TRUE((_val1) != (_val2))

#define IS_SET(_val, _flags) ((_val & _flags) == _flags)

static struct task_struct __current;

struct task_struct *get_current(void)
@@ -442,7 +444,7 @@ static bool test_simple_shrink(void)
	return true;
}

static bool test_merge_new(void)
static bool __test_merge_new(bool is_sticky, bool a_is_sticky, bool b_is_sticky, bool c_is_sticky)
{
	vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
	struct mm_struct mm = {};
@@ -470,23 +472,32 @@ static bool test_merge_new(void)
	struct vm_area_struct *vma, *vma_a, *vma_b, *vma_c, *vma_d;
	bool merged;

	if (is_sticky)
		vm_flags |= VM_STICKY;

	/*
	 * 0123456789abc
	 * AA B       CC
	 */
	vma_a = alloc_and_link_vma(&mm, 0, 0x2000, 0, vm_flags);
	ASSERT_NE(vma_a, NULL);
	if (a_is_sticky)
		vm_flags_set(vma_a, VM_STICKY);
	/* 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, vm_flags);
	ASSERT_NE(vma_b, NULL);
	if (b_is_sticky)
		vm_flags_set(vma_b, VM_STICKY);
	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, vm_flags);
	ASSERT_NE(vma_c, NULL);
	if (c_is_sticky)
		vm_flags_set(vma_c, VM_STICKY);
	INIT_LIST_HEAD(&vma_c->anon_vma_chain);
	list_add(&dummy_anon_vma_chain_c.same_vma, &vma_c->anon_vma_chain);

@@ -521,6 +532,8 @@ static bool test_merge_new(void)
	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
	ASSERT_TRUE(vma_write_started(vma));
	ASSERT_EQ(mm.map_count, 3);
	if (is_sticky || a_is_sticky || b_is_sticky)
		ASSERT_TRUE(IS_SET(vma->vm_flags, VM_STICKY));

	/*
	 * Merge to PREVIOUS VMA.
@@ -538,6 +551,8 @@ static bool test_merge_new(void)
	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
	ASSERT_TRUE(vma_write_started(vma));
	ASSERT_EQ(mm.map_count, 3);
	if (is_sticky || a_is_sticky)
		ASSERT_TRUE(IS_SET(vma->vm_flags, VM_STICKY));

	/*
	 * Merge to NEXT VMA.
@@ -557,6 +572,8 @@ static bool test_merge_new(void)
	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
	ASSERT_TRUE(vma_write_started(vma));
	ASSERT_EQ(mm.map_count, 3);
	if (is_sticky) /* D uses is_sticky. */
		ASSERT_TRUE(IS_SET(vma->vm_flags, VM_STICKY));

	/*
	 * Merge BOTH sides.
@@ -575,6 +592,8 @@ static bool test_merge_new(void)
	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
	ASSERT_TRUE(vma_write_started(vma));
	ASSERT_EQ(mm.map_count, 2);
	if (is_sticky || a_is_sticky)
		ASSERT_TRUE(IS_SET(vma->vm_flags, VM_STICKY));

	/*
	 * Merge to NEXT VMA.
@@ -593,6 +612,8 @@ static bool test_merge_new(void)
	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
	ASSERT_TRUE(vma_write_started(vma));
	ASSERT_EQ(mm.map_count, 2);
	if (is_sticky || c_is_sticky)
		ASSERT_TRUE(IS_SET(vma->vm_flags, VM_STICKY));

	/*
	 * Merge BOTH sides.
@@ -610,6 +631,8 @@ static bool test_merge_new(void)
	ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
	ASSERT_TRUE(vma_write_started(vma));
	ASSERT_EQ(mm.map_count, 1);
	if (is_sticky || a_is_sticky || c_is_sticky)
		ASSERT_TRUE(IS_SET(vma->vm_flags, VM_STICKY));

	/*
	 * Final state.
@@ -638,6 +661,20 @@ static bool test_merge_new(void)
	return true;
}

static bool test_merge_new(void)
{
	int i, j, k, l;

	/* Generate every possible permutation of sticky flags. */
	for (i = 0; i < 2; i++)
		for (j = 0; j < 2; j++)
			for (k = 0; k < 2; k++)
				for (l = 0; l < 2; l++)
					ASSERT_TRUE(__test_merge_new(i, j, k, l));

	return true;
}

static bool test_vma_merge_special_flags(void)
{
	vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
@@ -974,9 +1011,11 @@ static bool test_vma_merge_new_with_close(void)
	return true;
}

static bool test_merge_existing(void)
static bool __test_merge_existing(bool prev_is_sticky, bool middle_is_sticky, bool next_is_sticky)
{
	vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
	vm_flags_t prev_flags = vm_flags;
	vm_flags_t next_flags = vm_flags;
	struct mm_struct mm = {};
	VMA_ITERATOR(vmi, &mm, 0);
	struct vm_area_struct *vma, *vma_prev, *vma_next;
@@ -989,6 +1028,13 @@ static bool test_merge_existing(void)
	};
	struct anon_vma_chain avc = {};

	if (prev_is_sticky)
		prev_flags |= VM_STICKY;
	if (middle_is_sticky)
		vm_flags |= VM_STICKY;
	if (next_is_sticky)
		next_flags |= VM_STICKY;

	/*
	 * Merge right case - partial span.
	 *
@@ -1001,7 +1047,7 @@ static bool test_merge_existing(void)
	 */
	vma = alloc_and_link_vma(&mm, 0x2000, 0x6000, 2, vm_flags);
	vma->vm_ops = &vm_ops; /* This should have no impact. */
	vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, vm_flags);
	vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, next_flags);
	vma_next->vm_ops = &vm_ops; /* This should have no impact. */
	vmg_set_range_anon_vma(&vmg, 0x3000, 0x6000, 3, vm_flags, &dummy_anon_vma);
	vmg.middle = vma;
@@ -1019,6 +1065,8 @@ static bool test_merge_existing(void)
	ASSERT_TRUE(vma_write_started(vma));
	ASSERT_TRUE(vma_write_started(vma_next));
	ASSERT_EQ(mm.map_count, 2);
	if (middle_is_sticky || next_is_sticky)
		ASSERT_TRUE(IS_SET(vma_next->vm_flags, VM_STICKY));

	/* Clear down and reset. */
	ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
@@ -1034,7 +1082,7 @@ static bool test_merge_existing(void)
	 *   NNNNNNN
	 */
	vma = alloc_and_link_vma(&mm, 0x2000, 0x6000, 2, vm_flags);
	vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, vm_flags);
	vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, next_flags);
	vma_next->vm_ops = &vm_ops; /* This should have no impact. */
	vmg_set_range_anon_vma(&vmg, 0x2000, 0x6000, 2, vm_flags, &dummy_anon_vma);
	vmg.middle = vma;
@@ -1047,6 +1095,8 @@ static bool test_merge_existing(void)
	ASSERT_EQ(vma_next->anon_vma, &dummy_anon_vma);
	ASSERT_TRUE(vma_write_started(vma_next));
	ASSERT_EQ(mm.map_count, 1);
	if (middle_is_sticky || next_is_sticky)
		ASSERT_TRUE(IS_SET(vma_next->vm_flags, VM_STICKY));

	/* Clear down and reset. We should have deleted vma. */
	ASSERT_EQ(cleanup_mm(&mm, &vmi), 1);
@@ -1061,7 +1111,7 @@ static bool test_merge_existing(void)
	 * 0123456789
	 * PPPPPPV
	 */
	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags);
	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, prev_flags);
	vma_prev->vm_ops = &vm_ops; /* This should have no impact. */
	vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vm_flags);
	vma->vm_ops = &vm_ops; /* This should have no impact. */
@@ -1081,6 +1131,8 @@ static bool test_merge_existing(void)
	ASSERT_TRUE(vma_write_started(vma_prev));
	ASSERT_TRUE(vma_write_started(vma));
	ASSERT_EQ(mm.map_count, 2);
	if (prev_is_sticky || middle_is_sticky)
		ASSERT_TRUE(IS_SET(vma_prev->vm_flags, VM_STICKY));

	/* Clear down and reset. */
	ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
@@ -1095,7 +1147,7 @@ static bool test_merge_existing(void)
	 * 0123456789
	 * PPPPPPP
	 */
	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags);
	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, prev_flags);
	vma_prev->vm_ops = &vm_ops; /* This should have no impact. */
	vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vm_flags);
	vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, vm_flags, &dummy_anon_vma);
@@ -1110,6 +1162,8 @@ static bool test_merge_existing(void)
	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
	ASSERT_TRUE(vma_write_started(vma_prev));
	ASSERT_EQ(mm.map_count, 1);
	if (prev_is_sticky || middle_is_sticky)
		ASSERT_TRUE(IS_SET(vma_prev->vm_flags, VM_STICKY));

	/* Clear down and reset. We should have deleted vma. */
	ASSERT_EQ(cleanup_mm(&mm, &vmi), 1);
@@ -1124,10 +1178,10 @@ static bool test_merge_existing(void)
	 * 0123456789
	 * PPPPPPPPPP
	 */
	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags);
	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, prev_flags);
	vma_prev->vm_ops = &vm_ops; /* This should have no impact. */
	vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vm_flags);
	vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, vm_flags);
	vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, next_flags);
	vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, vm_flags, &dummy_anon_vma);
	vmg.prev = vma_prev;
	vmg.middle = vma;
@@ -1140,6 +1194,8 @@ static bool test_merge_existing(void)
	ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
	ASSERT_TRUE(vma_write_started(vma_prev));
	ASSERT_EQ(mm.map_count, 1);
	if (prev_is_sticky || middle_is_sticky || next_is_sticky)
		ASSERT_TRUE(IS_SET(vma_prev->vm_flags, VM_STICKY));

	/* Clear down and reset. We should have deleted prev and next. */
	ASSERT_EQ(cleanup_mm(&mm, &vmi), 1);
@@ -1159,9 +1215,9 @@ static bool test_merge_existing(void)
	 * PPPVVVVVNNN
	 */

	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vm_flags);
	vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, prev_flags);
	vma = alloc_and_link_vma(&mm, 0x3000, 0x8000, 3, vm_flags);
	vma_next = alloc_and_link_vma(&mm, 0x8000, 0xa000, 8, vm_flags);
	vma_next = alloc_and_link_vma(&mm, 0x8000, 0xa000, 8, next_flags);

	vmg_set_range(&vmg, 0x4000, 0x5000, 4, vm_flags);
	vmg.prev = vma;
@@ -1204,6 +1260,19 @@ static bool test_merge_existing(void)
	return true;
}

static bool test_merge_existing(void)
{
	int i, j, k;

	/* Generate every possible permutation of sticky flags. */
	for (i = 0; i < 2; i++)
		for (j = 0; j < 2; j++)
			for (k = 0; k < 2; k++)
				ASSERT_TRUE(__test_merge_existing(i, j, k));

	return true;
}

static bool test_anon_vma_non_mergeable(void)
{
	vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;