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