Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: selftest: convert drm_buddy selftest to KUnit

Considering the current adoption of the KUnit framework, convert the
DRM buddy selftest to the KUnit API.

Tested-by: David Gow <davidgow@google.com>
Acked-by: Daniel Latypov <dlatypov@google.com>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Maíra Canal <maira.canal@usp.br>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-9-maira.canal@usp.br

authored by

Maíra Canal and committed by
Javier Martinez Canillas
932da861 9eb11f52

+758 -1011
+1 -1
drivers/gpu/drm/selftests/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 - obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_buddy.o 2 + obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o
-15
drivers/gpu/drm/selftests/drm_buddy_selftests.h
··· 1 - /* SPDX-License-Identifier: GPL-2.0 */ 2 - /* List each unit test as selftest(name, function) 3 - * 4 - * The name is used as both an enum and expanded as igt__name to create 5 - * a module parameter. It must be unique and legal for a C identifier. 6 - * 7 - * Tests are executed in order by igt/drm_buddy 8 - */ 9 - selftest(sanitycheck, igt_sanitycheck) /* keep first (selfcheck for igt) */ 10 - selftest(buddy_alloc_limit, igt_buddy_alloc_limit) 11 - selftest(buddy_alloc_range, igt_buddy_alloc_range) 12 - selftest(buddy_alloc_optimistic, igt_buddy_alloc_optimistic) 13 - selftest(buddy_alloc_pessimistic, igt_buddy_alloc_pessimistic) 14 - selftest(buddy_alloc_smoke, igt_buddy_alloc_smoke) 15 - selftest(buddy_alloc_pathological, igt_buddy_alloc_pathological)
-994
drivers/gpu/drm/selftests/test-drm_buddy.c
··· 1 - // SPDX-License-Identifier: MIT 2 - /* 3 - * Copyright © 2019 Intel Corporation 4 - */ 5 - 6 - #define pr_fmt(fmt) "drm_buddy: " fmt 7 - 8 - #include <linux/module.h> 9 - #include <linux/prime_numbers.h> 10 - #include <linux/sched/signal.h> 11 - 12 - #include <drm/drm_buddy.h> 13 - 14 - #include "../lib/drm_random.h" 15 - 16 - #define TESTS "drm_buddy_selftests.h" 17 - #include "drm_selftest.h" 18 - 19 - #define IGT_TIMEOUT(name__) \ 20 - unsigned long name__ = jiffies + MAX_SCHEDULE_TIMEOUT 21 - 22 - static unsigned int random_seed; 23 - 24 - static inline u64 get_size(int order, u64 chunk_size) 25 - { 26 - return (1 << order) * chunk_size; 27 - } 28 - 29 - __printf(2, 3) 30 - static bool __igt_timeout(unsigned long timeout, const char *fmt, ...) 31 - { 32 - va_list va; 33 - 34 - if (!signal_pending(current)) { 35 - cond_resched(); 36 - if (time_before(jiffies, timeout)) 37 - return false; 38 - } 39 - 40 - if (fmt) { 41 - va_start(va, fmt); 42 - vprintk(fmt, va); 43 - va_end(va); 44 - } 45 - 46 - return true; 47 - } 48 - 49 - static inline const char *yesno(bool v) 50 - { 51 - return v ? "yes" : "no"; 52 - } 53 - 54 - static void __igt_dump_block(struct drm_buddy *mm, 55 - struct drm_buddy_block *block, 56 - bool buddy) 57 - { 58 - pr_err("block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%s buddy=%s\n", 59 - block->header, 60 - drm_buddy_block_state(block), 61 - drm_buddy_block_order(block), 62 - drm_buddy_block_offset(block), 63 - drm_buddy_block_size(mm, block), 64 - yesno(!block->parent), 65 - yesno(buddy)); 66 - } 67 - 68 - static void igt_dump_block(struct drm_buddy *mm, 69 - struct drm_buddy_block *block) 70 - { 71 - struct drm_buddy_block *buddy; 72 - 73 - __igt_dump_block(mm, block, false); 74 - 75 - buddy = drm_get_buddy(block); 76 - if (buddy) 77 - __igt_dump_block(mm, buddy, true); 78 - } 79 - 80 - static int igt_check_block(struct drm_buddy *mm, 81 - struct drm_buddy_block *block) 82 - { 83 - struct drm_buddy_block *buddy; 84 - unsigned int block_state; 85 - u64 block_size; 86 - u64 offset; 87 - int err = 0; 88 - 89 - block_state = drm_buddy_block_state(block); 90 - 91 - if (block_state != DRM_BUDDY_ALLOCATED && 92 - block_state != DRM_BUDDY_FREE && 93 - block_state != DRM_BUDDY_SPLIT) { 94 - pr_err("block state mismatch\n"); 95 - err = -EINVAL; 96 - } 97 - 98 - block_size = drm_buddy_block_size(mm, block); 99 - offset = drm_buddy_block_offset(block); 100 - 101 - if (block_size < mm->chunk_size) { 102 - pr_err("block size smaller than min size\n"); 103 - err = -EINVAL; 104 - } 105 - 106 - if (!is_power_of_2(block_size)) { 107 - pr_err("block size not power of two\n"); 108 - err = -EINVAL; 109 - } 110 - 111 - if (!IS_ALIGNED(block_size, mm->chunk_size)) { 112 - pr_err("block size not aligned to min size\n"); 113 - err = -EINVAL; 114 - } 115 - 116 - if (!IS_ALIGNED(offset, mm->chunk_size)) { 117 - pr_err("block offset not aligned to min size\n"); 118 - err = -EINVAL; 119 - } 120 - 121 - if (!IS_ALIGNED(offset, block_size)) { 122 - pr_err("block offset not aligned to block size\n"); 123 - err = -EINVAL; 124 - } 125 - 126 - buddy = drm_get_buddy(block); 127 - 128 - if (!buddy && block->parent) { 129 - pr_err("buddy has gone fishing\n"); 130 - err = -EINVAL; 131 - } 132 - 133 - if (buddy) { 134 - if (drm_buddy_block_offset(buddy) != (offset ^ block_size)) { 135 - pr_err("buddy has wrong offset\n"); 136 - err = -EINVAL; 137 - } 138 - 139 - if (drm_buddy_block_size(mm, buddy) != block_size) { 140 - pr_err("buddy size mismatch\n"); 141 - err = -EINVAL; 142 - } 143 - 144 - if (drm_buddy_block_state(buddy) == block_state && 145 - block_state == DRM_BUDDY_FREE) { 146 - pr_err("block and its buddy are free\n"); 147 - err = -EINVAL; 148 - } 149 - } 150 - 151 - return err; 152 - } 153 - 154 - static int igt_check_blocks(struct drm_buddy *mm, 155 - struct list_head *blocks, 156 - u64 expected_size, 157 - bool is_contiguous) 158 - { 159 - struct drm_buddy_block *block; 160 - struct drm_buddy_block *prev; 161 - u64 total; 162 - int err = 0; 163 - 164 - block = NULL; 165 - prev = NULL; 166 - total = 0; 167 - 168 - list_for_each_entry(block, blocks, link) { 169 - err = igt_check_block(mm, block); 170 - 171 - if (!drm_buddy_block_is_allocated(block)) { 172 - pr_err("block not allocated\n"), 173 - err = -EINVAL; 174 - } 175 - 176 - if (is_contiguous && prev) { 177 - u64 prev_block_size; 178 - u64 prev_offset; 179 - u64 offset; 180 - 181 - prev_offset = drm_buddy_block_offset(prev); 182 - prev_block_size = drm_buddy_block_size(mm, prev); 183 - offset = drm_buddy_block_offset(block); 184 - 185 - if (offset != (prev_offset + prev_block_size)) { 186 - pr_err("block offset mismatch\n"); 187 - err = -EINVAL; 188 - } 189 - } 190 - 191 - if (err) 192 - break; 193 - 194 - total += drm_buddy_block_size(mm, block); 195 - prev = block; 196 - } 197 - 198 - if (!err) { 199 - if (total != expected_size) { 200 - pr_err("size mismatch, expected=%llx, found=%llx\n", 201 - expected_size, total); 202 - err = -EINVAL; 203 - } 204 - return err; 205 - } 206 - 207 - if (prev) { 208 - pr_err("prev block, dump:\n"); 209 - igt_dump_block(mm, prev); 210 - } 211 - 212 - pr_err("bad block, dump:\n"); 213 - igt_dump_block(mm, block); 214 - 215 - return err; 216 - } 217 - 218 - static int igt_check_mm(struct drm_buddy *mm) 219 - { 220 - struct drm_buddy_block *root; 221 - struct drm_buddy_block *prev; 222 - unsigned int i; 223 - u64 total; 224 - int err = 0; 225 - 226 - if (!mm->n_roots) { 227 - pr_err("n_roots is zero\n"); 228 - return -EINVAL; 229 - } 230 - 231 - if (mm->n_roots != hweight64(mm->size)) { 232 - pr_err("n_roots mismatch, n_roots=%u, expected=%lu\n", 233 - mm->n_roots, hweight64(mm->size)); 234 - return -EINVAL; 235 - } 236 - 237 - root = NULL; 238 - prev = NULL; 239 - total = 0; 240 - 241 - for (i = 0; i < mm->n_roots; ++i) { 242 - struct drm_buddy_block *block; 243 - unsigned int order; 244 - 245 - root = mm->roots[i]; 246 - if (!root) { 247 - pr_err("root(%u) is NULL\n", i); 248 - err = -EINVAL; 249 - break; 250 - } 251 - 252 - err = igt_check_block(mm, root); 253 - 254 - if (!drm_buddy_block_is_free(root)) { 255 - pr_err("root not free\n"); 256 - err = -EINVAL; 257 - } 258 - 259 - order = drm_buddy_block_order(root); 260 - 261 - if (!i) { 262 - if (order != mm->max_order) { 263 - pr_err("max order root missing\n"); 264 - err = -EINVAL; 265 - } 266 - } 267 - 268 - if (prev) { 269 - u64 prev_block_size; 270 - u64 prev_offset; 271 - u64 offset; 272 - 273 - prev_offset = drm_buddy_block_offset(prev); 274 - prev_block_size = drm_buddy_block_size(mm, prev); 275 - offset = drm_buddy_block_offset(root); 276 - 277 - if (offset != (prev_offset + prev_block_size)) { 278 - pr_err("root offset mismatch\n"); 279 - err = -EINVAL; 280 - } 281 - } 282 - 283 - block = list_first_entry_or_null(&mm->free_list[order], 284 - struct drm_buddy_block, 285 - link); 286 - if (block != root) { 287 - pr_err("root mismatch at order=%u\n", order); 288 - err = -EINVAL; 289 - } 290 - 291 - if (err) 292 - break; 293 - 294 - prev = root; 295 - total += drm_buddy_block_size(mm, root); 296 - } 297 - 298 - if (!err) { 299 - if (total != mm->size) { 300 - pr_err("expected mm size=%llx, found=%llx\n", mm->size, 301 - total); 302 - err = -EINVAL; 303 - } 304 - return err; 305 - } 306 - 307 - if (prev) { 308 - pr_err("prev root(%u), dump:\n", i - 1); 309 - igt_dump_block(mm, prev); 310 - } 311 - 312 - if (root) { 313 - pr_err("bad root(%u), dump:\n", i); 314 - igt_dump_block(mm, root); 315 - } 316 - 317 - return err; 318 - } 319 - 320 - static void igt_mm_config(u64 *size, u64 *chunk_size) 321 - { 322 - DRM_RND_STATE(prng, random_seed); 323 - u32 s, ms; 324 - 325 - /* Nothing fancy, just try to get an interesting bit pattern */ 326 - 327 - prandom_seed_state(&prng, random_seed); 328 - 329 - /* Let size be a random number of pages up to 8 GB (2M pages) */ 330 - s = 1 + drm_prandom_u32_max_state((BIT(33 - 12)) - 1, &prng); 331 - /* Let the chunk size be a random power of 2 less than size */ 332 - ms = BIT(drm_prandom_u32_max_state(ilog2(s), &prng)); 333 - /* Round size down to the chunk size */ 334 - s &= -ms; 335 - 336 - /* Convert from pages to bytes */ 337 - *chunk_size = (u64)ms << 12; 338 - *size = (u64)s << 12; 339 - } 340 - 341 - static int igt_buddy_alloc_pathological(void *arg) 342 - { 343 - u64 mm_size, size, min_page_size, start = 0; 344 - struct drm_buddy_block *block; 345 - const int max_order = 3; 346 - unsigned long flags = 0; 347 - int order, top, err; 348 - struct drm_buddy mm; 349 - LIST_HEAD(blocks); 350 - LIST_HEAD(holes); 351 - LIST_HEAD(tmp); 352 - 353 - /* 354 - * Create a pot-sized mm, then allocate one of each possible 355 - * order within. This should leave the mm with exactly one 356 - * page left. Free the largest block, then whittle down again. 357 - * Eventually we will have a fully 50% fragmented mm. 358 - */ 359 - 360 - mm_size = PAGE_SIZE << max_order; 361 - err = drm_buddy_init(&mm, mm_size, PAGE_SIZE); 362 - if (err) { 363 - pr_err("buddy_init failed(%d)\n", err); 364 - return err; 365 - } 366 - BUG_ON(mm.max_order != max_order); 367 - 368 - for (top = max_order; top; top--) { 369 - /* Make room by freeing the largest allocated block */ 370 - block = list_first_entry_or_null(&blocks, typeof(*block), link); 371 - if (block) { 372 - list_del(&block->link); 373 - drm_buddy_free_block(&mm, block); 374 - } 375 - 376 - for (order = top; order--; ) { 377 - size = min_page_size = get_size(order, PAGE_SIZE); 378 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, 379 - min_page_size, &tmp, flags); 380 - if (err) { 381 - pr_info("buddy_alloc hit -ENOMEM with order=%d, top=%d\n", 382 - order, top); 383 - goto err; 384 - } 385 - 386 - block = list_first_entry_or_null(&tmp, 387 - struct drm_buddy_block, 388 - link); 389 - if (!block) { 390 - pr_err("alloc_blocks has no blocks\n"); 391 - err = -EINVAL; 392 - goto err; 393 - } 394 - 395 - list_move_tail(&block->link, &blocks); 396 - } 397 - 398 - /* There should be one final page for this sub-allocation */ 399 - size = min_page_size = get_size(0, PAGE_SIZE); 400 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); 401 - if (err) { 402 - pr_info("buddy_alloc hit -ENOMEM for hole\n"); 403 - goto err; 404 - } 405 - 406 - block = list_first_entry_or_null(&tmp, 407 - struct drm_buddy_block, 408 - link); 409 - if (!block) { 410 - pr_err("alloc_blocks has no blocks\n"); 411 - err = -EINVAL; 412 - goto err; 413 - } 414 - 415 - list_move_tail(&block->link, &holes); 416 - 417 - size = min_page_size = get_size(top, PAGE_SIZE); 418 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); 419 - if (!err) { 420 - pr_info("buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!", 421 - top, max_order); 422 - block = list_first_entry_or_null(&tmp, 423 - struct drm_buddy_block, 424 - link); 425 - if (!block) { 426 - pr_err("alloc_blocks has no blocks\n"); 427 - err = -EINVAL; 428 - goto err; 429 - } 430 - 431 - list_move_tail(&block->link, &blocks); 432 - err = -EINVAL; 433 - goto err; 434 - } 435 - } 436 - 437 - drm_buddy_free_list(&mm, &holes); 438 - 439 - /* Nothing larger than blocks of chunk_size now available */ 440 - for (order = 1; order <= max_order; order++) { 441 - size = min_page_size = get_size(order, PAGE_SIZE); 442 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); 443 - if (!err) { 444 - pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!", 445 - order); 446 - block = list_first_entry_or_null(&tmp, 447 - struct drm_buddy_block, 448 - link); 449 - if (!block) { 450 - pr_err("alloc_blocks has no blocks\n"); 451 - err = -EINVAL; 452 - goto err; 453 - } 454 - 455 - list_move_tail(&block->link, &blocks); 456 - err = -EINVAL; 457 - goto err; 458 - } 459 - } 460 - 461 - if (err) 462 - err = 0; 463 - 464 - err: 465 - list_splice_tail(&holes, &blocks); 466 - drm_buddy_free_list(&mm, &blocks); 467 - drm_buddy_fini(&mm); 468 - return err; 469 - } 470 - 471 - static int igt_buddy_alloc_smoke(void *arg) 472 - { 473 - u64 mm_size, min_page_size, chunk_size, start = 0; 474 - unsigned long flags = 0; 475 - struct drm_buddy mm; 476 - int *order; 477 - int err, i; 478 - 479 - DRM_RND_STATE(prng, random_seed); 480 - IGT_TIMEOUT(end_time); 481 - 482 - igt_mm_config(&mm_size, &chunk_size); 483 - 484 - err = drm_buddy_init(&mm, mm_size, chunk_size); 485 - if (err) { 486 - pr_err("buddy_init failed(%d)\n", err); 487 - return err; 488 - } 489 - 490 - order = drm_random_order(mm.max_order + 1, &prng); 491 - if (!order) { 492 - err = -ENOMEM; 493 - goto out_fini; 494 - } 495 - 496 - for (i = 0; i <= mm.max_order; ++i) { 497 - struct drm_buddy_block *block; 498 - int max_order = order[i]; 499 - bool timeout = false; 500 - LIST_HEAD(blocks); 501 - u64 total, size; 502 - LIST_HEAD(tmp); 503 - int order; 504 - 505 - err = igt_check_mm(&mm); 506 - if (err) { 507 - pr_err("pre-mm check failed, abort\n"); 508 - break; 509 - } 510 - 511 - order = max_order; 512 - total = 0; 513 - 514 - do { 515 - retry: 516 - size = min_page_size = get_size(order, chunk_size); 517 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, 518 - min_page_size, &tmp, flags); 519 - if (err) { 520 - if (err == -ENOMEM) { 521 - pr_info("buddy_alloc hit -ENOMEM with order=%d\n", 522 - order); 523 - } else { 524 - if (order--) { 525 - err = 0; 526 - goto retry; 527 - } 528 - 529 - pr_err("buddy_alloc with order=%d failed(%d)\n", 530 - order, err); 531 - } 532 - 533 - break; 534 - } 535 - 536 - block = list_first_entry_or_null(&tmp, 537 - struct drm_buddy_block, 538 - link); 539 - if (!block) { 540 - pr_err("alloc_blocks has no blocks\n"); 541 - err = -EINVAL; 542 - break; 543 - } 544 - 545 - list_move_tail(&block->link, &blocks); 546 - 547 - if (drm_buddy_block_order(block) != order) { 548 - pr_err("buddy_alloc order mismatch\n"); 549 - err = -EINVAL; 550 - break; 551 - } 552 - 553 - total += drm_buddy_block_size(&mm, block); 554 - 555 - if (__igt_timeout(end_time, NULL)) { 556 - timeout = true; 557 - break; 558 - } 559 - } while (total < mm.size); 560 - 561 - if (!err) 562 - err = igt_check_blocks(&mm, &blocks, total, false); 563 - 564 - drm_buddy_free_list(&mm, &blocks); 565 - 566 - if (!err) { 567 - err = igt_check_mm(&mm); 568 - if (err) 569 - pr_err("post-mm check failed\n"); 570 - } 571 - 572 - if (err || timeout) 573 - break; 574 - 575 - cond_resched(); 576 - } 577 - 578 - if (err == -ENOMEM) 579 - err = 0; 580 - 581 - kfree(order); 582 - out_fini: 583 - drm_buddy_fini(&mm); 584 - 585 - return err; 586 - } 587 - 588 - static int igt_buddy_alloc_pessimistic(void *arg) 589 - { 590 - u64 mm_size, size, min_page_size, start = 0; 591 - struct drm_buddy_block *block, *bn; 592 - const unsigned int max_order = 16; 593 - unsigned long flags = 0; 594 - struct drm_buddy mm; 595 - unsigned int order; 596 - LIST_HEAD(blocks); 597 - LIST_HEAD(tmp); 598 - int err; 599 - 600 - /* 601 - * Create a pot-sized mm, then allocate one of each possible 602 - * order within. This should leave the mm with exactly one 603 - * page left. 604 - */ 605 - 606 - mm_size = PAGE_SIZE << max_order; 607 - err = drm_buddy_init(&mm, mm_size, PAGE_SIZE); 608 - if (err) { 609 - pr_err("buddy_init failed(%d)\n", err); 610 - return err; 611 - } 612 - BUG_ON(mm.max_order != max_order); 613 - 614 - for (order = 0; order < max_order; order++) { 615 - size = min_page_size = get_size(order, PAGE_SIZE); 616 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); 617 - if (err) { 618 - pr_info("buddy_alloc hit -ENOMEM with order=%d\n", 619 - order); 620 - goto err; 621 - } 622 - 623 - block = list_first_entry_or_null(&tmp, 624 - struct drm_buddy_block, 625 - link); 626 - if (!block) { 627 - pr_err("alloc_blocks has no blocks\n"); 628 - err = -EINVAL; 629 - goto err; 630 - } 631 - 632 - list_move_tail(&block->link, &blocks); 633 - } 634 - 635 - /* And now the last remaining block available */ 636 - size = min_page_size = get_size(0, PAGE_SIZE); 637 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); 638 - if (err) { 639 - pr_info("buddy_alloc hit -ENOMEM on final alloc\n"); 640 - goto err; 641 - } 642 - 643 - block = list_first_entry_or_null(&tmp, 644 - struct drm_buddy_block, 645 - link); 646 - if (!block) { 647 - pr_err("alloc_blocks has no blocks\n"); 648 - err = -EINVAL; 649 - goto err; 650 - } 651 - 652 - list_move_tail(&block->link, &blocks); 653 - 654 - /* Should be completely full! */ 655 - for (order = max_order; order--; ) { 656 - size = min_page_size = get_size(order, PAGE_SIZE); 657 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); 658 - if (!err) { 659 - pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!", 660 - order); 661 - block = list_first_entry_or_null(&tmp, 662 - struct drm_buddy_block, 663 - link); 664 - if (!block) { 665 - pr_err("alloc_blocks has no blocks\n"); 666 - err = -EINVAL; 667 - goto err; 668 - } 669 - 670 - list_move_tail(&block->link, &blocks); 671 - err = -EINVAL; 672 - goto err; 673 - } 674 - } 675 - 676 - block = list_last_entry(&blocks, typeof(*block), link); 677 - list_del(&block->link); 678 - drm_buddy_free_block(&mm, block); 679 - 680 - /* As we free in increasing size, we make available larger blocks */ 681 - order = 1; 682 - list_for_each_entry_safe(block, bn, &blocks, link) { 683 - list_del(&block->link); 684 - drm_buddy_free_block(&mm, block); 685 - 686 - size = min_page_size = get_size(order, PAGE_SIZE); 687 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); 688 - if (err) { 689 - pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n", 690 - order); 691 - goto err; 692 - } 693 - 694 - block = list_first_entry_or_null(&tmp, 695 - struct drm_buddy_block, 696 - link); 697 - if (!block) { 698 - pr_err("alloc_blocks has no blocks\n"); 699 - err = -EINVAL; 700 - goto err; 701 - } 702 - 703 - list_del(&block->link); 704 - drm_buddy_free_block(&mm, block); 705 - order++; 706 - } 707 - 708 - /* To confirm, now the whole mm should be available */ 709 - size = min_page_size = get_size(max_order, PAGE_SIZE); 710 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); 711 - if (err) { 712 - pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n", 713 - max_order); 714 - goto err; 715 - } 716 - 717 - block = list_first_entry_or_null(&tmp, 718 - struct drm_buddy_block, 719 - link); 720 - if (!block) { 721 - pr_err("alloc_blocks has no blocks\n"); 722 - err = -EINVAL; 723 - goto err; 724 - } 725 - 726 - list_del(&block->link); 727 - drm_buddy_free_block(&mm, block); 728 - 729 - err: 730 - drm_buddy_free_list(&mm, &blocks); 731 - drm_buddy_fini(&mm); 732 - return err; 733 - } 734 - 735 - static int igt_buddy_alloc_optimistic(void *arg) 736 - { 737 - u64 mm_size, size, min_page_size, start = 0; 738 - struct drm_buddy_block *block; 739 - unsigned long flags = 0; 740 - const int max_order = 16; 741 - struct drm_buddy mm; 742 - LIST_HEAD(blocks); 743 - LIST_HEAD(tmp); 744 - int order, err; 745 - 746 - /* 747 - * Create a mm with one block of each order available, and 748 - * try to allocate them all. 749 - */ 750 - 751 - mm_size = PAGE_SIZE * ((1 << (max_order + 1)) - 1); 752 - err = drm_buddy_init(&mm, 753 - mm_size, 754 - PAGE_SIZE); 755 - if (err) { 756 - pr_err("buddy_init failed(%d)\n", err); 757 - return err; 758 - } 759 - 760 - BUG_ON(mm.max_order != max_order); 761 - 762 - for (order = 0; order <= max_order; order++) { 763 - size = min_page_size = get_size(order, PAGE_SIZE); 764 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); 765 - if (err) { 766 - pr_info("buddy_alloc hit -ENOMEM with order=%d\n", 767 - order); 768 - goto err; 769 - } 770 - 771 - block = list_first_entry_or_null(&tmp, 772 - struct drm_buddy_block, 773 - link); 774 - if (!block) { 775 - pr_err("alloc_blocks has no blocks\n"); 776 - err = -EINVAL; 777 - goto err; 778 - } 779 - 780 - list_move_tail(&block->link, &blocks); 781 - } 782 - 783 - /* Should be completely full! */ 784 - size = min_page_size = get_size(0, PAGE_SIZE); 785 - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); 786 - if (!err) { 787 - pr_info("buddy_alloc unexpectedly succeeded, it should be full!"); 788 - block = list_first_entry_or_null(&tmp, 789 - struct drm_buddy_block, 790 - link); 791 - if (!block) { 792 - pr_err("alloc_blocks has no blocks\n"); 793 - err = -EINVAL; 794 - goto err; 795 - } 796 - 797 - list_move_tail(&block->link, &blocks); 798 - err = -EINVAL; 799 - goto err; 800 - } else { 801 - err = 0; 802 - } 803 - 804 - err: 805 - drm_buddy_free_list(&mm, &blocks); 806 - drm_buddy_fini(&mm); 807 - return err; 808 - } 809 - 810 - static int igt_buddy_alloc_range(void *arg) 811 - { 812 - unsigned long flags = DRM_BUDDY_RANGE_ALLOCATION; 813 - u64 offset, size, rem, chunk_size, end; 814 - unsigned long page_num; 815 - struct drm_buddy mm; 816 - LIST_HEAD(blocks); 817 - int err; 818 - 819 - igt_mm_config(&size, &chunk_size); 820 - 821 - err = drm_buddy_init(&mm, size, chunk_size); 822 - if (err) { 823 - pr_err("buddy_init failed(%d)\n", err); 824 - return err; 825 - } 826 - 827 - err = igt_check_mm(&mm); 828 - if (err) { 829 - pr_err("pre-mm check failed, abort, abort, abort!\n"); 830 - goto err_fini; 831 - } 832 - 833 - rem = mm.size; 834 - offset = 0; 835 - 836 - for_each_prime_number_from(page_num, 1, ULONG_MAX - 1) { 837 - struct drm_buddy_block *block; 838 - LIST_HEAD(tmp); 839 - 840 - size = min(page_num * mm.chunk_size, rem); 841 - end = offset + size; 842 - 843 - err = drm_buddy_alloc_blocks(&mm, offset, end, size, mm.chunk_size, &tmp, flags); 844 - if (err) { 845 - if (err == -ENOMEM) { 846 - pr_info("alloc_range hit -ENOMEM with size=%llx\n", 847 - size); 848 - } else { 849 - pr_err("alloc_range with offset=%llx, size=%llx failed(%d)\n", 850 - offset, size, err); 851 - } 852 - 853 - break; 854 - } 855 - 856 - block = list_first_entry_or_null(&tmp, 857 - struct drm_buddy_block, 858 - link); 859 - if (!block) { 860 - pr_err("alloc_range has no blocks\n"); 861 - err = -EINVAL; 862 - break; 863 - } 864 - 865 - if (drm_buddy_block_offset(block) != offset) { 866 - pr_err("alloc_range start offset mismatch, found=%llx, expected=%llx\n", 867 - drm_buddy_block_offset(block), offset); 868 - err = -EINVAL; 869 - } 870 - 871 - if (!err) 872 - err = igt_check_blocks(&mm, &tmp, size, true); 873 - 874 - list_splice_tail(&tmp, &blocks); 875 - 876 - if (err) 877 - break; 878 - 879 - offset += size; 880 - 881 - rem -= size; 882 - if (!rem) 883 - break; 884 - 885 - cond_resched(); 886 - } 887 - 888 - if (err == -ENOMEM) 889 - err = 0; 890 - 891 - drm_buddy_free_list(&mm, &blocks); 892 - 893 - if (!err) { 894 - err = igt_check_mm(&mm); 895 - if (err) 896 - pr_err("post-mm check failed\n"); 897 - } 898 - 899 - err_fini: 900 - drm_buddy_fini(&mm); 901 - 902 - return err; 903 - } 904 - 905 - static int igt_buddy_alloc_limit(void *arg) 906 - { 907 - u64 size = U64_MAX, start = 0; 908 - struct drm_buddy_block *block; 909 - unsigned long flags = 0; 910 - LIST_HEAD(allocated); 911 - struct drm_buddy mm; 912 - int err; 913 - 914 - err = drm_buddy_init(&mm, size, PAGE_SIZE); 915 - if (err) 916 - return err; 917 - 918 - if (mm.max_order != DRM_BUDDY_MAX_ORDER) { 919 - pr_err("mm.max_order(%d) != %d\n", 920 - mm.max_order, DRM_BUDDY_MAX_ORDER); 921 - err = -EINVAL; 922 - goto out_fini; 923 - } 924 - 925 - size = mm.chunk_size << mm.max_order; 926 - err = drm_buddy_alloc_blocks(&mm, start, size, size, 927 - PAGE_SIZE, &allocated, flags); 928 - 929 - if (unlikely(err)) 930 - goto out_free; 931 - 932 - block = list_first_entry_or_null(&allocated, 933 - struct drm_buddy_block, 934 - link); 935 - 936 - if (!block) { 937 - err = -EINVAL; 938 - goto out_fini; 939 - } 940 - 941 - if (drm_buddy_block_order(block) != mm.max_order) { 942 - pr_err("block order(%d) != %d\n", 943 - drm_buddy_block_order(block), mm.max_order); 944 - err = -EINVAL; 945 - goto out_free; 946 - } 947 - 948 - if (drm_buddy_block_size(&mm, block) != 949 - BIT_ULL(mm.max_order) * PAGE_SIZE) { 950 - pr_err("block size(%llu) != %llu\n", 951 - drm_buddy_block_size(&mm, block), 952 - BIT_ULL(mm.max_order) * PAGE_SIZE); 953 - err = -EINVAL; 954 - goto out_free; 955 - } 956 - 957 - out_free: 958 - drm_buddy_free_list(&mm, &allocated); 959 - out_fini: 960 - drm_buddy_fini(&mm); 961 - return err; 962 - } 963 - 964 - static int igt_sanitycheck(void *ignored) 965 - { 966 - pr_info("%s - ok!\n", __func__); 967 - return 0; 968 - } 969 - 970 - #include "drm_selftest.c" 971 - 972 - static int __init test_drm_buddy_init(void) 973 - { 974 - int err; 975 - 976 - while (!random_seed) 977 - random_seed = get_random_int(); 978 - 979 - pr_info("Testing DRM buddy manager (struct drm_buddy), with random_seed=0x%x\n", 980 - random_seed); 981 - err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); 982 - 983 - return err > 0 ? 0 : err; 984 - } 985 - 986 - static void __exit test_drm_buddy_exit(void) 987 - { 988 - } 989 - 990 - module_init(test_drm_buddy_init); 991 - module_exit(test_drm_buddy_exit); 992 - 993 - MODULE_AUTHOR("Intel Corporation"); 994 - MODULE_LICENSE("GPL");
+1 -1
drivers/gpu/drm/tests/Makefile
··· 2 2 3 3 obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \ 4 4 drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o drm_plane_helper_test.o \ 5 - drm_dp_mst_helper_test.o drm_framebuffer_test.o 5 + drm_dp_mst_helper_test.o drm_framebuffer_test.o drm_buddy_test.o
+756
drivers/gpu/drm/tests/drm_buddy_test.c
··· 1 + // SPDX-License-Identifier: MIT 2 + /* 3 + * Copyright © 2019 Intel Corporation 4 + * Copyright © 2022 Maíra Canal <mairacanal@riseup.net> 5 + */ 6 + 7 + #include <kunit/test.h> 8 + 9 + #include <linux/prime_numbers.h> 10 + #include <linux/sched/signal.h> 11 + 12 + #include <drm/drm_buddy.h> 13 + 14 + #include "../lib/drm_random.h" 15 + 16 + #define IGT_TIMEOUT(name__) \ 17 + unsigned long name__ = jiffies + MAX_SCHEDULE_TIMEOUT 18 + 19 + static unsigned int random_seed; 20 + 21 + static inline u64 get_size(int order, u64 chunk_size) 22 + { 23 + return (1 << order) * chunk_size; 24 + } 25 + 26 + __printf(2, 3) 27 + static bool __igt_timeout(unsigned long timeout, const char *fmt, ...) 28 + { 29 + va_list va; 30 + 31 + if (!signal_pending(current)) { 32 + cond_resched(); 33 + if (time_before(jiffies, timeout)) 34 + return false; 35 + } 36 + 37 + if (fmt) { 38 + va_start(va, fmt); 39 + vprintk(fmt, va); 40 + va_end(va); 41 + } 42 + 43 + return true; 44 + } 45 + 46 + static void __igt_dump_block(struct kunit *test, struct drm_buddy *mm, 47 + struct drm_buddy_block *block, bool buddy) 48 + { 49 + kunit_err(test, "block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%d buddy=%d\n", 50 + block->header, drm_buddy_block_state(block), 51 + drm_buddy_block_order(block), drm_buddy_block_offset(block), 52 + drm_buddy_block_size(mm, block), !block->parent, buddy); 53 + } 54 + 55 + static void igt_dump_block(struct kunit *test, struct drm_buddy *mm, 56 + struct drm_buddy_block *block) 57 + { 58 + struct drm_buddy_block *buddy; 59 + 60 + __igt_dump_block(test, mm, block, false); 61 + 62 + buddy = drm_get_buddy(block); 63 + if (buddy) 64 + __igt_dump_block(test, mm, buddy, true); 65 + } 66 + 67 + static int igt_check_block(struct kunit *test, struct drm_buddy *mm, 68 + struct drm_buddy_block *block) 69 + { 70 + struct drm_buddy_block *buddy; 71 + unsigned int block_state; 72 + u64 block_size; 73 + u64 offset; 74 + int err = 0; 75 + 76 + block_state = drm_buddy_block_state(block); 77 + 78 + if (block_state != DRM_BUDDY_ALLOCATED && 79 + block_state != DRM_BUDDY_FREE && block_state != DRM_BUDDY_SPLIT) { 80 + kunit_err(test, "block state mismatch\n"); 81 + err = -EINVAL; 82 + } 83 + 84 + block_size = drm_buddy_block_size(mm, block); 85 + offset = drm_buddy_block_offset(block); 86 + 87 + if (block_size < mm->chunk_size) { 88 + kunit_err(test, "block size smaller than min size\n"); 89 + err = -EINVAL; 90 + } 91 + 92 + if (!is_power_of_2(block_size)) { 93 + kunit_err(test, "block size not power of two\n"); 94 + err = -EINVAL; 95 + } 96 + 97 + if (!IS_ALIGNED(block_size, mm->chunk_size)) { 98 + kunit_err(test, "block size not aligned to min size\n"); 99 + err = -EINVAL; 100 + } 101 + 102 + if (!IS_ALIGNED(offset, mm->chunk_size)) { 103 + kunit_err(test, "block offset not aligned to min size\n"); 104 + err = -EINVAL; 105 + } 106 + 107 + if (!IS_ALIGNED(offset, block_size)) { 108 + kunit_err(test, "block offset not aligned to block size\n"); 109 + err = -EINVAL; 110 + } 111 + 112 + buddy = drm_get_buddy(block); 113 + 114 + if (!buddy && block->parent) { 115 + kunit_err(test, "buddy has gone fishing\n"); 116 + err = -EINVAL; 117 + } 118 + 119 + if (buddy) { 120 + if (drm_buddy_block_offset(buddy) != (offset ^ block_size)) { 121 + kunit_err(test, "buddy has wrong offset\n"); 122 + err = -EINVAL; 123 + } 124 + 125 + if (drm_buddy_block_size(mm, buddy) != block_size) { 126 + kunit_err(test, "buddy size mismatch\n"); 127 + err = -EINVAL; 128 + } 129 + 130 + if (drm_buddy_block_state(buddy) == block_state && 131 + block_state == DRM_BUDDY_FREE) { 132 + kunit_err(test, "block and its buddy are free\n"); 133 + err = -EINVAL; 134 + } 135 + } 136 + 137 + return err; 138 + } 139 + 140 + static int igt_check_blocks(struct kunit *test, struct drm_buddy *mm, 141 + struct list_head *blocks, u64 expected_size, bool is_contiguous) 142 + { 143 + struct drm_buddy_block *block; 144 + struct drm_buddy_block *prev; 145 + u64 total; 146 + int err = 0; 147 + 148 + block = NULL; 149 + prev = NULL; 150 + total = 0; 151 + 152 + list_for_each_entry(block, blocks, link) { 153 + err = igt_check_block(test, mm, block); 154 + 155 + if (!drm_buddy_block_is_allocated(block)) { 156 + kunit_err(test, "block not allocated\n"); 157 + err = -EINVAL; 158 + } 159 + 160 + if (is_contiguous && prev) { 161 + u64 prev_block_size; 162 + u64 prev_offset; 163 + u64 offset; 164 + 165 + prev_offset = drm_buddy_block_offset(prev); 166 + prev_block_size = drm_buddy_block_size(mm, prev); 167 + offset = drm_buddy_block_offset(block); 168 + 169 + if (offset != (prev_offset + prev_block_size)) { 170 + kunit_err(test, "block offset mismatch\n"); 171 + err = -EINVAL; 172 + } 173 + } 174 + 175 + if (err) 176 + break; 177 + 178 + total += drm_buddy_block_size(mm, block); 179 + prev = block; 180 + } 181 + 182 + if (!err) { 183 + if (total != expected_size) { 184 + kunit_err(test, "size mismatch, expected=%llx, found=%llx\n", 185 + expected_size, total); 186 + err = -EINVAL; 187 + } 188 + return err; 189 + } 190 + 191 + if (prev) { 192 + kunit_err(test, "prev block, dump:\n"); 193 + igt_dump_block(test, mm, prev); 194 + } 195 + 196 + kunit_err(test, "bad block, dump:\n"); 197 + igt_dump_block(test, mm, block); 198 + 199 + return err; 200 + } 201 + 202 + static int igt_check_mm(struct kunit *test, struct drm_buddy *mm) 203 + { 204 + struct drm_buddy_block *root; 205 + struct drm_buddy_block *prev; 206 + unsigned int i; 207 + u64 total; 208 + int err = 0; 209 + 210 + if (!mm->n_roots) { 211 + kunit_err(test, "n_roots is zero\n"); 212 + return -EINVAL; 213 + } 214 + 215 + if (mm->n_roots != hweight64(mm->size)) { 216 + kunit_err(test, "n_roots mismatch, n_roots=%u, expected=%lu\n", 217 + mm->n_roots, hweight64(mm->size)); 218 + return -EINVAL; 219 + } 220 + 221 + root = NULL; 222 + prev = NULL; 223 + total = 0; 224 + 225 + for (i = 0; i < mm->n_roots; ++i) { 226 + struct drm_buddy_block *block; 227 + unsigned int order; 228 + 229 + root = mm->roots[i]; 230 + if (!root) { 231 + kunit_err(test, "root(%u) is NULL\n", i); 232 + err = -EINVAL; 233 + break; 234 + } 235 + 236 + err = igt_check_block(test, mm, root); 237 + 238 + if (!drm_buddy_block_is_free(root)) { 239 + kunit_err(test, "root not free\n"); 240 + err = -EINVAL; 241 + } 242 + 243 + order = drm_buddy_block_order(root); 244 + 245 + if (!i) { 246 + if (order != mm->max_order) { 247 + kunit_err(test, "max order root missing\n"); 248 + err = -EINVAL; 249 + } 250 + } 251 + 252 + if (prev) { 253 + u64 prev_block_size; 254 + u64 prev_offset; 255 + u64 offset; 256 + 257 + prev_offset = drm_buddy_block_offset(prev); 258 + prev_block_size = drm_buddy_block_size(mm, prev); 259 + offset = drm_buddy_block_offset(root); 260 + 261 + if (offset != (prev_offset + prev_block_size)) { 262 + kunit_err(test, "root offset mismatch\n"); 263 + err = -EINVAL; 264 + } 265 + } 266 + 267 + block = list_first_entry_or_null(&mm->free_list[order], 268 + struct drm_buddy_block, link); 269 + if (block != root) { 270 + kunit_err(test, "root mismatch at order=%u\n", order); 271 + err = -EINVAL; 272 + } 273 + 274 + if (err) 275 + break; 276 + 277 + prev = root; 278 + total += drm_buddy_block_size(mm, root); 279 + } 280 + 281 + if (!err) { 282 + if (total != mm->size) { 283 + kunit_err(test, "expected mm size=%llx, found=%llx\n", 284 + mm->size, total); 285 + err = -EINVAL; 286 + } 287 + return err; 288 + } 289 + 290 + if (prev) { 291 + kunit_err(test, "prev root(%u), dump:\n", i - 1); 292 + igt_dump_block(test, mm, prev); 293 + } 294 + 295 + if (root) { 296 + kunit_err(test, "bad root(%u), dump:\n", i); 297 + igt_dump_block(test, mm, root); 298 + } 299 + 300 + return err; 301 + } 302 + 303 + static void igt_mm_config(u64 *size, u64 *chunk_size) 304 + { 305 + DRM_RND_STATE(prng, random_seed); 306 + u32 s, ms; 307 + 308 + /* Nothing fancy, just try to get an interesting bit pattern */ 309 + 310 + prandom_seed_state(&prng, random_seed); 311 + 312 + /* Let size be a random number of pages up to 8 GB (2M pages) */ 313 + s = 1 + drm_prandom_u32_max_state((BIT(33 - 12)) - 1, &prng); 314 + /* Let the chunk size be a random power of 2 less than size */ 315 + ms = BIT(drm_prandom_u32_max_state(ilog2(s), &prng)); 316 + /* Round size down to the chunk size */ 317 + s &= -ms; 318 + 319 + /* Convert from pages to bytes */ 320 + *chunk_size = (u64)ms << 12; 321 + *size = (u64)s << 12; 322 + } 323 + 324 + static void igt_buddy_alloc_pathological(struct kunit *test) 325 + { 326 + u64 mm_size, size, start = 0; 327 + struct drm_buddy_block *block; 328 + const int max_order = 3; 329 + unsigned long flags = 0; 330 + int order, top; 331 + struct drm_buddy mm; 332 + LIST_HEAD(blocks); 333 + LIST_HEAD(holes); 334 + LIST_HEAD(tmp); 335 + 336 + /* 337 + * Create a pot-sized mm, then allocate one of each possible 338 + * order within. This should leave the mm with exactly one 339 + * page left. Free the largest block, then whittle down again. 340 + * Eventually we will have a fully 50% fragmented mm. 341 + */ 342 + 343 + mm_size = PAGE_SIZE << max_order; 344 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, PAGE_SIZE), 345 + "buddy_init failed\n"); 346 + 347 + KUNIT_EXPECT_EQ(test, mm.max_order, max_order); 348 + 349 + for (top = max_order; top; top--) { 350 + /* Make room by freeing the largest allocated block */ 351 + block = list_first_entry_or_null(&blocks, typeof(*block), link); 352 + if (block) { 353 + list_del(&block->link); 354 + drm_buddy_free_block(&mm, block); 355 + } 356 + 357 + for (order = top; order--;) { 358 + size = get_size(order, PAGE_SIZE); 359 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, 360 + mm_size, size, size, 361 + &tmp, flags), 362 + "buddy_alloc hit -ENOMEM with order=%d, top=%d\n", 363 + order, top); 364 + 365 + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); 366 + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 367 + 368 + list_move_tail(&block->link, &blocks); 369 + } 370 + 371 + /* There should be one final page for this sub-allocation */ 372 + size = get_size(0, PAGE_SIZE); 373 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, 374 + size, size, &tmp, flags), 375 + "buddy_alloc hit -ENOMEM for hole\n"); 376 + 377 + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); 378 + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 379 + 380 + list_move_tail(&block->link, &holes); 381 + 382 + size = get_size(top, PAGE_SIZE); 383 + KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, 384 + size, size, &tmp, flags), 385 + "buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!", 386 + top, max_order); 387 + } 388 + 389 + drm_buddy_free_list(&mm, &holes); 390 + 391 + /* Nothing larger than blocks of chunk_size now available */ 392 + for (order = 1; order <= max_order; order++) { 393 + size = get_size(order, PAGE_SIZE); 394 + KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, 395 + size, size, &tmp, flags), 396 + "buddy_alloc unexpectedly succeeded at order %d, it should be full!", 397 + order); 398 + } 399 + 400 + list_splice_tail(&holes, &blocks); 401 + drm_buddy_free_list(&mm, &blocks); 402 + drm_buddy_fini(&mm); 403 + } 404 + 405 + static void igt_buddy_alloc_smoke(struct kunit *test) 406 + { 407 + u64 mm_size, chunk_size, start = 0; 408 + unsigned long flags = 0; 409 + struct drm_buddy mm; 410 + int *order; 411 + int i; 412 + 413 + DRM_RND_STATE(prng, random_seed); 414 + IGT_TIMEOUT(end_time); 415 + 416 + igt_mm_config(&mm_size, &chunk_size); 417 + 418 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, chunk_size), 419 + "buddy_init failed\n"); 420 + 421 + order = drm_random_order(mm.max_order + 1, &prng); 422 + KUNIT_ASSERT_TRUE(test, order); 423 + 424 + for (i = 0; i <= mm.max_order; ++i) { 425 + struct drm_buddy_block *block; 426 + int max_order = order[i]; 427 + bool timeout = false; 428 + LIST_HEAD(blocks); 429 + u64 total, size; 430 + LIST_HEAD(tmp); 431 + int order, err; 432 + 433 + KUNIT_ASSERT_FALSE_MSG(test, igt_check_mm(test, &mm), 434 + "pre-mm check failed, abort\n"); 435 + 436 + order = max_order; 437 + total = 0; 438 + 439 + do { 440 + retry: 441 + size = get_size(order, chunk_size); 442 + err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags); 443 + if (err) { 444 + if (err == -ENOMEM) { 445 + KUNIT_FAIL(test, "buddy_alloc hit -ENOMEM with order=%d\n", 446 + order); 447 + } else { 448 + if (order--) { 449 + err = 0; 450 + goto retry; 451 + } 452 + 453 + KUNIT_FAIL(test, "buddy_alloc with order=%d failed\n", 454 + order); 455 + } 456 + 457 + break; 458 + } 459 + 460 + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); 461 + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 462 + 463 + list_move_tail(&block->link, &blocks); 464 + KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_order(block), order, 465 + "buddy_alloc order mismatch\n"); 466 + 467 + total += drm_buddy_block_size(&mm, block); 468 + 469 + if (__igt_timeout(end_time, NULL)) { 470 + timeout = true; 471 + break; 472 + } 473 + } while (total < mm.size); 474 + 475 + if (!err) 476 + err = igt_check_blocks(test, &mm, &blocks, total, false); 477 + 478 + drm_buddy_free_list(&mm, &blocks); 479 + 480 + if (!err) { 481 + KUNIT_EXPECT_FALSE_MSG(test, igt_check_mm(test, &mm), 482 + "post-mm check failed\n"); 483 + } 484 + 485 + if (err || timeout) 486 + break; 487 + 488 + cond_resched(); 489 + } 490 + 491 + kfree(order); 492 + drm_buddy_fini(&mm); 493 + } 494 + 495 + static void igt_buddy_alloc_pessimistic(struct kunit *test) 496 + { 497 + u64 mm_size, size, start = 0; 498 + struct drm_buddy_block *block, *bn; 499 + const unsigned int max_order = 16; 500 + unsigned long flags = 0; 501 + struct drm_buddy mm; 502 + unsigned int order; 503 + LIST_HEAD(blocks); 504 + LIST_HEAD(tmp); 505 + 506 + /* 507 + * Create a pot-sized mm, then allocate one of each possible 508 + * order within. This should leave the mm with exactly one 509 + * page left. 510 + */ 511 + 512 + mm_size = PAGE_SIZE << max_order; 513 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, PAGE_SIZE), 514 + "buddy_init failed\n"); 515 + 516 + KUNIT_EXPECT_EQ(test, mm.max_order, max_order); 517 + 518 + for (order = 0; order < max_order; order++) { 519 + size = get_size(order, PAGE_SIZE); 520 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, 521 + size, size, &tmp, flags), 522 + "buddy_alloc hit -ENOMEM with order=%d\n", 523 + order); 524 + 525 + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); 526 + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 527 + 528 + list_move_tail(&block->link, &blocks); 529 + } 530 + 531 + /* And now the last remaining block available */ 532 + size = get_size(0, PAGE_SIZE); 533 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, 534 + size, size, &tmp, flags), 535 + "buddy_alloc hit -ENOMEM on final alloc\n"); 536 + 537 + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); 538 + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 539 + 540 + list_move_tail(&block->link, &blocks); 541 + 542 + /* Should be completely full! */ 543 + for (order = max_order; order--;) { 544 + size = get_size(order, PAGE_SIZE); 545 + KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, 546 + size, size, &tmp, flags), 547 + "buddy_alloc unexpectedly succeeded, it should be full!"); 548 + } 549 + 550 + block = list_last_entry(&blocks, typeof(*block), link); 551 + list_del(&block->link); 552 + drm_buddy_free_block(&mm, block); 553 + 554 + /* As we free in increasing size, we make available larger blocks */ 555 + order = 1; 556 + list_for_each_entry_safe(block, bn, &blocks, link) { 557 + list_del(&block->link); 558 + drm_buddy_free_block(&mm, block); 559 + 560 + size = get_size(order, PAGE_SIZE); 561 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, 562 + size, size, &tmp, flags), 563 + "buddy_alloc hit -ENOMEM with order=%d\n", 564 + order); 565 + 566 + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); 567 + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 568 + 569 + list_del(&block->link); 570 + drm_buddy_free_block(&mm, block); 571 + order++; 572 + } 573 + 574 + /* To confirm, now the whole mm should be available */ 575 + size = get_size(max_order, PAGE_SIZE); 576 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, 577 + size, size, &tmp, flags), 578 + "buddy_alloc (realloc) hit -ENOMEM with order=%d\n", 579 + max_order); 580 + 581 + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); 582 + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 583 + 584 + list_del(&block->link); 585 + drm_buddy_free_block(&mm, block); 586 + drm_buddy_free_list(&mm, &blocks); 587 + drm_buddy_fini(&mm); 588 + } 589 + 590 + static void igt_buddy_alloc_optimistic(struct kunit *test) 591 + { 592 + u64 mm_size, size, start = 0; 593 + struct drm_buddy_block *block; 594 + unsigned long flags = 0; 595 + const int max_order = 16; 596 + struct drm_buddy mm; 597 + LIST_HEAD(blocks); 598 + LIST_HEAD(tmp); 599 + int order; 600 + 601 + /* 602 + * Create a mm with one block of each order available, and 603 + * try to allocate them all. 604 + */ 605 + 606 + mm_size = PAGE_SIZE * ((1 << (max_order + 1)) - 1); 607 + 608 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, PAGE_SIZE), 609 + "buddy_init failed\n"); 610 + 611 + KUNIT_EXPECT_EQ(test, mm.max_order, max_order); 612 + 613 + for (order = 0; order <= max_order; order++) { 614 + size = get_size(order, PAGE_SIZE); 615 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, 616 + size, size, &tmp, flags), 617 + "buddy_alloc hit -ENOMEM with order=%d\n", 618 + order); 619 + 620 + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); 621 + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); 622 + 623 + list_move_tail(&block->link, &blocks); 624 + } 625 + 626 + /* Should be completely full! */ 627 + size = get_size(0, PAGE_SIZE); 628 + KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, 629 + size, size, &tmp, flags), 630 + "buddy_alloc unexpectedly succeeded, it should be full!"); 631 + 632 + drm_buddy_free_list(&mm, &blocks); 633 + drm_buddy_fini(&mm); 634 + } 635 + 636 + static void igt_buddy_alloc_range(struct kunit *test) 637 + { 638 + unsigned long flags = DRM_BUDDY_RANGE_ALLOCATION; 639 + u64 offset, size, rem, chunk_size, end; 640 + unsigned long page_num; 641 + struct drm_buddy mm; 642 + LIST_HEAD(blocks); 643 + 644 + igt_mm_config(&size, &chunk_size); 645 + 646 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, size, chunk_size), 647 + "buddy_init failed"); 648 + 649 + KUNIT_ASSERT_FALSE_MSG(test, igt_check_mm(test, &mm), 650 + "pre-mm check failed, abort!"); 651 + 652 + rem = mm.size; 653 + offset = 0; 654 + 655 + for_each_prime_number_from(page_num, 1, ULONG_MAX - 1) { 656 + struct drm_buddy_block *block; 657 + LIST_HEAD(tmp); 658 + 659 + size = min(page_num * mm.chunk_size, rem); 660 + end = offset + size; 661 + 662 + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, offset, end, 663 + size, mm.chunk_size, 664 + &tmp, flags), 665 + "alloc_range with offset=%llx, size=%llx failed\n", offset, size); 666 + 667 + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); 668 + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_range has no blocks\n"); 669 + 670 + KUNIT_ASSERT_EQ_MSG(test, drm_buddy_block_offset(block), offset, 671 + "alloc_range start offset mismatch, found=%llx, expected=%llx\n", 672 + drm_buddy_block_offset(block), offset); 673 + 674 + KUNIT_ASSERT_FALSE(test, igt_check_blocks(test, &mm, &tmp, size, true)); 675 + 676 + list_splice_tail(&tmp, &blocks); 677 + 678 + offset += size; 679 + 680 + rem -= size; 681 + if (!rem) 682 + break; 683 + 684 + cond_resched(); 685 + } 686 + 687 + drm_buddy_free_list(&mm, &blocks); 688 + 689 + KUNIT_EXPECT_FALSE_MSG(test, igt_check_mm(test, &mm), "post-mm check failed\n"); 690 + 691 + drm_buddy_fini(&mm); 692 + } 693 + 694 + static void igt_buddy_alloc_limit(struct kunit *test) 695 + { 696 + u64 size = U64_MAX, start = 0; 697 + struct drm_buddy_block *block; 698 + unsigned long flags = 0; 699 + LIST_HEAD(allocated); 700 + struct drm_buddy mm; 701 + 702 + KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, size, PAGE_SIZE)); 703 + 704 + KUNIT_EXPECT_EQ_MSG(test, mm.max_order, DRM_BUDDY_MAX_ORDER, 705 + "mm.max_order(%d) != %d\n", mm.max_order, 706 + DRM_BUDDY_MAX_ORDER); 707 + 708 + size = mm.chunk_size << mm.max_order; 709 + KUNIT_EXPECT_FALSE(test, drm_buddy_alloc_blocks(&mm, start, size, size, 710 + PAGE_SIZE, &allocated, flags)); 711 + 712 + block = list_first_entry_or_null(&allocated, struct drm_buddy_block, link); 713 + KUNIT_EXPECT_TRUE(test, block); 714 + 715 + KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_order(block), mm.max_order, 716 + "block order(%d) != %d\n", 717 + drm_buddy_block_order(block), mm.max_order); 718 + 719 + KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_size(&mm, block), 720 + BIT_ULL(mm.max_order) * PAGE_SIZE, 721 + "block size(%llu) != %llu\n", 722 + drm_buddy_block_size(&mm, block), 723 + BIT_ULL(mm.max_order) * PAGE_SIZE); 724 + 725 + drm_buddy_free_list(&mm, &allocated); 726 + drm_buddy_fini(&mm); 727 + } 728 + 729 + static int drm_buddy_init_test(struct kunit *test) 730 + { 731 + while (!random_seed) 732 + random_seed = get_random_int(); 733 + 734 + return 0; 735 + } 736 + 737 + static struct kunit_case drm_buddy_tests[] = { 738 + KUNIT_CASE(igt_buddy_alloc_limit), 739 + KUNIT_CASE(igt_buddy_alloc_range), 740 + KUNIT_CASE(igt_buddy_alloc_optimistic), 741 + KUNIT_CASE(igt_buddy_alloc_pessimistic), 742 + KUNIT_CASE(igt_buddy_alloc_smoke), 743 + KUNIT_CASE(igt_buddy_alloc_pathological), 744 + {} 745 + }; 746 + 747 + static struct kunit_suite drm_buddy_test_suite = { 748 + .name = "drm_buddy", 749 + .init = drm_buddy_init_test, 750 + .test_cases = drm_buddy_tests, 751 + }; 752 + 753 + kunit_test_suite(drm_buddy_test_suite); 754 + 755 + MODULE_AUTHOR("Intel Corporation"); 756 + MODULE_LICENSE("GPL");