1 1.1 riastrad /* $NetBSD: i915_buddy.c,v 1.2 2021/12/18 23:45:31 riastradh Exp $ */ 2 1.1 riastrad 3 1.1 riastrad // SPDX-License-Identifier: MIT 4 1.1 riastrad /* 5 1.1 riastrad * Copyright 2019 Intel Corporation 6 1.1 riastrad */ 7 1.1 riastrad 8 1.1 riastrad #include <sys/cdefs.h> 9 1.1 riastrad __KERNEL_RCSID(0, "$NetBSD: i915_buddy.c,v 1.2 2021/12/18 23:45:31 riastradh Exp $"); 10 1.1 riastrad 11 1.1 riastrad #include <linux/prime_numbers.h> 12 1.1 riastrad 13 1.1 riastrad #include "../i915_selftest.h" 14 1.1 riastrad #include "i915_random.h" 15 1.1 riastrad 16 1.1 riastrad #define SZ_8G (1ULL << 33) 17 1.1 riastrad 18 1.1 riastrad static void __igt_dump_block(struct i915_buddy_mm *mm, 19 1.1 riastrad struct i915_buddy_block *block, 20 1.1 riastrad bool buddy) 21 1.1 riastrad { 22 1.1 riastrad pr_err("block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%s buddy=%s\n", 23 1.1 riastrad block->header, 24 1.1 riastrad i915_buddy_block_state(block), 25 1.1 riastrad i915_buddy_block_order(block), 26 1.1 riastrad i915_buddy_block_offset(block), 27 1.1 riastrad i915_buddy_block_size(mm, block), 28 1.1 riastrad yesno(!block->parent), 29 1.1 riastrad yesno(buddy)); 30 1.1 riastrad } 31 1.1 riastrad 32 1.1 riastrad static void igt_dump_block(struct i915_buddy_mm *mm, 33 1.1 riastrad struct i915_buddy_block *block) 34 1.1 riastrad { 35 1.1 riastrad struct i915_buddy_block *buddy; 36 1.1 riastrad 37 1.1 riastrad __igt_dump_block(mm, block, false); 38 1.1 riastrad 39 1.1 riastrad buddy = get_buddy(block); 40 1.1 riastrad if (buddy) 41 1.1 riastrad __igt_dump_block(mm, buddy, true); 42 1.1 riastrad } 43 1.1 riastrad 44 1.1 riastrad static int igt_check_block(struct i915_buddy_mm *mm, 45 1.1 riastrad struct i915_buddy_block *block) 46 1.1 riastrad { 47 1.1 riastrad struct i915_buddy_block *buddy; 48 1.1 riastrad unsigned int block_state; 49 1.1 riastrad u64 block_size; 50 1.1 riastrad u64 offset; 51 1.1 riastrad int err = 0; 52 1.1 riastrad 53 1.1 riastrad block_state = i915_buddy_block_state(block); 54 1.1 riastrad 55 1.1 riastrad if (block_state != I915_BUDDY_ALLOCATED && 56 1.1 riastrad block_state != I915_BUDDY_FREE && 57 1.1 riastrad block_state != I915_BUDDY_SPLIT) { 58 1.1 riastrad pr_err("block state mismatch\n"); 59 1.1 riastrad err = -EINVAL; 60 1.1 riastrad } 61 1.1 riastrad 62 1.1 riastrad block_size = i915_buddy_block_size(mm, block); 63 1.1 riastrad offset = i915_buddy_block_offset(block); 64 1.1 riastrad 65 1.1 riastrad if (block_size < mm->chunk_size) { 66 1.1 riastrad pr_err("block size smaller than min size\n"); 67 1.1 riastrad err = -EINVAL; 68 1.1 riastrad } 69 1.1 riastrad 70 1.1 riastrad if (!is_power_of_2(block_size)) { 71 1.1 riastrad pr_err("block size not power of two\n"); 72 1.1 riastrad err = -EINVAL; 73 1.1 riastrad } 74 1.1 riastrad 75 1.1 riastrad if (!IS_ALIGNED(block_size, mm->chunk_size)) { 76 1.1 riastrad pr_err("block size not aligned to min size\n"); 77 1.1 riastrad err = -EINVAL; 78 1.1 riastrad } 79 1.1 riastrad 80 1.1 riastrad if (!IS_ALIGNED(offset, mm->chunk_size)) { 81 1.1 riastrad pr_err("block offset not aligned to min size\n"); 82 1.1 riastrad err = -EINVAL; 83 1.1 riastrad } 84 1.1 riastrad 85 1.1 riastrad if (!IS_ALIGNED(offset, block_size)) { 86 1.1 riastrad pr_err("block offset not aligned to block size\n"); 87 1.1 riastrad err = -EINVAL; 88 1.1 riastrad } 89 1.1 riastrad 90 1.1 riastrad buddy = get_buddy(block); 91 1.1 riastrad 92 1.1 riastrad if (!buddy && block->parent) { 93 1.1 riastrad pr_err("buddy has gone fishing\n"); 94 1.1 riastrad err = -EINVAL; 95 1.1 riastrad } 96 1.1 riastrad 97 1.1 riastrad if (buddy) { 98 1.1 riastrad if (i915_buddy_block_offset(buddy) != (offset ^ block_size)) { 99 1.1 riastrad pr_err("buddy has wrong offset\n"); 100 1.1 riastrad err = -EINVAL; 101 1.1 riastrad } 102 1.1 riastrad 103 1.1 riastrad if (i915_buddy_block_size(mm, buddy) != block_size) { 104 1.1 riastrad pr_err("buddy size mismatch\n"); 105 1.1 riastrad err = -EINVAL; 106 1.1 riastrad } 107 1.1 riastrad 108 1.1 riastrad if (i915_buddy_block_state(buddy) == block_state && 109 1.1 riastrad block_state == I915_BUDDY_FREE) { 110 1.1 riastrad pr_err("block and its buddy are free\n"); 111 1.1 riastrad err = -EINVAL; 112 1.1 riastrad } 113 1.1 riastrad } 114 1.1 riastrad 115 1.1 riastrad return err; 116 1.1 riastrad } 117 1.1 riastrad 118 1.1 riastrad static int igt_check_blocks(struct i915_buddy_mm *mm, 119 1.1 riastrad struct list_head *blocks, 120 1.1 riastrad u64 expected_size, 121 1.1 riastrad bool is_contiguous) 122 1.1 riastrad { 123 1.1 riastrad struct i915_buddy_block *block; 124 1.1 riastrad struct i915_buddy_block *prev; 125 1.1 riastrad u64 total; 126 1.1 riastrad int err = 0; 127 1.1 riastrad 128 1.1 riastrad block = NULL; 129 1.1 riastrad prev = NULL; 130 1.1 riastrad total = 0; 131 1.1 riastrad 132 1.1 riastrad list_for_each_entry(block, blocks, link) { 133 1.1 riastrad err = igt_check_block(mm, block); 134 1.1 riastrad 135 1.1 riastrad if (!i915_buddy_block_is_allocated(block)) { 136 1.1 riastrad pr_err("block not allocated\n"), 137 1.1 riastrad err = -EINVAL; 138 1.1 riastrad } 139 1.1 riastrad 140 1.1 riastrad if (is_contiguous && prev) { 141 1.1 riastrad u64 prev_block_size; 142 1.1 riastrad u64 prev_offset; 143 1.1 riastrad u64 offset; 144 1.1 riastrad 145 1.1 riastrad prev_offset = i915_buddy_block_offset(prev); 146 1.1 riastrad prev_block_size = i915_buddy_block_size(mm, prev); 147 1.1 riastrad offset = i915_buddy_block_offset(block); 148 1.1 riastrad 149 1.1 riastrad if (offset != (prev_offset + prev_block_size)) { 150 1.1 riastrad pr_err("block offset mismatch\n"); 151 1.1 riastrad err = -EINVAL; 152 1.1 riastrad } 153 1.1 riastrad } 154 1.1 riastrad 155 1.1 riastrad if (err) 156 1.1 riastrad break; 157 1.1 riastrad 158 1.1 riastrad total += i915_buddy_block_size(mm, block); 159 1.1 riastrad prev = block; 160 1.1 riastrad } 161 1.1 riastrad 162 1.1 riastrad if (!err) { 163 1.1 riastrad if (total != expected_size) { 164 1.1 riastrad pr_err("size mismatch, expected=%llx, found=%llx\n", 165 1.1 riastrad expected_size, total); 166 1.1 riastrad err = -EINVAL; 167 1.1 riastrad } 168 1.1 riastrad return err; 169 1.1 riastrad } 170 1.1 riastrad 171 1.1 riastrad if (prev) { 172 1.1 riastrad pr_err("prev block, dump:\n"); 173 1.1 riastrad igt_dump_block(mm, prev); 174 1.1 riastrad } 175 1.1 riastrad 176 1.1 riastrad if (block) { 177 1.1 riastrad pr_err("bad block, dump:\n"); 178 1.1 riastrad igt_dump_block(mm, block); 179 1.1 riastrad } 180 1.1 riastrad 181 1.1 riastrad return err; 182 1.1 riastrad } 183 1.1 riastrad 184 1.1 riastrad static int igt_check_mm(struct i915_buddy_mm *mm) 185 1.1 riastrad { 186 1.1 riastrad struct i915_buddy_block *root; 187 1.1 riastrad struct i915_buddy_block *prev; 188 1.1 riastrad unsigned int i; 189 1.1 riastrad u64 total; 190 1.1 riastrad int err = 0; 191 1.1 riastrad 192 1.1 riastrad if (!mm->n_roots) { 193 1.1 riastrad pr_err("n_roots is zero\n"); 194 1.1 riastrad return -EINVAL; 195 1.1 riastrad } 196 1.1 riastrad 197 1.1 riastrad if (mm->n_roots != hweight64(mm->size)) { 198 1.1 riastrad pr_err("n_roots mismatch, n_roots=%u, expected=%lu\n", 199 1.1 riastrad mm->n_roots, hweight64(mm->size)); 200 1.1 riastrad return -EINVAL; 201 1.1 riastrad } 202 1.1 riastrad 203 1.1 riastrad root = NULL; 204 1.1 riastrad prev = NULL; 205 1.1 riastrad total = 0; 206 1.1 riastrad 207 1.1 riastrad for (i = 0; i < mm->n_roots; ++i) { 208 1.1 riastrad struct i915_buddy_block *block; 209 1.1 riastrad unsigned int order; 210 1.1 riastrad 211 1.1 riastrad root = mm->roots[i]; 212 1.1 riastrad if (!root) { 213 1.1 riastrad pr_err("root(%u) is NULL\n", i); 214 1.1 riastrad err = -EINVAL; 215 1.1 riastrad break; 216 1.1 riastrad } 217 1.1 riastrad 218 1.1 riastrad err = igt_check_block(mm, root); 219 1.1 riastrad 220 1.1 riastrad if (!i915_buddy_block_is_free(root)) { 221 1.1 riastrad pr_err("root not free\n"); 222 1.1 riastrad err = -EINVAL; 223 1.1 riastrad } 224 1.1 riastrad 225 1.1 riastrad order = i915_buddy_block_order(root); 226 1.1 riastrad 227 1.1 riastrad if (!i) { 228 1.1 riastrad if (order != mm->max_order) { 229 1.1 riastrad pr_err("max order root missing\n"); 230 1.1 riastrad err = -EINVAL; 231 1.1 riastrad } 232 1.1 riastrad } 233 1.1 riastrad 234 1.1 riastrad if (prev) { 235 1.1 riastrad u64 prev_block_size; 236 1.1 riastrad u64 prev_offset; 237 1.1 riastrad u64 offset; 238 1.1 riastrad 239 1.1 riastrad prev_offset = i915_buddy_block_offset(prev); 240 1.1 riastrad prev_block_size = i915_buddy_block_size(mm, prev); 241 1.1 riastrad offset = i915_buddy_block_offset(root); 242 1.1 riastrad 243 1.1 riastrad if (offset != (prev_offset + prev_block_size)) { 244 1.1 riastrad pr_err("root offset mismatch\n"); 245 1.1 riastrad err = -EINVAL; 246 1.1 riastrad } 247 1.1 riastrad } 248 1.1 riastrad 249 1.1 riastrad block = list_first_entry_or_null(&mm->free_list[order], 250 1.1 riastrad struct i915_buddy_block, 251 1.1 riastrad link); 252 1.1 riastrad if (block != root) { 253 1.1 riastrad pr_err("root mismatch at order=%u\n", order); 254 1.1 riastrad err = -EINVAL; 255 1.1 riastrad } 256 1.1 riastrad 257 1.1 riastrad if (err) 258 1.1 riastrad break; 259 1.1 riastrad 260 1.1 riastrad prev = root; 261 1.1 riastrad total += i915_buddy_block_size(mm, root); 262 1.1 riastrad } 263 1.1 riastrad 264 1.1 riastrad if (!err) { 265 1.1 riastrad if (total != mm->size) { 266 1.1 riastrad pr_err("expected mm size=%llx, found=%llx\n", mm->size, 267 1.1 riastrad total); 268 1.1 riastrad err = -EINVAL; 269 1.1 riastrad } 270 1.1 riastrad return err; 271 1.1 riastrad } 272 1.1 riastrad 273 1.1 riastrad if (prev) { 274 1.1 riastrad pr_err("prev root(%u), dump:\n", i - 1); 275 1.1 riastrad igt_dump_block(mm, prev); 276 1.1 riastrad } 277 1.1 riastrad 278 1.1 riastrad if (root) { 279 1.1 riastrad pr_err("bad root(%u), dump:\n", i); 280 1.1 riastrad igt_dump_block(mm, root); 281 1.1 riastrad } 282 1.1 riastrad 283 1.1 riastrad return err; 284 1.1 riastrad } 285 1.1 riastrad 286 1.1 riastrad static void igt_mm_config(u64 *size, u64 *chunk_size) 287 1.1 riastrad { 288 1.1 riastrad I915_RND_STATE(prng); 289 1.1 riastrad u64 s, ms; 290 1.1 riastrad 291 1.1 riastrad /* Nothing fancy, just try to get an interesting bit pattern */ 292 1.1 riastrad 293 1.1 riastrad prandom_seed_state(&prng, i915_selftest.random_seed); 294 1.1 riastrad 295 1.1 riastrad s = i915_prandom_u64_state(&prng) & (SZ_8G - 1); 296 1.1 riastrad ms = BIT_ULL(12 + (prandom_u32_state(&prng) % ilog2(s >> 12))); 297 1.1 riastrad s = max(s & -ms, ms); 298 1.1 riastrad 299 1.1 riastrad *chunk_size = ms; 300 1.1 riastrad *size = s; 301 1.1 riastrad } 302 1.1 riastrad 303 1.1 riastrad static int igt_buddy_alloc_smoke(void *arg) 304 1.1 riastrad { 305 1.1 riastrad struct i915_buddy_mm mm; 306 1.1 riastrad int max_order; 307 1.1 riastrad u64 chunk_size; 308 1.1 riastrad u64 mm_size; 309 1.1 riastrad int err; 310 1.1 riastrad 311 1.1 riastrad igt_mm_config(&mm_size, &chunk_size); 312 1.1 riastrad 313 1.1 riastrad pr_info("buddy_init with size=%llx, chunk_size=%llx\n", mm_size, chunk_size); 314 1.1 riastrad 315 1.1 riastrad err = i915_buddy_init(&mm, mm_size, chunk_size); 316 1.1 riastrad if (err) { 317 1.1 riastrad pr_err("buddy_init failed(%d)\n", err); 318 1.1 riastrad return err; 319 1.1 riastrad } 320 1.1 riastrad 321 1.1 riastrad for (max_order = mm.max_order; max_order >= 0; max_order--) { 322 1.1 riastrad struct i915_buddy_block *block; 323 1.1 riastrad int order; 324 1.1 riastrad LIST_HEAD(blocks); 325 1.1 riastrad u64 total; 326 1.1 riastrad 327 1.1 riastrad err = igt_check_mm(&mm); 328 1.1 riastrad if (err) { 329 1.1 riastrad pr_err("pre-mm check failed, abort\n"); 330 1.1 riastrad break; 331 1.1 riastrad } 332 1.1 riastrad 333 1.1 riastrad pr_info("filling from max_order=%u\n", max_order); 334 1.1 riastrad 335 1.1 riastrad order = max_order; 336 1.1 riastrad total = 0; 337 1.1 riastrad 338 1.1 riastrad do { 339 1.1 riastrad retry: 340 1.1 riastrad block = i915_buddy_alloc(&mm, order); 341 1.1 riastrad if (IS_ERR(block)) { 342 1.1 riastrad err = PTR_ERR(block); 343 1.1 riastrad if (err == -ENOMEM) { 344 1.1 riastrad pr_info("buddy_alloc hit -ENOMEM with order=%d\n", 345 1.1 riastrad order); 346 1.1 riastrad } else { 347 1.1 riastrad if (order--) { 348 1.1 riastrad err = 0; 349 1.1 riastrad goto retry; 350 1.1 riastrad } 351 1.1 riastrad 352 1.1 riastrad pr_err("buddy_alloc with order=%d failed(%d)\n", 353 1.1 riastrad order, err); 354 1.1 riastrad } 355 1.1 riastrad 356 1.1 riastrad break; 357 1.1 riastrad } 358 1.1 riastrad 359 1.1 riastrad list_add_tail(&block->link, &blocks); 360 1.1 riastrad 361 1.1 riastrad if (i915_buddy_block_order(block) != order) { 362 1.1 riastrad pr_err("buddy_alloc order mismatch\n"); 363 1.1 riastrad err = -EINVAL; 364 1.1 riastrad break; 365 1.1 riastrad } 366 1.1 riastrad 367 1.1 riastrad total += i915_buddy_block_size(&mm, block); 368 1.1 riastrad } while (total < mm.size); 369 1.1 riastrad 370 1.1 riastrad if (!err) 371 1.1 riastrad err = igt_check_blocks(&mm, &blocks, total, false); 372 1.1 riastrad 373 1.1 riastrad i915_buddy_free_list(&mm, &blocks); 374 1.1 riastrad 375 1.1 riastrad if (!err) { 376 1.1 riastrad err = igt_check_mm(&mm); 377 1.1 riastrad if (err) 378 1.1 riastrad pr_err("post-mm check failed\n"); 379 1.1 riastrad } 380 1.1 riastrad 381 1.1 riastrad if (err) 382 1.1 riastrad break; 383 1.1 riastrad 384 1.1 riastrad cond_resched(); 385 1.1 riastrad } 386 1.1 riastrad 387 1.1 riastrad if (err == -ENOMEM) 388 1.1 riastrad err = 0; 389 1.1 riastrad 390 1.1 riastrad i915_buddy_fini(&mm); 391 1.1 riastrad 392 1.1 riastrad return err; 393 1.1 riastrad } 394 1.1 riastrad 395 1.1 riastrad static int igt_buddy_alloc_pessimistic(void *arg) 396 1.1 riastrad { 397 1.1 riastrad const unsigned int max_order = 16; 398 1.1 riastrad struct i915_buddy_block *block, *bn; 399 1.1 riastrad struct i915_buddy_mm mm; 400 1.1 riastrad unsigned int order; 401 1.1 riastrad LIST_HEAD(blocks); 402 1.1 riastrad int err; 403 1.1 riastrad 404 1.1 riastrad /* 405 1.1 riastrad * Create a pot-sized mm, then allocate one of each possible 406 1.1 riastrad * order within. This should leave the mm with exactly one 407 1.1 riastrad * page left. 408 1.1 riastrad */ 409 1.1 riastrad 410 1.1 riastrad err = i915_buddy_init(&mm, PAGE_SIZE << max_order, PAGE_SIZE); 411 1.1 riastrad if (err) { 412 1.1 riastrad pr_err("buddy_init failed(%d)\n", err); 413 1.1 riastrad return err; 414 1.1 riastrad } 415 1.1 riastrad GEM_BUG_ON(mm.max_order != max_order); 416 1.1 riastrad 417 1.1 riastrad for (order = 0; order < max_order; order++) { 418 1.1 riastrad block = i915_buddy_alloc(&mm, order); 419 1.1 riastrad if (IS_ERR(block)) { 420 1.1 riastrad pr_info("buddy_alloc hit -ENOMEM with order=%d\n", 421 1.1 riastrad order); 422 1.1 riastrad err = PTR_ERR(block); 423 1.1 riastrad goto err; 424 1.1 riastrad } 425 1.1 riastrad 426 1.1 riastrad list_add_tail(&block->link, &blocks); 427 1.1 riastrad } 428 1.1 riastrad 429 1.1 riastrad /* And now the last remaining block available */ 430 1.1 riastrad block = i915_buddy_alloc(&mm, 0); 431 1.1 riastrad if (IS_ERR(block)) { 432 1.1 riastrad pr_info("buddy_alloc hit -ENOMEM on final alloc\n"); 433 1.1 riastrad err = PTR_ERR(block); 434 1.1 riastrad goto err; 435 1.1 riastrad } 436 1.1 riastrad list_add_tail(&block->link, &blocks); 437 1.1 riastrad 438 1.1 riastrad /* Should be completely full! */ 439 1.1 riastrad for (order = max_order; order--; ) { 440 1.1 riastrad block = i915_buddy_alloc(&mm, order); 441 1.1 riastrad if (!IS_ERR(block)) { 442 1.1 riastrad pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!", 443 1.1 riastrad order); 444 1.1 riastrad list_add_tail(&block->link, &blocks); 445 1.1 riastrad err = -EINVAL; 446 1.1 riastrad goto err; 447 1.1 riastrad } 448 1.1 riastrad } 449 1.1 riastrad 450 1.1 riastrad block = list_last_entry(&blocks, typeof(*block), link); 451 1.1 riastrad list_del(&block->link); 452 1.1 riastrad i915_buddy_free(&mm, block); 453 1.1 riastrad 454 1.1 riastrad /* As we free in increasing size, we make available larger blocks */ 455 1.1 riastrad order = 1; 456 1.1 riastrad list_for_each_entry_safe(block, bn, &blocks, link) { 457 1.1 riastrad list_del(&block->link); 458 1.1 riastrad i915_buddy_free(&mm, block); 459 1.1 riastrad 460 1.1 riastrad block = i915_buddy_alloc(&mm, order); 461 1.1 riastrad if (IS_ERR(block)) { 462 1.1 riastrad pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n", 463 1.1 riastrad order); 464 1.1 riastrad err = PTR_ERR(block); 465 1.1 riastrad goto err; 466 1.1 riastrad } 467 1.1 riastrad i915_buddy_free(&mm, block); 468 1.1 riastrad order++; 469 1.1 riastrad } 470 1.1 riastrad 471 1.1 riastrad /* To confirm, now the whole mm should be available */ 472 1.1 riastrad block = i915_buddy_alloc(&mm, max_order); 473 1.1 riastrad if (IS_ERR(block)) { 474 1.1 riastrad pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n", 475 1.1 riastrad max_order); 476 1.1 riastrad err = PTR_ERR(block); 477 1.1 riastrad goto err; 478 1.1 riastrad } 479 1.1 riastrad i915_buddy_free(&mm, block); 480 1.1 riastrad 481 1.1 riastrad err: 482 1.1 riastrad i915_buddy_free_list(&mm, &blocks); 483 1.1 riastrad i915_buddy_fini(&mm); 484 1.1 riastrad return err; 485 1.1 riastrad } 486 1.1 riastrad 487 1.1 riastrad static int igt_buddy_alloc_optimistic(void *arg) 488 1.1 riastrad { 489 1.1 riastrad const int max_order = 16; 490 1.1 riastrad struct i915_buddy_block *block; 491 1.1 riastrad struct i915_buddy_mm mm; 492 1.1 riastrad LIST_HEAD(blocks); 493 1.1 riastrad int order; 494 1.1 riastrad int err; 495 1.1 riastrad 496 1.1 riastrad /* 497 1.1 riastrad * Create a mm with one block of each order available, and 498 1.1 riastrad * try to allocate them all. 499 1.1 riastrad */ 500 1.1 riastrad 501 1.1 riastrad err = i915_buddy_init(&mm, 502 1.1 riastrad PAGE_SIZE * ((1 << (max_order + 1)) - 1), 503 1.1 riastrad PAGE_SIZE); 504 1.1 riastrad if (err) { 505 1.1 riastrad pr_err("buddy_init failed(%d)\n", err); 506 1.1 riastrad return err; 507 1.1 riastrad } 508 1.1 riastrad GEM_BUG_ON(mm.max_order != max_order); 509 1.1 riastrad 510 1.1 riastrad for (order = 0; order <= max_order; order++) { 511 1.1 riastrad block = i915_buddy_alloc(&mm, order); 512 1.1 riastrad if (IS_ERR(block)) { 513 1.1 riastrad pr_info("buddy_alloc hit -ENOMEM with order=%d\n", 514 1.1 riastrad order); 515 1.1 riastrad err = PTR_ERR(block); 516 1.1 riastrad goto err; 517 1.1 riastrad } 518 1.1 riastrad 519 1.1 riastrad list_add_tail(&block->link, &blocks); 520 1.1 riastrad } 521 1.1 riastrad 522 1.1 riastrad /* Should be completely full! */ 523 1.1 riastrad block = i915_buddy_alloc(&mm, 0); 524 1.1 riastrad if (!IS_ERR(block)) { 525 1.1 riastrad pr_info("buddy_alloc unexpectedly succeeded, it should be full!"); 526 1.1 riastrad list_add_tail(&block->link, &blocks); 527 1.1 riastrad err = -EINVAL; 528 1.1 riastrad goto err; 529 1.1 riastrad } 530 1.1 riastrad 531 1.1 riastrad err: 532 1.1 riastrad i915_buddy_free_list(&mm, &blocks); 533 1.1 riastrad i915_buddy_fini(&mm); 534 1.1 riastrad return err; 535 1.1 riastrad } 536 1.1 riastrad 537 1.1 riastrad static int igt_buddy_alloc_pathological(void *arg) 538 1.1 riastrad { 539 1.1 riastrad const int max_order = 16; 540 1.1 riastrad struct i915_buddy_block *block; 541 1.1 riastrad struct i915_buddy_mm mm; 542 1.1 riastrad LIST_HEAD(blocks); 543 1.1 riastrad LIST_HEAD(holes); 544 1.1 riastrad int order, top; 545 1.1 riastrad int err; 546 1.1 riastrad 547 1.1 riastrad /* 548 1.1 riastrad * Create a pot-sized mm, then allocate one of each possible 549 1.1 riastrad * order within. This should leave the mm with exactly one 550 1.1 riastrad * page left. Free the largest block, then whittle down again. 551 1.1 riastrad * Eventually we will have a fully 50% fragmented mm. 552 1.1 riastrad */ 553 1.1 riastrad 554 1.1 riastrad err = i915_buddy_init(&mm, PAGE_SIZE << max_order, PAGE_SIZE); 555 1.1 riastrad if (err) { 556 1.1 riastrad pr_err("buddy_init failed(%d)\n", err); 557 1.1 riastrad return err; 558 1.1 riastrad } 559 1.1 riastrad GEM_BUG_ON(mm.max_order != max_order); 560 1.1 riastrad 561 1.1 riastrad for (top = max_order; top; top--) { 562 1.1 riastrad /* Make room by freeing the largest allocated block */ 563 1.1 riastrad block = list_first_entry_or_null(&blocks, typeof(*block), link); 564 1.1 riastrad if (block) { 565 1.1 riastrad list_del(&block->link); 566 1.1 riastrad i915_buddy_free(&mm, block); 567 1.1 riastrad } 568 1.1 riastrad 569 1.1 riastrad for (order = top; order--; ) { 570 1.1 riastrad block = i915_buddy_alloc(&mm, order); 571 1.1 riastrad if (IS_ERR(block)) { 572 1.1 riastrad pr_info("buddy_alloc hit -ENOMEM with order=%d, top=%d\n", 573 1.1 riastrad order, top); 574 1.1 riastrad err = PTR_ERR(block); 575 1.1 riastrad goto err; 576 1.1 riastrad } 577 1.1 riastrad list_add_tail(&block->link, &blocks); 578 1.1 riastrad } 579 1.1 riastrad 580 1.1 riastrad /* There should be one final page for this sub-allocation */ 581 1.1 riastrad block = i915_buddy_alloc(&mm, 0); 582 1.1 riastrad if (IS_ERR(block)) { 583 1.1 riastrad pr_info("buddy_alloc hit -ENOMEM for hole\n"); 584 1.1 riastrad err = PTR_ERR(block); 585 1.1 riastrad goto err; 586 1.1 riastrad } 587 1.1 riastrad list_add_tail(&block->link, &holes); 588 1.1 riastrad 589 1.1 riastrad block = i915_buddy_alloc(&mm, top); 590 1.1 riastrad if (!IS_ERR(block)) { 591 1.1 riastrad pr_info("buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!", 592 1.1 riastrad top, max_order); 593 1.1 riastrad list_add_tail(&block->link, &blocks); 594 1.1 riastrad err = -EINVAL; 595 1.1 riastrad goto err; 596 1.1 riastrad } 597 1.1 riastrad } 598 1.1 riastrad 599 1.1 riastrad i915_buddy_free_list(&mm, &holes); 600 1.1 riastrad 601 1.1 riastrad /* Nothing larger than blocks of chunk_size now available */ 602 1.1 riastrad for (order = 1; order <= max_order; order++) { 603 1.1 riastrad block = i915_buddy_alloc(&mm, order); 604 1.1 riastrad if (!IS_ERR(block)) { 605 1.1 riastrad pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!", 606 1.1 riastrad order); 607 1.1 riastrad list_add_tail(&block->link, &blocks); 608 1.1 riastrad err = -EINVAL; 609 1.1 riastrad goto err; 610 1.1 riastrad } 611 1.1 riastrad } 612 1.1 riastrad 613 1.1 riastrad err: 614 1.1 riastrad list_splice_tail(&holes, &blocks); 615 1.1 riastrad i915_buddy_free_list(&mm, &blocks); 616 1.1 riastrad i915_buddy_fini(&mm); 617 1.1 riastrad return err; 618 1.1 riastrad } 619 1.1 riastrad 620 1.1 riastrad static int igt_buddy_alloc_range(void *arg) 621 1.1 riastrad { 622 1.1 riastrad struct i915_buddy_mm mm; 623 1.1 riastrad unsigned long page_num; 624 1.1 riastrad LIST_HEAD(blocks); 625 1.1 riastrad u64 chunk_size; 626 1.1 riastrad u64 offset; 627 1.1 riastrad u64 size; 628 1.1 riastrad u64 rem; 629 1.1 riastrad int err; 630 1.1 riastrad 631 1.1 riastrad igt_mm_config(&size, &chunk_size); 632 1.1 riastrad 633 1.1 riastrad pr_info("buddy_init with size=%llx, chunk_size=%llx\n", size, chunk_size); 634 1.1 riastrad 635 1.1 riastrad err = i915_buddy_init(&mm, size, chunk_size); 636 1.1 riastrad if (err) { 637 1.1 riastrad pr_err("buddy_init failed(%d)\n", err); 638 1.1 riastrad return err; 639 1.1 riastrad } 640 1.1 riastrad 641 1.1 riastrad err = igt_check_mm(&mm); 642 1.1 riastrad if (err) { 643 1.1 riastrad pr_err("pre-mm check failed, abort, abort, abort!\n"); 644 1.1 riastrad goto err_fini; 645 1.1 riastrad } 646 1.1 riastrad 647 1.1 riastrad rem = mm.size; 648 1.1 riastrad offset = 0; 649 1.1 riastrad 650 1.1 riastrad for_each_prime_number_from(page_num, 1, ULONG_MAX - 1) { 651 1.1 riastrad struct i915_buddy_block *block; 652 1.1 riastrad LIST_HEAD(tmp); 653 1.1 riastrad 654 1.1 riastrad size = min(page_num * mm.chunk_size, rem); 655 1.1 riastrad 656 1.1 riastrad err = i915_buddy_alloc_range(&mm, &tmp, offset, size); 657 1.1 riastrad if (err) { 658 1.1 riastrad if (err == -ENOMEM) { 659 1.1 riastrad pr_info("alloc_range hit -ENOMEM with size=%llx\n", 660 1.1 riastrad size); 661 1.1 riastrad } else { 662 1.1 riastrad pr_err("alloc_range with offset=%llx, size=%llx failed(%d)\n", 663 1.1 riastrad offset, size, err); 664 1.1 riastrad } 665 1.1 riastrad 666 1.1 riastrad break; 667 1.1 riastrad } 668 1.1 riastrad 669 1.1 riastrad block = list_first_entry_or_null(&tmp, 670 1.1 riastrad struct i915_buddy_block, 671 1.1 riastrad link); 672 1.1 riastrad if (!block) { 673 1.1 riastrad pr_err("alloc_range has no blocks\n"); 674 1.1 riastrad err = -EINVAL; 675 1.1 riastrad break; 676 1.1 riastrad } 677 1.1 riastrad 678 1.1 riastrad if (i915_buddy_block_offset(block) != offset) { 679 1.1 riastrad pr_err("alloc_range start offset mismatch, found=%llx, expected=%llx\n", 680 1.1 riastrad i915_buddy_block_offset(block), offset); 681 1.1 riastrad err = -EINVAL; 682 1.1 riastrad } 683 1.1 riastrad 684 1.1 riastrad if (!err) 685 1.1 riastrad err = igt_check_blocks(&mm, &tmp, size, true); 686 1.1 riastrad 687 1.1 riastrad list_splice_tail(&tmp, &blocks); 688 1.1 riastrad 689 1.1 riastrad if (err) 690 1.1 riastrad break; 691 1.1 riastrad 692 1.1 riastrad offset += size; 693 1.1 riastrad 694 1.1 riastrad rem -= size; 695 1.1 riastrad if (!rem) 696 1.1 riastrad break; 697 1.1 riastrad 698 1.1 riastrad cond_resched(); 699 1.1 riastrad } 700 1.1 riastrad 701 1.1 riastrad if (err == -ENOMEM) 702 1.1 riastrad err = 0; 703 1.1 riastrad 704 1.1 riastrad i915_buddy_free_list(&mm, &blocks); 705 1.1 riastrad 706 1.1 riastrad if (!err) { 707 1.1 riastrad err = igt_check_mm(&mm); 708 1.1 riastrad if (err) 709 1.1 riastrad pr_err("post-mm check failed\n"); 710 1.1 riastrad } 711 1.1 riastrad 712 1.1 riastrad err_fini: 713 1.1 riastrad i915_buddy_fini(&mm); 714 1.1 riastrad 715 1.1 riastrad return err; 716 1.1 riastrad } 717 1.1 riastrad 718 1.1 riastrad int i915_buddy_mock_selftests(void) 719 1.1 riastrad { 720 1.1 riastrad static const struct i915_subtest tests[] = { 721 1.1 riastrad SUBTEST(igt_buddy_alloc_pessimistic), 722 1.1 riastrad SUBTEST(igt_buddy_alloc_optimistic), 723 1.1 riastrad SUBTEST(igt_buddy_alloc_pathological), 724 1.1 riastrad SUBTEST(igt_buddy_alloc_smoke), 725 1.1 riastrad SUBTEST(igt_buddy_alloc_range), 726 1.1 riastrad }; 727 1.1 riastrad 728 1.1 riastrad return i915_subtests(tests, NULL); 729 1.1 riastrad } 730