Home | History | Annotate | Line # | Download | only in i915
      1  1.5  riastrad /*	$NetBSD: i915_buddy.c,v 1.5 2021/12/19 11:13:36 riastradh Exp $	*/
      2  1.1  riastrad 
      3  1.1  riastrad // SPDX-License-Identifier: MIT
      4  1.1  riastrad /*
      5  1.1  riastrad  * Copyright  2019 Intel Corporation
      6  1.1  riastrad  */
      7  1.1  riastrad 
      8  1.1  riastrad #include <sys/cdefs.h>
      9  1.5  riastrad __KERNEL_RCSID(0, "$NetBSD: i915_buddy.c,v 1.5 2021/12/19 11:13:36 riastradh Exp $");
     10  1.1  riastrad 
     11  1.3  riastrad #include <linux/err.h>
     12  1.1  riastrad #include <linux/kmemleak.h>
     13  1.1  riastrad #include <linux/slab.h>
     14  1.1  riastrad 
     15  1.1  riastrad #include "i915_buddy.h"
     16  1.1  riastrad 
     17  1.1  riastrad #include "i915_gem.h"
     18  1.1  riastrad #include "i915_globals.h"
     19  1.1  riastrad #include "i915_utils.h"
     20  1.1  riastrad 
     21  1.1  riastrad static struct i915_global_block {
     22  1.1  riastrad 	struct i915_global base;
     23  1.1  riastrad 	struct kmem_cache *slab_blocks;
     24  1.1  riastrad } global;
     25  1.1  riastrad 
     26  1.1  riastrad static void i915_global_buddy_shrink(void)
     27  1.1  riastrad {
     28  1.1  riastrad 	kmem_cache_shrink(global.slab_blocks);
     29  1.1  riastrad }
     30  1.1  riastrad 
     31  1.1  riastrad static void i915_global_buddy_exit(void)
     32  1.1  riastrad {
     33  1.1  riastrad 	kmem_cache_destroy(global.slab_blocks);
     34  1.1  riastrad }
     35  1.1  riastrad 
     36  1.1  riastrad static struct i915_global_block global = { {
     37  1.1  riastrad 	.shrink = i915_global_buddy_shrink,
     38  1.1  riastrad 	.exit = i915_global_buddy_exit,
     39  1.1  riastrad } };
     40  1.1  riastrad 
     41  1.4  riastrad #ifdef __NetBSD__
     42  1.4  riastrad #define	__init	/* called from i915_module.c */
     43  1.4  riastrad #endif
     44  1.4  riastrad 
     45  1.1  riastrad int __init i915_global_buddy_init(void)
     46  1.1  riastrad {
     47  1.1  riastrad 	global.slab_blocks = KMEM_CACHE(i915_buddy_block, SLAB_HWCACHE_ALIGN);
     48  1.1  riastrad 	if (!global.slab_blocks)
     49  1.1  riastrad 		return -ENOMEM;
     50  1.1  riastrad 
     51  1.1  riastrad 	i915_global_register(&global.base);
     52  1.1  riastrad 	return 0;
     53  1.1  riastrad }
     54  1.1  riastrad 
     55  1.1  riastrad static struct i915_buddy_block *i915_block_alloc(struct i915_buddy_block *parent,
     56  1.1  riastrad 						 unsigned int order,
     57  1.1  riastrad 						 u64 offset)
     58  1.1  riastrad {
     59  1.1  riastrad 	struct i915_buddy_block *block;
     60  1.1  riastrad 
     61  1.1  riastrad 	block = kmem_cache_zalloc(global.slab_blocks, GFP_KERNEL);
     62  1.1  riastrad 	if (!block)
     63  1.1  riastrad 		return NULL;
     64  1.1  riastrad 
     65  1.1  riastrad 	block->header = offset;
     66  1.1  riastrad 	block->header |= order;
     67  1.1  riastrad 	block->parent = parent;
     68  1.1  riastrad 
     69  1.1  riastrad 	return block;
     70  1.1  riastrad }
     71  1.1  riastrad 
     72  1.1  riastrad static void i915_block_free(struct i915_buddy_block *block)
     73  1.1  riastrad {
     74  1.1  riastrad 	kmem_cache_free(global.slab_blocks, block);
     75  1.1  riastrad }
     76  1.1  riastrad 
     77  1.1  riastrad static void mark_allocated(struct i915_buddy_block *block)
     78  1.1  riastrad {
     79  1.1  riastrad 	block->header &= ~I915_BUDDY_HEADER_STATE;
     80  1.1  riastrad 	block->header |= I915_BUDDY_ALLOCATED;
     81  1.1  riastrad 
     82  1.1  riastrad 	list_del(&block->link);
     83  1.1  riastrad }
     84  1.1  riastrad 
     85  1.1  riastrad static void mark_free(struct i915_buddy_mm *mm,
     86  1.1  riastrad 		      struct i915_buddy_block *block)
     87  1.1  riastrad {
     88  1.1  riastrad 	block->header &= ~I915_BUDDY_HEADER_STATE;
     89  1.1  riastrad 	block->header |= I915_BUDDY_FREE;
     90  1.1  riastrad 
     91  1.1  riastrad 	list_add(&block->link,
     92  1.1  riastrad 		 &mm->free_list[i915_buddy_block_order(block)]);
     93  1.1  riastrad }
     94  1.1  riastrad 
     95  1.1  riastrad static void mark_split(struct i915_buddy_block *block)
     96  1.1  riastrad {
     97  1.1  riastrad 	block->header &= ~I915_BUDDY_HEADER_STATE;
     98  1.1  riastrad 	block->header |= I915_BUDDY_SPLIT;
     99  1.1  riastrad 
    100  1.1  riastrad 	list_del(&block->link);
    101  1.1  riastrad }
    102  1.1  riastrad 
    103  1.1  riastrad int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size)
    104  1.1  riastrad {
    105  1.1  riastrad 	unsigned int i;
    106  1.1  riastrad 	u64 offset;
    107  1.1  riastrad 
    108  1.1  riastrad 	if (size < chunk_size)
    109  1.1  riastrad 		return -EINVAL;
    110  1.1  riastrad 
    111  1.1  riastrad 	if (chunk_size < PAGE_SIZE)
    112  1.1  riastrad 		return -EINVAL;
    113  1.1  riastrad 
    114  1.1  riastrad 	if (!is_power_of_2(chunk_size))
    115  1.1  riastrad 		return -EINVAL;
    116  1.1  riastrad 
    117  1.1  riastrad 	size = round_down(size, chunk_size);
    118  1.1  riastrad 
    119  1.1  riastrad 	mm->size = size;
    120  1.1  riastrad 	mm->chunk_size = chunk_size;
    121  1.1  riastrad 	mm->max_order = ilog2(size) - ilog2(chunk_size);
    122  1.1  riastrad 
    123  1.1  riastrad 	GEM_BUG_ON(mm->max_order > I915_BUDDY_MAX_ORDER);
    124  1.1  riastrad 
    125  1.1  riastrad 	mm->free_list = kmalloc_array(mm->max_order + 1,
    126  1.1  riastrad 				      sizeof(struct list_head),
    127  1.1  riastrad 				      GFP_KERNEL);
    128  1.1  riastrad 	if (!mm->free_list)
    129  1.1  riastrad 		return -ENOMEM;
    130  1.1  riastrad 
    131  1.1  riastrad 	for (i = 0; i <= mm->max_order; ++i)
    132  1.1  riastrad 		INIT_LIST_HEAD(&mm->free_list[i]);
    133  1.1  riastrad 
    134  1.1  riastrad 	mm->n_roots = hweight64(size);
    135  1.1  riastrad 
    136  1.1  riastrad 	mm->roots = kmalloc_array(mm->n_roots,
    137  1.1  riastrad 				  sizeof(struct i915_buddy_block *),
    138  1.1  riastrad 				  GFP_KERNEL);
    139  1.1  riastrad 	if (!mm->roots)
    140  1.1  riastrad 		goto out_free_list;
    141  1.1  riastrad 
    142  1.1  riastrad 	offset = 0;
    143  1.1  riastrad 	i = 0;
    144  1.1  riastrad 
    145  1.1  riastrad 	/*
    146  1.1  riastrad 	 * Split into power-of-two blocks, in case we are given a size that is
    147  1.1  riastrad 	 * not itself a power-of-two.
    148  1.1  riastrad 	 */
    149  1.1  riastrad 	do {
    150  1.1  riastrad 		struct i915_buddy_block *root;
    151  1.1  riastrad 		unsigned int order;
    152  1.1  riastrad 		u64 root_size;
    153  1.1  riastrad 
    154  1.1  riastrad 		root_size = rounddown_pow_of_two(size);
    155  1.1  riastrad 		order = ilog2(root_size) - ilog2(chunk_size);
    156  1.1  riastrad 
    157  1.1  riastrad 		root = i915_block_alloc(NULL, order, offset);
    158  1.1  riastrad 		if (!root)
    159  1.1  riastrad 			goto out_free_roots;
    160  1.1  riastrad 
    161  1.1  riastrad 		mark_free(mm, root);
    162  1.1  riastrad 
    163  1.1  riastrad 		GEM_BUG_ON(i > mm->max_order);
    164  1.1  riastrad 		GEM_BUG_ON(i915_buddy_block_size(mm, root) < chunk_size);
    165  1.1  riastrad 
    166  1.1  riastrad 		mm->roots[i] = root;
    167  1.1  riastrad 
    168  1.1  riastrad 		offset += root_size;
    169  1.1  riastrad 		size -= root_size;
    170  1.1  riastrad 		i++;
    171  1.1  riastrad 	} while (size);
    172  1.1  riastrad 
    173  1.1  riastrad 	return 0;
    174  1.1  riastrad 
    175  1.1  riastrad out_free_roots:
    176  1.1  riastrad 	while (i--)
    177  1.1  riastrad 		i915_block_free(mm->roots[i]);
    178  1.1  riastrad 	kfree(mm->roots);
    179  1.1  riastrad out_free_list:
    180  1.1  riastrad 	kfree(mm->free_list);
    181  1.1  riastrad 	return -ENOMEM;
    182  1.1  riastrad }
    183  1.1  riastrad 
    184  1.1  riastrad void i915_buddy_fini(struct i915_buddy_mm *mm)
    185  1.1  riastrad {
    186  1.1  riastrad 	int i;
    187  1.1  riastrad 
    188  1.1  riastrad 	for (i = 0; i < mm->n_roots; ++i) {
    189  1.1  riastrad 		GEM_WARN_ON(!i915_buddy_block_is_free(mm->roots[i]));
    190  1.1  riastrad 		i915_block_free(mm->roots[i]);
    191  1.1  riastrad 	}
    192  1.1  riastrad 
    193  1.1  riastrad 	kfree(mm->roots);
    194  1.1  riastrad 	kfree(mm->free_list);
    195  1.1  riastrad }
    196  1.1  riastrad 
    197  1.1  riastrad static int split_block(struct i915_buddy_mm *mm,
    198  1.1  riastrad 		       struct i915_buddy_block *block)
    199  1.1  riastrad {
    200  1.1  riastrad 	unsigned int block_order = i915_buddy_block_order(block) - 1;
    201  1.1  riastrad 	u64 offset = i915_buddy_block_offset(block);
    202  1.1  riastrad 
    203  1.1  riastrad 	GEM_BUG_ON(!i915_buddy_block_is_free(block));
    204  1.1  riastrad 	GEM_BUG_ON(!i915_buddy_block_order(block));
    205  1.1  riastrad 
    206  1.1  riastrad 	block->left = i915_block_alloc(block, block_order, offset);
    207  1.1  riastrad 	if (!block->left)
    208  1.1  riastrad 		return -ENOMEM;
    209  1.1  riastrad 
    210  1.1  riastrad 	block->right = i915_block_alloc(block, block_order,
    211  1.1  riastrad 					offset + (mm->chunk_size << block_order));
    212  1.1  riastrad 	if (!block->right) {
    213  1.1  riastrad 		i915_block_free(block->left);
    214  1.1  riastrad 		return -ENOMEM;
    215  1.1  riastrad 	}
    216  1.1  riastrad 
    217  1.1  riastrad 	mark_free(mm, block->left);
    218  1.1  riastrad 	mark_free(mm, block->right);
    219  1.1  riastrad 
    220  1.1  riastrad 	mark_split(block);
    221  1.1  riastrad 
    222  1.1  riastrad 	return 0;
    223  1.1  riastrad }
    224  1.1  riastrad 
    225  1.1  riastrad static struct i915_buddy_block *
    226  1.1  riastrad get_buddy(struct i915_buddy_block *block)
    227  1.1  riastrad {
    228  1.1  riastrad 	struct i915_buddy_block *parent;
    229  1.1  riastrad 
    230  1.1  riastrad 	parent = block->parent;
    231  1.1  riastrad 	if (!parent)
    232  1.1  riastrad 		return NULL;
    233  1.1  riastrad 
    234  1.1  riastrad 	if (parent->left == block)
    235  1.1  riastrad 		return parent->right;
    236  1.1  riastrad 
    237  1.1  riastrad 	return parent->left;
    238  1.1  riastrad }
    239  1.1  riastrad 
    240  1.1  riastrad static void __i915_buddy_free(struct i915_buddy_mm *mm,
    241  1.1  riastrad 			      struct i915_buddy_block *block)
    242  1.1  riastrad {
    243  1.1  riastrad 	struct i915_buddy_block *parent;
    244  1.1  riastrad 
    245  1.1  riastrad 	while ((parent = block->parent)) {
    246  1.1  riastrad 		struct i915_buddy_block *buddy;
    247  1.1  riastrad 
    248  1.1  riastrad 		buddy = get_buddy(block);
    249  1.1  riastrad 
    250  1.1  riastrad 		if (!i915_buddy_block_is_free(buddy))
    251  1.1  riastrad 			break;
    252  1.1  riastrad 
    253  1.1  riastrad 		list_del(&buddy->link);
    254  1.1  riastrad 
    255  1.1  riastrad 		i915_block_free(block);
    256  1.1  riastrad 		i915_block_free(buddy);
    257  1.1  riastrad 
    258  1.1  riastrad 		block = parent;
    259  1.1  riastrad 	}
    260  1.1  riastrad 
    261  1.1  riastrad 	mark_free(mm, block);
    262  1.1  riastrad }
    263  1.1  riastrad 
    264  1.1  riastrad void i915_buddy_free(struct i915_buddy_mm *mm,
    265  1.1  riastrad 		     struct i915_buddy_block *block)
    266  1.1  riastrad {
    267  1.1  riastrad 	GEM_BUG_ON(!i915_buddy_block_is_allocated(block));
    268  1.1  riastrad 	__i915_buddy_free(mm, block);
    269  1.1  riastrad }
    270  1.1  riastrad 
    271  1.1  riastrad void i915_buddy_free_list(struct i915_buddy_mm *mm, struct list_head *objects)
    272  1.1  riastrad {
    273  1.1  riastrad 	struct i915_buddy_block *block, *on;
    274  1.1  riastrad 
    275  1.1  riastrad 	list_for_each_entry_safe(block, on, objects, link) {
    276  1.1  riastrad 		i915_buddy_free(mm, block);
    277  1.1  riastrad 		cond_resched();
    278  1.1  riastrad 	}
    279  1.1  riastrad 	INIT_LIST_HEAD(objects);
    280  1.1  riastrad }
    281  1.1  riastrad 
    282  1.1  riastrad /*
    283  1.1  riastrad  * Allocate power-of-two block. The order value here translates to:
    284  1.1  riastrad  *
    285  1.1  riastrad  *   0 = 2^0 * mm->chunk_size
    286  1.1  riastrad  *   1 = 2^1 * mm->chunk_size
    287  1.1  riastrad  *   2 = 2^2 * mm->chunk_size
    288  1.1  riastrad  *   ...
    289  1.1  riastrad  */
    290  1.1  riastrad struct i915_buddy_block *
    291  1.1  riastrad i915_buddy_alloc(struct i915_buddy_mm *mm, unsigned int order)
    292  1.1  riastrad {
    293  1.1  riastrad 	struct i915_buddy_block *block = NULL;
    294  1.1  riastrad 	unsigned int i;
    295  1.1  riastrad 	int err;
    296  1.1  riastrad 
    297  1.1  riastrad 	for (i = order; i <= mm->max_order; ++i) {
    298  1.1  riastrad 		block = list_first_entry_or_null(&mm->free_list[i],
    299  1.1  riastrad 						 struct i915_buddy_block,
    300  1.1  riastrad 						 link);
    301  1.1  riastrad 		if (block)
    302  1.1  riastrad 			break;
    303  1.1  riastrad 	}
    304  1.1  riastrad 
    305  1.1  riastrad 	if (!block)
    306  1.1  riastrad 		return ERR_PTR(-ENOSPC);
    307  1.1  riastrad 
    308  1.1  riastrad 	GEM_BUG_ON(!i915_buddy_block_is_free(block));
    309  1.1  riastrad 
    310  1.1  riastrad 	while (i != order) {
    311  1.1  riastrad 		err = split_block(mm, block);
    312  1.1  riastrad 		if (unlikely(err))
    313  1.1  riastrad 			goto out_free;
    314  1.1  riastrad 
    315  1.1  riastrad 		/* Go low */
    316  1.1  riastrad 		block = block->left;
    317  1.1  riastrad 		i--;
    318  1.1  riastrad 	}
    319  1.1  riastrad 
    320  1.1  riastrad 	mark_allocated(block);
    321  1.1  riastrad 	kmemleak_update_trace(block);
    322  1.1  riastrad 	return block;
    323  1.1  riastrad 
    324  1.1  riastrad out_free:
    325  1.1  riastrad 	__i915_buddy_free(mm, block);
    326  1.1  riastrad 	return ERR_PTR(err);
    327  1.1  riastrad }
    328  1.1  riastrad 
    329  1.1  riastrad static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
    330  1.1  riastrad {
    331  1.1  riastrad 	return s1 <= e2 && e1 >= s2;
    332  1.1  riastrad }
    333  1.1  riastrad 
    334  1.1  riastrad static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2)
    335  1.1  riastrad {
    336  1.1  riastrad 	return s1 <= s2 && e1 >= e2;
    337  1.1  riastrad }
    338  1.1  riastrad 
    339  1.1  riastrad /*
    340  1.1  riastrad  * Allocate range. Note that it's safe to chain together multiple alloc_ranges
    341  1.1  riastrad  * with the same blocks list.
    342  1.1  riastrad  *
    343  1.1  riastrad  * Intended for pre-allocating portions of the address space, for example to
    344  1.1  riastrad  * reserve a block for the initial framebuffer or similar, hence the expectation
    345  1.1  riastrad  * here is that i915_buddy_alloc() is still the main vehicle for
    346  1.1  riastrad  * allocations, so if that's not the case then the drm_mm range allocator is
    347  1.1  riastrad  * probably a much better fit, and so you should probably go use that instead.
    348  1.1  riastrad  */
    349  1.1  riastrad int i915_buddy_alloc_range(struct i915_buddy_mm *mm,
    350  1.1  riastrad 			   struct list_head *blocks,
    351  1.1  riastrad 			   u64 start, u64 size)
    352  1.1  riastrad {
    353  1.1  riastrad 	struct i915_buddy_block *block;
    354  1.1  riastrad 	struct i915_buddy_block *buddy;
    355  1.5  riastrad 	struct list_head allocated = LIST_HEAD_INIT(allocated);
    356  1.5  riastrad 	struct list_head dfs = LIST_HEAD_INIT(dfs);
    357  1.1  riastrad 	u64 end;
    358  1.1  riastrad 	int err;
    359  1.1  riastrad 	int i;
    360  1.1  riastrad 
    361  1.1  riastrad 	if (size < mm->chunk_size)
    362  1.1  riastrad 		return -EINVAL;
    363  1.1  riastrad 
    364  1.1  riastrad 	if (!IS_ALIGNED(size | start, mm->chunk_size))
    365  1.1  riastrad 		return -EINVAL;
    366  1.1  riastrad 
    367  1.1  riastrad 	if (range_overflows(start, size, mm->size))
    368  1.1  riastrad 		return -EINVAL;
    369  1.1  riastrad 
    370  1.1  riastrad 	for (i = 0; i < mm->n_roots; ++i)
    371  1.1  riastrad 		list_add_tail(&mm->roots[i]->tmp_link, &dfs);
    372  1.1  riastrad 
    373  1.1  riastrad 	end = start + size - 1;
    374  1.1  riastrad 
    375  1.1  riastrad 	do {
    376  1.1  riastrad 		u64 block_start;
    377  1.1  riastrad 		u64 block_end;
    378  1.1  riastrad 
    379  1.1  riastrad 		block = list_first_entry_or_null(&dfs,
    380  1.1  riastrad 						 struct i915_buddy_block,
    381  1.1  riastrad 						 tmp_link);
    382  1.1  riastrad 		if (!block)
    383  1.1  riastrad 			break;
    384  1.1  riastrad 
    385  1.1  riastrad 		list_del(&block->tmp_link);
    386  1.1  riastrad 
    387  1.1  riastrad 		block_start = i915_buddy_block_offset(block);
    388  1.1  riastrad 		block_end = block_start + i915_buddy_block_size(mm, block) - 1;
    389  1.1  riastrad 
    390  1.1  riastrad 		if (!overlaps(start, end, block_start, block_end))
    391  1.1  riastrad 			continue;
    392  1.1  riastrad 
    393  1.1  riastrad 		if (i915_buddy_block_is_allocated(block)) {
    394  1.1  riastrad 			err = -ENOSPC;
    395  1.1  riastrad 			goto err_free;
    396  1.1  riastrad 		}
    397  1.1  riastrad 
    398  1.1  riastrad 		if (contains(start, end, block_start, block_end)) {
    399  1.1  riastrad 			if (!i915_buddy_block_is_free(block)) {
    400  1.1  riastrad 				err = -ENOSPC;
    401  1.1  riastrad 				goto err_free;
    402  1.1  riastrad 			}
    403  1.1  riastrad 
    404  1.1  riastrad 			mark_allocated(block);
    405  1.1  riastrad 			list_add_tail(&block->link, &allocated);
    406  1.1  riastrad 			continue;
    407  1.1  riastrad 		}
    408  1.1  riastrad 
    409  1.1  riastrad 		if (!i915_buddy_block_is_split(block)) {
    410  1.1  riastrad 			err = split_block(mm, block);
    411  1.1  riastrad 			if (unlikely(err))
    412  1.1  riastrad 				goto err_undo;
    413  1.1  riastrad 		}
    414  1.1  riastrad 
    415  1.1  riastrad 		list_add(&block->right->tmp_link, &dfs);
    416  1.1  riastrad 		list_add(&block->left->tmp_link, &dfs);
    417  1.1  riastrad 	} while (1);
    418  1.1  riastrad 
    419  1.1  riastrad 	list_splice_tail(&allocated, blocks);
    420  1.1  riastrad 	return 0;
    421  1.1  riastrad 
    422  1.1  riastrad err_undo:
    423  1.1  riastrad 	/*
    424  1.1  riastrad 	 * We really don't want to leave around a bunch of split blocks, since
    425  1.1  riastrad 	 * bigger is better, so make sure we merge everything back before we
    426  1.1  riastrad 	 * free the allocated blocks.
    427  1.1  riastrad 	 */
    428  1.1  riastrad 	buddy = get_buddy(block);
    429  1.1  riastrad 	if (buddy &&
    430  1.1  riastrad 	    (i915_buddy_block_is_free(block) &&
    431  1.1  riastrad 	     i915_buddy_block_is_free(buddy)))
    432  1.1  riastrad 		__i915_buddy_free(mm, block);
    433  1.1  riastrad 
    434  1.1  riastrad err_free:
    435  1.1  riastrad 	i915_buddy_free_list(mm, &allocated);
    436  1.1  riastrad 	return err;
    437  1.1  riastrad }
    438  1.1  riastrad 
    439  1.1  riastrad #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
    440  1.1  riastrad #include "selftests/i915_buddy.c"
    441  1.1  riastrad #endif
    442