18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2019 Intel Corporation 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/kmemleak.h> 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "i915_buddy.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "i915_gem.h" 128c2ecf20Sopenharmony_ci#include "i915_globals.h" 138c2ecf20Sopenharmony_ci#include "i915_utils.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic struct i915_global_block { 168c2ecf20Sopenharmony_ci struct i915_global base; 178c2ecf20Sopenharmony_ci struct kmem_cache *slab_blocks; 188c2ecf20Sopenharmony_ci} global; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic void i915_global_buddy_shrink(void) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci kmem_cache_shrink(global.slab_blocks); 238c2ecf20Sopenharmony_ci} 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void i915_global_buddy_exit(void) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci kmem_cache_destroy(global.slab_blocks); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic struct i915_global_block global = { { 318c2ecf20Sopenharmony_ci .shrink = i915_global_buddy_shrink, 328c2ecf20Sopenharmony_ci .exit = i915_global_buddy_exit, 338c2ecf20Sopenharmony_ci} }; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciint __init i915_global_buddy_init(void) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci global.slab_blocks = KMEM_CACHE(i915_buddy_block, SLAB_HWCACHE_ALIGN); 388c2ecf20Sopenharmony_ci if (!global.slab_blocks) 398c2ecf20Sopenharmony_ci return -ENOMEM; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci i915_global_register(&global.base); 428c2ecf20Sopenharmony_ci return 0; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct i915_buddy_block *i915_block_alloc(struct i915_buddy_block *parent, 468c2ecf20Sopenharmony_ci unsigned int order, 478c2ecf20Sopenharmony_ci u64 offset) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct i915_buddy_block *block; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci block = kmem_cache_zalloc(global.slab_blocks, GFP_KERNEL); 528c2ecf20Sopenharmony_ci if (!block) 538c2ecf20Sopenharmony_ci return NULL; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci block->header = offset; 568c2ecf20Sopenharmony_ci block->header |= order; 578c2ecf20Sopenharmony_ci block->parent = parent; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return block; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void i915_block_free(struct i915_buddy_block *block) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci kmem_cache_free(global.slab_blocks, block); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void mark_allocated(struct i915_buddy_block *block) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci block->header &= ~I915_BUDDY_HEADER_STATE; 708c2ecf20Sopenharmony_ci block->header |= I915_BUDDY_ALLOCATED; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci list_del(&block->link); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void mark_free(struct i915_buddy_mm *mm, 768c2ecf20Sopenharmony_ci struct i915_buddy_block *block) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci block->header &= ~I915_BUDDY_HEADER_STATE; 798c2ecf20Sopenharmony_ci block->header |= I915_BUDDY_FREE; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci list_add(&block->link, 828c2ecf20Sopenharmony_ci &mm->free_list[i915_buddy_block_order(block)]); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void mark_split(struct i915_buddy_block *block) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci block->header &= ~I915_BUDDY_HEADER_STATE; 888c2ecf20Sopenharmony_ci block->header |= I915_BUDDY_SPLIT; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci list_del(&block->link); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ciint i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci unsigned int i; 968c2ecf20Sopenharmony_ci u64 offset; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (size < chunk_size) 998c2ecf20Sopenharmony_ci return -EINVAL; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (chunk_size < PAGE_SIZE) 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (!is_power_of_2(chunk_size)) 1058c2ecf20Sopenharmony_ci return -EINVAL; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci size = round_down(size, chunk_size); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci mm->size = size; 1108c2ecf20Sopenharmony_ci mm->chunk_size = chunk_size; 1118c2ecf20Sopenharmony_ci mm->max_order = ilog2(size) - ilog2(chunk_size); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci GEM_BUG_ON(mm->max_order > I915_BUDDY_MAX_ORDER); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci mm->free_list = kmalloc_array(mm->max_order + 1, 1168c2ecf20Sopenharmony_ci sizeof(struct list_head), 1178c2ecf20Sopenharmony_ci GFP_KERNEL); 1188c2ecf20Sopenharmony_ci if (!mm->free_list) 1198c2ecf20Sopenharmony_ci return -ENOMEM; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci for (i = 0; i <= mm->max_order; ++i) 1228c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mm->free_list[i]); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mm->n_roots = hweight64(size); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci mm->roots = kmalloc_array(mm->n_roots, 1278c2ecf20Sopenharmony_ci sizeof(struct i915_buddy_block *), 1288c2ecf20Sopenharmony_ci GFP_KERNEL); 1298c2ecf20Sopenharmony_ci if (!mm->roots) 1308c2ecf20Sopenharmony_ci goto out_free_list; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci offset = 0; 1338c2ecf20Sopenharmony_ci i = 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * Split into power-of-two blocks, in case we are given a size that is 1378c2ecf20Sopenharmony_ci * not itself a power-of-two. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ci do { 1408c2ecf20Sopenharmony_ci struct i915_buddy_block *root; 1418c2ecf20Sopenharmony_ci unsigned int order; 1428c2ecf20Sopenharmony_ci u64 root_size; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci root_size = rounddown_pow_of_two(size); 1458c2ecf20Sopenharmony_ci order = ilog2(root_size) - ilog2(chunk_size); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci root = i915_block_alloc(NULL, order, offset); 1488c2ecf20Sopenharmony_ci if (!root) 1498c2ecf20Sopenharmony_ci goto out_free_roots; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci mark_free(mm, root); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci GEM_BUG_ON(i > mm->max_order); 1548c2ecf20Sopenharmony_ci GEM_BUG_ON(i915_buddy_block_size(mm, root) < chunk_size); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci mm->roots[i] = root; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci offset += root_size; 1598c2ecf20Sopenharmony_ci size -= root_size; 1608c2ecf20Sopenharmony_ci i++; 1618c2ecf20Sopenharmony_ci } while (size); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ciout_free_roots: 1668c2ecf20Sopenharmony_ci while (i--) 1678c2ecf20Sopenharmony_ci i915_block_free(mm->roots[i]); 1688c2ecf20Sopenharmony_ci kfree(mm->roots); 1698c2ecf20Sopenharmony_ciout_free_list: 1708c2ecf20Sopenharmony_ci kfree(mm->free_list); 1718c2ecf20Sopenharmony_ci return -ENOMEM; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_civoid i915_buddy_fini(struct i915_buddy_mm *mm) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int i; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci for (i = 0; i < mm->n_roots; ++i) { 1798c2ecf20Sopenharmony_ci GEM_WARN_ON(!i915_buddy_block_is_free(mm->roots[i])); 1808c2ecf20Sopenharmony_ci i915_block_free(mm->roots[i]); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci kfree(mm->roots); 1848c2ecf20Sopenharmony_ci kfree(mm->free_list); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int split_block(struct i915_buddy_mm *mm, 1888c2ecf20Sopenharmony_ci struct i915_buddy_block *block) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci unsigned int block_order = i915_buddy_block_order(block) - 1; 1918c2ecf20Sopenharmony_ci u64 offset = i915_buddy_block_offset(block); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci GEM_BUG_ON(!i915_buddy_block_is_free(block)); 1948c2ecf20Sopenharmony_ci GEM_BUG_ON(!i915_buddy_block_order(block)); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci block->left = i915_block_alloc(block, block_order, offset); 1978c2ecf20Sopenharmony_ci if (!block->left) 1988c2ecf20Sopenharmony_ci return -ENOMEM; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci block->right = i915_block_alloc(block, block_order, 2018c2ecf20Sopenharmony_ci offset + (mm->chunk_size << block_order)); 2028c2ecf20Sopenharmony_ci if (!block->right) { 2038c2ecf20Sopenharmony_ci i915_block_free(block->left); 2048c2ecf20Sopenharmony_ci return -ENOMEM; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci mark_free(mm, block->left); 2088c2ecf20Sopenharmony_ci mark_free(mm, block->right); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci mark_split(block); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic struct i915_buddy_block * 2168c2ecf20Sopenharmony_ciget_buddy(struct i915_buddy_block *block) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct i915_buddy_block *parent; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci parent = block->parent; 2218c2ecf20Sopenharmony_ci if (!parent) 2228c2ecf20Sopenharmony_ci return NULL; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (parent->left == block) 2258c2ecf20Sopenharmony_ci return parent->right; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return parent->left; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void __i915_buddy_free(struct i915_buddy_mm *mm, 2318c2ecf20Sopenharmony_ci struct i915_buddy_block *block) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct i915_buddy_block *parent; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci while ((parent = block->parent)) { 2368c2ecf20Sopenharmony_ci struct i915_buddy_block *buddy; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci buddy = get_buddy(block); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (!i915_buddy_block_is_free(buddy)) 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci list_del(&buddy->link); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci i915_block_free(block); 2468c2ecf20Sopenharmony_ci i915_block_free(buddy); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci block = parent; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci mark_free(mm, block); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_civoid i915_buddy_free(struct i915_buddy_mm *mm, 2558c2ecf20Sopenharmony_ci struct i915_buddy_block *block) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci GEM_BUG_ON(!i915_buddy_block_is_allocated(block)); 2588c2ecf20Sopenharmony_ci __i915_buddy_free(mm, block); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_civoid i915_buddy_free_list(struct i915_buddy_mm *mm, struct list_head *objects) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct i915_buddy_block *block, *on; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci list_for_each_entry_safe(block, on, objects, link) { 2668c2ecf20Sopenharmony_ci i915_buddy_free(mm, block); 2678c2ecf20Sopenharmony_ci cond_resched(); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci INIT_LIST_HEAD(objects); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* 2738c2ecf20Sopenharmony_ci * Allocate power-of-two block. The order value here translates to: 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * 0 = 2^0 * mm->chunk_size 2768c2ecf20Sopenharmony_ci * 1 = 2^1 * mm->chunk_size 2778c2ecf20Sopenharmony_ci * 2 = 2^2 * mm->chunk_size 2788c2ecf20Sopenharmony_ci * ... 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_cistruct i915_buddy_block * 2818c2ecf20Sopenharmony_cii915_buddy_alloc(struct i915_buddy_mm *mm, unsigned int order) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct i915_buddy_block *block = NULL; 2848c2ecf20Sopenharmony_ci unsigned int i; 2858c2ecf20Sopenharmony_ci int err; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci for (i = order; i <= mm->max_order; ++i) { 2888c2ecf20Sopenharmony_ci block = list_first_entry_or_null(&mm->free_list[i], 2898c2ecf20Sopenharmony_ci struct i915_buddy_block, 2908c2ecf20Sopenharmony_ci link); 2918c2ecf20Sopenharmony_ci if (block) 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (!block) 2968c2ecf20Sopenharmony_ci return ERR_PTR(-ENOSPC); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci GEM_BUG_ON(!i915_buddy_block_is_free(block)); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci while (i != order) { 3018c2ecf20Sopenharmony_ci err = split_block(mm, block); 3028c2ecf20Sopenharmony_ci if (unlikely(err)) 3038c2ecf20Sopenharmony_ci goto out_free; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Go low */ 3068c2ecf20Sopenharmony_ci block = block->left; 3078c2ecf20Sopenharmony_ci i--; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci mark_allocated(block); 3118c2ecf20Sopenharmony_ci kmemleak_update_trace(block); 3128c2ecf20Sopenharmony_ci return block; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ciout_free: 3158c2ecf20Sopenharmony_ci if (i != order) 3168c2ecf20Sopenharmony_ci __i915_buddy_free(mm, block); 3178c2ecf20Sopenharmony_ci return ERR_PTR(err); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci return s1 <= e2 && e1 >= s2; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci return s1 <= s2 && e1 >= e2; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* 3318c2ecf20Sopenharmony_ci * Allocate range. Note that it's safe to chain together multiple alloc_ranges 3328c2ecf20Sopenharmony_ci * with the same blocks list. 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * Intended for pre-allocating portions of the address space, for example to 3358c2ecf20Sopenharmony_ci * reserve a block for the initial framebuffer or similar, hence the expectation 3368c2ecf20Sopenharmony_ci * here is that i915_buddy_alloc() is still the main vehicle for 3378c2ecf20Sopenharmony_ci * allocations, so if that's not the case then the drm_mm range allocator is 3388c2ecf20Sopenharmony_ci * probably a much better fit, and so you should probably go use that instead. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ciint i915_buddy_alloc_range(struct i915_buddy_mm *mm, 3418c2ecf20Sopenharmony_ci struct list_head *blocks, 3428c2ecf20Sopenharmony_ci u64 start, u64 size) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct i915_buddy_block *block; 3458c2ecf20Sopenharmony_ci struct i915_buddy_block *buddy; 3468c2ecf20Sopenharmony_ci LIST_HEAD(allocated); 3478c2ecf20Sopenharmony_ci LIST_HEAD(dfs); 3488c2ecf20Sopenharmony_ci u64 end; 3498c2ecf20Sopenharmony_ci int err; 3508c2ecf20Sopenharmony_ci int i; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (size < mm->chunk_size) 3538c2ecf20Sopenharmony_ci return -EINVAL; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!IS_ALIGNED(size | start, mm->chunk_size)) 3568c2ecf20Sopenharmony_ci return -EINVAL; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (range_overflows(start, size, mm->size)) 3598c2ecf20Sopenharmony_ci return -EINVAL; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci for (i = 0; i < mm->n_roots; ++i) 3628c2ecf20Sopenharmony_ci list_add_tail(&mm->roots[i]->tmp_link, &dfs); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci end = start + size - 1; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci do { 3678c2ecf20Sopenharmony_ci u64 block_start; 3688c2ecf20Sopenharmony_ci u64 block_end; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci block = list_first_entry_or_null(&dfs, 3718c2ecf20Sopenharmony_ci struct i915_buddy_block, 3728c2ecf20Sopenharmony_ci tmp_link); 3738c2ecf20Sopenharmony_ci if (!block) 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci list_del(&block->tmp_link); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci block_start = i915_buddy_block_offset(block); 3798c2ecf20Sopenharmony_ci block_end = block_start + i915_buddy_block_size(mm, block) - 1; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (!overlaps(start, end, block_start, block_end)) 3828c2ecf20Sopenharmony_ci continue; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (i915_buddy_block_is_allocated(block)) { 3858c2ecf20Sopenharmony_ci err = -ENOSPC; 3868c2ecf20Sopenharmony_ci goto err_free; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (contains(start, end, block_start, block_end)) { 3908c2ecf20Sopenharmony_ci if (!i915_buddy_block_is_free(block)) { 3918c2ecf20Sopenharmony_ci err = -ENOSPC; 3928c2ecf20Sopenharmony_ci goto err_free; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci mark_allocated(block); 3968c2ecf20Sopenharmony_ci list_add_tail(&block->link, &allocated); 3978c2ecf20Sopenharmony_ci continue; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (!i915_buddy_block_is_split(block)) { 4018c2ecf20Sopenharmony_ci err = split_block(mm, block); 4028c2ecf20Sopenharmony_ci if (unlikely(err)) 4038c2ecf20Sopenharmony_ci goto err_undo; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci list_add(&block->right->tmp_link, &dfs); 4078c2ecf20Sopenharmony_ci list_add(&block->left->tmp_link, &dfs); 4088c2ecf20Sopenharmony_ci } while (1); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci list_splice_tail(&allocated, blocks); 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cierr_undo: 4148c2ecf20Sopenharmony_ci /* 4158c2ecf20Sopenharmony_ci * We really don't want to leave around a bunch of split blocks, since 4168c2ecf20Sopenharmony_ci * bigger is better, so make sure we merge everything back before we 4178c2ecf20Sopenharmony_ci * free the allocated blocks. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci buddy = get_buddy(block); 4208c2ecf20Sopenharmony_ci if (buddy && 4218c2ecf20Sopenharmony_ci (i915_buddy_block_is_free(block) && 4228c2ecf20Sopenharmony_ci i915_buddy_block_is_free(buddy))) 4238c2ecf20Sopenharmony_ci __i915_buddy_free(mm, block); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cierr_free: 4268c2ecf20Sopenharmony_ci i915_buddy_free_list(mm, &allocated); 4278c2ecf20Sopenharmony_ci return err; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) 4318c2ecf20Sopenharmony_ci#include "selftests/i915_buddy.c" 4328c2ecf20Sopenharmony_ci#endif 433