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 24f5bd7da0SArunpravin Paneer Selvam static void gpu_test_buddy_subtree_offset_alignment_stress(struct kunit *test) 25f5bd7da0SArunpravin Paneer Selvam { 26f5bd7da0SArunpravin Paneer Selvam struct gpu_buddy_block *block; 27f5bd7da0SArunpravin Paneer Selvam struct rb_node *node = NULL; 28f5bd7da0SArunpravin Paneer Selvam const u64 mm_size = SZ_2M; 29f5bd7da0SArunpravin Paneer Selvam const u64 alignments[] = { 30f5bd7da0SArunpravin Paneer Selvam SZ_1M, 31f5bd7da0SArunpravin Paneer Selvam SZ_512K, 32f5bd7da0SArunpravin Paneer Selvam SZ_256K, 33f5bd7da0SArunpravin Paneer Selvam SZ_128K, 34f5bd7da0SArunpravin Paneer Selvam SZ_64K, 35f5bd7da0SArunpravin Paneer Selvam SZ_32K, 36f5bd7da0SArunpravin Paneer Selvam SZ_16K, 37f5bd7da0SArunpravin Paneer Selvam SZ_8K, 38f5bd7da0SArunpravin Paneer Selvam }; 39f5bd7da0SArunpravin Paneer Selvam struct list_head allocated[ARRAY_SIZE(alignments)]; 40f5bd7da0SArunpravin Paneer Selvam unsigned int i, max_subtree_align = 0; 41f5bd7da0SArunpravin Paneer Selvam int ret, tree, order; 42f5bd7da0SArunpravin Paneer Selvam struct gpu_buddy mm; 43f5bd7da0SArunpravin Paneer Selvam 44f5bd7da0SArunpravin Paneer Selvam KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), 45f5bd7da0SArunpravin Paneer Selvam "buddy_init failed\n"); 46f5bd7da0SArunpravin Paneer Selvam 47f5bd7da0SArunpravin Paneer Selvam for (i = 0; i < ARRAY_SIZE(allocated); i++) 48f5bd7da0SArunpravin Paneer Selvam INIT_LIST_HEAD(&allocated[i]); 49f5bd7da0SArunpravin Paneer Selvam 50f5bd7da0SArunpravin Paneer Selvam /* 51f5bd7da0SArunpravin Paneer Selvam * Exercise subtree_max_alignment tracking by allocating blocks with descending 52f5bd7da0SArunpravin Paneer Selvam * alignment constraints and freeing them in reverse order. This verifies that 53f5bd7da0SArunpravin Paneer Selvam * free-tree augmentation correctly propagates the maximum offset alignment 54f5bd7da0SArunpravin Paneer Selvam * present in each subtree at every stage. 55f5bd7da0SArunpravin Paneer Selvam */ 56f5bd7da0SArunpravin Paneer Selvam 57f5bd7da0SArunpravin Paneer Selvam for (i = 0; i < ARRAY_SIZE(alignments); i++) { 58f5bd7da0SArunpravin Paneer Selvam struct gpu_buddy_block *root = NULL; 59f5bd7da0SArunpravin Paneer Selvam unsigned int expected; 60f5bd7da0SArunpravin Paneer Selvam u64 align; 61f5bd7da0SArunpravin Paneer Selvam 62f5bd7da0SArunpravin Paneer Selvam align = alignments[i]; 63f5bd7da0SArunpravin Paneer Selvam expected = ilog2(align) - 1; 64f5bd7da0SArunpravin Paneer Selvam 65f5bd7da0SArunpravin Paneer Selvam for (;;) { 66f5bd7da0SArunpravin Paneer Selvam ret = gpu_buddy_alloc_blocks(&mm, 67f5bd7da0SArunpravin Paneer Selvam 0, mm_size, 68f5bd7da0SArunpravin Paneer Selvam SZ_4K, align, 69f5bd7da0SArunpravin Paneer Selvam &allocated[i], 70f5bd7da0SArunpravin Paneer Selvam 0); 71f5bd7da0SArunpravin Paneer Selvam if (ret) 72f5bd7da0SArunpravin Paneer Selvam break; 73f5bd7da0SArunpravin Paneer Selvam 74f5bd7da0SArunpravin Paneer Selvam block = list_last_entry(&allocated[i], 75f5bd7da0SArunpravin Paneer Selvam struct gpu_buddy_block, 76f5bd7da0SArunpravin Paneer Selvam link); 77f5bd7da0SArunpravin Paneer Selvam KUNIT_EXPECT_TRUE(test, IS_ALIGNED(gpu_buddy_block_offset(block), align)); 78f5bd7da0SArunpravin Paneer Selvam } 79f5bd7da0SArunpravin Paneer Selvam 80f5bd7da0SArunpravin Paneer Selvam for (order = mm.max_order; order >= 0 && !root; order--) { 81f5bd7da0SArunpravin Paneer Selvam for (tree = 0; tree < 2; tree++) { 82f5bd7da0SArunpravin Paneer Selvam node = mm.free_trees[tree][order].rb_node; 83f5bd7da0SArunpravin Paneer Selvam if (node) { 84f5bd7da0SArunpravin Paneer Selvam root = container_of(node, 85f5bd7da0SArunpravin Paneer Selvam struct gpu_buddy_block, 86f5bd7da0SArunpravin Paneer Selvam rb); 87f5bd7da0SArunpravin Paneer Selvam break; 88f5bd7da0SArunpravin Paneer Selvam } 89f5bd7da0SArunpravin Paneer Selvam } 90f5bd7da0SArunpravin Paneer Selvam } 91f5bd7da0SArunpravin Paneer Selvam 92f5bd7da0SArunpravin Paneer Selvam KUNIT_ASSERT_NOT_NULL(test, root); 93f5bd7da0SArunpravin Paneer Selvam KUNIT_EXPECT_EQ(test, root->subtree_max_alignment, expected); 94f5bd7da0SArunpravin Paneer Selvam } 95f5bd7da0SArunpravin Paneer Selvam 96f5bd7da0SArunpravin Paneer Selvam for (i = ARRAY_SIZE(alignments); i-- > 0; ) { 97f5bd7da0SArunpravin Paneer Selvam gpu_buddy_free_list(&mm, &allocated[i], 0); 98f5bd7da0SArunpravin Paneer Selvam 99f5bd7da0SArunpravin Paneer Selvam for (order = 0; order <= mm.max_order; order++) { 100f5bd7da0SArunpravin Paneer Selvam for (tree = 0; tree < 2; tree++) { 101f5bd7da0SArunpravin Paneer Selvam node = mm.free_trees[tree][order].rb_node; 102f5bd7da0SArunpravin Paneer Selvam if (!node) 103f5bd7da0SArunpravin Paneer Selvam continue; 104f5bd7da0SArunpravin Paneer Selvam 105f5bd7da0SArunpravin Paneer Selvam block = container_of(node, struct gpu_buddy_block, rb); 106f5bd7da0SArunpravin Paneer Selvam max_subtree_align = max(max_subtree_align, 107f5bd7da0SArunpravin Paneer Selvam block->subtree_max_alignment); 108f5bd7da0SArunpravin Paneer Selvam } 109f5bd7da0SArunpravin Paneer Selvam } 110f5bd7da0SArunpravin Paneer Selvam 111f5bd7da0SArunpravin Paneer Selvam KUNIT_EXPECT_GE(test, max_subtree_align, ilog2(alignments[i])); 112f5bd7da0SArunpravin Paneer Selvam } 113f5bd7da0SArunpravin Paneer Selvam 114f5bd7da0SArunpravin Paneer Selvam gpu_buddy_fini(&mm); 115f5bd7da0SArunpravin Paneer Selvam } 116f5bd7da0SArunpravin Paneer Selvam 117f5bd7da0SArunpravin Paneer Selvam static void gpu_test_buddy_offset_aligned_allocation(struct kunit *test) 118f5bd7da0SArunpravin Paneer Selvam { 119f5bd7da0SArunpravin Paneer Selvam struct gpu_buddy_block *block, *tmp; 120f5bd7da0SArunpravin Paneer Selvam int num_blocks, i, count = 0; 121f5bd7da0SArunpravin Paneer Selvam LIST_HEAD(allocated); 122f5bd7da0SArunpravin Paneer Selvam struct gpu_buddy mm; 123f5bd7da0SArunpravin Paneer Selvam u64 mm_size = SZ_4M; 124f5bd7da0SArunpravin Paneer Selvam LIST_HEAD(freed); 125f5bd7da0SArunpravin Paneer Selvam 126f5bd7da0SArunpravin Paneer Selvam KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), 127f5bd7da0SArunpravin Paneer Selvam "buddy_init failed\n"); 128f5bd7da0SArunpravin Paneer Selvam 129f5bd7da0SArunpravin Paneer Selvam num_blocks = mm_size / SZ_256K; 130f5bd7da0SArunpravin Paneer Selvam /* 131f5bd7da0SArunpravin Paneer Selvam * Allocate multiple sizes under a fixed offset alignment. 132f5bd7da0SArunpravin Paneer Selvam * Ensures alignment handling is independent of allocation size and 133f5bd7da0SArunpravin Paneer Selvam * exercises subtree max-alignment pruning for small requests. 134f5bd7da0SArunpravin Paneer Selvam */ 135f5bd7da0SArunpravin Paneer Selvam for (i = 0; i < num_blocks; i++) 136f5bd7da0SArunpravin Paneer Selvam KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_8K, SZ_256K, 137f5bd7da0SArunpravin Paneer Selvam &allocated, 0), 138f5bd7da0SArunpravin Paneer Selvam "buddy_alloc hit an error size=%u\n", SZ_8K); 139f5bd7da0SArunpravin Paneer Selvam 140f5bd7da0SArunpravin Paneer Selvam list_for_each_entry(block, &allocated, link) { 141f5bd7da0SArunpravin Paneer Selvam /* Ensure the allocated block uses the expected 8 KB size */ 142f5bd7da0SArunpravin Paneer Selvam KUNIT_EXPECT_EQ(test, gpu_buddy_block_size(&mm, block), SZ_8K); 143f5bd7da0SArunpravin Paneer Selvam /* Ensure the block starts at a 256 KB-aligned offset for proper alignment */ 144f5bd7da0SArunpravin Paneer Selvam KUNIT_EXPECT_TRUE(test, IS_ALIGNED(gpu_buddy_block_offset(block), SZ_256K)); 145f5bd7da0SArunpravin Paneer Selvam } 146f5bd7da0SArunpravin Paneer Selvam gpu_buddy_free_list(&mm, &allocated, 0); 147f5bd7da0SArunpravin Paneer Selvam 148f5bd7da0SArunpravin Paneer Selvam for (i = 0; i < num_blocks; i++) 149f5bd7da0SArunpravin Paneer Selvam KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_16K, SZ_256K, 150f5bd7da0SArunpravin Paneer Selvam &allocated, 0), 151f5bd7da0SArunpravin Paneer Selvam "buddy_alloc hit an error size=%u\n", SZ_16K); 152f5bd7da0SArunpravin Paneer Selvam 153f5bd7da0SArunpravin Paneer Selvam list_for_each_entry(block, &allocated, link) { 154f5bd7da0SArunpravin Paneer Selvam /* Ensure the allocated block uses the expected 16 KB size */ 155f5bd7da0SArunpravin Paneer Selvam KUNIT_EXPECT_EQ(test, gpu_buddy_block_size(&mm, block), SZ_16K); 156f5bd7da0SArunpravin Paneer Selvam /* Ensure the block starts at a 256 KB-aligned offset for proper alignment */ 157f5bd7da0SArunpravin Paneer Selvam KUNIT_EXPECT_TRUE(test, IS_ALIGNED(gpu_buddy_block_offset(block), SZ_256K)); 158f5bd7da0SArunpravin Paneer Selvam } 159f5bd7da0SArunpravin Paneer Selvam 160f5bd7da0SArunpravin Paneer Selvam /* 161f5bd7da0SArunpravin Paneer Selvam * Free alternating aligned blocks to introduce fragmentation. 162f5bd7da0SArunpravin Paneer Selvam * Ensures offset-aligned allocations remain valid after frees and 163f5bd7da0SArunpravin Paneer Selvam * verifies subtree max-alignment metadata is correctly maintained. 164f5bd7da0SArunpravin Paneer Selvam */ 165f5bd7da0SArunpravin Paneer Selvam list_for_each_entry_safe(block, tmp, &allocated, link) { 166f5bd7da0SArunpravin Paneer Selvam if (count % 2 == 0) 167f5bd7da0SArunpravin Paneer Selvam list_move_tail(&block->link, &freed); 168f5bd7da0SArunpravin Paneer Selvam count++; 169f5bd7da0SArunpravin Paneer Selvam } 170f5bd7da0SArunpravin Paneer Selvam gpu_buddy_free_list(&mm, &freed, 0); 171f5bd7da0SArunpravin Paneer Selvam 172f5bd7da0SArunpravin Paneer Selvam for (i = 0; i < num_blocks / 2; i++) 173f5bd7da0SArunpravin Paneer Selvam KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_16K, SZ_256K, 174f5bd7da0SArunpravin Paneer Selvam &allocated, 0), 175f5bd7da0SArunpravin Paneer Selvam "buddy_alloc hit an error size=%u\n", SZ_16K); 176f5bd7da0SArunpravin Paneer Selvam 177f5bd7da0SArunpravin Paneer Selvam /* 178f5bd7da0SArunpravin Paneer Selvam * Allocate with offset alignment after all slots are used; must fail. 179f5bd7da0SArunpravin Paneer Selvam * Confirms that no aligned offsets remain. 180f5bd7da0SArunpravin Paneer Selvam */ 181f5bd7da0SArunpravin Paneer Selvam KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, SZ_16K, SZ_256K, 182f5bd7da0SArunpravin Paneer Selvam &allocated, 0), 183f5bd7da0SArunpravin Paneer Selvam "buddy_alloc hit an error size=%u\n", SZ_16K); 184f5bd7da0SArunpravin Paneer Selvam gpu_buddy_free_list(&mm, &allocated, 0); 185f5bd7da0SArunpravin Paneer Selvam gpu_buddy_fini(&mm); 186f5bd7da0SArunpravin Paneer Selvam } 187f5bd7da0SArunpravin 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 529*bdca18a6SSanjay Yadav static void gpu_test_buddy_alloc_range(struct kunit *test) 530*bdca18a6SSanjay Yadav { 531*bdca18a6SSanjay Yadav GPU_RND_STATE(prng, random_seed); 532*bdca18a6SSanjay Yadav struct gpu_buddy_block *block; 533*bdca18a6SSanjay Yadav struct gpu_buddy mm; 534*bdca18a6SSanjay Yadav u32 mm_size, total; 535*bdca18a6SSanjay Yadav LIST_HEAD(blocks); 536*bdca18a6SSanjay Yadav LIST_HEAD(tmp); 537*bdca18a6SSanjay Yadav u32 ps = SZ_4K; 538*bdca18a6SSanjay Yadav int ret; 539*bdca18a6SSanjay Yadav 540*bdca18a6SSanjay Yadav mm_size = SZ_16M; 541*bdca18a6SSanjay Yadav 542*bdca18a6SSanjay Yadav KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, ps), 543*bdca18a6SSanjay Yadav "buddy_init failed\n"); 544*bdca18a6SSanjay Yadav 545*bdca18a6SSanjay Yadav /* 546*bdca18a6SSanjay Yadav * Basic exact-range allocation. 547*bdca18a6SSanjay Yadav * Allocate the entire mm as one exact range (start + size == end). 548*bdca18a6SSanjay Yadav * This is the simplest case exercising __gpu_buddy_alloc_range. 549*bdca18a6SSanjay Yadav */ 550*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, mm_size, mm_size, ps, &blocks, 0); 551*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ_MSG(test, ret, 0, 552*bdca18a6SSanjay Yadav "exact-range alloc of full mm failed\n"); 553*bdca18a6SSanjay Yadav 554*bdca18a6SSanjay Yadav total = 0; 555*bdca18a6SSanjay Yadav list_for_each_entry(block, &blocks, link) { 556*bdca18a6SSanjay Yadav u64 offset = gpu_buddy_block_offset(block); 557*bdca18a6SSanjay Yadav u64 bsize = gpu_buddy_block_size(&mm, block); 558*bdca18a6SSanjay Yadav 559*bdca18a6SSanjay Yadav KUNIT_EXPECT_TRUE_MSG(test, offset + bsize <= (u64)mm_size, 560*bdca18a6SSanjay Yadav "block [%llx, %llx) outside mm\n", offset, offset + bsize); 561*bdca18a6SSanjay Yadav total += (u32)bsize; 562*bdca18a6SSanjay Yadav } 563*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, total, mm_size); 564*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); 565*bdca18a6SSanjay Yadav 566*bdca18a6SSanjay Yadav /* Full mm should be exhausted */ 567*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, ps, ps, ps, &tmp, 0); 568*bdca18a6SSanjay Yadav KUNIT_EXPECT_NE_MSG(test, ret, 0, "alloc should fail when mm is full\n"); 569*bdca18a6SSanjay Yadav 570*bdca18a6SSanjay Yadav gpu_buddy_free_list(&mm, &blocks, 0); 571*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, mm.avail, (u64)mm_size); 572*bdca18a6SSanjay Yadav gpu_buddy_fini(&mm); 573*bdca18a6SSanjay Yadav 574*bdca18a6SSanjay Yadav /* 575*bdca18a6SSanjay Yadav * Exact-range allocation of sub-ranges. 576*bdca18a6SSanjay Yadav * Split the mm into four equal quarters and allocate each as an exact 577*bdca18a6SSanjay Yadav * range. Validates splitting and non-overlapping exact allocations. 578*bdca18a6SSanjay Yadav */ 579*bdca18a6SSanjay Yadav KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 580*bdca18a6SSanjay Yadav 581*bdca18a6SSanjay Yadav { 582*bdca18a6SSanjay Yadav u32 quarter = mm_size / 4; 583*bdca18a6SSanjay Yadav int i; 584*bdca18a6SSanjay Yadav 585*bdca18a6SSanjay Yadav for (i = 0; i < 4; i++) { 586*bdca18a6SSanjay Yadav u32 start = i * quarter; 587*bdca18a6SSanjay Yadav u32 end = start + quarter; 588*bdca18a6SSanjay Yadav 589*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, start, end, quarter, ps, &blocks, 0); 590*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ_MSG(test, ret, 0, 591*bdca18a6SSanjay Yadav "exact-range alloc quarter %d [%x, %x) failed\n", 592*bdca18a6SSanjay Yadav i, start, end); 593*bdca18a6SSanjay Yadav } 594*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); 595*bdca18a6SSanjay Yadav gpu_buddy_free_list(&mm, &blocks, 0); 596*bdca18a6SSanjay Yadav } 597*bdca18a6SSanjay Yadav 598*bdca18a6SSanjay Yadav gpu_buddy_fini(&mm); 599*bdca18a6SSanjay Yadav 600*bdca18a6SSanjay Yadav /* 601*bdca18a6SSanjay Yadav * Minimum chunk-size exact range at various offsets. 602*bdca18a6SSanjay Yadav * Allocate single-page exact ranges at the start, middle and end. 603*bdca18a6SSanjay Yadav */ 604*bdca18a6SSanjay Yadav KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 605*bdca18a6SSanjay Yadav 606*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, ps, ps, ps, &blocks, 0); 607*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 608*bdca18a6SSanjay Yadav 609*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, mm_size / 2, mm_size / 2 + ps, ps, ps, &blocks, 0); 610*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 611*bdca18a6SSanjay Yadav 612*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, mm_size - ps, mm_size, ps, ps, &blocks, 0); 613*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 614*bdca18a6SSanjay Yadav 615*bdca18a6SSanjay Yadav total = 0; 616*bdca18a6SSanjay Yadav list_for_each_entry(block, &blocks, link) 617*bdca18a6SSanjay Yadav total += (u32)gpu_buddy_block_size(&mm, block); 618*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, total, 3 * ps); 619*bdca18a6SSanjay Yadav 620*bdca18a6SSanjay Yadav gpu_buddy_free_list(&mm, &blocks, 0); 621*bdca18a6SSanjay Yadav gpu_buddy_fini(&mm); 622*bdca18a6SSanjay Yadav 623*bdca18a6SSanjay Yadav /* 624*bdca18a6SSanjay Yadav * Non power-of-two mm size (multiple roots). 625*bdca18a6SSanjay Yadav * Exact-range allocations that span root boundaries must still work. 626*bdca18a6SSanjay Yadav */ 627*bdca18a6SSanjay Yadav mm_size = SZ_4M + SZ_2M + SZ_1M; /* 7 MiB, three roots */ 628*bdca18a6SSanjay Yadav 629*bdca18a6SSanjay Yadav KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 630*bdca18a6SSanjay Yadav KUNIT_EXPECT_GT(test, mm.n_roots, 1U); 631*bdca18a6SSanjay Yadav 632*bdca18a6SSanjay Yadav /* Allocate first 4M root exactly */ 633*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, SZ_4M, SZ_4M, ps, &blocks, 0); 634*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 635*bdca18a6SSanjay Yadav 636*bdca18a6SSanjay Yadav /* Allocate second root (4M-6M) exactly */ 637*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, SZ_4M, SZ_4M + SZ_2M, SZ_2M, ps, &blocks, 0); 638*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 639*bdca18a6SSanjay Yadav 640*bdca18a6SSanjay Yadav /* Allocate third root (6M-7M) exactly */ 641*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, SZ_4M + SZ_2M, mm_size, SZ_1M, ps, &blocks, 0); 642*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 643*bdca18a6SSanjay Yadav 644*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); 645*bdca18a6SSanjay Yadav gpu_buddy_free_list(&mm, &blocks, 0); 646*bdca18a6SSanjay Yadav 647*bdca18a6SSanjay Yadav /* Cross-root exact-range: the entire non-pot mm */ 648*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, mm_size, mm_size, ps, &blocks, 0); 649*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 650*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); 651*bdca18a6SSanjay Yadav 652*bdca18a6SSanjay Yadav gpu_buddy_free_list(&mm, &blocks, 0); 653*bdca18a6SSanjay Yadav gpu_buddy_fini(&mm); 654*bdca18a6SSanjay Yadav 655*bdca18a6SSanjay Yadav /* 656*bdca18a6SSanjay Yadav * Randomized exact-range allocations. 657*bdca18a6SSanjay Yadav * Divide the mm into N random-sized, contiguous, page-aligned slices 658*bdca18a6SSanjay Yadav * and allocate each as an exact range in random order. 659*bdca18a6SSanjay Yadav */ 660*bdca18a6SSanjay Yadav mm_size = SZ_16M; 661*bdca18a6SSanjay Yadav KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 662*bdca18a6SSanjay Yadav 663*bdca18a6SSanjay Yadav { 664*bdca18a6SSanjay Yadav #define N_RAND_RANGES 16 665*bdca18a6SSanjay Yadav u32 ranges[N_RAND_RANGES + 1]; /* boundaries */ 666*bdca18a6SSanjay Yadav u32 order_arr[N_RAND_RANGES]; 667*bdca18a6SSanjay Yadav u32 remaining = mm_size; 668*bdca18a6SSanjay Yadav int i; 669*bdca18a6SSanjay Yadav 670*bdca18a6SSanjay Yadav ranges[0] = 0; 671*bdca18a6SSanjay Yadav for (i = 0; i < N_RAND_RANGES - 1; i++) { 672*bdca18a6SSanjay Yadav u32 max_chunk = remaining - (N_RAND_RANGES - 1 - i) * ps; 673*bdca18a6SSanjay Yadav u32 sz = max(round_up(prandom_u32_state(&prng) % max_chunk, ps), ps); 674*bdca18a6SSanjay Yadav 675*bdca18a6SSanjay Yadav ranges[i + 1] = ranges[i] + sz; 676*bdca18a6SSanjay Yadav remaining -= sz; 677*bdca18a6SSanjay Yadav } 678*bdca18a6SSanjay Yadav ranges[N_RAND_RANGES] = mm_size; 679*bdca18a6SSanjay Yadav 680*bdca18a6SSanjay Yadav /* Create a random order */ 681*bdca18a6SSanjay Yadav for (i = 0; i < N_RAND_RANGES; i++) 682*bdca18a6SSanjay Yadav order_arr[i] = i; 683*bdca18a6SSanjay Yadav for (i = N_RAND_RANGES - 1; i > 0; i--) { 684*bdca18a6SSanjay Yadav u32 j = prandom_u32_state(&prng) % (i + 1); 685*bdca18a6SSanjay Yadav u32 tmp_val = order_arr[i]; 686*bdca18a6SSanjay Yadav 687*bdca18a6SSanjay Yadav order_arr[i] = order_arr[j]; 688*bdca18a6SSanjay Yadav order_arr[j] = tmp_val; 689*bdca18a6SSanjay Yadav } 690*bdca18a6SSanjay Yadav 691*bdca18a6SSanjay Yadav for (i = 0; i < N_RAND_RANGES; i++) { 692*bdca18a6SSanjay Yadav u32 idx = order_arr[i]; 693*bdca18a6SSanjay Yadav u32 start = ranges[idx]; 694*bdca18a6SSanjay Yadav u32 end = ranges[idx + 1]; 695*bdca18a6SSanjay Yadav u32 sz = end - start; 696*bdca18a6SSanjay Yadav 697*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, start, end, sz, ps, &blocks, 0); 698*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ_MSG(test, ret, 0, 699*bdca18a6SSanjay Yadav "random exact-range [%x, %x) sz=%x failed\n", 700*bdca18a6SSanjay Yadav start, end, sz); 701*bdca18a6SSanjay Yadav } 702*bdca18a6SSanjay Yadav 703*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); 704*bdca18a6SSanjay Yadav gpu_buddy_free_list(&mm, &blocks, 0); 705*bdca18a6SSanjay Yadav #undef N_RAND_RANGES 706*bdca18a6SSanjay Yadav } 707*bdca18a6SSanjay Yadav 708*bdca18a6SSanjay Yadav gpu_buddy_fini(&mm); 709*bdca18a6SSanjay Yadav 710*bdca18a6SSanjay Yadav /* 711*bdca18a6SSanjay Yadav * Negative case - partially allocated range. 712*bdca18a6SSanjay Yadav * Allocate the first half, then try to exact-range allocate the full 713*bdca18a6SSanjay Yadav * mm. This must fail because the first half is already occupied. 714*bdca18a6SSanjay Yadav */ 715*bdca18a6SSanjay Yadav mm_size = SZ_16M; 716*bdca18a6SSanjay Yadav KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 717*bdca18a6SSanjay Yadav 718*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, mm_size / 2, mm_size / 2, ps, &blocks, 0); 719*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 720*bdca18a6SSanjay Yadav 721*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, mm_size, mm_size, ps, &tmp, 0); 722*bdca18a6SSanjay Yadav KUNIT_EXPECT_NE_MSG(test, ret, 0, 723*bdca18a6SSanjay Yadav "exact-range alloc should fail when range is partially used\n"); 724*bdca18a6SSanjay Yadav 725*bdca18a6SSanjay Yadav /* Also try the already-occupied sub-range directly */ 726*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, mm_size / 2, mm_size / 2, ps, &tmp, 0); 727*bdca18a6SSanjay Yadav KUNIT_EXPECT_NE_MSG(test, ret, 0, 728*bdca18a6SSanjay Yadav "double alloc of same exact range should fail\n"); 729*bdca18a6SSanjay Yadav 730*bdca18a6SSanjay Yadav /* The free second half should still be allocatable */ 731*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, mm_size / 2, mm_size, mm_size / 2, ps, &blocks, 0); 732*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 733*bdca18a6SSanjay Yadav 734*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, mm.avail, 0ULL); 735*bdca18a6SSanjay Yadav gpu_buddy_free_list(&mm, &blocks, 0); 736*bdca18a6SSanjay Yadav gpu_buddy_fini(&mm); 737*bdca18a6SSanjay Yadav 738*bdca18a6SSanjay Yadav /* 739*bdca18a6SSanjay Yadav * Negative case - checkerboard partial allocation. 740*bdca18a6SSanjay Yadav * Allocate every other page-sized chunk in a small mm, then try to 741*bdca18a6SSanjay Yadav * exact-range allocate a range covering two pages (one allocated, one 742*bdca18a6SSanjay Yadav * free). This must fail. 743*bdca18a6SSanjay Yadav */ 744*bdca18a6SSanjay Yadav mm_size = SZ_64K; 745*bdca18a6SSanjay Yadav KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 746*bdca18a6SSanjay Yadav 747*bdca18a6SSanjay Yadav { 748*bdca18a6SSanjay Yadav u32 off; 749*bdca18a6SSanjay Yadav 750*bdca18a6SSanjay Yadav for (off = 0; off < mm_size; off += 2 * ps) { 751*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, off, off + ps, ps, ps, &blocks, 0); 752*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 753*bdca18a6SSanjay Yadav } 754*bdca18a6SSanjay Yadav 755*bdca18a6SSanjay Yadav /* Try exact range over a pair [allocated, free] */ 756*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, 2 * ps, 2 * ps, ps, &tmp, 0); 757*bdca18a6SSanjay Yadav KUNIT_EXPECT_NE_MSG(test, ret, 0, 758*bdca18a6SSanjay Yadav "exact-range over partially allocated pair should fail\n"); 759*bdca18a6SSanjay Yadav 760*bdca18a6SSanjay Yadav /* The free pages individually should still work */ 761*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, ps, 2 * ps, ps, ps, &blocks, 0); 762*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 763*bdca18a6SSanjay Yadav 764*bdca18a6SSanjay Yadav gpu_buddy_free_list(&mm, &blocks, 0); 765*bdca18a6SSanjay Yadav } 766*bdca18a6SSanjay Yadav 767*bdca18a6SSanjay Yadav gpu_buddy_fini(&mm); 768*bdca18a6SSanjay Yadav 769*bdca18a6SSanjay Yadav /* Negative case - misaligned start/end/size */ 770*bdca18a6SSanjay Yadav mm_size = SZ_16M; 771*bdca18a6SSanjay Yadav KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 772*bdca18a6SSanjay Yadav 773*bdca18a6SSanjay Yadav /* start not aligned to chunk_size */ 774*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, ps / 2, ps / 2 + ps, ps, ps, &tmp, 0); 775*bdca18a6SSanjay Yadav KUNIT_EXPECT_NE(test, ret, 0); 776*bdca18a6SSanjay Yadav 777*bdca18a6SSanjay Yadav /* size not aligned */ 778*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, ps + 1, ps + 1, ps, &tmp, 0); 779*bdca18a6SSanjay Yadav KUNIT_EXPECT_NE(test, ret, 0); 780*bdca18a6SSanjay Yadav 781*bdca18a6SSanjay Yadav /* end exceeds mm size */ 782*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, mm_size, mm_size + ps, ps, ps, &tmp, 0); 783*bdca18a6SSanjay Yadav KUNIT_EXPECT_NE(test, ret, 0); 784*bdca18a6SSanjay Yadav 785*bdca18a6SSanjay Yadav gpu_buddy_fini(&mm); 786*bdca18a6SSanjay Yadav 787*bdca18a6SSanjay Yadav /* 788*bdca18a6SSanjay Yadav * Free and re-allocate the same exact range. 789*bdca18a6SSanjay Yadav * This exercises merge-on-free followed by exact-range re-split. 790*bdca18a6SSanjay Yadav */ 791*bdca18a6SSanjay Yadav mm_size = SZ_16M; 792*bdca18a6SSanjay Yadav KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 793*bdca18a6SSanjay Yadav 794*bdca18a6SSanjay Yadav { 795*bdca18a6SSanjay Yadav int i; 796*bdca18a6SSanjay Yadav 797*bdca18a6SSanjay Yadav for (i = 0; i < 5; i++) { 798*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, SZ_4M, SZ_4M + SZ_2M, 799*bdca18a6SSanjay Yadav SZ_2M, ps, &blocks, 0); 800*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ_MSG(test, ret, 0, 801*bdca18a6SSanjay Yadav "re-alloc iteration %d failed\n", i); 802*bdca18a6SSanjay Yadav 803*bdca18a6SSanjay Yadav total = 0; 804*bdca18a6SSanjay Yadav list_for_each_entry(block, &blocks, link) { 805*bdca18a6SSanjay Yadav u64 offset = gpu_buddy_block_offset(block); 806*bdca18a6SSanjay Yadav u64 bsize = gpu_buddy_block_size(&mm, block); 807*bdca18a6SSanjay Yadav 808*bdca18a6SSanjay Yadav KUNIT_EXPECT_GE(test, offset, (u64)SZ_4M); 809*bdca18a6SSanjay Yadav KUNIT_EXPECT_LE(test, offset + bsize, (u64)(SZ_4M + SZ_2M)); 810*bdca18a6SSanjay Yadav total += (u32)bsize; 811*bdca18a6SSanjay Yadav } 812*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, total, SZ_2M); 813*bdca18a6SSanjay Yadav 814*bdca18a6SSanjay Yadav gpu_buddy_free_list(&mm, &blocks, 0); 815*bdca18a6SSanjay Yadav } 816*bdca18a6SSanjay Yadav 817*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, mm.avail, (u64)mm_size); 818*bdca18a6SSanjay Yadav } 819*bdca18a6SSanjay Yadav 820*bdca18a6SSanjay Yadav gpu_buddy_fini(&mm); 821*bdca18a6SSanjay Yadav 822*bdca18a6SSanjay Yadav /* 823*bdca18a6SSanjay Yadav * Various power-of-two exact ranges within a large mm. 824*bdca18a6SSanjay Yadav * Allocate non-overlapping power-of-two exact ranges at their natural 825*bdca18a6SSanjay Yadav * alignment, validating that the allocator handles different orders. 826*bdca18a6SSanjay Yadav */ 827*bdca18a6SSanjay Yadav mm_size = SZ_16M; 828*bdca18a6SSanjay Yadav KUNIT_ASSERT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 829*bdca18a6SSanjay Yadav 830*bdca18a6SSanjay Yadav /* Allocate 4K at offset 0 */ 831*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, 0, SZ_4K, SZ_4K, ps, &blocks, 0); 832*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 833*bdca18a6SSanjay Yadav 834*bdca18a6SSanjay Yadav /* Allocate 64K at offset 64K */ 835*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, SZ_64K, SZ_64K + SZ_64K, SZ_64K, ps, &blocks, 0); 836*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 837*bdca18a6SSanjay Yadav 838*bdca18a6SSanjay Yadav /* Allocate 1M at offset 1M */ 839*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, SZ_1M, SZ_1M + SZ_1M, SZ_1M, ps, &blocks, 0); 840*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 841*bdca18a6SSanjay Yadav 842*bdca18a6SSanjay Yadav /* Allocate 4M at offset 4M */ 843*bdca18a6SSanjay Yadav ret = gpu_buddy_alloc_blocks(&mm, SZ_4M, SZ_4M + SZ_4M, SZ_4M, ps, &blocks, 0); 844*bdca18a6SSanjay Yadav KUNIT_ASSERT_EQ(test, ret, 0); 845*bdca18a6SSanjay Yadav 846*bdca18a6SSanjay Yadav total = 0; 847*bdca18a6SSanjay Yadav list_for_each_entry(block, &blocks, link) 848*bdca18a6SSanjay Yadav total += (u32)gpu_buddy_block_size(&mm, block); 849*bdca18a6SSanjay Yadav KUNIT_EXPECT_EQ(test, total, SZ_4K + SZ_64K + SZ_1M + SZ_4M); 850*bdca18a6SSanjay Yadav 851*bdca18a6SSanjay Yadav gpu_buddy_free_list(&mm, &blocks, 0); 852*bdca18a6SSanjay Yadav gpu_buddy_fini(&mm); 853*bdca18a6SSanjay Yadav } 854*bdca18a6SSanjay Yadav 855ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_clear(struct kunit *test) 8564a9671a0SJoel Fernandes { 8574a9671a0SJoel Fernandes unsigned long n_pages, total, i = 0; 8584a9671a0SJoel Fernandes const unsigned long ps = SZ_4K; 859ba110db8SJoel Fernandes struct gpu_buddy_block *block; 8604a9671a0SJoel Fernandes const int max_order = 12; 8614a9671a0SJoel Fernandes LIST_HEAD(allocated); 862ba110db8SJoel Fernandes struct gpu_buddy mm; 8634a9671a0SJoel Fernandes unsigned int order; 8644a9671a0SJoel Fernandes u32 mm_size, size; 8654a9671a0SJoel Fernandes LIST_HEAD(dirty); 8664a9671a0SJoel Fernandes LIST_HEAD(clean); 8674a9671a0SJoel Fernandes 8684a9671a0SJoel Fernandes mm_size = SZ_4K << max_order; 869ba110db8SJoel Fernandes KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 8704a9671a0SJoel Fernandes 8714a9671a0SJoel Fernandes KUNIT_EXPECT_EQ(test, mm.max_order, max_order); 8724a9671a0SJoel Fernandes 8734a9671a0SJoel Fernandes /* 8744a9671a0SJoel Fernandes * Idea is to allocate and free some random portion of the address space, 8754a9671a0SJoel Fernandes * returning those pages as non-dirty and randomly alternate between 8764a9671a0SJoel Fernandes * requesting dirty and non-dirty pages (not going over the limit 8774a9671a0SJoel Fernandes * we freed as non-dirty), putting that into two separate lists. 8784a9671a0SJoel Fernandes * Loop over both lists at the end checking that the dirty list 8794a9671a0SJoel Fernandes * is indeed all dirty pages and vice versa. Free it all again, 8804a9671a0SJoel Fernandes * keeping the dirty/clear status. 8814a9671a0SJoel Fernandes */ 882ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 8834a9671a0SJoel Fernandes 5 * ps, ps, &allocated, 884ba110db8SJoel Fernandes GPU_BUDDY_TOPDOWN_ALLOCATION), 8854a9671a0SJoel Fernandes "buddy_alloc hit an error size=%lu\n", 5 * ps); 886ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED); 8874a9671a0SJoel Fernandes 8884a9671a0SJoel Fernandes n_pages = 10; 8894a9671a0SJoel Fernandes do { 8904a9671a0SJoel Fernandes unsigned long flags; 8914a9671a0SJoel Fernandes struct list_head *list; 8924a9671a0SJoel Fernandes int slot = i % 2; 8934a9671a0SJoel Fernandes 8944a9671a0SJoel Fernandes if (slot == 0) { 8954a9671a0SJoel Fernandes list = &dirty; 8964a9671a0SJoel Fernandes flags = 0; 8974a9671a0SJoel Fernandes } else { 8984a9671a0SJoel Fernandes list = &clean; 899ba110db8SJoel Fernandes flags = GPU_BUDDY_CLEAR_ALLOCATION; 9004a9671a0SJoel Fernandes } 9014a9671a0SJoel Fernandes 902ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 9034a9671a0SJoel Fernandes ps, ps, list, 9044a9671a0SJoel Fernandes flags), 9054a9671a0SJoel Fernandes "buddy_alloc hit an error size=%lu\n", ps); 9064a9671a0SJoel Fernandes } while (++i < n_pages); 9074a9671a0SJoel Fernandes 9084a9671a0SJoel Fernandes list_for_each_entry(block, &clean, link) 909ba110db8SJoel Fernandes KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), true); 9104a9671a0SJoel Fernandes 9114a9671a0SJoel Fernandes list_for_each_entry(block, &dirty, link) 912ba110db8SJoel Fernandes KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), false); 9134a9671a0SJoel Fernandes 914ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED); 9154a9671a0SJoel Fernandes 9164a9671a0SJoel Fernandes /* 9174a9671a0SJoel Fernandes * Trying to go over the clear limit for some allocation. 9184a9671a0SJoel Fernandes * The allocation should never fail with reasonable page-size. 9194a9671a0SJoel Fernandes */ 920ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 9214a9671a0SJoel Fernandes 10 * ps, ps, &clean, 922ba110db8SJoel Fernandes GPU_BUDDY_CLEAR_ALLOCATION), 9234a9671a0SJoel Fernandes "buddy_alloc hit an error size=%lu\n", 10 * ps); 9244a9671a0SJoel Fernandes 925ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED); 926ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &dirty, 0); 927ba110db8SJoel Fernandes gpu_buddy_fini(&mm); 9284a9671a0SJoel Fernandes 929ba110db8SJoel Fernandes KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 9304a9671a0SJoel Fernandes 9314a9671a0SJoel Fernandes /* 9324a9671a0SJoel Fernandes * Create a new mm. Intentionally fragment the address space by creating 9334a9671a0SJoel Fernandes * two alternating lists. Free both lists, one as dirty the other as clean. 9344a9671a0SJoel Fernandes * Try to allocate double the previous size with matching min_page_size. The 9354a9671a0SJoel Fernandes * allocation should never fail as it calls the force_merge. Also check that 9364a9671a0SJoel Fernandes * the page is always dirty after force_merge. Free the page as dirty, then 9374a9671a0SJoel Fernandes * repeat the whole thing, increment the order until we hit the max_order. 9384a9671a0SJoel Fernandes */ 9394a9671a0SJoel Fernandes 9404a9671a0SJoel Fernandes i = 0; 9414a9671a0SJoel Fernandes n_pages = mm_size / ps; 9424a9671a0SJoel Fernandes do { 9434a9671a0SJoel Fernandes struct list_head *list; 9444a9671a0SJoel Fernandes int slot = i % 2; 9454a9671a0SJoel Fernandes 9464a9671a0SJoel Fernandes if (slot == 0) 9474a9671a0SJoel Fernandes list = &dirty; 9484a9671a0SJoel Fernandes else 9494a9671a0SJoel Fernandes list = &clean; 9504a9671a0SJoel Fernandes 951ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 9524a9671a0SJoel Fernandes ps, ps, list, 0), 9534a9671a0SJoel Fernandes "buddy_alloc hit an error size=%lu\n", ps); 9544a9671a0SJoel Fernandes } while (++i < n_pages); 9554a9671a0SJoel Fernandes 956ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED); 957ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &dirty, 0); 9584a9671a0SJoel Fernandes 9594a9671a0SJoel Fernandes order = 1; 9604a9671a0SJoel Fernandes do { 9614a9671a0SJoel Fernandes size = SZ_4K << order; 9624a9671a0SJoel Fernandes 963ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 9644a9671a0SJoel Fernandes size, size, &allocated, 965ba110db8SJoel Fernandes GPU_BUDDY_CLEAR_ALLOCATION), 9664a9671a0SJoel Fernandes "buddy_alloc hit an error size=%u\n", size); 9674a9671a0SJoel Fernandes total = 0; 9684a9671a0SJoel Fernandes list_for_each_entry(block, &allocated, link) { 9694a9671a0SJoel Fernandes if (size != mm_size) 970ba110db8SJoel Fernandes KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), false); 971ba110db8SJoel Fernandes total += gpu_buddy_block_size(&mm, block); 9724a9671a0SJoel Fernandes } 9734a9671a0SJoel Fernandes KUNIT_EXPECT_EQ(test, total, size); 9744a9671a0SJoel Fernandes 975ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &allocated, 0); 9764a9671a0SJoel Fernandes } while (++order <= max_order); 9774a9671a0SJoel Fernandes 978ba110db8SJoel Fernandes gpu_buddy_fini(&mm); 9794a9671a0SJoel Fernandes 9804a9671a0SJoel Fernandes /* 9814a9671a0SJoel Fernandes * Create a new mm with a non power-of-two size. Allocate a random size from each 9824a9671a0SJoel Fernandes * root, free as cleared and then call fini. This will ensure the multi-root 9834a9671a0SJoel Fernandes * force merge during fini. 9844a9671a0SJoel Fernandes */ 9854a9671a0SJoel Fernandes mm_size = (SZ_4K << max_order) + (SZ_4K << (max_order - 2)); 9864a9671a0SJoel Fernandes 987ba110db8SJoel Fernandes KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 9884a9671a0SJoel Fernandes KUNIT_EXPECT_EQ(test, mm.max_order, max_order); 989ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, SZ_4K << max_order, 9904a9671a0SJoel Fernandes 4 * ps, ps, &allocated, 991ba110db8SJoel Fernandes GPU_BUDDY_RANGE_ALLOCATION), 9924a9671a0SJoel Fernandes "buddy_alloc hit an error size=%lu\n", 4 * ps); 993ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED); 994ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, SZ_4K << max_order, 9954a9671a0SJoel Fernandes 2 * ps, ps, &allocated, 996ba110db8SJoel Fernandes GPU_BUDDY_CLEAR_ALLOCATION), 9974a9671a0SJoel Fernandes "buddy_alloc hit an error size=%lu\n", 2 * ps); 998ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED); 999ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, SZ_4K << max_order, mm_size, 10004a9671a0SJoel Fernandes ps, ps, &allocated, 1001ba110db8SJoel Fernandes GPU_BUDDY_RANGE_ALLOCATION), 10024a9671a0SJoel Fernandes "buddy_alloc hit an error size=%lu\n", ps); 1003ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED); 1004ba110db8SJoel Fernandes gpu_buddy_fini(&mm); 10054a9671a0SJoel Fernandes } 10064a9671a0SJoel Fernandes 1007ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_contiguous(struct kunit *test) 10084a9671a0SJoel Fernandes { 10094a9671a0SJoel Fernandes const unsigned long ps = SZ_4K, mm_size = 16 * 3 * SZ_4K; 10104a9671a0SJoel Fernandes unsigned long i, n_pages, total; 1011ba110db8SJoel Fernandes struct gpu_buddy_block *block; 1012ba110db8SJoel Fernandes struct gpu_buddy mm; 10134a9671a0SJoel Fernandes LIST_HEAD(left); 10144a9671a0SJoel Fernandes LIST_HEAD(middle); 10154a9671a0SJoel Fernandes LIST_HEAD(right); 10164a9671a0SJoel Fernandes LIST_HEAD(allocated); 10174a9671a0SJoel Fernandes 1018ba110db8SJoel Fernandes KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); 10194a9671a0SJoel Fernandes 10204a9671a0SJoel Fernandes /* 10214a9671a0SJoel Fernandes * Idea is to fragment the address space by alternating block 10224a9671a0SJoel Fernandes * allocations between three different lists; one for left, middle and 10234a9671a0SJoel Fernandes * right. We can then free a list to simulate fragmentation. In 1024ba110db8SJoel Fernandes * particular we want to exercise the GPU_BUDDY_CONTIGUOUS_ALLOCATION, 10254a9671a0SJoel Fernandes * including the try_harder path. 10264a9671a0SJoel Fernandes */ 10274a9671a0SJoel Fernandes 10284a9671a0SJoel Fernandes i = 0; 10294a9671a0SJoel Fernandes n_pages = mm_size / ps; 10304a9671a0SJoel Fernandes do { 10314a9671a0SJoel Fernandes struct list_head *list; 10324a9671a0SJoel Fernandes int slot = i % 3; 10334a9671a0SJoel Fernandes 10344a9671a0SJoel Fernandes if (slot == 0) 10354a9671a0SJoel Fernandes list = &left; 10364a9671a0SJoel Fernandes else if (slot == 1) 10374a9671a0SJoel Fernandes list = &middle; 10384a9671a0SJoel Fernandes else 10394a9671a0SJoel Fernandes list = &right; 10404a9671a0SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, 1041ba110db8SJoel Fernandes gpu_buddy_alloc_blocks(&mm, 0, mm_size, 10424a9671a0SJoel Fernandes ps, ps, list, 0), 10434a9671a0SJoel Fernandes "buddy_alloc hit an error size=%lu\n", 10444a9671a0SJoel Fernandes ps); 10454a9671a0SJoel Fernandes } while (++i < n_pages); 10464a9671a0SJoel Fernandes 1047ba110db8SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 10484a9671a0SJoel Fernandes 3 * ps, ps, &allocated, 1049ba110db8SJoel Fernandes GPU_BUDDY_CONTIGUOUS_ALLOCATION), 10504a9671a0SJoel Fernandes "buddy_alloc didn't error size=%lu\n", 3 * ps); 10514a9671a0SJoel Fernandes 1052ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &middle, 0); 1053ba110db8SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 10544a9671a0SJoel Fernandes 3 * ps, ps, &allocated, 1055ba110db8SJoel Fernandes GPU_BUDDY_CONTIGUOUS_ALLOCATION), 10564a9671a0SJoel Fernandes "buddy_alloc didn't error size=%lu\n", 3 * ps); 1057ba110db8SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 10584a9671a0SJoel Fernandes 2 * ps, ps, &allocated, 1059ba110db8SJoel Fernandes GPU_BUDDY_CONTIGUOUS_ALLOCATION), 10604a9671a0SJoel Fernandes "buddy_alloc didn't error size=%lu\n", 2 * ps); 10614a9671a0SJoel Fernandes 1062ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &right, 0); 1063ba110db8SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 10644a9671a0SJoel Fernandes 3 * ps, ps, &allocated, 1065ba110db8SJoel Fernandes GPU_BUDDY_CONTIGUOUS_ALLOCATION), 10664a9671a0SJoel Fernandes "buddy_alloc didn't error size=%lu\n", 3 * ps); 10674a9671a0SJoel Fernandes /* 10684a9671a0SJoel Fernandes * At this point we should have enough contiguous space for 2 blocks, 10694a9671a0SJoel Fernandes * however they are never buddies (since we freed middle and right) so 10704a9671a0SJoel Fernandes * will require the try_harder logic to find them. 10714a9671a0SJoel Fernandes */ 1072ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 10734a9671a0SJoel Fernandes 2 * ps, ps, &allocated, 1074ba110db8SJoel Fernandes GPU_BUDDY_CONTIGUOUS_ALLOCATION), 10754a9671a0SJoel Fernandes "buddy_alloc hit an error size=%lu\n", 2 * ps); 10764a9671a0SJoel Fernandes 1077ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &left, 0); 1078ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 10794a9671a0SJoel Fernandes 3 * ps, ps, &allocated, 1080ba110db8SJoel Fernandes GPU_BUDDY_CONTIGUOUS_ALLOCATION), 10814a9671a0SJoel Fernandes "buddy_alloc hit an error size=%lu\n", 3 * ps); 10824a9671a0SJoel Fernandes 10834a9671a0SJoel Fernandes total = 0; 10844a9671a0SJoel Fernandes list_for_each_entry(block, &allocated, link) 1085ba110db8SJoel Fernandes total += gpu_buddy_block_size(&mm, block); 10864a9671a0SJoel Fernandes 10874a9671a0SJoel Fernandes KUNIT_ASSERT_EQ(test, total, ps * 2 + ps * 3); 10884a9671a0SJoel Fernandes 1089ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &allocated, 0); 1090ba110db8SJoel Fernandes gpu_buddy_fini(&mm); 10914a9671a0SJoel Fernandes } 10924a9671a0SJoel Fernandes 1093ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_pathological(struct kunit *test) 10944a9671a0SJoel Fernandes { 10954a9671a0SJoel Fernandes u64 mm_size, size, start = 0; 1096ba110db8SJoel Fernandes struct gpu_buddy_block *block; 10974a9671a0SJoel Fernandes const int max_order = 3; 10984a9671a0SJoel Fernandes unsigned long flags = 0; 10994a9671a0SJoel Fernandes int order, top; 1100ba110db8SJoel Fernandes struct gpu_buddy mm; 11014a9671a0SJoel Fernandes LIST_HEAD(blocks); 11024a9671a0SJoel Fernandes LIST_HEAD(holes); 11034a9671a0SJoel Fernandes LIST_HEAD(tmp); 11044a9671a0SJoel Fernandes 11054a9671a0SJoel Fernandes /* 11064a9671a0SJoel Fernandes * Create a pot-sized mm, then allocate one of each possible 11074a9671a0SJoel Fernandes * order within. This should leave the mm with exactly one 11084a9671a0SJoel Fernandes * page left. Free the largest block, then whittle down again. 11094a9671a0SJoel Fernandes * Eventually we will have a fully 50% fragmented mm. 11104a9671a0SJoel Fernandes */ 11114a9671a0SJoel Fernandes 11124a9671a0SJoel Fernandes mm_size = SZ_4K << max_order; 1113ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), 11144a9671a0SJoel Fernandes "buddy_init failed\n"); 11154a9671a0SJoel Fernandes 11164a9671a0SJoel Fernandes KUNIT_EXPECT_EQ(test, mm.max_order, max_order); 11174a9671a0SJoel Fernandes 11184a9671a0SJoel Fernandes for (top = max_order; top; top--) { 11194a9671a0SJoel Fernandes /* Make room by freeing the largest allocated block */ 11204a9671a0SJoel Fernandes block = list_first_entry_or_null(&blocks, typeof(*block), link); 11214a9671a0SJoel Fernandes if (block) { 11224a9671a0SJoel Fernandes list_del(&block->link); 1123ba110db8SJoel Fernandes gpu_buddy_free_block(&mm, block); 11244a9671a0SJoel Fernandes } 11254a9671a0SJoel Fernandes 11264a9671a0SJoel Fernandes for (order = top; order--;) { 11274a9671a0SJoel Fernandes size = get_size(order, mm.chunk_size); 1128ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, 11294a9671a0SJoel Fernandes mm_size, size, size, 11304a9671a0SJoel Fernandes &tmp, flags), 11314a9671a0SJoel Fernandes "buddy_alloc hit -ENOMEM with order=%d, top=%d\n", 11324a9671a0SJoel Fernandes order, top); 11334a9671a0SJoel Fernandes 1134ba110db8SJoel Fernandes block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); 11354a9671a0SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 11364a9671a0SJoel Fernandes 11374a9671a0SJoel Fernandes list_move_tail(&block->link, &blocks); 11384a9671a0SJoel Fernandes } 11394a9671a0SJoel Fernandes 11404a9671a0SJoel Fernandes /* There should be one final page for this sub-allocation */ 11414a9671a0SJoel Fernandes size = get_size(0, mm.chunk_size); 1142ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, 11434a9671a0SJoel Fernandes size, size, &tmp, flags), 11444a9671a0SJoel Fernandes "buddy_alloc hit -ENOMEM for hole\n"); 11454a9671a0SJoel Fernandes 1146ba110db8SJoel Fernandes block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); 11474a9671a0SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 11484a9671a0SJoel Fernandes 11494a9671a0SJoel Fernandes list_move_tail(&block->link, &holes); 11504a9671a0SJoel Fernandes 11514a9671a0SJoel Fernandes size = get_size(top, mm.chunk_size); 1152ba110db8SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, 11534a9671a0SJoel Fernandes size, size, &tmp, flags), 11544a9671a0SJoel Fernandes "buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!", 11554a9671a0SJoel Fernandes top, max_order); 11564a9671a0SJoel Fernandes } 11574a9671a0SJoel Fernandes 1158ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &holes, 0); 11594a9671a0SJoel Fernandes 11604a9671a0SJoel Fernandes /* Nothing larger than blocks of chunk_size now available */ 11614a9671a0SJoel Fernandes for (order = 1; order <= max_order; order++) { 11624a9671a0SJoel Fernandes size = get_size(order, mm.chunk_size); 1163ba110db8SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, 11644a9671a0SJoel Fernandes size, size, &tmp, flags), 11654a9671a0SJoel Fernandes "buddy_alloc unexpectedly succeeded at order %d, it should be full!", 11664a9671a0SJoel Fernandes order); 11674a9671a0SJoel Fernandes } 11684a9671a0SJoel Fernandes 11694a9671a0SJoel Fernandes list_splice_tail(&holes, &blocks); 1170ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &blocks, 0); 1171ba110db8SJoel Fernandes gpu_buddy_fini(&mm); 11724a9671a0SJoel Fernandes } 11734a9671a0SJoel Fernandes 1174ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_pessimistic(struct kunit *test) 11754a9671a0SJoel Fernandes { 11764a9671a0SJoel Fernandes u64 mm_size, size, start = 0; 1177ba110db8SJoel Fernandes struct gpu_buddy_block *block, *bn; 11784a9671a0SJoel Fernandes const unsigned int max_order = 16; 11794a9671a0SJoel Fernandes unsigned long flags = 0; 1180ba110db8SJoel Fernandes struct gpu_buddy mm; 11814a9671a0SJoel Fernandes unsigned int order; 11824a9671a0SJoel Fernandes LIST_HEAD(blocks); 11834a9671a0SJoel Fernandes LIST_HEAD(tmp); 11844a9671a0SJoel Fernandes 11854a9671a0SJoel Fernandes /* 11864a9671a0SJoel Fernandes * Create a pot-sized mm, then allocate one of each possible 11874a9671a0SJoel Fernandes * order within. This should leave the mm with exactly one 11884a9671a0SJoel Fernandes * page left. 11894a9671a0SJoel Fernandes */ 11904a9671a0SJoel Fernandes 11914a9671a0SJoel Fernandes mm_size = SZ_4K << max_order; 1192ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), 11934a9671a0SJoel Fernandes "buddy_init failed\n"); 11944a9671a0SJoel Fernandes 11954a9671a0SJoel Fernandes KUNIT_EXPECT_EQ(test, mm.max_order, max_order); 11964a9671a0SJoel Fernandes 11974a9671a0SJoel Fernandes for (order = 0; order < max_order; order++) { 11984a9671a0SJoel Fernandes size = get_size(order, mm.chunk_size); 1199ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, 12004a9671a0SJoel Fernandes size, size, &tmp, flags), 12014a9671a0SJoel Fernandes "buddy_alloc hit -ENOMEM with order=%d\n", 12024a9671a0SJoel Fernandes order); 12034a9671a0SJoel Fernandes 1204ba110db8SJoel Fernandes block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); 12054a9671a0SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 12064a9671a0SJoel Fernandes 12074a9671a0SJoel Fernandes list_move_tail(&block->link, &blocks); 12084a9671a0SJoel Fernandes } 12094a9671a0SJoel Fernandes 12104a9671a0SJoel Fernandes /* And now the last remaining block available */ 12114a9671a0SJoel Fernandes size = get_size(0, mm.chunk_size); 1212ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, 12134a9671a0SJoel Fernandes size, size, &tmp, flags), 12144a9671a0SJoel Fernandes "buddy_alloc hit -ENOMEM on final alloc\n"); 12154a9671a0SJoel Fernandes 1216ba110db8SJoel Fernandes block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); 12174a9671a0SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 12184a9671a0SJoel Fernandes 12194a9671a0SJoel Fernandes list_move_tail(&block->link, &blocks); 12204a9671a0SJoel Fernandes 12214a9671a0SJoel Fernandes /* Should be completely full! */ 12224a9671a0SJoel Fernandes for (order = max_order; order--;) { 12234a9671a0SJoel Fernandes size = get_size(order, mm.chunk_size); 1224ba110db8SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, 12254a9671a0SJoel Fernandes size, size, &tmp, flags), 12264a9671a0SJoel Fernandes "buddy_alloc unexpectedly succeeded, it should be full!"); 12274a9671a0SJoel Fernandes } 12284a9671a0SJoel Fernandes 12294a9671a0SJoel Fernandes block = list_last_entry(&blocks, typeof(*block), link); 12304a9671a0SJoel Fernandes list_del(&block->link); 1231ba110db8SJoel Fernandes gpu_buddy_free_block(&mm, block); 12324a9671a0SJoel Fernandes 12334a9671a0SJoel Fernandes /* As we free in increasing size, we make available larger blocks */ 12344a9671a0SJoel Fernandes order = 1; 12354a9671a0SJoel Fernandes list_for_each_entry_safe(block, bn, &blocks, link) { 12364a9671a0SJoel Fernandes list_del(&block->link); 1237ba110db8SJoel Fernandes gpu_buddy_free_block(&mm, block); 12384a9671a0SJoel Fernandes 12394a9671a0SJoel Fernandes size = get_size(order, mm.chunk_size); 1240ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, 12414a9671a0SJoel Fernandes size, size, &tmp, flags), 12424a9671a0SJoel Fernandes "buddy_alloc hit -ENOMEM with order=%d\n", 12434a9671a0SJoel Fernandes order); 12444a9671a0SJoel Fernandes 1245ba110db8SJoel Fernandes block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); 12464a9671a0SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 12474a9671a0SJoel Fernandes 12484a9671a0SJoel Fernandes list_del(&block->link); 1249ba110db8SJoel Fernandes gpu_buddy_free_block(&mm, block); 12504a9671a0SJoel Fernandes order++; 12514a9671a0SJoel Fernandes } 12524a9671a0SJoel Fernandes 12534a9671a0SJoel Fernandes /* To confirm, now the whole mm should be available */ 12544a9671a0SJoel Fernandes size = get_size(max_order, mm.chunk_size); 1255ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, 12564a9671a0SJoel Fernandes size, size, &tmp, flags), 12574a9671a0SJoel Fernandes "buddy_alloc (realloc) hit -ENOMEM with order=%d\n", 12584a9671a0SJoel Fernandes max_order); 12594a9671a0SJoel Fernandes 1260ba110db8SJoel Fernandes block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); 12614a9671a0SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 12624a9671a0SJoel Fernandes 12634a9671a0SJoel Fernandes list_del(&block->link); 1264ba110db8SJoel Fernandes gpu_buddy_free_block(&mm, block); 1265ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &blocks, 0); 1266ba110db8SJoel Fernandes gpu_buddy_fini(&mm); 12674a9671a0SJoel Fernandes } 12684a9671a0SJoel Fernandes 1269ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_optimistic(struct kunit *test) 12704a9671a0SJoel Fernandes { 12714a9671a0SJoel Fernandes u64 mm_size, size, start = 0; 1272ba110db8SJoel Fernandes struct gpu_buddy_block *block; 12734a9671a0SJoel Fernandes unsigned long flags = 0; 12744a9671a0SJoel Fernandes const int max_order = 16; 1275ba110db8SJoel Fernandes struct gpu_buddy mm; 12764a9671a0SJoel Fernandes LIST_HEAD(blocks); 12774a9671a0SJoel Fernandes LIST_HEAD(tmp); 12784a9671a0SJoel Fernandes int order; 12794a9671a0SJoel Fernandes 12804a9671a0SJoel Fernandes /* 12814a9671a0SJoel Fernandes * Create a mm with one block of each order available, and 12824a9671a0SJoel Fernandes * try to allocate them all. 12834a9671a0SJoel Fernandes */ 12844a9671a0SJoel Fernandes 12854a9671a0SJoel Fernandes mm_size = SZ_4K * ((1 << (max_order + 1)) - 1); 12864a9671a0SJoel Fernandes 1287ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), 12884a9671a0SJoel Fernandes "buddy_init failed\n"); 12894a9671a0SJoel Fernandes 12904a9671a0SJoel Fernandes KUNIT_EXPECT_EQ(test, mm.max_order, max_order); 12914a9671a0SJoel Fernandes 12924a9671a0SJoel Fernandes for (order = 0; order <= max_order; order++) { 12934a9671a0SJoel Fernandes size = get_size(order, mm.chunk_size); 1294ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, 12954a9671a0SJoel Fernandes size, size, &tmp, flags), 12964a9671a0SJoel Fernandes "buddy_alloc hit -ENOMEM with order=%d\n", 12974a9671a0SJoel Fernandes order); 12984a9671a0SJoel Fernandes 1299ba110db8SJoel Fernandes block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); 13004a9671a0SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 13014a9671a0SJoel Fernandes 13024a9671a0SJoel Fernandes list_move_tail(&block->link, &blocks); 13034a9671a0SJoel Fernandes } 13044a9671a0SJoel Fernandes 13054a9671a0SJoel Fernandes /* Should be completely full! */ 13064a9671a0SJoel Fernandes size = get_size(0, mm.chunk_size); 1307ba110db8SJoel Fernandes KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, 13084a9671a0SJoel Fernandes size, size, &tmp, flags), 13094a9671a0SJoel Fernandes "buddy_alloc unexpectedly succeeded, it should be full!"); 13104a9671a0SJoel Fernandes 1311ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &blocks, 0); 1312ba110db8SJoel Fernandes gpu_buddy_fini(&mm); 13134a9671a0SJoel Fernandes } 13144a9671a0SJoel Fernandes 1315ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_limit(struct kunit *test) 13164a9671a0SJoel Fernandes { 13174a9671a0SJoel Fernandes u64 size = U64_MAX, start = 0; 1318ba110db8SJoel Fernandes struct gpu_buddy_block *block; 13194a9671a0SJoel Fernandes unsigned long flags = 0; 13204a9671a0SJoel Fernandes LIST_HEAD(allocated); 1321ba110db8SJoel Fernandes struct gpu_buddy mm; 13224a9671a0SJoel Fernandes 1323ba110db8SJoel Fernandes KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, size, SZ_4K)); 13244a9671a0SJoel Fernandes 1325ba110db8SJoel Fernandes KUNIT_EXPECT_EQ_MSG(test, mm.max_order, GPU_BUDDY_MAX_ORDER, 13264a9671a0SJoel Fernandes "mm.max_order(%d) != %d\n", mm.max_order, 1327ba110db8SJoel Fernandes GPU_BUDDY_MAX_ORDER); 13284a9671a0SJoel Fernandes 13294a9671a0SJoel Fernandes size = mm.chunk_size << mm.max_order; 1330ba110db8SJoel Fernandes KUNIT_EXPECT_FALSE(test, gpu_buddy_alloc_blocks(&mm, start, size, size, 13314a9671a0SJoel Fernandes mm.chunk_size, &allocated, flags)); 13324a9671a0SJoel Fernandes 1333ba110db8SJoel Fernandes block = list_first_entry_or_null(&allocated, struct gpu_buddy_block, link); 13344a9671a0SJoel Fernandes KUNIT_EXPECT_TRUE(test, block); 13354a9671a0SJoel Fernandes 1336ba110db8SJoel Fernandes KUNIT_EXPECT_EQ_MSG(test, gpu_buddy_block_order(block), mm.max_order, 13374a9671a0SJoel Fernandes "block order(%d) != %d\n", 1338ba110db8SJoel Fernandes gpu_buddy_block_order(block), mm.max_order); 13394a9671a0SJoel Fernandes 1340ba110db8SJoel Fernandes KUNIT_EXPECT_EQ_MSG(test, gpu_buddy_block_size(&mm, block), 13414a9671a0SJoel Fernandes BIT_ULL(mm.max_order) * mm.chunk_size, 13424a9671a0SJoel Fernandes "block size(%llu) != %llu\n", 1343ba110db8SJoel Fernandes gpu_buddy_block_size(&mm, block), 13444a9671a0SJoel Fernandes BIT_ULL(mm.max_order) * mm.chunk_size); 13454a9671a0SJoel Fernandes 1346ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &allocated, 0); 1347ba110db8SJoel Fernandes gpu_buddy_fini(&mm); 13484a9671a0SJoel Fernandes } 13494a9671a0SJoel Fernandes 1350ba110db8SJoel Fernandes static void gpu_test_buddy_alloc_exceeds_max_order(struct kunit *test) 13514a9671a0SJoel Fernandes { 13524a9671a0SJoel Fernandes u64 mm_size = SZ_8G + SZ_2G, size = SZ_8G + SZ_1G, min_block_size = SZ_8G; 1353ba110db8SJoel Fernandes struct gpu_buddy mm; 13544a9671a0SJoel Fernandes LIST_HEAD(blocks); 13554a9671a0SJoel Fernandes int err; 13564a9671a0SJoel Fernandes 1357ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), 13584a9671a0SJoel Fernandes "buddy_init failed\n"); 13594a9671a0SJoel Fernandes 13604a9671a0SJoel Fernandes /* CONTIGUOUS allocation should succeed via try_harder fallback */ 1361ba110db8SJoel Fernandes KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, size, 13624a9671a0SJoel Fernandes SZ_4K, &blocks, 1363ba110db8SJoel Fernandes GPU_BUDDY_CONTIGUOUS_ALLOCATION), 13644a9671a0SJoel Fernandes "buddy_alloc hit an error size=%llu\n", size); 1365ba110db8SJoel Fernandes gpu_buddy_free_list(&mm, &blocks, 0); 13664a9671a0SJoel Fernandes 13674a9671a0SJoel Fernandes /* Non-CONTIGUOUS with large min_block_size should return -EINVAL */ 1368ba110db8SJoel Fernandes err = gpu_buddy_alloc_blocks(&mm, 0, mm_size, size, min_block_size, &blocks, 0); 13694a9671a0SJoel Fernandes KUNIT_EXPECT_EQ(test, err, -EINVAL); 13704a9671a0SJoel Fernandes 13714a9671a0SJoel Fernandes /* Non-CONTIGUOUS + RANGE with large min_block_size should return -EINVAL */ 1372ba110db8SJoel Fernandes err = gpu_buddy_alloc_blocks(&mm, 0, mm_size, size, min_block_size, &blocks, 1373ba110db8SJoel Fernandes GPU_BUDDY_RANGE_ALLOCATION); 13744a9671a0SJoel Fernandes KUNIT_EXPECT_EQ(test, err, -EINVAL); 13754a9671a0SJoel Fernandes 13764a9671a0SJoel Fernandes /* CONTIGUOUS + RANGE should return -EINVAL (no try_harder for RANGE) */ 1377ba110db8SJoel Fernandes err = gpu_buddy_alloc_blocks(&mm, 0, mm_size, size, SZ_4K, &blocks, 1378ba110db8SJoel Fernandes GPU_BUDDY_CONTIGUOUS_ALLOCATION | GPU_BUDDY_RANGE_ALLOCATION); 13794a9671a0SJoel Fernandes KUNIT_EXPECT_EQ(test, err, -EINVAL); 13804a9671a0SJoel Fernandes 1381ba110db8SJoel Fernandes gpu_buddy_fini(&mm); 13824a9671a0SJoel Fernandes } 13834a9671a0SJoel Fernandes 1384ba110db8SJoel Fernandes static int gpu_buddy_suite_init(struct kunit_suite *suite) 13854a9671a0SJoel Fernandes { 13864a9671a0SJoel Fernandes while (!random_seed) 13874a9671a0SJoel Fernandes random_seed = get_random_u32(); 13884a9671a0SJoel Fernandes 1389ba110db8SJoel Fernandes kunit_info(suite, "Testing GPU buddy manager, with random_seed=0x%x\n", 13904a9671a0SJoel Fernandes random_seed); 13914a9671a0SJoel Fernandes 13924a9671a0SJoel Fernandes return 0; 13934a9671a0SJoel Fernandes } 13944a9671a0SJoel Fernandes 1395ba110db8SJoel Fernandes static struct kunit_case gpu_buddy_tests[] = { 1396ba110db8SJoel Fernandes KUNIT_CASE(gpu_test_buddy_alloc_limit), 1397ba110db8SJoel Fernandes KUNIT_CASE(gpu_test_buddy_alloc_optimistic), 1398ba110db8SJoel Fernandes KUNIT_CASE(gpu_test_buddy_alloc_pessimistic), 1399ba110db8SJoel Fernandes KUNIT_CASE(gpu_test_buddy_alloc_pathological), 1400ba110db8SJoel Fernandes KUNIT_CASE(gpu_test_buddy_alloc_contiguous), 1401ba110db8SJoel Fernandes KUNIT_CASE(gpu_test_buddy_alloc_clear), 1402*bdca18a6SSanjay Yadav KUNIT_CASE(gpu_test_buddy_alloc_range), 1403ba110db8SJoel Fernandes KUNIT_CASE(gpu_test_buddy_alloc_range_bias), 14045ea5b6ffSMaxime Ripard KUNIT_CASE_SLOW(gpu_test_buddy_fragmentation_performance), 1405ba110db8SJoel Fernandes KUNIT_CASE(gpu_test_buddy_alloc_exceeds_max_order), 1406f5bd7da0SArunpravin Paneer Selvam KUNIT_CASE(gpu_test_buddy_offset_aligned_allocation), 1407f5bd7da0SArunpravin Paneer Selvam KUNIT_CASE(gpu_test_buddy_subtree_offset_alignment_stress), 14084a9671a0SJoel Fernandes {} 14094a9671a0SJoel Fernandes }; 14104a9671a0SJoel Fernandes 1411ba110db8SJoel Fernandes static struct kunit_suite gpu_buddy_test_suite = { 1412ba110db8SJoel Fernandes .name = "gpu_buddy", 1413ba110db8SJoel Fernandes .suite_init = gpu_buddy_suite_init, 1414ba110db8SJoel Fernandes .test_cases = gpu_buddy_tests, 14154a9671a0SJoel Fernandes }; 14164a9671a0SJoel Fernandes 1417ba110db8SJoel Fernandes kunit_test_suite(gpu_buddy_test_suite); 14184a9671a0SJoel Fernandes 14194a9671a0SJoel Fernandes MODULE_AUTHOR("Intel Corporation"); 1420ba110db8SJoel Fernandes MODULE_DESCRIPTION("Kunit test for gpu_buddy functions"); 14214a9671a0SJoel Fernandes MODULE_LICENSE("GPL"); 1422