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