1 // SPDX-License-Identifier: GPL-2.0 2 #ifdef CONFIG_BCACHEFS_TESTS 3 4 #include "bcachefs.h" 5 #include "btree_update.h" 6 #include "journal_reclaim.h" 7 #include "snapshot.h" 8 #include "tests.h" 9 10 #include "linux/kthread.h" 11 #include "linux/random.h" 12 13 static void delete_test_keys(struct bch_fs *c) 14 { 15 int ret; 16 17 ret = bch2_btree_delete_range(c, BTREE_ID_extents, 18 SPOS(0, 0, U32_MAX), 19 POS(0, U64_MAX), 20 0, NULL); 21 BUG_ON(ret); 22 23 ret = bch2_btree_delete_range(c, BTREE_ID_xattrs, 24 SPOS(0, 0, U32_MAX), 25 POS(0, U64_MAX), 26 0, NULL); 27 BUG_ON(ret); 28 } 29 30 /* unit tests */ 31 32 static int test_delete(struct bch_fs *c, u64 nr) 33 { 34 struct btree_trans *trans = bch2_trans_get(c); 35 struct btree_iter iter; 36 struct bkey_i_cookie k; 37 int ret; 38 39 bkey_cookie_init(&k.k_i); 40 k.k.p.snapshot = U32_MAX; 41 42 bch2_trans_iter_init(trans, &iter, BTREE_ID_xattrs, k.k.p, 43 BTREE_ITER_INTENT); 44 45 ret = commit_do(trans, NULL, NULL, 0, 46 bch2_btree_iter_traverse(&iter) ?: 47 bch2_trans_update(trans, &iter, &k.k_i, 0)); 48 bch_err_msg(c, ret, "update error"); 49 if (ret) 50 goto err; 51 52 pr_info("deleting once"); 53 ret = commit_do(trans, NULL, NULL, 0, 54 bch2_btree_iter_traverse(&iter) ?: 55 bch2_btree_delete_at(trans, &iter, 0)); 56 bch_err_msg(c, ret, "delete error (first)"); 57 if (ret) 58 goto err; 59 60 pr_info("deleting twice"); 61 ret = commit_do(trans, NULL, NULL, 0, 62 bch2_btree_iter_traverse(&iter) ?: 63 bch2_btree_delete_at(trans, &iter, 0)); 64 bch_err_msg(c, ret, "delete error (second)"); 65 if (ret) 66 goto err; 67 err: 68 bch2_trans_iter_exit(trans, &iter); 69 bch2_trans_put(trans); 70 return ret; 71 } 72 73 static int test_delete_written(struct bch_fs *c, u64 nr) 74 { 75 struct btree_trans *trans = bch2_trans_get(c); 76 struct btree_iter iter; 77 struct bkey_i_cookie k; 78 int ret; 79 80 bkey_cookie_init(&k.k_i); 81 k.k.p.snapshot = U32_MAX; 82 83 bch2_trans_iter_init(trans, &iter, BTREE_ID_xattrs, k.k.p, 84 BTREE_ITER_INTENT); 85 86 ret = commit_do(trans, NULL, NULL, 0, 87 bch2_btree_iter_traverse(&iter) ?: 88 bch2_trans_update(trans, &iter, &k.k_i, 0)); 89 bch_err_msg(c, ret, "update error"); 90 if (ret) 91 goto err; 92 93 bch2_trans_unlock(trans); 94 bch2_journal_flush_all_pins(&c->journal); 95 96 ret = commit_do(trans, NULL, NULL, 0, 97 bch2_btree_iter_traverse(&iter) ?: 98 bch2_btree_delete_at(trans, &iter, 0)); 99 bch_err_msg(c, ret, "delete error"); 100 if (ret) 101 goto err; 102 err: 103 bch2_trans_iter_exit(trans, &iter); 104 bch2_trans_put(trans); 105 return ret; 106 } 107 108 static int test_iterate(struct bch_fs *c, u64 nr) 109 { 110 struct btree_trans *trans = bch2_trans_get(c); 111 struct btree_iter iter = { NULL }; 112 struct bkey_s_c k; 113 u64 i; 114 int ret = 0; 115 116 delete_test_keys(c); 117 118 pr_info("inserting test keys"); 119 120 for (i = 0; i < nr; i++) { 121 struct bkey_i_cookie ck; 122 123 bkey_cookie_init(&ck.k_i); 124 ck.k.p.offset = i; 125 ck.k.p.snapshot = U32_MAX; 126 127 ret = bch2_btree_insert(c, BTREE_ID_xattrs, &ck.k_i, NULL, 0); 128 bch_err_msg(c, ret, "insert error"); 129 if (ret) 130 goto err; 131 } 132 133 pr_info("iterating forwards"); 134 135 i = 0; 136 137 ret = for_each_btree_key2_upto(trans, iter, BTREE_ID_xattrs, 138 SPOS(0, 0, U32_MAX), POS(0, U64_MAX), 139 0, k, ({ 140 BUG_ON(k.k->p.offset != i++); 141 0; 142 })); 143 bch_err_msg(c, ret, "error iterating forwards"); 144 if (ret) 145 goto err; 146 147 BUG_ON(i != nr); 148 149 pr_info("iterating backwards"); 150 151 ret = for_each_btree_key_reverse(trans, iter, BTREE_ID_xattrs, 152 SPOS(0, U64_MAX, U32_MAX), 0, k, 153 ({ 154 BUG_ON(k.k->p.offset != --i); 155 0; 156 })); 157 bch_err_msg(c, ret, "error iterating backwards"); 158 if (ret) 159 goto err; 160 161 BUG_ON(i); 162 err: 163 bch2_trans_iter_exit(trans, &iter); 164 bch2_trans_put(trans); 165 return ret; 166 } 167 168 static int test_iterate_extents(struct bch_fs *c, u64 nr) 169 { 170 struct btree_trans *trans = bch2_trans_get(c); 171 struct btree_iter iter = { NULL }; 172 struct bkey_s_c k; 173 u64 i; 174 int ret = 0; 175 176 delete_test_keys(c); 177 178 pr_info("inserting test extents"); 179 180 for (i = 0; i < nr; i += 8) { 181 struct bkey_i_cookie ck; 182 183 bkey_cookie_init(&ck.k_i); 184 ck.k.p.offset = i + 8; 185 ck.k.p.snapshot = U32_MAX; 186 ck.k.size = 8; 187 188 ret = bch2_btree_insert(c, BTREE_ID_extents, &ck.k_i, NULL, 0); 189 bch_err_msg(c, ret, "insert error"); 190 if (ret) 191 goto err; 192 } 193 194 pr_info("iterating forwards"); 195 196 i = 0; 197 198 ret = for_each_btree_key2_upto(trans, iter, BTREE_ID_extents, 199 SPOS(0, 0, U32_MAX), POS(0, U64_MAX), 200 0, k, ({ 201 BUG_ON(bkey_start_offset(k.k) != i); 202 i = k.k->p.offset; 203 0; 204 })); 205 bch_err_msg(c, ret, "error iterating forwards"); 206 if (ret) 207 goto err; 208 209 BUG_ON(i != nr); 210 211 pr_info("iterating backwards"); 212 213 ret = for_each_btree_key_reverse(trans, iter, BTREE_ID_extents, 214 SPOS(0, U64_MAX, U32_MAX), 0, k, 215 ({ 216 BUG_ON(k.k->p.offset != i); 217 i = bkey_start_offset(k.k); 218 0; 219 })); 220 bch_err_msg(c, ret, "error iterating backwards"); 221 if (ret) 222 goto err; 223 224 BUG_ON(i); 225 err: 226 bch2_trans_iter_exit(trans, &iter); 227 bch2_trans_put(trans); 228 return ret; 229 } 230 231 static int test_iterate_slots(struct bch_fs *c, u64 nr) 232 { 233 struct btree_trans *trans = bch2_trans_get(c); 234 struct btree_iter iter = { NULL }; 235 struct bkey_s_c k; 236 u64 i; 237 int ret = 0; 238 239 delete_test_keys(c); 240 241 pr_info("inserting test keys"); 242 243 for (i = 0; i < nr; i++) { 244 struct bkey_i_cookie ck; 245 246 bkey_cookie_init(&ck.k_i); 247 ck.k.p.offset = i * 2; 248 ck.k.p.snapshot = U32_MAX; 249 250 ret = bch2_btree_insert(c, BTREE_ID_xattrs, &ck.k_i, NULL, 0); 251 bch_err_msg(c, ret, "insert error"); 252 if (ret) 253 goto err; 254 } 255 256 pr_info("iterating forwards"); 257 258 i = 0; 259 260 ret = for_each_btree_key2_upto(trans, iter, BTREE_ID_xattrs, 261 SPOS(0, 0, U32_MAX), POS(0, U64_MAX), 262 0, k, ({ 263 BUG_ON(k.k->p.offset != i); 264 i += 2; 265 0; 266 })); 267 bch_err_msg(c, ret, "error iterating forwards"); 268 if (ret) 269 goto err; 270 271 BUG_ON(i != nr * 2); 272 273 pr_info("iterating forwards by slots"); 274 275 i = 0; 276 277 ret = for_each_btree_key2_upto(trans, iter, BTREE_ID_xattrs, 278 SPOS(0, 0, U32_MAX), POS(0, U64_MAX), 279 BTREE_ITER_SLOTS, k, ({ 280 if (i >= nr * 2) 281 break; 282 283 BUG_ON(k.k->p.offset != i); 284 BUG_ON(bkey_deleted(k.k) != (i & 1)); 285 286 i++; 287 0; 288 })); 289 if (ret < 0) { 290 bch_err_msg(c, ret, "error iterating forwards by slots"); 291 goto err; 292 } 293 ret = 0; 294 err: 295 bch2_trans_put(trans); 296 return ret; 297 } 298 299 static int test_iterate_slots_extents(struct bch_fs *c, u64 nr) 300 { 301 struct btree_trans *trans = bch2_trans_get(c); 302 struct btree_iter iter = { NULL }; 303 struct bkey_s_c k; 304 u64 i; 305 int ret = 0; 306 307 delete_test_keys(c); 308 309 pr_info("inserting test keys"); 310 311 for (i = 0; i < nr; i += 16) { 312 struct bkey_i_cookie ck; 313 314 bkey_cookie_init(&ck.k_i); 315 ck.k.p.offset = i + 16; 316 ck.k.p.snapshot = U32_MAX; 317 ck.k.size = 8; 318 319 ret = bch2_btree_insert(c, BTREE_ID_extents, &ck.k_i, NULL, 0); 320 bch_err_msg(c, ret, "insert error"); 321 if (ret) 322 goto err; 323 } 324 325 pr_info("iterating forwards"); 326 327 i = 0; 328 329 ret = for_each_btree_key2_upto(trans, iter, BTREE_ID_extents, 330 SPOS(0, 0, U32_MAX), POS(0, U64_MAX), 331 0, k, ({ 332 BUG_ON(bkey_start_offset(k.k) != i + 8); 333 BUG_ON(k.k->size != 8); 334 i += 16; 335 0; 336 })); 337 bch_err_msg(c, ret, "error iterating forwards"); 338 if (ret) 339 goto err; 340 341 BUG_ON(i != nr); 342 343 pr_info("iterating forwards by slots"); 344 345 i = 0; 346 347 ret = for_each_btree_key2_upto(trans, iter, BTREE_ID_extents, 348 SPOS(0, 0, U32_MAX), POS(0, U64_MAX), 349 BTREE_ITER_SLOTS, k, ({ 350 if (i == nr) 351 break; 352 BUG_ON(bkey_deleted(k.k) != !(i % 16)); 353 354 BUG_ON(bkey_start_offset(k.k) != i); 355 BUG_ON(k.k->size != 8); 356 i = k.k->p.offset; 357 0; 358 })); 359 bch_err_msg(c, ret, "error iterating forwards by slots"); 360 if (ret) 361 goto err; 362 ret = 0; 363 err: 364 bch2_trans_put(trans); 365 return 0; 366 } 367 368 /* 369 * XXX: we really want to make sure we've got a btree with depth > 0 for these 370 * tests 371 */ 372 static int test_peek_end(struct bch_fs *c, u64 nr) 373 { 374 struct btree_trans *trans = bch2_trans_get(c); 375 struct btree_iter iter; 376 struct bkey_s_c k; 377 378 bch2_trans_iter_init(trans, &iter, BTREE_ID_xattrs, 379 SPOS(0, 0, U32_MAX), 0); 380 381 lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek_upto(&iter, POS(0, U64_MAX)))); 382 BUG_ON(k.k); 383 384 lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek_upto(&iter, POS(0, U64_MAX)))); 385 BUG_ON(k.k); 386 387 bch2_trans_iter_exit(trans, &iter); 388 bch2_trans_put(trans); 389 return 0; 390 } 391 392 static int test_peek_end_extents(struct bch_fs *c, u64 nr) 393 { 394 struct btree_trans *trans = bch2_trans_get(c); 395 struct btree_iter iter; 396 struct bkey_s_c k; 397 398 bch2_trans_iter_init(trans, &iter, BTREE_ID_extents, 399 SPOS(0, 0, U32_MAX), 0); 400 401 lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek_upto(&iter, POS(0, U64_MAX)))); 402 BUG_ON(k.k); 403 404 lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek_upto(&iter, POS(0, U64_MAX)))); 405 BUG_ON(k.k); 406 407 bch2_trans_iter_exit(trans, &iter); 408 bch2_trans_put(trans); 409 return 0; 410 } 411 412 /* extent unit tests */ 413 414 static u64 test_version; 415 416 static int insert_test_extent(struct bch_fs *c, 417 u64 start, u64 end) 418 { 419 struct bkey_i_cookie k; 420 int ret; 421 422 bkey_cookie_init(&k.k_i); 423 k.k_i.k.p.offset = end; 424 k.k_i.k.p.snapshot = U32_MAX; 425 k.k_i.k.size = end - start; 426 k.k_i.k.version.lo = test_version++; 427 428 ret = bch2_btree_insert(c, BTREE_ID_extents, &k.k_i, NULL, 0); 429 bch_err_fn(c, ret); 430 return ret; 431 } 432 433 static int __test_extent_overwrite(struct bch_fs *c, 434 u64 e1_start, u64 e1_end, 435 u64 e2_start, u64 e2_end) 436 { 437 int ret; 438 439 ret = insert_test_extent(c, e1_start, e1_end) ?: 440 insert_test_extent(c, e2_start, e2_end); 441 442 delete_test_keys(c); 443 return ret; 444 } 445 446 static int test_extent_overwrite_front(struct bch_fs *c, u64 nr) 447 { 448 return __test_extent_overwrite(c, 0, 64, 0, 32) ?: 449 __test_extent_overwrite(c, 8, 64, 0, 32); 450 } 451 452 static int test_extent_overwrite_back(struct bch_fs *c, u64 nr) 453 { 454 return __test_extent_overwrite(c, 0, 64, 32, 64) ?: 455 __test_extent_overwrite(c, 0, 64, 32, 72); 456 } 457 458 static int test_extent_overwrite_middle(struct bch_fs *c, u64 nr) 459 { 460 return __test_extent_overwrite(c, 0, 64, 32, 40); 461 } 462 463 static int test_extent_overwrite_all(struct bch_fs *c, u64 nr) 464 { 465 return __test_extent_overwrite(c, 32, 64, 0, 64) ?: 466 __test_extent_overwrite(c, 32, 64, 0, 128) ?: 467 __test_extent_overwrite(c, 32, 64, 32, 64) ?: 468 __test_extent_overwrite(c, 32, 64, 32, 128); 469 } 470 471 static int insert_test_overlapping_extent(struct bch_fs *c, u64 inum, u64 start, u32 len, u32 snapid) 472 { 473 struct bkey_i_cookie k; 474 int ret; 475 476 bkey_cookie_init(&k.k_i); 477 k.k_i.k.p.inode = inum; 478 k.k_i.k.p.offset = start + len; 479 k.k_i.k.p.snapshot = snapid; 480 k.k_i.k.size = len; 481 482 ret = bch2_trans_do(c, NULL, NULL, 0, 483 bch2_btree_insert_nonextent(trans, BTREE_ID_extents, &k.k_i, 484 BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE)); 485 bch_err_fn(c, ret); 486 return ret; 487 } 488 489 static int test_extent_create_overlapping(struct bch_fs *c, u64 inum) 490 { 491 return insert_test_overlapping_extent(c, inum, 0, 16, U32_MAX - 2) ?: /* overwrite entire */ 492 insert_test_overlapping_extent(c, inum, 2, 8, U32_MAX - 2) ?: 493 insert_test_overlapping_extent(c, inum, 4, 4, U32_MAX) ?: 494 insert_test_overlapping_extent(c, inum, 32, 8, U32_MAX - 2) ?: /* overwrite front/back */ 495 insert_test_overlapping_extent(c, inum, 36, 8, U32_MAX) ?: 496 insert_test_overlapping_extent(c, inum, 60, 8, U32_MAX - 2) ?: 497 insert_test_overlapping_extent(c, inum, 64, 8, U32_MAX); 498 } 499 500 /* snapshot unit tests */ 501 502 /* Test skipping over keys in unrelated snapshots: */ 503 static int test_snapshot_filter(struct bch_fs *c, u32 snapid_lo, u32 snapid_hi) 504 { 505 struct btree_trans *trans; 506 struct btree_iter iter; 507 struct bkey_s_c k; 508 struct bkey_i_cookie cookie; 509 int ret; 510 511 bkey_cookie_init(&cookie.k_i); 512 cookie.k.p.snapshot = snapid_hi; 513 ret = bch2_btree_insert(c, BTREE_ID_xattrs, &cookie.k_i, NULL, 0); 514 if (ret) 515 return ret; 516 517 trans = bch2_trans_get(c); 518 bch2_trans_iter_init(trans, &iter, BTREE_ID_xattrs, 519 SPOS(0, 0, snapid_lo), 0); 520 lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek_upto(&iter, POS(0, U64_MAX)))); 521 522 BUG_ON(k.k->p.snapshot != U32_MAX); 523 524 bch2_trans_iter_exit(trans, &iter); 525 bch2_trans_put(trans); 526 return ret; 527 } 528 529 static int test_snapshots(struct bch_fs *c, u64 nr) 530 { 531 struct bkey_i_cookie cookie; 532 u32 snapids[2]; 533 u32 snapid_subvols[2] = { 1, 1 }; 534 int ret; 535 536 bkey_cookie_init(&cookie.k_i); 537 cookie.k.p.snapshot = U32_MAX; 538 ret = bch2_btree_insert(c, BTREE_ID_xattrs, &cookie.k_i, NULL, 0); 539 if (ret) 540 return ret; 541 542 ret = bch2_trans_do(c, NULL, NULL, 0, 543 bch2_snapshot_node_create(trans, U32_MAX, 544 snapids, 545 snapid_subvols, 546 2)); 547 if (ret) 548 return ret; 549 550 if (snapids[0] > snapids[1]) 551 swap(snapids[0], snapids[1]); 552 553 ret = test_snapshot_filter(c, snapids[0], snapids[1]); 554 bch_err_msg(c, ret, "from test_snapshot_filter"); 555 return ret; 556 } 557 558 /* perf tests */ 559 560 static u64 test_rand(void) 561 { 562 u64 v; 563 564 get_random_bytes(&v, sizeof(v)); 565 return v; 566 } 567 568 static int rand_insert(struct bch_fs *c, u64 nr) 569 { 570 struct btree_trans *trans = bch2_trans_get(c); 571 struct bkey_i_cookie k; 572 int ret = 0; 573 u64 i; 574 575 for (i = 0; i < nr; i++) { 576 bkey_cookie_init(&k.k_i); 577 k.k.p.offset = test_rand(); 578 k.k.p.snapshot = U32_MAX; 579 580 ret = commit_do(trans, NULL, NULL, 0, 581 bch2_btree_insert_trans(trans, BTREE_ID_xattrs, &k.k_i, 0)); 582 if (ret) 583 break; 584 } 585 586 bch2_trans_put(trans); 587 return ret; 588 } 589 590 static int rand_insert_multi(struct bch_fs *c, u64 nr) 591 { 592 struct btree_trans *trans = bch2_trans_get(c); 593 struct bkey_i_cookie k[8]; 594 int ret = 0; 595 unsigned j; 596 u64 i; 597 598 for (i = 0; i < nr; i += ARRAY_SIZE(k)) { 599 for (j = 0; j < ARRAY_SIZE(k); j++) { 600 bkey_cookie_init(&k[j].k_i); 601 k[j].k.p.offset = test_rand(); 602 k[j].k.p.snapshot = U32_MAX; 603 } 604 605 ret = commit_do(trans, NULL, NULL, 0, 606 bch2_btree_insert_trans(trans, BTREE_ID_xattrs, &k[0].k_i, 0) ?: 607 bch2_btree_insert_trans(trans, BTREE_ID_xattrs, &k[1].k_i, 0) ?: 608 bch2_btree_insert_trans(trans, BTREE_ID_xattrs, &k[2].k_i, 0) ?: 609 bch2_btree_insert_trans(trans, BTREE_ID_xattrs, &k[3].k_i, 0) ?: 610 bch2_btree_insert_trans(trans, BTREE_ID_xattrs, &k[4].k_i, 0) ?: 611 bch2_btree_insert_trans(trans, BTREE_ID_xattrs, &k[5].k_i, 0) ?: 612 bch2_btree_insert_trans(trans, BTREE_ID_xattrs, &k[6].k_i, 0) ?: 613 bch2_btree_insert_trans(trans, BTREE_ID_xattrs, &k[7].k_i, 0)); 614 if (ret) 615 break; 616 } 617 618 bch2_trans_put(trans); 619 return ret; 620 } 621 622 static int rand_lookup(struct bch_fs *c, u64 nr) 623 { 624 struct btree_trans *trans = bch2_trans_get(c); 625 struct btree_iter iter; 626 struct bkey_s_c k; 627 int ret = 0; 628 u64 i; 629 630 bch2_trans_iter_init(trans, &iter, BTREE_ID_xattrs, 631 SPOS(0, 0, U32_MAX), 0); 632 633 for (i = 0; i < nr; i++) { 634 bch2_btree_iter_set_pos(&iter, SPOS(0, test_rand(), U32_MAX)); 635 636 lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek(&iter))); 637 ret = bkey_err(k); 638 if (ret) 639 break; 640 } 641 642 bch2_trans_iter_exit(trans, &iter); 643 bch2_trans_put(trans); 644 return ret; 645 } 646 647 static int rand_mixed_trans(struct btree_trans *trans, 648 struct btree_iter *iter, 649 struct bkey_i_cookie *cookie, 650 u64 i, u64 pos) 651 { 652 struct bkey_s_c k; 653 int ret; 654 655 bch2_btree_iter_set_pos(iter, SPOS(0, pos, U32_MAX)); 656 657 k = bch2_btree_iter_peek(iter); 658 ret = bkey_err(k); 659 bch_err_msg(trans->c, ret, "lookup error"); 660 if (ret) 661 return ret; 662 663 if (!(i & 3) && k.k) { 664 bkey_cookie_init(&cookie->k_i); 665 cookie->k.p = iter->pos; 666 ret = bch2_trans_update(trans, iter, &cookie->k_i, 0); 667 } 668 669 return ret; 670 } 671 672 static int rand_mixed(struct bch_fs *c, u64 nr) 673 { 674 struct btree_trans *trans = bch2_trans_get(c); 675 struct btree_iter iter; 676 struct bkey_i_cookie cookie; 677 int ret = 0; 678 u64 i, rand; 679 680 bch2_trans_iter_init(trans, &iter, BTREE_ID_xattrs, 681 SPOS(0, 0, U32_MAX), 0); 682 683 for (i = 0; i < nr; i++) { 684 rand = test_rand(); 685 ret = commit_do(trans, NULL, NULL, 0, 686 rand_mixed_trans(trans, &iter, &cookie, i, rand)); 687 if (ret) 688 break; 689 } 690 691 bch2_trans_iter_exit(trans, &iter); 692 bch2_trans_put(trans); 693 return ret; 694 } 695 696 static int __do_delete(struct btree_trans *trans, struct bpos pos) 697 { 698 struct btree_iter iter; 699 struct bkey_s_c k; 700 int ret = 0; 701 702 bch2_trans_iter_init(trans, &iter, BTREE_ID_xattrs, pos, 703 BTREE_ITER_INTENT); 704 k = bch2_btree_iter_peek(&iter); 705 ret = bkey_err(k); 706 if (ret) 707 goto err; 708 709 if (!k.k) 710 goto err; 711 712 ret = bch2_btree_delete_at(trans, &iter, 0); 713 err: 714 bch2_trans_iter_exit(trans, &iter); 715 return ret; 716 } 717 718 static int rand_delete(struct bch_fs *c, u64 nr) 719 { 720 struct btree_trans *trans = bch2_trans_get(c); 721 int ret = 0; 722 u64 i; 723 724 for (i = 0; i < nr; i++) { 725 struct bpos pos = SPOS(0, test_rand(), U32_MAX); 726 727 ret = commit_do(trans, NULL, NULL, 0, 728 __do_delete(trans, pos)); 729 if (ret) 730 break; 731 } 732 733 bch2_trans_put(trans); 734 return ret; 735 } 736 737 static int seq_insert(struct bch_fs *c, u64 nr) 738 { 739 struct btree_iter iter; 740 struct bkey_s_c k; 741 struct bkey_i_cookie insert; 742 743 bkey_cookie_init(&insert.k_i); 744 745 return bch2_trans_run(c, 746 for_each_btree_key_commit(trans, iter, BTREE_ID_xattrs, 747 SPOS(0, 0, U32_MAX), 748 BTREE_ITER_SLOTS|BTREE_ITER_INTENT, k, 749 NULL, NULL, 0, ({ 750 if (iter.pos.offset >= nr) 751 break; 752 insert.k.p = iter.pos; 753 bch2_trans_update(trans, &iter, &insert.k_i, 0); 754 }))); 755 } 756 757 static int seq_lookup(struct bch_fs *c, u64 nr) 758 { 759 struct btree_iter iter; 760 struct bkey_s_c k; 761 762 return bch2_trans_run(c, 763 for_each_btree_key2_upto(trans, iter, BTREE_ID_xattrs, 764 SPOS(0, 0, U32_MAX), POS(0, U64_MAX), 765 0, k, 766 0)); 767 } 768 769 static int seq_overwrite(struct bch_fs *c, u64 nr) 770 { 771 struct btree_iter iter; 772 struct bkey_s_c k; 773 774 return bch2_trans_run(c, 775 for_each_btree_key_commit(trans, iter, BTREE_ID_xattrs, 776 SPOS(0, 0, U32_MAX), 777 BTREE_ITER_INTENT, k, 778 NULL, NULL, 0, ({ 779 struct bkey_i_cookie u; 780 781 bkey_reassemble(&u.k_i, k); 782 bch2_trans_update(trans, &iter, &u.k_i, 0); 783 }))); 784 } 785 786 static int seq_delete(struct bch_fs *c, u64 nr) 787 { 788 return bch2_btree_delete_range(c, BTREE_ID_xattrs, 789 SPOS(0, 0, U32_MAX), 790 POS(0, U64_MAX), 791 0, NULL); 792 } 793 794 typedef int (*perf_test_fn)(struct bch_fs *, u64); 795 796 struct test_job { 797 struct bch_fs *c; 798 u64 nr; 799 unsigned nr_threads; 800 perf_test_fn fn; 801 802 atomic_t ready; 803 wait_queue_head_t ready_wait; 804 805 atomic_t done; 806 struct completion done_completion; 807 808 u64 start; 809 u64 finish; 810 int ret; 811 }; 812 813 static int btree_perf_test_thread(void *data) 814 { 815 struct test_job *j = data; 816 int ret; 817 818 if (atomic_dec_and_test(&j->ready)) { 819 wake_up(&j->ready_wait); 820 j->start = sched_clock(); 821 } else { 822 wait_event(j->ready_wait, !atomic_read(&j->ready)); 823 } 824 825 ret = j->fn(j->c, div64_u64(j->nr, j->nr_threads)); 826 if (ret) { 827 bch_err(j->c, "%ps: error %s", j->fn, bch2_err_str(ret)); 828 j->ret = ret; 829 } 830 831 if (atomic_dec_and_test(&j->done)) { 832 j->finish = sched_clock(); 833 complete(&j->done_completion); 834 } 835 836 return 0; 837 } 838 839 int bch2_btree_perf_test(struct bch_fs *c, const char *testname, 840 u64 nr, unsigned nr_threads) 841 { 842 struct test_job j = { .c = c, .nr = nr, .nr_threads = nr_threads }; 843 char name_buf[20]; 844 struct printbuf nr_buf = PRINTBUF; 845 struct printbuf per_sec_buf = PRINTBUF; 846 unsigned i; 847 u64 time; 848 849 atomic_set(&j.ready, nr_threads); 850 init_waitqueue_head(&j.ready_wait); 851 852 atomic_set(&j.done, nr_threads); 853 init_completion(&j.done_completion); 854 855 #define perf_test(_test) \ 856 if (!strcmp(testname, #_test)) j.fn = _test 857 858 perf_test(rand_insert); 859 perf_test(rand_insert_multi); 860 perf_test(rand_lookup); 861 perf_test(rand_mixed); 862 perf_test(rand_delete); 863 864 perf_test(seq_insert); 865 perf_test(seq_lookup); 866 perf_test(seq_overwrite); 867 perf_test(seq_delete); 868 869 /* a unit test, not a perf test: */ 870 perf_test(test_delete); 871 perf_test(test_delete_written); 872 perf_test(test_iterate); 873 perf_test(test_iterate_extents); 874 perf_test(test_iterate_slots); 875 perf_test(test_iterate_slots_extents); 876 perf_test(test_peek_end); 877 perf_test(test_peek_end_extents); 878 879 perf_test(test_extent_overwrite_front); 880 perf_test(test_extent_overwrite_back); 881 perf_test(test_extent_overwrite_middle); 882 perf_test(test_extent_overwrite_all); 883 perf_test(test_extent_create_overlapping); 884 885 perf_test(test_snapshots); 886 887 if (!j.fn) { 888 pr_err("unknown test %s", testname); 889 return -EINVAL; 890 } 891 892 //pr_info("running test %s:", testname); 893 894 if (nr_threads == 1) 895 btree_perf_test_thread(&j); 896 else 897 for (i = 0; i < nr_threads; i++) 898 kthread_run(btree_perf_test_thread, &j, 899 "bcachefs perf test[%u]", i); 900 901 while (wait_for_completion_interruptible(&j.done_completion)) 902 ; 903 904 time = j.finish - j.start; 905 906 scnprintf(name_buf, sizeof(name_buf), "%s:", testname); 907 prt_human_readable_u64(&nr_buf, nr); 908 prt_human_readable_u64(&per_sec_buf, div64_u64(nr * NSEC_PER_SEC, time)); 909 printk(KERN_INFO "%-12s %s with %u threads in %5llu sec, %5llu nsec per iter, %5s per sec\n", 910 name_buf, nr_buf.buf, nr_threads, 911 div_u64(time, NSEC_PER_SEC), 912 div_u64(time * nr_threads, nr), 913 per_sec_buf.buf); 914 printbuf_exit(&per_sec_buf); 915 printbuf_exit(&nr_buf); 916 return j.ret; 917 } 918 919 #endif /* CONFIG_BCACHEFS_TESTS */ 920