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