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