1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2021 Intel Corporation 4 */ 5 6 #include <kunit/test-bug.h> 7 8 #include <linux/export.h> 9 #include <linux/kmemleak.h> 10 #include <linux/module.h> 11 #include <linux/sizes.h> 12 13 #include <drm/drm_buddy.h> 14 #include <drm/drm_print.h> 15 16 enum drm_buddy_free_tree { 17 DRM_BUDDY_CLEAR_TREE = 0, 18 DRM_BUDDY_DIRTY_TREE, 19 DRM_BUDDY_MAX_FREE_TREES, 20 }; 21 22 static struct kmem_cache *slab_blocks; 23 24 #define for_each_free_tree(tree) \ 25 for ((tree) = 0; (tree) < DRM_BUDDY_MAX_FREE_TREES; (tree)++) 26 27 static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm, 28 struct drm_buddy_block *parent, 29 unsigned int order, 30 u64 offset) 31 { 32 struct drm_buddy_block *block; 33 34 BUG_ON(order > DRM_BUDDY_MAX_ORDER); 35 36 block = kmem_cache_zalloc(slab_blocks, GFP_KERNEL); 37 if (!block) 38 return NULL; 39 40 block->header = offset; 41 block->header |= order; 42 block->parent = parent; 43 44 RB_CLEAR_NODE(&block->rb); 45 46 BUG_ON(block->header & DRM_BUDDY_HEADER_UNUSED); 47 return block; 48 } 49 50 static void drm_block_free(struct drm_buddy *mm, 51 struct drm_buddy_block *block) 52 { 53 kmem_cache_free(slab_blocks, block); 54 } 55 56 static enum drm_buddy_free_tree 57 get_block_tree(struct drm_buddy_block *block) 58 { 59 return drm_buddy_block_is_clear(block) ? 60 DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE; 61 } 62 63 static struct drm_buddy_block * 64 rbtree_get_free_block(const struct rb_node *node) 65 { 66 return node ? rb_entry(node, struct drm_buddy_block, rb) : NULL; 67 } 68 69 static struct drm_buddy_block * 70 rbtree_last_free_block(struct rb_root *root) 71 { 72 return rbtree_get_free_block(rb_last(root)); 73 } 74 75 static bool rbtree_is_empty(struct rb_root *root) 76 { 77 return RB_EMPTY_ROOT(root); 78 } 79 80 static bool drm_buddy_block_offset_less(const struct drm_buddy_block *block, 81 const struct drm_buddy_block *node) 82 { 83 return drm_buddy_block_offset(block) < drm_buddy_block_offset(node); 84 } 85 86 static bool rbtree_block_offset_less(struct rb_node *block, 87 const struct rb_node *node) 88 { 89 return drm_buddy_block_offset_less(rbtree_get_free_block(block), 90 rbtree_get_free_block(node)); 91 } 92 93 static void rbtree_insert(struct drm_buddy *mm, 94 struct drm_buddy_block *block, 95 enum drm_buddy_free_tree tree) 96 { 97 rb_add(&block->rb, 98 &mm->free_trees[tree][drm_buddy_block_order(block)], 99 rbtree_block_offset_less); 100 } 101 102 static void rbtree_remove(struct drm_buddy *mm, 103 struct drm_buddy_block *block) 104 { 105 unsigned int order = drm_buddy_block_order(block); 106 enum drm_buddy_free_tree tree; 107 struct rb_root *root; 108 109 tree = get_block_tree(block); 110 root = &mm->free_trees[tree][order]; 111 112 rb_erase(&block->rb, root); 113 RB_CLEAR_NODE(&block->rb); 114 } 115 116 static void clear_reset(struct drm_buddy_block *block) 117 { 118 block->header &= ~DRM_BUDDY_HEADER_CLEAR; 119 } 120 121 static void mark_cleared(struct drm_buddy_block *block) 122 { 123 block->header |= DRM_BUDDY_HEADER_CLEAR; 124 } 125 126 static void mark_allocated(struct drm_buddy *mm, 127 struct drm_buddy_block *block) 128 { 129 block->header &= ~DRM_BUDDY_HEADER_STATE; 130 block->header |= DRM_BUDDY_ALLOCATED; 131 132 rbtree_remove(mm, block); 133 } 134 135 static void mark_free(struct drm_buddy *mm, 136 struct drm_buddy_block *block) 137 { 138 enum drm_buddy_free_tree tree; 139 140 block->header &= ~DRM_BUDDY_HEADER_STATE; 141 block->header |= DRM_BUDDY_FREE; 142 143 tree = get_block_tree(block); 144 rbtree_insert(mm, block, tree); 145 } 146 147 static void mark_split(struct drm_buddy *mm, 148 struct drm_buddy_block *block) 149 { 150 block->header &= ~DRM_BUDDY_HEADER_STATE; 151 block->header |= DRM_BUDDY_SPLIT; 152 153 rbtree_remove(mm, block); 154 } 155 156 static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2) 157 { 158 return s1 <= e2 && e1 >= s2; 159 } 160 161 static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2) 162 { 163 return s1 <= s2 && e1 >= e2; 164 } 165 166 static struct drm_buddy_block * 167 __get_buddy(struct drm_buddy_block *block) 168 { 169 struct drm_buddy_block *parent; 170 171 parent = block->parent; 172 if (!parent) 173 return NULL; 174 175 if (parent->left == block) 176 return parent->right; 177 178 return parent->left; 179 } 180 181 static unsigned int __drm_buddy_free(struct drm_buddy *mm, 182 struct drm_buddy_block *block, 183 bool force_merge) 184 { 185 struct drm_buddy_block *parent; 186 unsigned int order; 187 188 while ((parent = block->parent)) { 189 struct drm_buddy_block *buddy; 190 191 buddy = __get_buddy(block); 192 193 if (!drm_buddy_block_is_free(buddy)) 194 break; 195 196 if (!force_merge) { 197 /* 198 * Check the block and its buddy clear state and exit 199 * the loop if they both have the dissimilar state. 200 */ 201 if (drm_buddy_block_is_clear(block) != 202 drm_buddy_block_is_clear(buddy)) 203 break; 204 205 if (drm_buddy_block_is_clear(block)) 206 mark_cleared(parent); 207 } 208 209 rbtree_remove(mm, buddy); 210 if (force_merge && drm_buddy_block_is_clear(buddy)) 211 mm->clear_avail -= drm_buddy_block_size(mm, buddy); 212 213 drm_block_free(mm, block); 214 drm_block_free(mm, buddy); 215 216 block = parent; 217 } 218 219 order = drm_buddy_block_order(block); 220 mark_free(mm, block); 221 222 return order; 223 } 224 225 static int __force_merge(struct drm_buddy *mm, 226 u64 start, 227 u64 end, 228 unsigned int min_order) 229 { 230 unsigned int tree, order; 231 int i; 232 233 if (!min_order) 234 return -ENOMEM; 235 236 if (min_order > mm->max_order) 237 return -EINVAL; 238 239 for_each_free_tree(tree) { 240 for (i = min_order - 1; i >= 0; i--) { 241 struct rb_node *iter = rb_last(&mm->free_trees[tree][i]); 242 243 while (iter) { 244 struct drm_buddy_block *block, *buddy; 245 u64 block_start, block_end; 246 247 block = rbtree_get_free_block(iter); 248 iter = rb_prev(iter); 249 250 if (!block || !block->parent) 251 continue; 252 253 block_start = drm_buddy_block_offset(block); 254 block_end = block_start + drm_buddy_block_size(mm, block) - 1; 255 256 if (!contains(start, end, block_start, block_end)) 257 continue; 258 259 buddy = __get_buddy(block); 260 if (!drm_buddy_block_is_free(buddy)) 261 continue; 262 263 WARN_ON(drm_buddy_block_is_clear(block) == 264 drm_buddy_block_is_clear(buddy)); 265 266 /* 267 * Advance to the next node when the current node is the buddy, 268 * as freeing the block will also remove its buddy from the tree. 269 */ 270 if (iter == &buddy->rb) 271 iter = rb_prev(iter); 272 273 rbtree_remove(mm, block); 274 if (drm_buddy_block_is_clear(block)) 275 mm->clear_avail -= drm_buddy_block_size(mm, block); 276 277 order = __drm_buddy_free(mm, block, true); 278 if (order >= min_order) 279 return 0; 280 } 281 } 282 } 283 284 return -ENOMEM; 285 } 286 287 /** 288 * drm_buddy_init - init memory manager 289 * 290 * @mm: DRM buddy manager to initialize 291 * @size: size in bytes to manage 292 * @chunk_size: minimum page size in bytes for our allocations 293 * 294 * Initializes the memory manager and its resources. 295 * 296 * Returns: 297 * 0 on success, error code on failure. 298 */ 299 int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size) 300 { 301 unsigned int i, j, root_count = 0; 302 u64 offset = 0; 303 304 if (size < chunk_size) 305 return -EINVAL; 306 307 if (chunk_size < SZ_4K) 308 return -EINVAL; 309 310 if (!is_power_of_2(chunk_size)) 311 return -EINVAL; 312 313 size = round_down(size, chunk_size); 314 315 mm->size = size; 316 mm->avail = size; 317 mm->clear_avail = 0; 318 mm->chunk_size = chunk_size; 319 mm->max_order = ilog2(size) - ilog2(chunk_size); 320 321 BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER); 322 323 mm->free_trees = kmalloc_objs(*mm->free_trees, DRM_BUDDY_MAX_FREE_TREES); 324 if (!mm->free_trees) 325 return -ENOMEM; 326 327 for_each_free_tree(i) { 328 mm->free_trees[i] = kmalloc_objs(struct rb_root, 329 mm->max_order + 1, GFP_KERNEL); 330 if (!mm->free_trees[i]) 331 goto out_free_tree; 332 333 for (j = 0; j <= mm->max_order; ++j) 334 mm->free_trees[i][j] = RB_ROOT; 335 } 336 337 mm->n_roots = hweight64(size); 338 339 mm->roots = kmalloc_objs(struct drm_buddy_block *, mm->n_roots); 340 if (!mm->roots) 341 goto out_free_tree; 342 343 /* 344 * Split into power-of-two blocks, in case we are given a size that is 345 * not itself a power-of-two. 346 */ 347 do { 348 struct drm_buddy_block *root; 349 unsigned int order; 350 u64 root_size; 351 352 order = ilog2(size) - ilog2(chunk_size); 353 root_size = chunk_size << order; 354 355 root = drm_block_alloc(mm, NULL, order, offset); 356 if (!root) 357 goto out_free_roots; 358 359 mark_free(mm, root); 360 361 BUG_ON(root_count > mm->max_order); 362 BUG_ON(drm_buddy_block_size(mm, root) < chunk_size); 363 364 mm->roots[root_count] = root; 365 366 offset += root_size; 367 size -= root_size; 368 root_count++; 369 } while (size); 370 371 return 0; 372 373 out_free_roots: 374 while (root_count--) 375 drm_block_free(mm, mm->roots[root_count]); 376 kfree(mm->roots); 377 out_free_tree: 378 while (i--) 379 kfree(mm->free_trees[i]); 380 kfree(mm->free_trees); 381 return -ENOMEM; 382 } 383 EXPORT_SYMBOL(drm_buddy_init); 384 385 /** 386 * drm_buddy_fini - tear down the memory manager 387 * 388 * @mm: DRM buddy manager to free 389 * 390 * Cleanup memory manager resources and the freetree 391 */ 392 void drm_buddy_fini(struct drm_buddy *mm) 393 { 394 u64 root_size, size, start; 395 unsigned int order; 396 int i; 397 398 size = mm->size; 399 400 for (i = 0; i < mm->n_roots; ++i) { 401 order = ilog2(size) - ilog2(mm->chunk_size); 402 start = drm_buddy_block_offset(mm->roots[i]); 403 __force_merge(mm, start, start + size, order); 404 405 if (WARN_ON(!drm_buddy_block_is_free(mm->roots[i]))) 406 kunit_fail_current_test("buddy_fini() root"); 407 408 drm_block_free(mm, mm->roots[i]); 409 410 root_size = mm->chunk_size << order; 411 size -= root_size; 412 } 413 414 WARN_ON(mm->avail != mm->size); 415 416 for_each_free_tree(i) 417 kfree(mm->free_trees[i]); 418 kfree(mm->free_trees); 419 kfree(mm->roots); 420 } 421 EXPORT_SYMBOL(drm_buddy_fini); 422 423 static int split_block(struct drm_buddy *mm, 424 struct drm_buddy_block *block) 425 { 426 unsigned int block_order = drm_buddy_block_order(block) - 1; 427 u64 offset = drm_buddy_block_offset(block); 428 429 BUG_ON(!drm_buddy_block_is_free(block)); 430 BUG_ON(!drm_buddy_block_order(block)); 431 432 block->left = drm_block_alloc(mm, block, block_order, offset); 433 if (!block->left) 434 return -ENOMEM; 435 436 block->right = drm_block_alloc(mm, block, block_order, 437 offset + (mm->chunk_size << block_order)); 438 if (!block->right) { 439 drm_block_free(mm, block->left); 440 return -ENOMEM; 441 } 442 443 mark_split(mm, block); 444 445 if (drm_buddy_block_is_clear(block)) { 446 mark_cleared(block->left); 447 mark_cleared(block->right); 448 clear_reset(block); 449 } 450 451 mark_free(mm, block->left); 452 mark_free(mm, block->right); 453 454 return 0; 455 } 456 457 /** 458 * drm_get_buddy - get buddy address 459 * 460 * @block: DRM buddy block 461 * 462 * Returns the corresponding buddy block for @block, or NULL 463 * if this is a root block and can't be merged further. 464 * Requires some kind of locking to protect against 465 * any concurrent allocate and free operations. 466 */ 467 struct drm_buddy_block * 468 drm_get_buddy(struct drm_buddy_block *block) 469 { 470 return __get_buddy(block); 471 } 472 EXPORT_SYMBOL(drm_get_buddy); 473 474 /** 475 * drm_buddy_reset_clear - reset blocks clear state 476 * 477 * @mm: DRM buddy manager 478 * @is_clear: blocks clear state 479 * 480 * Reset the clear state based on @is_clear value for each block 481 * in the freetree. 482 */ 483 void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear) 484 { 485 enum drm_buddy_free_tree src_tree, dst_tree; 486 u64 root_size, size, start; 487 unsigned int order; 488 int i; 489 490 size = mm->size; 491 for (i = 0; i < mm->n_roots; ++i) { 492 order = ilog2(size) - ilog2(mm->chunk_size); 493 start = drm_buddy_block_offset(mm->roots[i]); 494 __force_merge(mm, start, start + size, order); 495 496 root_size = mm->chunk_size << order; 497 size -= root_size; 498 } 499 500 src_tree = is_clear ? DRM_BUDDY_DIRTY_TREE : DRM_BUDDY_CLEAR_TREE; 501 dst_tree = is_clear ? DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE; 502 503 for (i = 0; i <= mm->max_order; ++i) { 504 struct rb_root *root = &mm->free_trees[src_tree][i]; 505 struct drm_buddy_block *block, *tmp; 506 507 rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { 508 rbtree_remove(mm, block); 509 if (is_clear) { 510 mark_cleared(block); 511 mm->clear_avail += drm_buddy_block_size(mm, block); 512 } else { 513 clear_reset(block); 514 mm->clear_avail -= drm_buddy_block_size(mm, block); 515 } 516 517 rbtree_insert(mm, block, dst_tree); 518 } 519 } 520 } 521 EXPORT_SYMBOL(drm_buddy_reset_clear); 522 523 /** 524 * drm_buddy_free_block - free a block 525 * 526 * @mm: DRM buddy manager 527 * @block: block to be freed 528 */ 529 void drm_buddy_free_block(struct drm_buddy *mm, 530 struct drm_buddy_block *block) 531 { 532 BUG_ON(!drm_buddy_block_is_allocated(block)); 533 mm->avail += drm_buddy_block_size(mm, block); 534 if (drm_buddy_block_is_clear(block)) 535 mm->clear_avail += drm_buddy_block_size(mm, block); 536 537 __drm_buddy_free(mm, block, false); 538 } 539 EXPORT_SYMBOL(drm_buddy_free_block); 540 541 static void __drm_buddy_free_list(struct drm_buddy *mm, 542 struct list_head *objects, 543 bool mark_clear, 544 bool mark_dirty) 545 { 546 struct drm_buddy_block *block, *on; 547 548 WARN_ON(mark_dirty && mark_clear); 549 550 list_for_each_entry_safe(block, on, objects, link) { 551 if (mark_clear) 552 mark_cleared(block); 553 else if (mark_dirty) 554 clear_reset(block); 555 drm_buddy_free_block(mm, block); 556 cond_resched(); 557 } 558 INIT_LIST_HEAD(objects); 559 } 560 561 static void drm_buddy_free_list_internal(struct drm_buddy *mm, 562 struct list_head *objects) 563 { 564 /* 565 * Don't touch the clear/dirty bit, since allocation is still internal 566 * at this point. For example we might have just failed part of the 567 * allocation. 568 */ 569 __drm_buddy_free_list(mm, objects, false, false); 570 } 571 572 /** 573 * drm_buddy_free_list - free blocks 574 * 575 * @mm: DRM buddy manager 576 * @objects: input list head to free blocks 577 * @flags: optional flags like DRM_BUDDY_CLEARED 578 */ 579 void drm_buddy_free_list(struct drm_buddy *mm, 580 struct list_head *objects, 581 unsigned int flags) 582 { 583 bool mark_clear = flags & DRM_BUDDY_CLEARED; 584 585 __drm_buddy_free_list(mm, objects, mark_clear, !mark_clear); 586 } 587 EXPORT_SYMBOL(drm_buddy_free_list); 588 589 static bool block_incompatible(struct drm_buddy_block *block, unsigned int flags) 590 { 591 bool needs_clear = flags & DRM_BUDDY_CLEAR_ALLOCATION; 592 593 return needs_clear != drm_buddy_block_is_clear(block); 594 } 595 596 static struct drm_buddy_block * 597 __alloc_range_bias(struct drm_buddy *mm, 598 u64 start, u64 end, 599 unsigned int order, 600 unsigned long flags, 601 bool fallback) 602 { 603 u64 req_size = mm->chunk_size << order; 604 struct drm_buddy_block *block; 605 struct drm_buddy_block *buddy; 606 LIST_HEAD(dfs); 607 int err; 608 int i; 609 610 end = end - 1; 611 612 for (i = 0; i < mm->n_roots; ++i) 613 list_add_tail(&mm->roots[i]->tmp_link, &dfs); 614 615 do { 616 u64 block_start; 617 u64 block_end; 618 619 block = list_first_entry_or_null(&dfs, 620 struct drm_buddy_block, 621 tmp_link); 622 if (!block) 623 break; 624 625 list_del(&block->tmp_link); 626 627 if (drm_buddy_block_order(block) < order) 628 continue; 629 630 block_start = drm_buddy_block_offset(block); 631 block_end = block_start + drm_buddy_block_size(mm, block) - 1; 632 633 if (!overlaps(start, end, block_start, block_end)) 634 continue; 635 636 if (drm_buddy_block_is_allocated(block)) 637 continue; 638 639 if (block_start < start || block_end > end) { 640 u64 adjusted_start = max(block_start, start); 641 u64 adjusted_end = min(block_end, end); 642 643 if (round_down(adjusted_end + 1, req_size) <= 644 round_up(adjusted_start, req_size)) 645 continue; 646 } 647 648 if (!fallback && block_incompatible(block, flags)) 649 continue; 650 651 if (contains(start, end, block_start, block_end) && 652 order == drm_buddy_block_order(block)) { 653 /* 654 * Find the free block within the range. 655 */ 656 if (drm_buddy_block_is_free(block)) 657 return block; 658 659 continue; 660 } 661 662 if (!drm_buddy_block_is_split(block)) { 663 err = split_block(mm, block); 664 if (unlikely(err)) 665 goto err_undo; 666 } 667 668 list_add(&block->right->tmp_link, &dfs); 669 list_add(&block->left->tmp_link, &dfs); 670 } while (1); 671 672 return ERR_PTR(-ENOSPC); 673 674 err_undo: 675 /* 676 * We really don't want to leave around a bunch of split blocks, since 677 * bigger is better, so make sure we merge everything back before we 678 * free the allocated blocks. 679 */ 680 buddy = __get_buddy(block); 681 if (buddy && 682 (drm_buddy_block_is_free(block) && 683 drm_buddy_block_is_free(buddy))) 684 __drm_buddy_free(mm, block, false); 685 return ERR_PTR(err); 686 } 687 688 static struct drm_buddy_block * 689 __drm_buddy_alloc_range_bias(struct drm_buddy *mm, 690 u64 start, u64 end, 691 unsigned int order, 692 unsigned long flags) 693 { 694 struct drm_buddy_block *block; 695 bool fallback = false; 696 697 block = __alloc_range_bias(mm, start, end, order, 698 flags, fallback); 699 if (IS_ERR(block)) 700 return __alloc_range_bias(mm, start, end, order, 701 flags, !fallback); 702 703 return block; 704 } 705 706 static struct drm_buddy_block * 707 get_maxblock(struct drm_buddy *mm, 708 unsigned int order, 709 enum drm_buddy_free_tree tree) 710 { 711 struct drm_buddy_block *max_block = NULL, *block = NULL; 712 struct rb_root *root; 713 unsigned int i; 714 715 for (i = order; i <= mm->max_order; ++i) { 716 root = &mm->free_trees[tree][i]; 717 block = rbtree_last_free_block(root); 718 if (!block) 719 continue; 720 721 if (!max_block) { 722 max_block = block; 723 continue; 724 } 725 726 if (drm_buddy_block_offset(block) > 727 drm_buddy_block_offset(max_block)) { 728 max_block = block; 729 } 730 } 731 732 return max_block; 733 } 734 735 static struct drm_buddy_block * 736 alloc_from_freetree(struct drm_buddy *mm, 737 unsigned int order, 738 unsigned long flags) 739 { 740 struct drm_buddy_block *block = NULL; 741 struct rb_root *root; 742 enum drm_buddy_free_tree tree; 743 unsigned int tmp; 744 int err; 745 746 tree = (flags & DRM_BUDDY_CLEAR_ALLOCATION) ? 747 DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE; 748 749 if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) { 750 block = get_maxblock(mm, order, tree); 751 if (block) 752 /* Store the obtained block order */ 753 tmp = drm_buddy_block_order(block); 754 } else { 755 for (tmp = order; tmp <= mm->max_order; ++tmp) { 756 /* Get RB tree root for this order and tree */ 757 root = &mm->free_trees[tree][tmp]; 758 block = rbtree_last_free_block(root); 759 if (block) 760 break; 761 } 762 } 763 764 if (!block) { 765 /* Try allocating from the other tree */ 766 tree = (tree == DRM_BUDDY_CLEAR_TREE) ? 767 DRM_BUDDY_DIRTY_TREE : DRM_BUDDY_CLEAR_TREE; 768 769 for (tmp = order; tmp <= mm->max_order; ++tmp) { 770 root = &mm->free_trees[tree][tmp]; 771 block = rbtree_last_free_block(root); 772 if (block) 773 break; 774 } 775 776 if (!block) 777 return ERR_PTR(-ENOSPC); 778 } 779 780 BUG_ON(!drm_buddy_block_is_free(block)); 781 782 while (tmp != order) { 783 err = split_block(mm, block); 784 if (unlikely(err)) 785 goto err_undo; 786 787 block = block->right; 788 tmp--; 789 } 790 return block; 791 792 err_undo: 793 if (tmp != order) 794 __drm_buddy_free(mm, block, false); 795 return ERR_PTR(err); 796 } 797 798 static int __alloc_range(struct drm_buddy *mm, 799 struct list_head *dfs, 800 u64 start, u64 size, 801 struct list_head *blocks, 802 u64 *total_allocated_on_err) 803 { 804 struct drm_buddy_block *block; 805 struct drm_buddy_block *buddy; 806 u64 total_allocated = 0; 807 LIST_HEAD(allocated); 808 u64 end; 809 int err; 810 811 end = start + size - 1; 812 813 do { 814 u64 block_start; 815 u64 block_end; 816 817 block = list_first_entry_or_null(dfs, 818 struct drm_buddy_block, 819 tmp_link); 820 if (!block) 821 break; 822 823 list_del(&block->tmp_link); 824 825 block_start = drm_buddy_block_offset(block); 826 block_end = block_start + drm_buddy_block_size(mm, block) - 1; 827 828 if (!overlaps(start, end, block_start, block_end)) 829 continue; 830 831 if (drm_buddy_block_is_allocated(block)) { 832 err = -ENOSPC; 833 goto err_free; 834 } 835 836 if (contains(start, end, block_start, block_end)) { 837 if (drm_buddy_block_is_free(block)) { 838 mark_allocated(mm, block); 839 total_allocated += drm_buddy_block_size(mm, block); 840 mm->avail -= drm_buddy_block_size(mm, block); 841 if (drm_buddy_block_is_clear(block)) 842 mm->clear_avail -= drm_buddy_block_size(mm, block); 843 list_add_tail(&block->link, &allocated); 844 continue; 845 } else if (!mm->clear_avail) { 846 err = -ENOSPC; 847 goto err_free; 848 } 849 } 850 851 if (!drm_buddy_block_is_split(block)) { 852 err = split_block(mm, block); 853 if (unlikely(err)) 854 goto err_undo; 855 } 856 857 list_add(&block->right->tmp_link, dfs); 858 list_add(&block->left->tmp_link, dfs); 859 } while (1); 860 861 if (total_allocated < size) { 862 err = -ENOSPC; 863 goto err_free; 864 } 865 866 list_splice_tail(&allocated, blocks); 867 868 return 0; 869 870 err_undo: 871 /* 872 * We really don't want to leave around a bunch of split blocks, since 873 * bigger is better, so make sure we merge everything back before we 874 * free the allocated blocks. 875 */ 876 buddy = __get_buddy(block); 877 if (buddy && 878 (drm_buddy_block_is_free(block) && 879 drm_buddy_block_is_free(buddy))) 880 __drm_buddy_free(mm, block, false); 881 882 err_free: 883 if (err == -ENOSPC && total_allocated_on_err) { 884 list_splice_tail(&allocated, blocks); 885 *total_allocated_on_err = total_allocated; 886 } else { 887 drm_buddy_free_list_internal(mm, &allocated); 888 } 889 890 return err; 891 } 892 893 static int __drm_buddy_alloc_range(struct drm_buddy *mm, 894 u64 start, 895 u64 size, 896 u64 *total_allocated_on_err, 897 struct list_head *blocks) 898 { 899 LIST_HEAD(dfs); 900 int i; 901 902 for (i = 0; i < mm->n_roots; ++i) 903 list_add_tail(&mm->roots[i]->tmp_link, &dfs); 904 905 return __alloc_range(mm, &dfs, start, size, 906 blocks, total_allocated_on_err); 907 } 908 909 static int __alloc_contig_try_harder(struct drm_buddy *mm, 910 u64 size, 911 u64 min_block_size, 912 struct list_head *blocks) 913 { 914 u64 rhs_offset, lhs_offset, lhs_size, filled; 915 struct drm_buddy_block *block; 916 unsigned int tree, order; 917 LIST_HEAD(blocks_lhs); 918 unsigned long pages; 919 u64 modify_size; 920 int err; 921 922 modify_size = rounddown_pow_of_two(size); 923 pages = modify_size >> ilog2(mm->chunk_size); 924 order = fls(pages) - 1; 925 if (order == 0) 926 return -ENOSPC; 927 928 for_each_free_tree(tree) { 929 struct rb_root *root; 930 struct rb_node *iter; 931 932 root = &mm->free_trees[tree][order]; 933 if (rbtree_is_empty(root)) 934 continue; 935 936 iter = rb_last(root); 937 while (iter) { 938 block = rbtree_get_free_block(iter); 939 940 /* Allocate blocks traversing RHS */ 941 rhs_offset = drm_buddy_block_offset(block); 942 err = __drm_buddy_alloc_range(mm, rhs_offset, size, 943 &filled, blocks); 944 if (!err || err != -ENOSPC) 945 return err; 946 947 lhs_size = max((size - filled), min_block_size); 948 if (!IS_ALIGNED(lhs_size, min_block_size)) 949 lhs_size = round_up(lhs_size, min_block_size); 950 951 /* Allocate blocks traversing LHS */ 952 lhs_offset = drm_buddy_block_offset(block) - lhs_size; 953 err = __drm_buddy_alloc_range(mm, lhs_offset, lhs_size, 954 NULL, &blocks_lhs); 955 if (!err) { 956 list_splice(&blocks_lhs, blocks); 957 return 0; 958 } else if (err != -ENOSPC) { 959 drm_buddy_free_list_internal(mm, blocks); 960 return err; 961 } 962 /* Free blocks for the next iteration */ 963 drm_buddy_free_list_internal(mm, blocks); 964 965 iter = rb_prev(iter); 966 } 967 } 968 969 return -ENOSPC; 970 } 971 972 /** 973 * drm_buddy_block_trim - free unused pages 974 * 975 * @mm: DRM buddy manager 976 * @start: start address to begin the trimming. 977 * @new_size: original size requested 978 * @blocks: Input and output list of allocated blocks. 979 * MUST contain single block as input to be trimmed. 980 * On success will contain the newly allocated blocks 981 * making up the @new_size. Blocks always appear in 982 * ascending order 983 * 984 * For contiguous allocation, we round up the size to the nearest 985 * power of two value, drivers consume *actual* size, so remaining 986 * portions are unused and can be optionally freed with this function 987 * 988 * Returns: 989 * 0 on success, error code on failure. 990 */ 991 int drm_buddy_block_trim(struct drm_buddy *mm, 992 u64 *start, 993 u64 new_size, 994 struct list_head *blocks) 995 { 996 struct drm_buddy_block *parent; 997 struct drm_buddy_block *block; 998 u64 block_start, block_end; 999 LIST_HEAD(dfs); 1000 u64 new_start; 1001 int err; 1002 1003 if (!list_is_singular(blocks)) 1004 return -EINVAL; 1005 1006 block = list_first_entry(blocks, 1007 struct drm_buddy_block, 1008 link); 1009 1010 block_start = drm_buddy_block_offset(block); 1011 block_end = block_start + drm_buddy_block_size(mm, block); 1012 1013 if (WARN_ON(!drm_buddy_block_is_allocated(block))) 1014 return -EINVAL; 1015 1016 if (new_size > drm_buddy_block_size(mm, block)) 1017 return -EINVAL; 1018 1019 if (!new_size || !IS_ALIGNED(new_size, mm->chunk_size)) 1020 return -EINVAL; 1021 1022 if (new_size == drm_buddy_block_size(mm, block)) 1023 return 0; 1024 1025 new_start = block_start; 1026 if (start) { 1027 new_start = *start; 1028 1029 if (new_start < block_start) 1030 return -EINVAL; 1031 1032 if (!IS_ALIGNED(new_start, mm->chunk_size)) 1033 return -EINVAL; 1034 1035 if (range_overflows(new_start, new_size, block_end)) 1036 return -EINVAL; 1037 } 1038 1039 list_del(&block->link); 1040 mark_free(mm, block); 1041 mm->avail += drm_buddy_block_size(mm, block); 1042 if (drm_buddy_block_is_clear(block)) 1043 mm->clear_avail += drm_buddy_block_size(mm, block); 1044 1045 /* Prevent recursively freeing this node */ 1046 parent = block->parent; 1047 block->parent = NULL; 1048 1049 list_add(&block->tmp_link, &dfs); 1050 err = __alloc_range(mm, &dfs, new_start, new_size, blocks, NULL); 1051 if (err) { 1052 mark_allocated(mm, block); 1053 mm->avail -= drm_buddy_block_size(mm, block); 1054 if (drm_buddy_block_is_clear(block)) 1055 mm->clear_avail -= drm_buddy_block_size(mm, block); 1056 list_add(&block->link, blocks); 1057 } 1058 1059 block->parent = parent; 1060 return err; 1061 } 1062 EXPORT_SYMBOL(drm_buddy_block_trim); 1063 1064 static struct drm_buddy_block * 1065 __drm_buddy_alloc_blocks(struct drm_buddy *mm, 1066 u64 start, u64 end, 1067 unsigned int order, 1068 unsigned long flags) 1069 { 1070 if (flags & DRM_BUDDY_RANGE_ALLOCATION) 1071 /* Allocate traversing within the range */ 1072 return __drm_buddy_alloc_range_bias(mm, start, end, 1073 order, flags); 1074 else 1075 /* Allocate from freetree */ 1076 return alloc_from_freetree(mm, order, flags); 1077 } 1078 1079 /** 1080 * drm_buddy_alloc_blocks - allocate power-of-two blocks 1081 * 1082 * @mm: DRM buddy manager to allocate from 1083 * @start: start of the allowed range for this block 1084 * @end: end of the allowed range for this block 1085 * @size: size of the allocation in bytes 1086 * @min_block_size: alignment of the allocation 1087 * @blocks: output list head to add allocated blocks 1088 * @flags: DRM_BUDDY_*_ALLOCATION flags 1089 * 1090 * alloc_range_bias() called on range limitations, which traverses 1091 * the tree and returns the desired block. 1092 * 1093 * alloc_from_freetree() called when *no* range restrictions 1094 * are enforced, which picks the block from the freetree. 1095 * 1096 * Returns: 1097 * 0 on success, error code on failure. 1098 */ 1099 int drm_buddy_alloc_blocks(struct drm_buddy *mm, 1100 u64 start, u64 end, u64 size, 1101 u64 min_block_size, 1102 struct list_head *blocks, 1103 unsigned long flags) 1104 { 1105 struct drm_buddy_block *block = NULL; 1106 u64 original_size, original_min_size; 1107 unsigned int min_order, order; 1108 LIST_HEAD(allocated); 1109 unsigned long pages; 1110 int err; 1111 1112 if (size < mm->chunk_size) 1113 return -EINVAL; 1114 1115 if (min_block_size < mm->chunk_size) 1116 return -EINVAL; 1117 1118 if (!is_power_of_2(min_block_size)) 1119 return -EINVAL; 1120 1121 if (!IS_ALIGNED(start | end | size, mm->chunk_size)) 1122 return -EINVAL; 1123 1124 if (end > mm->size) 1125 return -EINVAL; 1126 1127 if (range_overflows(start, size, mm->size)) 1128 return -EINVAL; 1129 1130 /* Actual range allocation */ 1131 if (start + size == end) { 1132 if (!IS_ALIGNED(start | end, min_block_size)) 1133 return -EINVAL; 1134 1135 return __drm_buddy_alloc_range(mm, start, size, NULL, blocks); 1136 } 1137 1138 original_size = size; 1139 original_min_size = min_block_size; 1140 1141 /* Roundup the size to power of 2 */ 1142 if (flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION) { 1143 size = roundup_pow_of_two(size); 1144 min_block_size = size; 1145 /* Align size value to min_block_size */ 1146 } else if (!IS_ALIGNED(size, min_block_size)) { 1147 size = round_up(size, min_block_size); 1148 } 1149 1150 pages = size >> ilog2(mm->chunk_size); 1151 order = fls(pages) - 1; 1152 min_order = ilog2(min_block_size) - ilog2(mm->chunk_size); 1153 1154 if (order > mm->max_order || size > mm->size) { 1155 if ((flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION) && 1156 !(flags & DRM_BUDDY_RANGE_ALLOCATION)) 1157 return __alloc_contig_try_harder(mm, original_size, 1158 original_min_size, blocks); 1159 1160 return -EINVAL; 1161 } 1162 1163 do { 1164 order = min(order, (unsigned int)fls(pages) - 1); 1165 BUG_ON(order > mm->max_order); 1166 BUG_ON(order < min_order); 1167 1168 do { 1169 block = __drm_buddy_alloc_blocks(mm, start, 1170 end, 1171 order, 1172 flags); 1173 if (!IS_ERR(block)) 1174 break; 1175 1176 if (order-- == min_order) { 1177 /* Try allocation through force merge method */ 1178 if (mm->clear_avail && 1179 !__force_merge(mm, start, end, min_order)) { 1180 block = __drm_buddy_alloc_blocks(mm, start, 1181 end, 1182 min_order, 1183 flags); 1184 if (!IS_ERR(block)) { 1185 order = min_order; 1186 break; 1187 } 1188 } 1189 1190 /* 1191 * Try contiguous block allocation through 1192 * try harder method. 1193 */ 1194 if (flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION && 1195 !(flags & DRM_BUDDY_RANGE_ALLOCATION)) 1196 return __alloc_contig_try_harder(mm, 1197 original_size, 1198 original_min_size, 1199 blocks); 1200 err = -ENOSPC; 1201 goto err_free; 1202 } 1203 } while (1); 1204 1205 mark_allocated(mm, block); 1206 mm->avail -= drm_buddy_block_size(mm, block); 1207 if (drm_buddy_block_is_clear(block)) 1208 mm->clear_avail -= drm_buddy_block_size(mm, block); 1209 kmemleak_update_trace(block); 1210 list_add_tail(&block->link, &allocated); 1211 1212 pages -= BIT(order); 1213 1214 if (!pages) 1215 break; 1216 } while (1); 1217 1218 /* Trim the allocated block to the required size */ 1219 if (!(flags & DRM_BUDDY_TRIM_DISABLE) && 1220 original_size != size) { 1221 struct list_head *trim_list; 1222 LIST_HEAD(temp); 1223 u64 trim_size; 1224 1225 trim_list = &allocated; 1226 trim_size = original_size; 1227 1228 if (!list_is_singular(&allocated)) { 1229 block = list_last_entry(&allocated, typeof(*block), link); 1230 list_move(&block->link, &temp); 1231 trim_list = &temp; 1232 trim_size = drm_buddy_block_size(mm, block) - 1233 (size - original_size); 1234 } 1235 1236 drm_buddy_block_trim(mm, 1237 NULL, 1238 trim_size, 1239 trim_list); 1240 1241 if (!list_empty(&temp)) 1242 list_splice_tail(trim_list, &allocated); 1243 } 1244 1245 list_splice_tail(&allocated, blocks); 1246 return 0; 1247 1248 err_free: 1249 drm_buddy_free_list_internal(mm, &allocated); 1250 return err; 1251 } 1252 EXPORT_SYMBOL(drm_buddy_alloc_blocks); 1253 1254 /** 1255 * drm_buddy_block_print - print block information 1256 * 1257 * @mm: DRM buddy manager 1258 * @block: DRM buddy block 1259 * @p: DRM printer to use 1260 */ 1261 void drm_buddy_block_print(struct drm_buddy *mm, 1262 struct drm_buddy_block *block, 1263 struct drm_printer *p) 1264 { 1265 u64 start = drm_buddy_block_offset(block); 1266 u64 size = drm_buddy_block_size(mm, block); 1267 1268 drm_printf(p, "%#018llx-%#018llx: %llu\n", start, start + size, size); 1269 } 1270 EXPORT_SYMBOL(drm_buddy_block_print); 1271 1272 /** 1273 * drm_buddy_print - print allocator state 1274 * 1275 * @mm: DRM buddy manager 1276 * @p: DRM printer to use 1277 */ 1278 void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p) 1279 { 1280 int order; 1281 1282 drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB, clear_free: %lluMiB\n", 1283 mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); 1284 1285 for (order = mm->max_order; order >= 0; order--) { 1286 struct drm_buddy_block *block, *tmp; 1287 struct rb_root *root; 1288 u64 count = 0, free; 1289 unsigned int tree; 1290 1291 for_each_free_tree(tree) { 1292 root = &mm->free_trees[tree][order]; 1293 1294 rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { 1295 BUG_ON(!drm_buddy_block_is_free(block)); 1296 count++; 1297 } 1298 } 1299 1300 drm_printf(p, "order-%2d ", order); 1301 1302 free = count * (mm->chunk_size << order); 1303 if (free < SZ_1M) 1304 drm_printf(p, "free: %8llu KiB", free >> 10); 1305 else 1306 drm_printf(p, "free: %8llu MiB", free >> 20); 1307 1308 drm_printf(p, ", blocks: %llu\n", count); 1309 } 1310 } 1311 EXPORT_SYMBOL(drm_buddy_print); 1312 1313 static void drm_buddy_module_exit(void) 1314 { 1315 kmem_cache_destroy(slab_blocks); 1316 } 1317 1318 static int __init drm_buddy_module_init(void) 1319 { 1320 slab_blocks = KMEM_CACHE(drm_buddy_block, 0); 1321 if (!slab_blocks) 1322 return -ENOMEM; 1323 1324 return 0; 1325 } 1326 1327 module_init(drm_buddy_module_init); 1328 module_exit(drm_buddy_module_exit); 1329 1330 MODULE_DESCRIPTION("DRM Buddy Allocator"); 1331 MODULE_LICENSE("Dual MIT/GPL"); 1332