xref: /linux/drivers/gpu/tests/gpu_buddy_test.c (revision f5bd7da05a5988506dedcb3e67aecb3a13a4cdf0)
14a9671a0SJoel Fernandes // SPDX-License-Identifier: MIT
24a9671a0SJoel Fernandes /*
34a9671a0SJoel Fernandes  * Copyright © 2019 Intel Corporation
44a9671a0SJoel Fernandes  * Copyright © 2022 Maíra Canal <mairacanal@riseup.net>
54a9671a0SJoel Fernandes  */
64a9671a0SJoel Fernandes 
74a9671a0SJoel Fernandes #include <kunit/test.h>
84a9671a0SJoel Fernandes 
94a9671a0SJoel Fernandes #include <linux/prime_numbers.h>
104a9671a0SJoel Fernandes #include <linux/sched/signal.h>
114a9671a0SJoel Fernandes #include <linux/sizes.h>
124a9671a0SJoel Fernandes 
134a9671a0SJoel Fernandes #include <linux/gpu_buddy.h>
144a9671a0SJoel Fernandes 
154a9671a0SJoel Fernandes #include "gpu_random.h"
164a9671a0SJoel Fernandes 
174a9671a0SJoel Fernandes static unsigned int random_seed;
184a9671a0SJoel Fernandes 
194a9671a0SJoel Fernandes static inline u64 get_size(int order, u64 chunk_size)
204a9671a0SJoel Fernandes {
214a9671a0SJoel Fernandes 	return (1 << order) * chunk_size;
224a9671a0SJoel Fernandes }
234a9671a0SJoel Fernandes 
24*f5bd7da0SArunpravin Paneer Selvam static void gpu_test_buddy_subtree_offset_alignment_stress(struct kunit *test)
25*f5bd7da0SArunpravin Paneer Selvam {
26*f5bd7da0SArunpravin Paneer Selvam 	struct gpu_buddy_block *block;
27*f5bd7da0SArunpravin Paneer Selvam 	struct rb_node *node = NULL;
28*f5bd7da0SArunpravin Paneer Selvam 	const u64 mm_size = SZ_2M;
29*f5bd7da0SArunpravin Paneer Selvam 	const u64 alignments[] = {
30*f5bd7da0SArunpravin Paneer Selvam 		SZ_1M,
31*f5bd7da0SArunpravin Paneer Selvam 		SZ_512K,
32*f5bd7da0SArunpravin Paneer Selvam 		SZ_256K,
33*f5bd7da0SArunpravin Paneer Selvam 		SZ_128K,
34*f5bd7da0SArunpravin Paneer Selvam 		SZ_64K,
35*f5bd7da0SArunpravin Paneer Selvam 		SZ_32K,
36*f5bd7da0SArunpravin Paneer Selvam 		SZ_16K,
37*f5bd7da0SArunpravin Paneer Selvam 		SZ_8K,
38*f5bd7da0SArunpravin Paneer Selvam 	};
39*f5bd7da0SArunpravin Paneer Selvam 	struct list_head allocated[ARRAY_SIZE(alignments)];
40*f5bd7da0SArunpravin Paneer Selvam 	unsigned int i, max_subtree_align = 0;
41*f5bd7da0SArunpravin Paneer Selvam 	int ret, tree, order;
42*f5bd7da0SArunpravin Paneer Selvam 	struct gpu_buddy mm;
43*f5bd7da0SArunpravin Paneer Selvam 
44*f5bd7da0SArunpravin Paneer Selvam 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K),
45*f5bd7da0SArunpravin Paneer Selvam 			       "buddy_init failed\n");
46*f5bd7da0SArunpravin Paneer Selvam 
47*f5bd7da0SArunpravin Paneer Selvam 	for (i = 0; i < ARRAY_SIZE(allocated); i++)
48*f5bd7da0SArunpravin Paneer Selvam 		INIT_LIST_HEAD(&allocated[i]);
49*f5bd7da0SArunpravin Paneer Selvam 
50*f5bd7da0SArunpravin Paneer Selvam 	/*
51*f5bd7da0SArunpravin Paneer Selvam 	 * Exercise subtree_max_alignment tracking by allocating blocks with descending
52*f5bd7da0SArunpravin Paneer Selvam 	 * alignment constraints and freeing them in reverse order. This verifies that
53*f5bd7da0SArunpravin Paneer Selvam 	 * free-tree augmentation correctly propagates the maximum offset alignment
54*f5bd7da0SArunpravin Paneer Selvam 	 * present in each subtree at every stage.
55*f5bd7da0SArunpravin Paneer Selvam 	 */
56*f5bd7da0SArunpravin Paneer Selvam 
57*f5bd7da0SArunpravin Paneer Selvam 	for (i = 0; i < ARRAY_SIZE(alignments); i++) {
58*f5bd7da0SArunpravin Paneer Selvam 		struct gpu_buddy_block *root = NULL;
59*f5bd7da0SArunpravin Paneer Selvam 		unsigned int expected;
60*f5bd7da0SArunpravin Paneer Selvam 		u64 align;
61*f5bd7da0SArunpravin Paneer Selvam 
62*f5bd7da0SArunpravin Paneer Selvam 		align = alignments[i];
63*f5bd7da0SArunpravin Paneer Selvam 		expected = ilog2(align) - 1;
64*f5bd7da0SArunpravin Paneer Selvam 
65*f5bd7da0SArunpravin Paneer Selvam 		for (;;) {
66*f5bd7da0SArunpravin Paneer Selvam 			ret = gpu_buddy_alloc_blocks(&mm,
67*f5bd7da0SArunpravin Paneer Selvam 						     0, mm_size,
68*f5bd7da0SArunpravin Paneer Selvam 						     SZ_4K, align,
69*f5bd7da0SArunpravin Paneer Selvam 						     &allocated[i],
70*f5bd7da0SArunpravin Paneer Selvam 						     0);
71*f5bd7da0SArunpravin Paneer Selvam 			if (ret)
72*f5bd7da0SArunpravin Paneer Selvam 				break;
73*f5bd7da0SArunpravin Paneer Selvam 
74*f5bd7da0SArunpravin Paneer Selvam 			block = list_last_entry(&allocated[i],
75*f5bd7da0SArunpravin Paneer Selvam 						struct gpu_buddy_block,
76*f5bd7da0SArunpravin Paneer Selvam 						link);
77*f5bd7da0SArunpravin Paneer Selvam 			KUNIT_EXPECT_TRUE(test, IS_ALIGNED(gpu_buddy_block_offset(block), align));
78*f5bd7da0SArunpravin Paneer Selvam 		}
79*f5bd7da0SArunpravin Paneer Selvam 
80*f5bd7da0SArunpravin Paneer Selvam 		for (order = mm.max_order; order >= 0 && !root; order--) {
81*f5bd7da0SArunpravin Paneer Selvam 			for (tree = 0; tree < 2; tree++) {
82*f5bd7da0SArunpravin Paneer Selvam 				node = mm.free_trees[tree][order].rb_node;
83*f5bd7da0SArunpravin Paneer Selvam 				if (node) {
84*f5bd7da0SArunpravin Paneer Selvam 					root = container_of(node,
85*f5bd7da0SArunpravin Paneer Selvam 							    struct gpu_buddy_block,
86*f5bd7da0SArunpravin Paneer Selvam 							    rb);
87*f5bd7da0SArunpravin Paneer Selvam 					break;
88*f5bd7da0SArunpravin Paneer Selvam 				}
89*f5bd7da0SArunpravin Paneer Selvam 			}
90*f5bd7da0SArunpravin Paneer Selvam 		}
91*f5bd7da0SArunpravin Paneer Selvam 
92*f5bd7da0SArunpravin Paneer Selvam 		KUNIT_ASSERT_NOT_NULL(test, root);
93*f5bd7da0SArunpravin Paneer Selvam 		KUNIT_EXPECT_EQ(test, root->subtree_max_alignment, expected);
94*f5bd7da0SArunpravin Paneer Selvam 	}
95*f5bd7da0SArunpravin Paneer Selvam 
96*f5bd7da0SArunpravin Paneer Selvam 	for (i = ARRAY_SIZE(alignments); i-- > 0; ) {
97*f5bd7da0SArunpravin Paneer Selvam 		gpu_buddy_free_list(&mm, &allocated[i], 0);
98*f5bd7da0SArunpravin Paneer Selvam 
99*f5bd7da0SArunpravin Paneer Selvam 		for (order = 0; order <= mm.max_order; order++) {
100*f5bd7da0SArunpravin Paneer Selvam 			for (tree = 0; tree < 2; tree++) {
101*f5bd7da0SArunpravin Paneer Selvam 				node = mm.free_trees[tree][order].rb_node;
102*f5bd7da0SArunpravin Paneer Selvam 				if (!node)
103*f5bd7da0SArunpravin Paneer Selvam 					continue;
104*f5bd7da0SArunpravin Paneer Selvam 
105*f5bd7da0SArunpravin Paneer Selvam 				block = container_of(node, struct gpu_buddy_block, rb);
106*f5bd7da0SArunpravin Paneer Selvam 				max_subtree_align = max(max_subtree_align,
107*f5bd7da0SArunpravin Paneer Selvam 							block->subtree_max_alignment);
108*f5bd7da0SArunpravin Paneer Selvam 			}
109*f5bd7da0SArunpravin Paneer Selvam 		}
110*f5bd7da0SArunpravin Paneer Selvam 
111*f5bd7da0SArunpravin Paneer Selvam 		KUNIT_EXPECT_GE(test, max_subtree_align, ilog2(alignments[i]));
112*f5bd7da0SArunpravin Paneer Selvam 	}
113*f5bd7da0SArunpravin Paneer Selvam 
114*f5bd7da0SArunpravin Paneer Selvam 	gpu_buddy_fini(&mm);
115*f5bd7da0SArunpravin Paneer Selvam }
116*f5bd7da0SArunpravin Paneer Selvam 
117*f5bd7da0SArunpravin Paneer Selvam static void gpu_test_buddy_offset_aligned_allocation(struct kunit *test)
118*f5bd7da0SArunpravin Paneer Selvam {
119*f5bd7da0SArunpravin Paneer Selvam 	struct gpu_buddy_block *block, *tmp;
120*f5bd7da0SArunpravin Paneer Selvam 	int num_blocks, i, count = 0;
121*f5bd7da0SArunpravin Paneer Selvam 	LIST_HEAD(allocated);
122*f5bd7da0SArunpravin Paneer Selvam 	struct gpu_buddy mm;
123*f5bd7da0SArunpravin Paneer Selvam 	u64 mm_size = SZ_4M;
124*f5bd7da0SArunpravin Paneer Selvam 	LIST_HEAD(freed);
125*f5bd7da0SArunpravin Paneer Selvam 
126*f5bd7da0SArunpravin Paneer Selvam 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K),
127*f5bd7da0SArunpravin Paneer Selvam 			       "buddy_init failed\n");
128*f5bd7da0SArunpravin Paneer Selvam 
129*f5bd7da0SArunpravin Paneer Selvam 	num_blocks = mm_size / SZ_256K;
130*f5bd7da0SArunpravin Paneer Selvam 	/*
131*f5bd7da0SArunpravin Paneer Selvam 	 * Allocate multiple sizes under a fixed offset alignment.
132*f5bd7da0SArunpravin Paneer Selvam 	 * Ensures alignment handling is independent of allocation size and
133*f5bd7da0SArunpravin Paneer Selvam 	 * exercises subtree max-alignment pruning for small requests.
134*f5bd7da0SArunpravin Paneer Selvam 	 */
135*f5bd7da0SArunpravin Paneer Selvam 	for (i = 0; i < num_blocks; i++)
136*f5bd7da0SArunpravin Paneer Selvam 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_8K, SZ_256K,
137*f5bd7da0SArunpravin Paneer Selvam 								    &allocated, 0),
138*f5bd7da0SArunpravin Paneer Selvam 					"buddy_alloc hit an error size=%u\n", SZ_8K);
139*f5bd7da0SArunpravin Paneer Selvam 
140*f5bd7da0SArunpravin Paneer Selvam 	list_for_each_entry(block, &allocated, link) {
141*f5bd7da0SArunpravin Paneer Selvam 		/* Ensure the allocated block uses the expected 8 KB size */
142*f5bd7da0SArunpravin Paneer Selvam 		KUNIT_EXPECT_EQ(test, gpu_buddy_block_size(&mm, block), SZ_8K);
143*f5bd7da0SArunpravin Paneer Selvam 		/* Ensure the block starts at a 256 KB-aligned offset for proper alignment */
144*f5bd7da0SArunpravin Paneer Selvam 		KUNIT_EXPECT_TRUE(test, IS_ALIGNED(gpu_buddy_block_offset(block), SZ_256K));
145*f5bd7da0SArunpravin Paneer Selvam 	}
146*f5bd7da0SArunpravin Paneer Selvam 	gpu_buddy_free_list(&mm, &allocated, 0);
147*f5bd7da0SArunpravin Paneer Selvam 
148*f5bd7da0SArunpravin Paneer Selvam 	for (i = 0; i < num_blocks; i++)
149*f5bd7da0SArunpravin Paneer Selvam 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_16K, SZ_256K,
150*f5bd7da0SArunpravin Paneer Selvam 								    &allocated, 0),
151*f5bd7da0SArunpravin Paneer Selvam 					"buddy_alloc hit an error size=%u\n", SZ_16K);
152*f5bd7da0SArunpravin Paneer Selvam 
153*f5bd7da0SArunpravin Paneer Selvam 	list_for_each_entry(block, &allocated, link) {
154*f5bd7da0SArunpravin Paneer Selvam 		/* Ensure the allocated block uses the expected 16 KB size */
155*f5bd7da0SArunpravin Paneer Selvam 		KUNIT_EXPECT_EQ(test, gpu_buddy_block_size(&mm, block), SZ_16K);
156*f5bd7da0SArunpravin Paneer Selvam 		/* Ensure the block starts at a 256 KB-aligned offset for proper alignment */
157*f5bd7da0SArunpravin Paneer Selvam 		KUNIT_EXPECT_TRUE(test, IS_ALIGNED(gpu_buddy_block_offset(block), SZ_256K));
158*f5bd7da0SArunpravin Paneer Selvam 	}
159*f5bd7da0SArunpravin Paneer Selvam 
160*f5bd7da0SArunpravin Paneer Selvam 	/*
161*f5bd7da0SArunpravin Paneer Selvam 	 * Free alternating aligned blocks to introduce fragmentation.
162*f5bd7da0SArunpravin Paneer Selvam 	 * Ensures offset-aligned allocations remain valid after frees and
163*f5bd7da0SArunpravin Paneer Selvam 	 * verifies subtree max-alignment metadata is correctly maintained.
164*f5bd7da0SArunpravin Paneer Selvam 	 */
165*f5bd7da0SArunpravin Paneer Selvam 	list_for_each_entry_safe(block, tmp, &allocated, link) {
166*f5bd7da0SArunpravin Paneer Selvam 		if (count % 2 == 0)
167*f5bd7da0SArunpravin Paneer Selvam 			list_move_tail(&block->link, &freed);
168*f5bd7da0SArunpravin Paneer Selvam 		count++;
169*f5bd7da0SArunpravin Paneer Selvam 	}
170*f5bd7da0SArunpravin Paneer Selvam 	gpu_buddy_free_list(&mm, &freed, 0);
171*f5bd7da0SArunpravin Paneer Selvam 
172*f5bd7da0SArunpravin Paneer Selvam 	for (i = 0; i < num_blocks / 2; i++)
173*f5bd7da0SArunpravin Paneer Selvam 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_16K, SZ_256K,
174*f5bd7da0SArunpravin Paneer Selvam 								    &allocated, 0),
175*f5bd7da0SArunpravin Paneer Selvam 					"buddy_alloc hit an error size=%u\n", SZ_16K);
176*f5bd7da0SArunpravin Paneer Selvam 
177*f5bd7da0SArunpravin Paneer Selvam 	/*
178*f5bd7da0SArunpravin Paneer Selvam 	 * Allocate with offset alignment after all slots are used; must fail.
179*f5bd7da0SArunpravin Paneer Selvam 	 * Confirms that no aligned offsets remain.
180*f5bd7da0SArunpravin Paneer Selvam 	 */
181*f5bd7da0SArunpravin Paneer Selvam 	KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_16K, SZ_256K,
182*f5bd7da0SArunpravin Paneer Selvam 							   &allocated, 0),
183*f5bd7da0SArunpravin Paneer Selvam 			       "buddy_alloc hit an error size=%u\n", SZ_16K);
184*f5bd7da0SArunpravin Paneer Selvam 	gpu_buddy_free_list(&mm, &allocated, 0);
185*f5bd7da0SArunpravin Paneer Selvam 	gpu_buddy_fini(&mm);
186*f5bd7da0SArunpravin Paneer Selvam }
187*f5bd7da0SArunpravin Paneer Selvam 
188ba110db8SJoel Fernandes static void gpu_test_buddy_fragmentation_performance(struct kunit *test)
1894a9671a0SJoel Fernandes {
190ba110db8SJoel Fernandes 	struct gpu_buddy_block *block, *tmp;
1914a9671a0SJoel Fernandes 	int num_blocks, i, ret, count = 0;
1924a9671a0SJoel Fernandes 	LIST_HEAD(allocated_blocks);
1934a9671a0SJoel Fernandes 	unsigned long elapsed_ms;
1944a9671a0SJoel Fernandes 	LIST_HEAD(reverse_list);
1954a9671a0SJoel Fernandes 	LIST_HEAD(test_blocks);
1964a9671a0SJoel Fernandes 	LIST_HEAD(clear_list);
1974a9671a0SJoel Fernandes 	LIST_HEAD(dirty_list);
1984a9671a0SJoel Fernandes 	LIST_HEAD(free_list);
199ba110db8SJoel Fernandes 	struct gpu_buddy mm;
2004a9671a0SJoel Fernandes 	u64 mm_size = SZ_4G;
2014a9671a0SJoel Fernandes 	ktime_t start, end;
2024a9671a0SJoel Fernandes 
2034a9671a0SJoel Fernandes 	/*
2044a9671a0SJoel Fernandes 	 * Allocation under severe fragmentation
2054a9671a0SJoel Fernandes 	 *
2064a9671a0SJoel Fernandes 	 * Create severe fragmentation by allocating the entire 4 GiB address space
2074a9671a0SJoel Fernandes 	 * as tiny 8 KiB blocks but forcing a 64 KiB alignment. The resulting pattern
2084a9671a0SJoel Fernandes 	 * leaves many scattered holes. Split the allocations into two groups and
2094a9671a0SJoel Fernandes 	 * return them with different flags to block coalescing, then repeatedly
2104a9671a0SJoel Fernandes 	 * allocate and free 64 KiB blocks while timing the loop. This stresses how
2114a9671a0SJoel Fernandes 	 * quickly the allocator can satisfy larger, aligned requests from a pool of
2124a9671a0SJoel Fernandes 	 * highly fragmented space.
2134a9671a0SJoel Fernandes 	 */
214ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K),
2154a9671a0SJoel Fernandes 			       "buddy_init failed\n");
2164a9671a0SJoel Fernandes 
2174a9671a0SJoel Fernandes 	num_blocks = mm_size / SZ_64K;
2184a9671a0SJoel Fernandes 
2194a9671a0SJoel Fernandes 	start = ktime_get();
2204a9671a0SJoel Fernandes 	/* Allocate with maximum fragmentation - 8K blocks with 64K alignment */
2214a9671a0SJoel Fernandes 	for (i = 0; i < num_blocks; i++)
222ba110db8SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_8K, SZ_64K,
2234a9671a0SJoel Fernandes 								    &allocated_blocks, 0),
2244a9671a0SJoel Fernandes 					"buddy_alloc hit an error size=%u\n", SZ_8K);
2254a9671a0SJoel Fernandes 
2264a9671a0SJoel Fernandes 	list_for_each_entry_safe(block, tmp, &allocated_blocks, link) {
2274a9671a0SJoel Fernandes 		if (count % 4 == 0 || count % 4 == 3)
2284a9671a0SJoel Fernandes 			list_move_tail(&block->link, &clear_list);
2294a9671a0SJoel Fernandes 		else
2304a9671a0SJoel Fernandes 			list_move_tail(&block->link, &dirty_list);
2314a9671a0SJoel Fernandes 		count++;
2324a9671a0SJoel Fernandes 	}
2334a9671a0SJoel Fernandes 
2344a9671a0SJoel Fernandes 	/* Free with different flags to ensure no coalescing */
235ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &clear_list, GPU_BUDDY_CLEARED);
236ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &dirty_list, 0);
2374a9671a0SJoel Fernandes 
2384a9671a0SJoel Fernandes 	for (i = 0; i < num_blocks; i++)
239ba110db8SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_64K, SZ_64K,
2404a9671a0SJoel Fernandes 								    &test_blocks, 0),
2414a9671a0SJoel Fernandes 					"buddy_alloc hit an error size=%u\n", SZ_64K);
242ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &test_blocks, 0);
2434a9671a0SJoel Fernandes 
2444a9671a0SJoel Fernandes 	end = ktime_get();
2454a9671a0SJoel Fernandes 	elapsed_ms = ktime_to_ms(ktime_sub(end, start));
2464a9671a0SJoel Fernandes 
2474a9671a0SJoel Fernandes 	kunit_info(test, "Fragmented allocation took %lu ms\n", elapsed_ms);
2484a9671a0SJoel Fernandes 
249ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
2504a9671a0SJoel Fernandes 
2514a9671a0SJoel Fernandes 	/*
2524a9671a0SJoel Fernandes 	 * Reverse free order under fragmentation
2534a9671a0SJoel Fernandes 	 *
2544a9671a0SJoel Fernandes 	 * Construct a fragmented 4 GiB space by allocating every 8 KiB block with
2554a9671a0SJoel Fernandes 	 * 64 KiB alignment, creating a dense scatter of small regions. Half of the
2564a9671a0SJoel Fernandes 	 * blocks are selectively freed to form sparse gaps, while the remaining
2574a9671a0SJoel Fernandes 	 * allocations are preserved, reordered in reverse, and released back with
2584a9671a0SJoel Fernandes 	 * the cleared flag. This models a pathological reverse-ordered free pattern
2594a9671a0SJoel Fernandes 	 * and measures how quickly the allocator can merge and reclaim space when
2604a9671a0SJoel Fernandes 	 * deallocation occurs in the opposite order of allocation, exposing the
2614a9671a0SJoel Fernandes 	 * cost difference between a linear freelist scan and an ordered tree lookup.
2624a9671a0SJoel Fernandes 	 */
263ba110db8SJoel Fernandes 	ret = gpu_buddy_init(&mm, mm_size, SZ_4K);
2644a9671a0SJoel Fernandes 	KUNIT_ASSERT_EQ(test, ret, 0);
2654a9671a0SJoel Fernandes 
2664a9671a0SJoel Fernandes 	start = ktime_get();
2674a9671a0SJoel Fernandes 	/* Allocate maximum fragmentation */
2684a9671a0SJoel Fernandes 	for (i = 0; i < num_blocks; i++)
269ba110db8SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_8K, SZ_64K,
2704a9671a0SJoel Fernandes 								    &allocated_blocks, 0),
2714a9671a0SJoel Fernandes 					"buddy_alloc hit an error size=%u\n", SZ_8K);
2724a9671a0SJoel Fernandes 
2734a9671a0SJoel Fernandes 	list_for_each_entry_safe(block, tmp, &allocated_blocks, link) {
2744a9671a0SJoel Fernandes 		if (count % 2 == 0)
2754a9671a0SJoel Fernandes 			list_move_tail(&block->link, &free_list);
2764a9671a0SJoel Fernandes 		count++;
2774a9671a0SJoel Fernandes 	}
278ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &free_list, GPU_BUDDY_CLEARED);
2794a9671a0SJoel Fernandes 
2804a9671a0SJoel Fernandes 	list_for_each_entry_safe_reverse(block, tmp, &allocated_blocks, link)
2814a9671a0SJoel Fernandes 		list_move(&block->link, &reverse_list);
282ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &reverse_list, GPU_BUDDY_CLEARED);
2834a9671a0SJoel Fernandes 
2844a9671a0SJoel Fernandes 	end = ktime_get();
2854a9671a0SJoel Fernandes 	elapsed_ms = ktime_to_ms(ktime_sub(end, start));
2864a9671a0SJoel Fernandes 
2874a9671a0SJoel Fernandes 	kunit_info(test, "Reverse-ordered free took %lu ms\n", elapsed_ms);
2884a9671a0SJoel Fernandes 
289ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
2904a9671a0SJoel Fernandes }
2914a9671a0SJoel Fernandes 
292ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_range_bias(struct kunit *test)
2934a9671a0SJoel Fernandes {
2944a9671a0SJoel Fernandes 	u32 mm_size, size, ps, bias_size, bias_start, bias_end, bias_rem;
295ba110db8SJoel Fernandes 	GPU_RND_STATE(prng, random_seed);
2964a9671a0SJoel Fernandes 	unsigned int i, count, *order;
297ba110db8SJoel Fernandes 	struct gpu_buddy_block *block;
2984a9671a0SJoel Fernandes 	unsigned long flags;
299ba110db8SJoel Fernandes 	struct gpu_buddy mm;
3004a9671a0SJoel Fernandes 	LIST_HEAD(allocated);
3014a9671a0SJoel Fernandes 
3024a9671a0SJoel Fernandes 	bias_size = SZ_1M;
3034a9671a0SJoel Fernandes 	ps = roundup_pow_of_two(prandom_u32_state(&prng) % bias_size);
3044a9671a0SJoel Fernandes 	ps = max(SZ_4K, ps);
3054a9671a0SJoel Fernandes 	mm_size = (SZ_8M-1) & ~(ps-1); /* Multiple roots */
3064a9671a0SJoel Fernandes 
3074a9671a0SJoel Fernandes 	kunit_info(test, "mm_size=%u, ps=%u\n", mm_size, ps);
3084a9671a0SJoel Fernandes 
309ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, ps),
3104a9671a0SJoel Fernandes 			       "buddy_init failed\n");
3114a9671a0SJoel Fernandes 
3124a9671a0SJoel Fernandes 	count = mm_size / bias_size;
313ba110db8SJoel Fernandes 	order = gpu_random_order(count, &prng);
3144a9671a0SJoel Fernandes 	KUNIT_EXPECT_TRUE(test, order);
3154a9671a0SJoel Fernandes 
3164a9671a0SJoel Fernandes 	/*
3174a9671a0SJoel Fernandes 	 * Idea is to split the address space into uniform bias ranges, and then
3184a9671a0SJoel Fernandes 	 * in some random order allocate within each bias, using various
3194a9671a0SJoel Fernandes 	 * patterns within. This should detect if allocations leak out from a
3204a9671a0SJoel Fernandes 	 * given bias, for example.
3214a9671a0SJoel Fernandes 	 */
3224a9671a0SJoel Fernandes 
3234a9671a0SJoel Fernandes 	for (i = 0; i < count; i++) {
3244a9671a0SJoel Fernandes 		LIST_HEAD(tmp);
3254a9671a0SJoel Fernandes 		u32 size;
3264a9671a0SJoel Fernandes 
3274a9671a0SJoel Fernandes 		bias_start = order[i] * bias_size;
3284a9671a0SJoel Fernandes 		bias_end = bias_start + bias_size;
3294a9671a0SJoel Fernandes 		bias_rem = bias_size;
3304a9671a0SJoel Fernandes 
3314a9671a0SJoel Fernandes 		/* internal round_up too big */
3324a9671a0SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test,
333ba110db8SJoel Fernandes 				      gpu_buddy_alloc_blocks(&mm, bias_start,
3344a9671a0SJoel Fernandes 							     bias_end, bias_size + ps, bias_size,
3354a9671a0SJoel Fernandes 							     &allocated,
336ba110db8SJoel Fernandes 							     GPU_BUDDY_RANGE_ALLOCATION),
3374a9671a0SJoel Fernandes 				      "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n",
3384a9671a0SJoel Fernandes 				      bias_start, bias_end, bias_size, bias_size);
3394a9671a0SJoel Fernandes 
3404a9671a0SJoel Fernandes 		/* size too big */
3414a9671a0SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test,
342ba110db8SJoel Fernandes 				      gpu_buddy_alloc_blocks(&mm, bias_start,
3434a9671a0SJoel Fernandes 							     bias_end, bias_size + ps, ps,
3444a9671a0SJoel Fernandes 							     &allocated,
345ba110db8SJoel Fernandes 							     GPU_BUDDY_RANGE_ALLOCATION),
3464a9671a0SJoel Fernandes 				      "buddy_alloc didn't fail with bias(%x-%x), size=%u, ps=%u\n",
3474a9671a0SJoel Fernandes 				      bias_start, bias_end, bias_size + ps, ps);
3484a9671a0SJoel Fernandes 
3494a9671a0SJoel Fernandes 		/* bias range too small for size */
3504a9671a0SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test,
351ba110db8SJoel Fernandes 				      gpu_buddy_alloc_blocks(&mm, bias_start + ps,
3524a9671a0SJoel Fernandes 							     bias_end, bias_size, ps,
3534a9671a0SJoel Fernandes 							     &allocated,
354ba110db8SJoel Fernandes 							     GPU_BUDDY_RANGE_ALLOCATION),
3554a9671a0SJoel Fernandes 				      "buddy_alloc didn't fail with bias(%x-%x), size=%u, ps=%u\n",
3564a9671a0SJoel Fernandes 				      bias_start + ps, bias_end, bias_size, ps);
3574a9671a0SJoel Fernandes 
3584a9671a0SJoel Fernandes 		/* bias misaligned */
3594a9671a0SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test,
360ba110db8SJoel Fernandes 				      gpu_buddy_alloc_blocks(&mm, bias_start + ps,
3614a9671a0SJoel Fernandes 							     bias_end - ps,
3624a9671a0SJoel Fernandes 							     bias_size >> 1, bias_size >> 1,
3634a9671a0SJoel Fernandes 							     &allocated,
364ba110db8SJoel Fernandes 							     GPU_BUDDY_RANGE_ALLOCATION),
3654a9671a0SJoel Fernandes 				      "buddy_alloc h didn't fail with bias(%x-%x), size=%u, ps=%u\n",
3664a9671a0SJoel Fernandes 				      bias_start + ps, bias_end - ps, bias_size >> 1, bias_size >> 1);
3674a9671a0SJoel Fernandes 
3684a9671a0SJoel Fernandes 		/* single big page */
3694a9671a0SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test,
370ba110db8SJoel Fernandes 				       gpu_buddy_alloc_blocks(&mm, bias_start,
3714a9671a0SJoel Fernandes 							      bias_end, bias_size, bias_size,
3724a9671a0SJoel Fernandes 							      &tmp,
373ba110db8SJoel Fernandes 							      GPU_BUDDY_RANGE_ALLOCATION),
3744a9671a0SJoel Fernandes 				       "buddy_alloc i failed with bias(%x-%x), size=%u, ps=%u\n",
3754a9671a0SJoel Fernandes 				       bias_start, bias_end, bias_size, bias_size);
376ba110db8SJoel Fernandes 		gpu_buddy_free_list(&mm, &tmp, 0);
3774a9671a0SJoel Fernandes 
3784a9671a0SJoel Fernandes 		/* single page with internal round_up */
3794a9671a0SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test,
380ba110db8SJoel Fernandes 				       gpu_buddy_alloc_blocks(&mm, bias_start,
3814a9671a0SJoel Fernandes 							      bias_end, ps, bias_size,
3824a9671a0SJoel Fernandes 							      &tmp,
383ba110db8SJoel Fernandes 							      GPU_BUDDY_RANGE_ALLOCATION),
3844a9671a0SJoel Fernandes 				       "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n",
3854a9671a0SJoel Fernandes 				       bias_start, bias_end, ps, bias_size);
386ba110db8SJoel Fernandes 		gpu_buddy_free_list(&mm, &tmp, 0);
3874a9671a0SJoel Fernandes 
3884a9671a0SJoel Fernandes 		/* random size within */
3894a9671a0SJoel Fernandes 		size = max(round_up(prandom_u32_state(&prng) % bias_rem, ps), ps);
3904a9671a0SJoel Fernandes 		if (size)
3914a9671a0SJoel Fernandes 			KUNIT_ASSERT_FALSE_MSG(test,
392ba110db8SJoel Fernandes 					       gpu_buddy_alloc_blocks(&mm, bias_start,
3934a9671a0SJoel Fernandes 								      bias_end, size, ps,
3944a9671a0SJoel Fernandes 								      &tmp,
395ba110db8SJoel Fernandes 								      GPU_BUDDY_RANGE_ALLOCATION),
3964a9671a0SJoel Fernandes 					       "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n",
3974a9671a0SJoel Fernandes 					       bias_start, bias_end, size, ps);
3984a9671a0SJoel Fernandes 
3994a9671a0SJoel Fernandes 		bias_rem -= size;
4004a9671a0SJoel Fernandes 		/* too big for current avail */
4014a9671a0SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test,
402ba110db8SJoel Fernandes 				      gpu_buddy_alloc_blocks(&mm, bias_start,
4034a9671a0SJoel Fernandes 							     bias_end, bias_rem + ps, ps,
4044a9671a0SJoel Fernandes 							     &allocated,
405ba110db8SJoel Fernandes 							     GPU_BUDDY_RANGE_ALLOCATION),
4064a9671a0SJoel Fernandes 				      "buddy_alloc didn't fail with bias(%x-%x), size=%u, ps=%u\n",
4074a9671a0SJoel Fernandes 				      bias_start, bias_end, bias_rem + ps, ps);
4084a9671a0SJoel Fernandes 
4094a9671a0SJoel Fernandes 		if (bias_rem) {
4104a9671a0SJoel Fernandes 			/* random fill of the remainder */
4114a9671a0SJoel Fernandes 			size = max(round_up(prandom_u32_state(&prng) % bias_rem, ps), ps);
4124a9671a0SJoel Fernandes 			size = max(size, ps);
4134a9671a0SJoel Fernandes 
4144a9671a0SJoel Fernandes 			KUNIT_ASSERT_FALSE_MSG(test,
415ba110db8SJoel Fernandes 					       gpu_buddy_alloc_blocks(&mm, bias_start,
4164a9671a0SJoel Fernandes 								      bias_end, size, ps,
4174a9671a0SJoel Fernandes 								      &allocated,
418ba110db8SJoel Fernandes 								      GPU_BUDDY_RANGE_ALLOCATION),
4194a9671a0SJoel Fernandes 					       "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n",
4204a9671a0SJoel Fernandes 					       bias_start, bias_end, size, ps);
4214a9671a0SJoel Fernandes 			/*
4224a9671a0SJoel Fernandes 			 * Intentionally allow some space to be left
4234a9671a0SJoel Fernandes 			 * unallocated, and ideally not always on the bias
4244a9671a0SJoel Fernandes 			 * boundaries.
4254a9671a0SJoel Fernandes 			 */
426ba110db8SJoel Fernandes 			gpu_buddy_free_list(&mm, &tmp, 0);
4274a9671a0SJoel Fernandes 		} else {
4284a9671a0SJoel Fernandes 			list_splice_tail(&tmp, &allocated);
4294a9671a0SJoel Fernandes 		}
4304a9671a0SJoel Fernandes 	}
4314a9671a0SJoel Fernandes 
4324a9671a0SJoel Fernandes 	kfree(order);
433ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &allocated, 0);
434ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
4354a9671a0SJoel Fernandes 
4364a9671a0SJoel Fernandes 	/*
4374a9671a0SJoel Fernandes 	 * Something more free-form. Idea is to pick a random starting bias
4384a9671a0SJoel Fernandes 	 * range within the address space and then start filling it up. Also
4394a9671a0SJoel Fernandes 	 * randomly grow the bias range in both directions as we go along. This
4404a9671a0SJoel Fernandes 	 * should give us bias start/end which is not always uniform like above,
4414a9671a0SJoel Fernandes 	 * and in some cases will require the allocator to jump over already
4424a9671a0SJoel Fernandes 	 * allocated nodes in the middle of the address space.
4434a9671a0SJoel Fernandes 	 */
4444a9671a0SJoel Fernandes 
445ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, ps),
4464a9671a0SJoel Fernandes 			       "buddy_init failed\n");
4474a9671a0SJoel Fernandes 
4484a9671a0SJoel Fernandes 	bias_start = round_up(prandom_u32_state(&prng) % (mm_size - ps), ps);
4494a9671a0SJoel Fernandes 	bias_end = round_up(bias_start + prandom_u32_state(&prng) % (mm_size - bias_start), ps);
4504a9671a0SJoel Fernandes 	bias_end = max(bias_end, bias_start + ps);
4514a9671a0SJoel Fernandes 	bias_rem = bias_end - bias_start;
4524a9671a0SJoel Fernandes 
4534a9671a0SJoel Fernandes 	do {
4544a9671a0SJoel Fernandes 		u32 size = max(round_up(prandom_u32_state(&prng) % bias_rem, ps), ps);
4554a9671a0SJoel Fernandes 
4564a9671a0SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test,
457ba110db8SJoel Fernandes 				       gpu_buddy_alloc_blocks(&mm, bias_start,
4584a9671a0SJoel Fernandes 							      bias_end, size, ps,
4594a9671a0SJoel Fernandes 							      &allocated,
460ba110db8SJoel Fernandes 							      GPU_BUDDY_RANGE_ALLOCATION),
4614a9671a0SJoel Fernandes 				       "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n",
4624a9671a0SJoel Fernandes 				       bias_start, bias_end, size, ps);
4634a9671a0SJoel Fernandes 		bias_rem -= size;
4644a9671a0SJoel Fernandes 
4654a9671a0SJoel Fernandes 		/*
4664a9671a0SJoel Fernandes 		 * Try to randomly grow the bias range in both directions, or
4674a9671a0SJoel Fernandes 		 * only one, or perhaps don't grow at all.
4684a9671a0SJoel Fernandes 		 */
4694a9671a0SJoel Fernandes 		do {
4704a9671a0SJoel Fernandes 			u32 old_bias_start = bias_start;
4714a9671a0SJoel Fernandes 			u32 old_bias_end = bias_end;
4724a9671a0SJoel Fernandes 
4734a9671a0SJoel Fernandes 			if (bias_start)
4744a9671a0SJoel Fernandes 				bias_start -= round_up(prandom_u32_state(&prng) % bias_start, ps);
4754a9671a0SJoel Fernandes 			if (bias_end != mm_size)
4764a9671a0SJoel Fernandes 				bias_end += round_up(prandom_u32_state(&prng) % (mm_size - bias_end), ps);
4774a9671a0SJoel Fernandes 
4784a9671a0SJoel Fernandes 			bias_rem += old_bias_start - bias_start;
4794a9671a0SJoel Fernandes 			bias_rem += bias_end - old_bias_end;
4804a9671a0SJoel Fernandes 		} while (!bias_rem && (bias_start || bias_end != mm_size));
4814a9671a0SJoel Fernandes 	} while (bias_rem);
4824a9671a0SJoel Fernandes 
4834a9671a0SJoel Fernandes 	KUNIT_ASSERT_EQ(test, bias_start, 0);
4844a9671a0SJoel Fernandes 	KUNIT_ASSERT_EQ(test, bias_end, mm_size);
4854a9671a0SJoel Fernandes 	KUNIT_ASSERT_TRUE_MSG(test,
486ba110db8SJoel Fernandes 			      gpu_buddy_alloc_blocks(&mm, bias_start, bias_end,
4874a9671a0SJoel Fernandes 						     ps, ps,
4884a9671a0SJoel Fernandes 						     &allocated,
489ba110db8SJoel Fernandes 						     GPU_BUDDY_RANGE_ALLOCATION),
4904a9671a0SJoel Fernandes 			      "buddy_alloc passed with bias(%x-%x), size=%u\n",
4914a9671a0SJoel Fernandes 			      bias_start, bias_end, ps);
4924a9671a0SJoel Fernandes 
493ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &allocated, 0);
494ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
4954a9671a0SJoel Fernandes 
4964a9671a0SJoel Fernandes 	/*
497ba110db8SJoel Fernandes 	 * Allocate cleared blocks in the bias range when the GPU buddy's clear avail is
4984a9671a0SJoel Fernandes 	 * zero. This will validate the bias range allocation in scenarios like system boot
4994a9671a0SJoel Fernandes 	 * when no cleared blocks are available and exercise the fallback path too. The resulting
5004a9671a0SJoel Fernandes 	 * blocks should always be dirty.
5014a9671a0SJoel Fernandes 	 */
5024a9671a0SJoel Fernandes 
503ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, ps),
5044a9671a0SJoel Fernandes 			       "buddy_init failed\n");
5054a9671a0SJoel Fernandes 
5064a9671a0SJoel Fernandes 	bias_start = round_up(prandom_u32_state(&prng) % (mm_size - ps), ps);
5074a9671a0SJoel Fernandes 	bias_end = round_up(bias_start + prandom_u32_state(&prng) % (mm_size - bias_start), ps);
5084a9671a0SJoel Fernandes 	bias_end = max(bias_end, bias_start + ps);
5094a9671a0SJoel Fernandes 	bias_rem = bias_end - bias_start;
5104a9671a0SJoel Fernandes 
511ba110db8SJoel Fernandes 	flags = GPU_BUDDY_CLEAR_ALLOCATION | GPU_BUDDY_RANGE_ALLOCATION;
5124a9671a0SJoel Fernandes 	size = max(round_up(prandom_u32_state(&prng) % bias_rem, ps), ps);
5134a9671a0SJoel Fernandes 
5144a9671a0SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test,
515ba110db8SJoel Fernandes 			       gpu_buddy_alloc_blocks(&mm, bias_start,
5164a9671a0SJoel Fernandes 						      bias_end, size, ps,
5174a9671a0SJoel Fernandes 						      &allocated,
5184a9671a0SJoel Fernandes 						      flags),
5194a9671a0SJoel Fernandes 			       "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n",
5204a9671a0SJoel Fernandes 			       bias_start, bias_end, size, ps);
5214a9671a0SJoel Fernandes 
5224a9671a0SJoel Fernandes 	list_for_each_entry(block, &allocated, link)
523ba110db8SJoel Fernandes 		KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), false);
5244a9671a0SJoel Fernandes 
525ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &allocated, 0);
526ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
5274a9671a0SJoel Fernandes }
5284a9671a0SJoel Fernandes 
529ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_clear(struct kunit *test)
5304a9671a0SJoel Fernandes {
5314a9671a0SJoel Fernandes 	unsigned long n_pages, total, i = 0;
5324a9671a0SJoel Fernandes 	const unsigned long ps = SZ_4K;
533ba110db8SJoel Fernandes 	struct gpu_buddy_block *block;
5344a9671a0SJoel Fernandes 	const int max_order = 12;
5354a9671a0SJoel Fernandes 	LIST_HEAD(allocated);
536ba110db8SJoel Fernandes 	struct gpu_buddy mm;
5374a9671a0SJoel Fernandes 	unsigned int order;
5384a9671a0SJoel Fernandes 	u32 mm_size, size;
5394a9671a0SJoel Fernandes 	LIST_HEAD(dirty);
5404a9671a0SJoel Fernandes 	LIST_HEAD(clean);
5414a9671a0SJoel Fernandes 
5424a9671a0SJoel Fernandes 	mm_size = SZ_4K << max_order;
543ba110db8SJoel Fernandes 	KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps));
5444a9671a0SJoel Fernandes 
5454a9671a0SJoel Fernandes 	KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
5464a9671a0SJoel Fernandes 
5474a9671a0SJoel Fernandes 	/*
5484a9671a0SJoel Fernandes 	 * Idea is to allocate and free some random portion of the address space,
5494a9671a0SJoel Fernandes 	 * returning those pages as non-dirty and randomly alternate between
5504a9671a0SJoel Fernandes 	 * requesting dirty and non-dirty pages (not going over the limit
5514a9671a0SJoel Fernandes 	 * we freed as non-dirty), putting that into two separate lists.
5524a9671a0SJoel Fernandes 	 * Loop over both lists at the end checking that the dirty list
5534a9671a0SJoel Fernandes 	 * is indeed all dirty pages and vice versa. Free it all again,
5544a9671a0SJoel Fernandes 	 * keeping the dirty/clear status.
5554a9671a0SJoel Fernandes 	 */
556ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
5574a9671a0SJoel Fernandes 							    5 * ps, ps, &allocated,
558ba110db8SJoel Fernandes 							    GPU_BUDDY_TOPDOWN_ALLOCATION),
5594a9671a0SJoel Fernandes 				"buddy_alloc hit an error size=%lu\n", 5 * ps);
560ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED);
5614a9671a0SJoel Fernandes 
5624a9671a0SJoel Fernandes 	n_pages = 10;
5634a9671a0SJoel Fernandes 	do {
5644a9671a0SJoel Fernandes 		unsigned long flags;
5654a9671a0SJoel Fernandes 		struct list_head *list;
5664a9671a0SJoel Fernandes 		int slot = i % 2;
5674a9671a0SJoel Fernandes 
5684a9671a0SJoel Fernandes 		if (slot == 0) {
5694a9671a0SJoel Fernandes 			list = &dirty;
5704a9671a0SJoel Fernandes 			flags = 0;
5714a9671a0SJoel Fernandes 		} else {
5724a9671a0SJoel Fernandes 			list = &clean;
573ba110db8SJoel Fernandes 			flags = GPU_BUDDY_CLEAR_ALLOCATION;
5744a9671a0SJoel Fernandes 		}
5754a9671a0SJoel Fernandes 
576ba110db8SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
5774a9671a0SJoel Fernandes 								    ps, ps, list,
5784a9671a0SJoel Fernandes 								    flags),
5794a9671a0SJoel Fernandes 					"buddy_alloc hit an error size=%lu\n", ps);
5804a9671a0SJoel Fernandes 	} while (++i < n_pages);
5814a9671a0SJoel Fernandes 
5824a9671a0SJoel Fernandes 	list_for_each_entry(block, &clean, link)
583ba110db8SJoel Fernandes 		KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), true);
5844a9671a0SJoel Fernandes 
5854a9671a0SJoel Fernandes 	list_for_each_entry(block, &dirty, link)
586ba110db8SJoel Fernandes 		KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), false);
5874a9671a0SJoel Fernandes 
588ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED);
5894a9671a0SJoel Fernandes 
5904a9671a0SJoel Fernandes 	/*
5914a9671a0SJoel Fernandes 	 * Trying to go over the clear limit for some allocation.
5924a9671a0SJoel Fernandes 	 * The allocation should never fail with reasonable page-size.
5934a9671a0SJoel Fernandes 	 */
594ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
5954a9671a0SJoel Fernandes 							    10 * ps, ps, &clean,
596ba110db8SJoel Fernandes 							    GPU_BUDDY_CLEAR_ALLOCATION),
5974a9671a0SJoel Fernandes 				"buddy_alloc hit an error size=%lu\n", 10 * ps);
5984a9671a0SJoel Fernandes 
599ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED);
600ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &dirty, 0);
601ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
6024a9671a0SJoel Fernandes 
603ba110db8SJoel Fernandes 	KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps));
6044a9671a0SJoel Fernandes 
6054a9671a0SJoel Fernandes 	/*
6064a9671a0SJoel Fernandes 	 * Create a new mm. Intentionally fragment the address space by creating
6074a9671a0SJoel Fernandes 	 * two alternating lists. Free both lists, one as dirty the other as clean.
6084a9671a0SJoel Fernandes 	 * Try to allocate double the previous size with matching min_page_size. The
6094a9671a0SJoel Fernandes 	 * allocation should never fail as it calls the force_merge. Also check that
6104a9671a0SJoel Fernandes 	 * the page is always dirty after force_merge. Free the page as dirty, then
6114a9671a0SJoel Fernandes 	 * repeat the whole thing, increment the order until we hit the max_order.
6124a9671a0SJoel Fernandes 	 */
6134a9671a0SJoel Fernandes 
6144a9671a0SJoel Fernandes 	i = 0;
6154a9671a0SJoel Fernandes 	n_pages = mm_size / ps;
6164a9671a0SJoel Fernandes 	do {
6174a9671a0SJoel Fernandes 		struct list_head *list;
6184a9671a0SJoel Fernandes 		int slot = i % 2;
6194a9671a0SJoel Fernandes 
6204a9671a0SJoel Fernandes 		if (slot == 0)
6214a9671a0SJoel Fernandes 			list = &dirty;
6224a9671a0SJoel Fernandes 		else
6234a9671a0SJoel Fernandes 			list = &clean;
6244a9671a0SJoel Fernandes 
625ba110db8SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
6264a9671a0SJoel Fernandes 								    ps, ps, list, 0),
6274a9671a0SJoel Fernandes 					"buddy_alloc hit an error size=%lu\n", ps);
6284a9671a0SJoel Fernandes 	} while (++i < n_pages);
6294a9671a0SJoel Fernandes 
630ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED);
631ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &dirty, 0);
6324a9671a0SJoel Fernandes 
6334a9671a0SJoel Fernandes 	order = 1;
6344a9671a0SJoel Fernandes 	do {
6354a9671a0SJoel Fernandes 		size = SZ_4K << order;
6364a9671a0SJoel Fernandes 
637ba110db8SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
6384a9671a0SJoel Fernandes 								    size, size, &allocated,
639ba110db8SJoel Fernandes 								    GPU_BUDDY_CLEAR_ALLOCATION),
6404a9671a0SJoel Fernandes 					"buddy_alloc hit an error size=%u\n", size);
6414a9671a0SJoel Fernandes 		total = 0;
6424a9671a0SJoel Fernandes 		list_for_each_entry(block, &allocated, link) {
6434a9671a0SJoel Fernandes 			if (size != mm_size)
644ba110db8SJoel Fernandes 				KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), false);
645ba110db8SJoel Fernandes 			total += gpu_buddy_block_size(&mm, block);
6464a9671a0SJoel Fernandes 		}
6474a9671a0SJoel Fernandes 		KUNIT_EXPECT_EQ(test, total, size);
6484a9671a0SJoel Fernandes 
649ba110db8SJoel Fernandes 		gpu_buddy_free_list(&mm, &allocated, 0);
6504a9671a0SJoel Fernandes 	} while (++order <= max_order);
6514a9671a0SJoel Fernandes 
652ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
6534a9671a0SJoel Fernandes 
6544a9671a0SJoel Fernandes 	/*
6554a9671a0SJoel Fernandes 	 * Create a new mm with a non power-of-two size. Allocate a random size from each
6564a9671a0SJoel Fernandes 	 * root, free as cleared and then call fini. This will ensure the multi-root
6574a9671a0SJoel Fernandes 	 * force merge during fini.
6584a9671a0SJoel Fernandes 	 */
6594a9671a0SJoel Fernandes 	mm_size = (SZ_4K << max_order) + (SZ_4K << (max_order - 2));
6604a9671a0SJoel Fernandes 
661ba110db8SJoel Fernandes 	KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps));
6624a9671a0SJoel Fernandes 	KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
663ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, SZ_4K << max_order,
6644a9671a0SJoel Fernandes 							    4 * ps, ps, &allocated,
665ba110db8SJoel Fernandes 							    GPU_BUDDY_RANGE_ALLOCATION),
6664a9671a0SJoel Fernandes 				"buddy_alloc hit an error size=%lu\n", 4 * ps);
667ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED);
668ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, SZ_4K << max_order,
6694a9671a0SJoel Fernandes 							    2 * ps, ps, &allocated,
670ba110db8SJoel Fernandes 							    GPU_BUDDY_CLEAR_ALLOCATION),
6714a9671a0SJoel Fernandes 				"buddy_alloc hit an error size=%lu\n", 2 * ps);
672ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED);
673ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, SZ_4K << max_order, mm_size,
6744a9671a0SJoel Fernandes 							    ps, ps, &allocated,
675ba110db8SJoel Fernandes 							    GPU_BUDDY_RANGE_ALLOCATION),
6764a9671a0SJoel Fernandes 				"buddy_alloc hit an error size=%lu\n", ps);
677ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED);
678ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
6794a9671a0SJoel Fernandes }
6804a9671a0SJoel Fernandes 
681ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_contiguous(struct kunit *test)
6824a9671a0SJoel Fernandes {
6834a9671a0SJoel Fernandes 	const unsigned long ps = SZ_4K, mm_size = 16 * 3 * SZ_4K;
6844a9671a0SJoel Fernandes 	unsigned long i, n_pages, total;
685ba110db8SJoel Fernandes 	struct gpu_buddy_block *block;
686ba110db8SJoel Fernandes 	struct gpu_buddy mm;
6874a9671a0SJoel Fernandes 	LIST_HEAD(left);
6884a9671a0SJoel Fernandes 	LIST_HEAD(middle);
6894a9671a0SJoel Fernandes 	LIST_HEAD(right);
6904a9671a0SJoel Fernandes 	LIST_HEAD(allocated);
6914a9671a0SJoel Fernandes 
692ba110db8SJoel Fernandes 	KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps));
6934a9671a0SJoel Fernandes 
6944a9671a0SJoel Fernandes 	/*
6954a9671a0SJoel Fernandes 	 * Idea is to fragment the address space by alternating block
6964a9671a0SJoel Fernandes 	 * allocations between three different lists; one for left, middle and
6974a9671a0SJoel Fernandes 	 * right. We can then free a list to simulate fragmentation. In
698ba110db8SJoel Fernandes 	 * particular we want to exercise the GPU_BUDDY_CONTIGUOUS_ALLOCATION,
6994a9671a0SJoel Fernandes 	 * including the try_harder path.
7004a9671a0SJoel Fernandes 	 */
7014a9671a0SJoel Fernandes 
7024a9671a0SJoel Fernandes 	i = 0;
7034a9671a0SJoel Fernandes 	n_pages = mm_size / ps;
7044a9671a0SJoel Fernandes 	do {
7054a9671a0SJoel Fernandes 		struct list_head *list;
7064a9671a0SJoel Fernandes 		int slot = i % 3;
7074a9671a0SJoel Fernandes 
7084a9671a0SJoel Fernandes 		if (slot == 0)
7094a9671a0SJoel Fernandes 			list = &left;
7104a9671a0SJoel Fernandes 		else if (slot == 1)
7114a9671a0SJoel Fernandes 			list = &middle;
7124a9671a0SJoel Fernandes 		else
7134a9671a0SJoel Fernandes 			list = &right;
7144a9671a0SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test,
715ba110db8SJoel Fernandes 				       gpu_buddy_alloc_blocks(&mm, 0, mm_size,
7164a9671a0SJoel Fernandes 							      ps, ps, list, 0),
7174a9671a0SJoel Fernandes 				       "buddy_alloc hit an error size=%lu\n",
7184a9671a0SJoel Fernandes 				       ps);
7194a9671a0SJoel Fernandes 	} while (++i < n_pages);
7204a9671a0SJoel Fernandes 
721ba110db8SJoel Fernandes 	KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
7224a9671a0SJoel Fernandes 							   3 * ps, ps, &allocated,
723ba110db8SJoel Fernandes 							   GPU_BUDDY_CONTIGUOUS_ALLOCATION),
7244a9671a0SJoel Fernandes 			       "buddy_alloc didn't error size=%lu\n", 3 * ps);
7254a9671a0SJoel Fernandes 
726ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &middle, 0);
727ba110db8SJoel Fernandes 	KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
7284a9671a0SJoel Fernandes 							   3 * ps, ps, &allocated,
729ba110db8SJoel Fernandes 							   GPU_BUDDY_CONTIGUOUS_ALLOCATION),
7304a9671a0SJoel Fernandes 			       "buddy_alloc didn't error size=%lu\n", 3 * ps);
731ba110db8SJoel Fernandes 	KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
7324a9671a0SJoel Fernandes 							   2 * ps, ps, &allocated,
733ba110db8SJoel Fernandes 							   GPU_BUDDY_CONTIGUOUS_ALLOCATION),
7344a9671a0SJoel Fernandes 			       "buddy_alloc didn't error size=%lu\n", 2 * ps);
7354a9671a0SJoel Fernandes 
736ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &right, 0);
737ba110db8SJoel Fernandes 	KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
7384a9671a0SJoel Fernandes 							   3 * ps, ps, &allocated,
739ba110db8SJoel Fernandes 							   GPU_BUDDY_CONTIGUOUS_ALLOCATION),
7404a9671a0SJoel Fernandes 			       "buddy_alloc didn't error size=%lu\n", 3 * ps);
7414a9671a0SJoel Fernandes 	/*
7424a9671a0SJoel Fernandes 	 * At this point we should have enough contiguous space for 2 blocks,
7434a9671a0SJoel Fernandes 	 * however they are never buddies (since we freed middle and right) so
7444a9671a0SJoel Fernandes 	 * will require the try_harder logic to find them.
7454a9671a0SJoel Fernandes 	 */
746ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
7474a9671a0SJoel Fernandes 							    2 * ps, ps, &allocated,
748ba110db8SJoel Fernandes 							    GPU_BUDDY_CONTIGUOUS_ALLOCATION),
7494a9671a0SJoel Fernandes 			       "buddy_alloc hit an error size=%lu\n", 2 * ps);
7504a9671a0SJoel Fernandes 
751ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &left, 0);
752ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
7534a9671a0SJoel Fernandes 							    3 * ps, ps, &allocated,
754ba110db8SJoel Fernandes 							    GPU_BUDDY_CONTIGUOUS_ALLOCATION),
7554a9671a0SJoel Fernandes 			       "buddy_alloc hit an error size=%lu\n", 3 * ps);
7564a9671a0SJoel Fernandes 
7574a9671a0SJoel Fernandes 	total = 0;
7584a9671a0SJoel Fernandes 	list_for_each_entry(block, &allocated, link)
759ba110db8SJoel Fernandes 		total += gpu_buddy_block_size(&mm, block);
7604a9671a0SJoel Fernandes 
7614a9671a0SJoel Fernandes 	KUNIT_ASSERT_EQ(test, total, ps * 2 + ps * 3);
7624a9671a0SJoel Fernandes 
763ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &allocated, 0);
764ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
7654a9671a0SJoel Fernandes }
7664a9671a0SJoel Fernandes 
767ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_pathological(struct kunit *test)
7684a9671a0SJoel Fernandes {
7694a9671a0SJoel Fernandes 	u64 mm_size, size, start = 0;
770ba110db8SJoel Fernandes 	struct gpu_buddy_block *block;
7714a9671a0SJoel Fernandes 	const int max_order = 3;
7724a9671a0SJoel Fernandes 	unsigned long flags = 0;
7734a9671a0SJoel Fernandes 	int order, top;
774ba110db8SJoel Fernandes 	struct gpu_buddy mm;
7754a9671a0SJoel Fernandes 	LIST_HEAD(blocks);
7764a9671a0SJoel Fernandes 	LIST_HEAD(holes);
7774a9671a0SJoel Fernandes 	LIST_HEAD(tmp);
7784a9671a0SJoel Fernandes 
7794a9671a0SJoel Fernandes 	/*
7804a9671a0SJoel Fernandes 	 * Create a pot-sized mm, then allocate one of each possible
7814a9671a0SJoel Fernandes 	 * order within. This should leave the mm with exactly one
7824a9671a0SJoel Fernandes 	 * page left. Free the largest block, then whittle down again.
7834a9671a0SJoel Fernandes 	 * Eventually we will have a fully 50% fragmented mm.
7844a9671a0SJoel Fernandes 	 */
7854a9671a0SJoel Fernandes 
7864a9671a0SJoel Fernandes 	mm_size = SZ_4K << max_order;
787ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K),
7884a9671a0SJoel Fernandes 			       "buddy_init failed\n");
7894a9671a0SJoel Fernandes 
7904a9671a0SJoel Fernandes 	KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
7914a9671a0SJoel Fernandes 
7924a9671a0SJoel Fernandes 	for (top = max_order; top; top--) {
7934a9671a0SJoel Fernandes 		/* Make room by freeing the largest allocated block */
7944a9671a0SJoel Fernandes 		block = list_first_entry_or_null(&blocks, typeof(*block), link);
7954a9671a0SJoel Fernandes 		if (block) {
7964a9671a0SJoel Fernandes 			list_del(&block->link);
797ba110db8SJoel Fernandes 			gpu_buddy_free_block(&mm, block);
7984a9671a0SJoel Fernandes 		}
7994a9671a0SJoel Fernandes 
8004a9671a0SJoel Fernandes 		for (order = top; order--;) {
8014a9671a0SJoel Fernandes 			size = get_size(order, mm.chunk_size);
802ba110db8SJoel Fernandes 			KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start,
8034a9671a0SJoel Fernandes 									    mm_size, size, size,
8044a9671a0SJoel Fernandes 										&tmp, flags),
8054a9671a0SJoel Fernandes 					"buddy_alloc hit -ENOMEM with order=%d, top=%d\n",
8064a9671a0SJoel Fernandes 					order, top);
8074a9671a0SJoel Fernandes 
808ba110db8SJoel Fernandes 			block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link);
8094a9671a0SJoel Fernandes 			KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
8104a9671a0SJoel Fernandes 
8114a9671a0SJoel Fernandes 			list_move_tail(&block->link, &blocks);
8124a9671a0SJoel Fernandes 		}
8134a9671a0SJoel Fernandes 
8144a9671a0SJoel Fernandes 		/* There should be one final page for this sub-allocation */
8154a9671a0SJoel Fernandes 		size = get_size(0, mm.chunk_size);
816ba110db8SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size,
8174a9671a0SJoel Fernandes 								    size, size, &tmp, flags),
8184a9671a0SJoel Fernandes 							   "buddy_alloc hit -ENOMEM for hole\n");
8194a9671a0SJoel Fernandes 
820ba110db8SJoel Fernandes 		block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link);
8214a9671a0SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
8224a9671a0SJoel Fernandes 
8234a9671a0SJoel Fernandes 		list_move_tail(&block->link, &holes);
8244a9671a0SJoel Fernandes 
8254a9671a0SJoel Fernandes 		size = get_size(top, mm.chunk_size);
826ba110db8SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size,
8274a9671a0SJoel Fernandes 								   size, size, &tmp, flags),
8284a9671a0SJoel Fernandes 							  "buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!",
8294a9671a0SJoel Fernandes 							  top, max_order);
8304a9671a0SJoel Fernandes 	}
8314a9671a0SJoel Fernandes 
832ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &holes, 0);
8334a9671a0SJoel Fernandes 
8344a9671a0SJoel Fernandes 	/* Nothing larger than blocks of chunk_size now available */
8354a9671a0SJoel Fernandes 	for (order = 1; order <= max_order; order++) {
8364a9671a0SJoel Fernandes 		size = get_size(order, mm.chunk_size);
837ba110db8SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size,
8384a9671a0SJoel Fernandes 								   size, size, &tmp, flags),
8394a9671a0SJoel Fernandes 							  "buddy_alloc unexpectedly succeeded at order %d, it should be full!",
8404a9671a0SJoel Fernandes 							  order);
8414a9671a0SJoel Fernandes 	}
8424a9671a0SJoel Fernandes 
8434a9671a0SJoel Fernandes 	list_splice_tail(&holes, &blocks);
844ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &blocks, 0);
845ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
8464a9671a0SJoel Fernandes }
8474a9671a0SJoel Fernandes 
848ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_pessimistic(struct kunit *test)
8494a9671a0SJoel Fernandes {
8504a9671a0SJoel Fernandes 	u64 mm_size, size, start = 0;
851ba110db8SJoel Fernandes 	struct gpu_buddy_block *block, *bn;
8524a9671a0SJoel Fernandes 	const unsigned int max_order = 16;
8534a9671a0SJoel Fernandes 	unsigned long flags = 0;
854ba110db8SJoel Fernandes 	struct gpu_buddy mm;
8554a9671a0SJoel Fernandes 	unsigned int order;
8564a9671a0SJoel Fernandes 	LIST_HEAD(blocks);
8574a9671a0SJoel Fernandes 	LIST_HEAD(tmp);
8584a9671a0SJoel Fernandes 
8594a9671a0SJoel Fernandes 	/*
8604a9671a0SJoel Fernandes 	 * Create a pot-sized mm, then allocate one of each possible
8614a9671a0SJoel Fernandes 	 * order within. This should leave the mm with exactly one
8624a9671a0SJoel Fernandes 	 * page left.
8634a9671a0SJoel Fernandes 	 */
8644a9671a0SJoel Fernandes 
8654a9671a0SJoel Fernandes 	mm_size = SZ_4K << max_order;
866ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K),
8674a9671a0SJoel Fernandes 			       "buddy_init failed\n");
8684a9671a0SJoel Fernandes 
8694a9671a0SJoel Fernandes 	KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
8704a9671a0SJoel Fernandes 
8714a9671a0SJoel Fernandes 	for (order = 0; order < max_order; order++) {
8724a9671a0SJoel Fernandes 		size = get_size(order, mm.chunk_size);
873ba110db8SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size,
8744a9671a0SJoel Fernandes 								    size, size, &tmp, flags),
8754a9671a0SJoel Fernandes 							   "buddy_alloc hit -ENOMEM with order=%d\n",
8764a9671a0SJoel Fernandes 							   order);
8774a9671a0SJoel Fernandes 
878ba110db8SJoel Fernandes 		block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link);
8794a9671a0SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
8804a9671a0SJoel Fernandes 
8814a9671a0SJoel Fernandes 		list_move_tail(&block->link, &blocks);
8824a9671a0SJoel Fernandes 	}
8834a9671a0SJoel Fernandes 
8844a9671a0SJoel Fernandes 	/* And now the last remaining block available */
8854a9671a0SJoel Fernandes 	size = get_size(0, mm.chunk_size);
886ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size,
8874a9671a0SJoel Fernandes 							    size, size, &tmp, flags),
8884a9671a0SJoel Fernandes 						   "buddy_alloc hit -ENOMEM on final alloc\n");
8894a9671a0SJoel Fernandes 
890ba110db8SJoel Fernandes 	block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link);
8914a9671a0SJoel Fernandes 	KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
8924a9671a0SJoel Fernandes 
8934a9671a0SJoel Fernandes 	list_move_tail(&block->link, &blocks);
8944a9671a0SJoel Fernandes 
8954a9671a0SJoel Fernandes 	/* Should be completely full! */
8964a9671a0SJoel Fernandes 	for (order = max_order; order--;) {
8974a9671a0SJoel Fernandes 		size = get_size(order, mm.chunk_size);
898ba110db8SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size,
8994a9671a0SJoel Fernandes 								   size, size, &tmp, flags),
9004a9671a0SJoel Fernandes 							  "buddy_alloc unexpectedly succeeded, it should be full!");
9014a9671a0SJoel Fernandes 	}
9024a9671a0SJoel Fernandes 
9034a9671a0SJoel Fernandes 	block = list_last_entry(&blocks, typeof(*block), link);
9044a9671a0SJoel Fernandes 	list_del(&block->link);
905ba110db8SJoel Fernandes 	gpu_buddy_free_block(&mm, block);
9064a9671a0SJoel Fernandes 
9074a9671a0SJoel Fernandes 	/* As we free in increasing size, we make available larger blocks */
9084a9671a0SJoel Fernandes 	order = 1;
9094a9671a0SJoel Fernandes 	list_for_each_entry_safe(block, bn, &blocks, link) {
9104a9671a0SJoel Fernandes 		list_del(&block->link);
911ba110db8SJoel Fernandes 		gpu_buddy_free_block(&mm, block);
9124a9671a0SJoel Fernandes 
9134a9671a0SJoel Fernandes 		size = get_size(order, mm.chunk_size);
914ba110db8SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size,
9154a9671a0SJoel Fernandes 								    size, size, &tmp, flags),
9164a9671a0SJoel Fernandes 							   "buddy_alloc hit -ENOMEM with order=%d\n",
9174a9671a0SJoel Fernandes 							   order);
9184a9671a0SJoel Fernandes 
919ba110db8SJoel Fernandes 		block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link);
9204a9671a0SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
9214a9671a0SJoel Fernandes 
9224a9671a0SJoel Fernandes 		list_del(&block->link);
923ba110db8SJoel Fernandes 		gpu_buddy_free_block(&mm, block);
9244a9671a0SJoel Fernandes 		order++;
9254a9671a0SJoel Fernandes 	}
9264a9671a0SJoel Fernandes 
9274a9671a0SJoel Fernandes 	/* To confirm, now the whole mm should be available */
9284a9671a0SJoel Fernandes 	size = get_size(max_order, mm.chunk_size);
929ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size,
9304a9671a0SJoel Fernandes 							    size, size, &tmp, flags),
9314a9671a0SJoel Fernandes 						   "buddy_alloc (realloc) hit -ENOMEM with order=%d\n",
9324a9671a0SJoel Fernandes 						   max_order);
9334a9671a0SJoel Fernandes 
934ba110db8SJoel Fernandes 	block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link);
9354a9671a0SJoel Fernandes 	KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
9364a9671a0SJoel Fernandes 
9374a9671a0SJoel Fernandes 	list_del(&block->link);
938ba110db8SJoel Fernandes 	gpu_buddy_free_block(&mm, block);
939ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &blocks, 0);
940ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
9414a9671a0SJoel Fernandes }
9424a9671a0SJoel Fernandes 
943ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_optimistic(struct kunit *test)
9444a9671a0SJoel Fernandes {
9454a9671a0SJoel Fernandes 	u64 mm_size, size, start = 0;
946ba110db8SJoel Fernandes 	struct gpu_buddy_block *block;
9474a9671a0SJoel Fernandes 	unsigned long flags = 0;
9484a9671a0SJoel Fernandes 	const int max_order = 16;
949ba110db8SJoel Fernandes 	struct gpu_buddy mm;
9504a9671a0SJoel Fernandes 	LIST_HEAD(blocks);
9514a9671a0SJoel Fernandes 	LIST_HEAD(tmp);
9524a9671a0SJoel Fernandes 	int order;
9534a9671a0SJoel Fernandes 
9544a9671a0SJoel Fernandes 	/*
9554a9671a0SJoel Fernandes 	 * Create a mm with one block of each order available, and
9564a9671a0SJoel Fernandes 	 * try to allocate them all.
9574a9671a0SJoel Fernandes 	 */
9584a9671a0SJoel Fernandes 
9594a9671a0SJoel Fernandes 	mm_size = SZ_4K * ((1 << (max_order + 1)) - 1);
9604a9671a0SJoel Fernandes 
961ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K),
9624a9671a0SJoel Fernandes 			       "buddy_init failed\n");
9634a9671a0SJoel Fernandes 
9644a9671a0SJoel Fernandes 	KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
9654a9671a0SJoel Fernandes 
9664a9671a0SJoel Fernandes 	for (order = 0; order <= max_order; order++) {
9674a9671a0SJoel Fernandes 		size = get_size(order, mm.chunk_size);
968ba110db8SJoel Fernandes 		KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size,
9694a9671a0SJoel Fernandes 								    size, size, &tmp, flags),
9704a9671a0SJoel Fernandes 							   "buddy_alloc hit -ENOMEM with order=%d\n",
9714a9671a0SJoel Fernandes 							   order);
9724a9671a0SJoel Fernandes 
973ba110db8SJoel Fernandes 		block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link);
9744a9671a0SJoel Fernandes 		KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
9754a9671a0SJoel Fernandes 
9764a9671a0SJoel Fernandes 		list_move_tail(&block->link, &blocks);
9774a9671a0SJoel Fernandes 	}
9784a9671a0SJoel Fernandes 
9794a9671a0SJoel Fernandes 	/* Should be completely full! */
9804a9671a0SJoel Fernandes 	size = get_size(0, mm.chunk_size);
981ba110db8SJoel Fernandes 	KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size,
9824a9671a0SJoel Fernandes 							   size, size, &tmp, flags),
9834a9671a0SJoel Fernandes 						  "buddy_alloc unexpectedly succeeded, it should be full!");
9844a9671a0SJoel Fernandes 
985ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &blocks, 0);
986ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
9874a9671a0SJoel Fernandes }
9884a9671a0SJoel Fernandes 
989ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_limit(struct kunit *test)
9904a9671a0SJoel Fernandes {
9914a9671a0SJoel Fernandes 	u64 size = U64_MAX, start = 0;
992ba110db8SJoel Fernandes 	struct gpu_buddy_block *block;
9934a9671a0SJoel Fernandes 	unsigned long flags = 0;
9944a9671a0SJoel Fernandes 	LIST_HEAD(allocated);
995ba110db8SJoel Fernandes 	struct gpu_buddy mm;
9964a9671a0SJoel Fernandes 
997ba110db8SJoel Fernandes 	KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, size, SZ_4K));
9984a9671a0SJoel Fernandes 
999ba110db8SJoel Fernandes 	KUNIT_EXPECT_EQ_MSG(test, mm.max_order, GPU_BUDDY_MAX_ORDER,
10004a9671a0SJoel Fernandes 			    "mm.max_order(%d) != %d\n", mm.max_order,
1001ba110db8SJoel Fernandes 						GPU_BUDDY_MAX_ORDER);
10024a9671a0SJoel Fernandes 
10034a9671a0SJoel Fernandes 	size = mm.chunk_size << mm.max_order;
1004ba110db8SJoel Fernandes 	KUNIT_EXPECT_FALSE(test, gpu_buddy_alloc_blocks(&mm, start, size, size,
10054a9671a0SJoel Fernandes 							mm.chunk_size, &allocated, flags));
10064a9671a0SJoel Fernandes 
1007ba110db8SJoel Fernandes 	block = list_first_entry_or_null(&allocated, struct gpu_buddy_block, link);
10084a9671a0SJoel Fernandes 	KUNIT_EXPECT_TRUE(test, block);
10094a9671a0SJoel Fernandes 
1010ba110db8SJoel Fernandes 	KUNIT_EXPECT_EQ_MSG(test, gpu_buddy_block_order(block), mm.max_order,
10114a9671a0SJoel Fernandes 			    "block order(%d) != %d\n",
1012ba110db8SJoel Fernandes 						gpu_buddy_block_order(block), mm.max_order);
10134a9671a0SJoel Fernandes 
1014ba110db8SJoel Fernandes 	KUNIT_EXPECT_EQ_MSG(test, gpu_buddy_block_size(&mm, block),
10154a9671a0SJoel Fernandes 			    BIT_ULL(mm.max_order) * mm.chunk_size,
10164a9671a0SJoel Fernandes 						"block size(%llu) != %llu\n",
1017ba110db8SJoel Fernandes 						gpu_buddy_block_size(&mm, block),
10184a9671a0SJoel Fernandes 						BIT_ULL(mm.max_order) * mm.chunk_size);
10194a9671a0SJoel Fernandes 
1020ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &allocated, 0);
1021ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
10224a9671a0SJoel Fernandes }
10234a9671a0SJoel Fernandes 
1024ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_exceeds_max_order(struct kunit *test)
10254a9671a0SJoel Fernandes {
10264a9671a0SJoel Fernandes 	u64 mm_size = SZ_8G + SZ_2G, size = SZ_8G + SZ_1G, min_block_size = SZ_8G;
1027ba110db8SJoel Fernandes 	struct gpu_buddy mm;
10284a9671a0SJoel Fernandes 	LIST_HEAD(blocks);
10294a9671a0SJoel Fernandes 	int err;
10304a9671a0SJoel Fernandes 
1031ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K),
10324a9671a0SJoel Fernandes 			       "buddy_init failed\n");
10334a9671a0SJoel Fernandes 
10344a9671a0SJoel Fernandes 	/* CONTIGUOUS allocation should succeed via try_harder fallback */
1035ba110db8SJoel Fernandes 	KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, size,
10364a9671a0SJoel Fernandes 							    SZ_4K, &blocks,
1037ba110db8SJoel Fernandes 							    GPU_BUDDY_CONTIGUOUS_ALLOCATION),
10384a9671a0SJoel Fernandes 			       "buddy_alloc hit an error size=%llu\n", size);
1039ba110db8SJoel Fernandes 	gpu_buddy_free_list(&mm, &blocks, 0);
10404a9671a0SJoel Fernandes 
10414a9671a0SJoel Fernandes 	/* Non-CONTIGUOUS with large min_block_size should return -EINVAL */
1042ba110db8SJoel Fernandes 	err = gpu_buddy_alloc_blocks(&mm, 0, mm_size, size, min_block_size, &blocks, 0);
10434a9671a0SJoel Fernandes 	KUNIT_EXPECT_EQ(test, err, -EINVAL);
10444a9671a0SJoel Fernandes 
10454a9671a0SJoel Fernandes 	/* Non-CONTIGUOUS + RANGE with large min_block_size should return -EINVAL */
1046ba110db8SJoel Fernandes 	err = gpu_buddy_alloc_blocks(&mm, 0, mm_size, size, min_block_size, &blocks,
1047ba110db8SJoel Fernandes 				     GPU_BUDDY_RANGE_ALLOCATION);
10484a9671a0SJoel Fernandes 	KUNIT_EXPECT_EQ(test, err, -EINVAL);
10494a9671a0SJoel Fernandes 
10504a9671a0SJoel Fernandes 	/* CONTIGUOUS + RANGE should return -EINVAL (no try_harder for RANGE) */
1051ba110db8SJoel Fernandes 	err = gpu_buddy_alloc_blocks(&mm, 0, mm_size, size, SZ_4K, &blocks,
1052ba110db8SJoel Fernandes 				     GPU_BUDDY_CONTIGUOUS_ALLOCATION | GPU_BUDDY_RANGE_ALLOCATION);
10534a9671a0SJoel Fernandes 	KUNIT_EXPECT_EQ(test, err, -EINVAL);
10544a9671a0SJoel Fernandes 
1055ba110db8SJoel Fernandes 	gpu_buddy_fini(&mm);
10564a9671a0SJoel Fernandes }
10574a9671a0SJoel Fernandes 
1058ba110db8SJoel Fernandes static int gpu_buddy_suite_init(struct kunit_suite *suite)
10594a9671a0SJoel Fernandes {
10604a9671a0SJoel Fernandes 	while (!random_seed)
10614a9671a0SJoel Fernandes 		random_seed = get_random_u32();
10624a9671a0SJoel Fernandes 
1063ba110db8SJoel Fernandes 	kunit_info(suite, "Testing GPU buddy manager, with random_seed=0x%x\n",
10644a9671a0SJoel Fernandes 		   random_seed);
10654a9671a0SJoel Fernandes 
10664a9671a0SJoel Fernandes 	return 0;
10674a9671a0SJoel Fernandes }
10684a9671a0SJoel Fernandes 
1069ba110db8SJoel Fernandes static struct kunit_case gpu_buddy_tests[] = {
1070ba110db8SJoel Fernandes 	KUNIT_CASE(gpu_test_buddy_alloc_limit),
1071ba110db8SJoel Fernandes 	KUNIT_CASE(gpu_test_buddy_alloc_optimistic),
1072ba110db8SJoel Fernandes 	KUNIT_CASE(gpu_test_buddy_alloc_pessimistic),
1073ba110db8SJoel Fernandes 	KUNIT_CASE(gpu_test_buddy_alloc_pathological),
1074ba110db8SJoel Fernandes 	KUNIT_CASE(gpu_test_buddy_alloc_contiguous),
1075ba110db8SJoel Fernandes 	KUNIT_CASE(gpu_test_buddy_alloc_clear),
1076ba110db8SJoel Fernandes 	KUNIT_CASE(gpu_test_buddy_alloc_range_bias),
10775ea5b6ffSMaxime Ripard 	KUNIT_CASE_SLOW(gpu_test_buddy_fragmentation_performance),
1078ba110db8SJoel Fernandes 	KUNIT_CASE(gpu_test_buddy_alloc_exceeds_max_order),
1079*f5bd7da0SArunpravin Paneer Selvam 	KUNIT_CASE(gpu_test_buddy_offset_aligned_allocation),
1080*f5bd7da0SArunpravin Paneer Selvam 	KUNIT_CASE(gpu_test_buddy_subtree_offset_alignment_stress),
10814a9671a0SJoel Fernandes 	{}
10824a9671a0SJoel Fernandes };
10834a9671a0SJoel Fernandes 
1084ba110db8SJoel Fernandes static struct kunit_suite gpu_buddy_test_suite = {
1085ba110db8SJoel Fernandes 	.name = "gpu_buddy",
1086ba110db8SJoel Fernandes 	.suite_init = gpu_buddy_suite_init,
1087ba110db8SJoel Fernandes 	.test_cases = gpu_buddy_tests,
10884a9671a0SJoel Fernandes };
10894a9671a0SJoel Fernandes 
1090ba110db8SJoel Fernandes kunit_test_suite(gpu_buddy_test_suite);
10914a9671a0SJoel Fernandes 
10924a9671a0SJoel Fernandes MODULE_AUTHOR("Intel Corporation");
1093ba110db8SJoel Fernandes MODULE_DESCRIPTION("Kunit test for gpu_buddy functions");
10944a9671a0SJoel Fernandes MODULE_LICENSE("GPL");
1095