1 // SPDX-License-Identifier: MIT
3 * Copyright © 2019 Intel Corporation
6 #include <linux/prime_numbers.h>
8 #include "../i915_selftest.h"
9 #include "i915_random.h"
11 #define SZ_8G (1ULL << 33)
13 static void __igt_dump_block(struct i915_buddy_mm
*mm
,
14 struct i915_buddy_block
*block
,
17 pr_err("block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%s buddy=%s\n",
19 i915_buddy_block_state(block
),
20 i915_buddy_block_order(block
),
21 i915_buddy_block_offset(block
),
22 i915_buddy_block_size(mm
, block
),
23 yesno(!block
->parent
),
27 static void igt_dump_block(struct i915_buddy_mm
*mm
,
28 struct i915_buddy_block
*block
)
30 struct i915_buddy_block
*buddy
;
32 __igt_dump_block(mm
, block
, false);
34 buddy
= get_buddy(block
);
36 __igt_dump_block(mm
, buddy
, true);
39 static int igt_check_block(struct i915_buddy_mm
*mm
,
40 struct i915_buddy_block
*block
)
42 struct i915_buddy_block
*buddy
;
43 unsigned int block_state
;
48 block_state
= i915_buddy_block_state(block
);
50 if (block_state
!= I915_BUDDY_ALLOCATED
&&
51 block_state
!= I915_BUDDY_FREE
&&
52 block_state
!= I915_BUDDY_SPLIT
) {
53 pr_err("block state mismatch\n");
57 block_size
= i915_buddy_block_size(mm
, block
);
58 offset
= i915_buddy_block_offset(block
);
60 if (block_size
< mm
->chunk_size
) {
61 pr_err("block size smaller than min size\n");
65 if (!is_power_of_2(block_size
)) {
66 pr_err("block size not power of two\n");
70 if (!IS_ALIGNED(block_size
, mm
->chunk_size
)) {
71 pr_err("block size not aligned to min size\n");
75 if (!IS_ALIGNED(offset
, mm
->chunk_size
)) {
76 pr_err("block offset not aligned to min size\n");
80 if (!IS_ALIGNED(offset
, block_size
)) {
81 pr_err("block offset not aligned to block size\n");
85 buddy
= get_buddy(block
);
87 if (!buddy
&& block
->parent
) {
88 pr_err("buddy has gone fishing\n");
93 if (i915_buddy_block_offset(buddy
) != (offset
^ block_size
)) {
94 pr_err("buddy has wrong offset\n");
98 if (i915_buddy_block_size(mm
, buddy
) != block_size
) {
99 pr_err("buddy size mismatch\n");
103 if (i915_buddy_block_state(buddy
) == block_state
&&
104 block_state
== I915_BUDDY_FREE
) {
105 pr_err("block and its buddy are free\n");
113 static int igt_check_blocks(struct i915_buddy_mm
*mm
,
114 struct list_head
*blocks
,
118 struct i915_buddy_block
*block
;
119 struct i915_buddy_block
*prev
;
127 list_for_each_entry(block
, blocks
, link
) {
128 err
= igt_check_block(mm
, block
);
130 if (!i915_buddy_block_is_allocated(block
)) {
131 pr_err("block not allocated\n"),
135 if (is_contiguous
&& prev
) {
140 prev_offset
= i915_buddy_block_offset(prev
);
141 prev_block_size
= i915_buddy_block_size(mm
, prev
);
142 offset
= i915_buddy_block_offset(block
);
144 if (offset
!= (prev_offset
+ prev_block_size
)) {
145 pr_err("block offset mismatch\n");
153 total
+= i915_buddy_block_size(mm
, block
);
158 if (total
!= expected_size
) {
159 pr_err("size mismatch, expected=%llx, found=%llx\n",
160 expected_size
, total
);
167 pr_err("prev block, dump:\n");
168 igt_dump_block(mm
, prev
);
172 pr_err("bad block, dump:\n");
173 igt_dump_block(mm
, block
);
179 static int igt_check_mm(struct i915_buddy_mm
*mm
)
181 struct i915_buddy_block
*root
;
182 struct i915_buddy_block
*prev
;
188 pr_err("n_roots is zero\n");
192 if (mm
->n_roots
!= hweight64(mm
->size
)) {
193 pr_err("n_roots mismatch, n_roots=%u, expected=%lu\n",
194 mm
->n_roots
, hweight64(mm
->size
));
202 for (i
= 0; i
< mm
->n_roots
; ++i
) {
203 struct i915_buddy_block
*block
;
208 pr_err("root(%u) is NULL\n", i
);
213 err
= igt_check_block(mm
, root
);
215 if (!i915_buddy_block_is_free(root
)) {
216 pr_err("root not free\n");
220 order
= i915_buddy_block_order(root
);
223 if (order
!= mm
->max_order
) {
224 pr_err("max order root missing\n");
234 prev_offset
= i915_buddy_block_offset(prev
);
235 prev_block_size
= i915_buddy_block_size(mm
, prev
);
236 offset
= i915_buddy_block_offset(root
);
238 if (offset
!= (prev_offset
+ prev_block_size
)) {
239 pr_err("root offset mismatch\n");
244 block
= list_first_entry_or_null(&mm
->free_list
[order
],
245 struct i915_buddy_block
,
248 pr_err("root mismatch at order=%u\n", order
);
256 total
+= i915_buddy_block_size(mm
, root
);
260 if (total
!= mm
->size
) {
261 pr_err("expected mm size=%llx, found=%llx\n", mm
->size
,
269 pr_err("prev root(%u), dump:\n", i
- 1);
270 igt_dump_block(mm
, prev
);
274 pr_err("bad root(%u), dump:\n", i
);
275 igt_dump_block(mm
, root
);
281 static void igt_mm_config(u64
*size
, u64
*chunk_size
)
283 I915_RND_STATE(prng
);
286 /* Nothing fancy, just try to get an interesting bit pattern */
288 prandom_seed_state(&prng
, i915_selftest
.random_seed
);
290 s
= i915_prandom_u64_state(&prng
) & (SZ_8G
- 1);
291 ms
= BIT_ULL(12 + (prandom_u32_state(&prng
) % ilog2(s
>> 12)));
292 s
= max(s
& -ms
, ms
);
298 static int igt_buddy_alloc_smoke(void *arg
)
300 struct i915_buddy_mm mm
;
306 igt_mm_config(&mm_size
, &chunk_size
);
308 pr_info("buddy_init with size=%llx, chunk_size=%llx\n", mm_size
, chunk_size
);
310 err
= i915_buddy_init(&mm
, mm_size
, chunk_size
);
312 pr_err("buddy_init failed(%d)\n", err
);
316 for (max_order
= mm
.max_order
; max_order
>= 0; max_order
--) {
317 struct i915_buddy_block
*block
;
322 err
= igt_check_mm(&mm
);
324 pr_err("pre-mm check failed, abort\n");
328 pr_info("filling from max_order=%u\n", max_order
);
335 block
= i915_buddy_alloc(&mm
, order
);
337 err
= PTR_ERR(block
);
338 if (err
== -ENOMEM
) {
339 pr_info("buddy_alloc hit -ENOMEM with order=%d\n",
347 pr_err("buddy_alloc with order=%d failed(%d)\n",
354 list_add_tail(&block
->link
, &blocks
);
356 if (i915_buddy_block_order(block
) != order
) {
357 pr_err("buddy_alloc order mismatch\n");
362 total
+= i915_buddy_block_size(&mm
, block
);
363 } while (total
< mm
.size
);
366 err
= igt_check_blocks(&mm
, &blocks
, total
, false);
368 i915_buddy_free_list(&mm
, &blocks
);
371 err
= igt_check_mm(&mm
);
373 pr_err("post-mm check failed\n");
385 i915_buddy_fini(&mm
);
390 static int igt_buddy_alloc_pessimistic(void *arg
)
392 const unsigned int max_order
= 16;
393 struct i915_buddy_block
*block
, *bn
;
394 struct i915_buddy_mm mm
;
400 * Create a pot-sized mm, then allocate one of each possible
401 * order within. This should leave the mm with exactly one
405 err
= i915_buddy_init(&mm
, PAGE_SIZE
<< max_order
, PAGE_SIZE
);
407 pr_err("buddy_init failed(%d)\n", err
);
410 GEM_BUG_ON(mm
.max_order
!= max_order
);
412 for (order
= 0; order
< max_order
; order
++) {
413 block
= i915_buddy_alloc(&mm
, order
);
415 pr_info("buddy_alloc hit -ENOMEM with order=%d\n",
417 err
= PTR_ERR(block
);
421 list_add_tail(&block
->link
, &blocks
);
424 /* And now the last remaining block available */
425 block
= i915_buddy_alloc(&mm
, 0);
427 pr_info("buddy_alloc hit -ENOMEM on final alloc\n");
428 err
= PTR_ERR(block
);
431 list_add_tail(&block
->link
, &blocks
);
433 /* Should be completely full! */
434 for (order
= max_order
; order
--; ) {
435 block
= i915_buddy_alloc(&mm
, order
);
436 if (!IS_ERR(block
)) {
437 pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!",
439 list_add_tail(&block
->link
, &blocks
);
445 block
= list_last_entry(&blocks
, typeof(*block
), link
);
446 list_del(&block
->link
);
447 i915_buddy_free(&mm
, block
);
449 /* As we free in increasing size, we make available larger blocks */
451 list_for_each_entry_safe(block
, bn
, &blocks
, link
) {
452 list_del(&block
->link
);
453 i915_buddy_free(&mm
, block
);
455 block
= i915_buddy_alloc(&mm
, order
);
457 pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n",
459 err
= PTR_ERR(block
);
462 i915_buddy_free(&mm
, block
);
466 /* To confirm, now the whole mm should be available */
467 block
= i915_buddy_alloc(&mm
, max_order
);
469 pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n",
471 err
= PTR_ERR(block
);
474 i915_buddy_free(&mm
, block
);
477 i915_buddy_free_list(&mm
, &blocks
);
478 i915_buddy_fini(&mm
);
482 static int igt_buddy_alloc_optimistic(void *arg
)
484 const int max_order
= 16;
485 struct i915_buddy_block
*block
;
486 struct i915_buddy_mm mm
;
492 * Create a mm with one block of each order available, and
493 * try to allocate them all.
496 err
= i915_buddy_init(&mm
,
497 PAGE_SIZE
* ((1 << (max_order
+ 1)) - 1),
500 pr_err("buddy_init failed(%d)\n", err
);
503 GEM_BUG_ON(mm
.max_order
!= max_order
);
505 for (order
= 0; order
<= max_order
; order
++) {
506 block
= i915_buddy_alloc(&mm
, order
);
508 pr_info("buddy_alloc hit -ENOMEM with order=%d\n",
510 err
= PTR_ERR(block
);
514 list_add_tail(&block
->link
, &blocks
);
517 /* Should be completely full! */
518 block
= i915_buddy_alloc(&mm
, 0);
519 if (!IS_ERR(block
)) {
520 pr_info("buddy_alloc unexpectedly succeeded, it should be full!");
521 list_add_tail(&block
->link
, &blocks
);
527 i915_buddy_free_list(&mm
, &blocks
);
528 i915_buddy_fini(&mm
);
532 static int igt_buddy_alloc_pathological(void *arg
)
534 const int max_order
= 16;
535 struct i915_buddy_block
*block
;
536 struct i915_buddy_mm mm
;
543 * Create a pot-sized mm, then allocate one of each possible
544 * order within. This should leave the mm with exactly one
545 * page left. Free the largest block, then whittle down again.
546 * Eventually we will have a fully 50% fragmented mm.
549 err
= i915_buddy_init(&mm
, PAGE_SIZE
<< max_order
, PAGE_SIZE
);
551 pr_err("buddy_init failed(%d)\n", err
);
554 GEM_BUG_ON(mm
.max_order
!= max_order
);
556 for (top
= max_order
; top
; top
--) {
557 /* Make room by freeing the largest allocated block */
558 block
= list_first_entry_or_null(&blocks
, typeof(*block
), link
);
560 list_del(&block
->link
);
561 i915_buddy_free(&mm
, block
);
564 for (order
= top
; order
--; ) {
565 block
= i915_buddy_alloc(&mm
, order
);
567 pr_info("buddy_alloc hit -ENOMEM with order=%d, top=%d\n",
569 err
= PTR_ERR(block
);
572 list_add_tail(&block
->link
, &blocks
);
575 /* There should be one final page for this sub-allocation */
576 block
= i915_buddy_alloc(&mm
, 0);
578 pr_info("buddy_alloc hit -ENOMEM for hole\n");
579 err
= PTR_ERR(block
);
582 list_add_tail(&block
->link
, &holes
);
584 block
= i915_buddy_alloc(&mm
, top
);
585 if (!IS_ERR(block
)) {
586 pr_info("buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!",
588 list_add_tail(&block
->link
, &blocks
);
594 i915_buddy_free_list(&mm
, &holes
);
596 /* Nothing larger than blocks of chunk_size now available */
597 for (order
= 1; order
<= max_order
; order
++) {
598 block
= i915_buddy_alloc(&mm
, order
);
599 if (!IS_ERR(block
)) {
600 pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!",
602 list_add_tail(&block
->link
, &blocks
);
609 list_splice_tail(&holes
, &blocks
);
610 i915_buddy_free_list(&mm
, &blocks
);
611 i915_buddy_fini(&mm
);
615 static int igt_buddy_alloc_range(void *arg
)
617 struct i915_buddy_mm mm
;
618 unsigned long page_num
;
626 igt_mm_config(&size
, &chunk_size
);
628 pr_info("buddy_init with size=%llx, chunk_size=%llx\n", size
, chunk_size
);
630 err
= i915_buddy_init(&mm
, size
, chunk_size
);
632 pr_err("buddy_init failed(%d)\n", err
);
636 err
= igt_check_mm(&mm
);
638 pr_err("pre-mm check failed, abort, abort, abort!\n");
645 for_each_prime_number_from(page_num
, 1, ULONG_MAX
- 1) {
646 struct i915_buddy_block
*block
;
649 size
= min(page_num
* mm
.chunk_size
, rem
);
651 err
= i915_buddy_alloc_range(&mm
, &tmp
, offset
, size
);
653 if (err
== -ENOMEM
) {
654 pr_info("alloc_range hit -ENOMEM with size=%llx\n",
657 pr_err("alloc_range with offset=%llx, size=%llx failed(%d)\n",
664 block
= list_first_entry_or_null(&tmp
,
665 struct i915_buddy_block
,
668 pr_err("alloc_range has no blocks\n");
673 if (i915_buddy_block_offset(block
) != offset
) {
674 pr_err("alloc_range start offset mismatch, found=%llx, expected=%llx\n",
675 i915_buddy_block_offset(block
), offset
);
680 err
= igt_check_blocks(&mm
, &tmp
, size
, true);
682 list_splice_tail(&tmp
, &blocks
);
699 i915_buddy_free_list(&mm
, &blocks
);
702 err
= igt_check_mm(&mm
);
704 pr_err("post-mm check failed\n");
708 i915_buddy_fini(&mm
);
713 int i915_buddy_mock_selftests(void)
715 static const struct i915_subtest tests
[] = {
716 SUBTEST(igt_buddy_alloc_pessimistic
),
717 SUBTEST(igt_buddy_alloc_optimistic
),
718 SUBTEST(igt_buddy_alloc_pathological
),
719 SUBTEST(igt_buddy_alloc_smoke
),
720 SUBTEST(igt_buddy_alloc_range
),
723 return i915_subtests(tests
, NULL
);