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