// SPDX-License-Identifier: GPL-2.0
#ifdef CONFIG_BCACHEFS_TESTS
#include "bcachefs.h"
#include "btree_update.h"
#include "journal_reclaim.h"
#include "subvolume.h"
#include "tests.h"
#include "linux/kthread.h"
#include "linux/random.h"
static void delete_test_keys(struct bch_fs *c)
{
int ret;
ret = bch2_btree_delete_range(c, BTREE_ID_extents,
SPOS(0, 0, U32_MAX), SPOS_MAX,
0,
NULL);
BUG_ON(ret);
ret = bch2_btree_delete_range(c, BTREE_ID_xattrs,
SPOS(0, 0, U32_MAX), SPOS_MAX,
0, NULL);
BUG_ON(ret);
}
/* unit tests */
static int test_delete(struct bch_fs *c, u64 nr)
{
struct btree_trans trans;
struct btree_iter iter;
struct bkey_i_cookie k;
int ret;
bkey_cookie_init(&k.k_i);
k.k.p.snapshot = U32_MAX;
bch2_trans_init(&trans, c, 0, 0);
bch2_trans_iter_init(&trans, &iter, BTREE_ID_xattrs, k.k.p,
BTREE_ITER_INTENT);
ret = commit_do(&trans, NULL, NULL, 0,
bch2_btree_iter_traverse(&iter) ?:
bch2_trans_update(&trans, &iter, &k.k_i, 0));
if (ret) {
bch_err(c, "update error in test_delete: %s", bch2_err_str(ret));
goto err;
}
pr_info("deleting once");
ret = commit_do(&trans, NULL, NULL, 0,
bch2_btree_iter_traverse(&iter) ?:
bch2_btree_delete_at(&trans, &iter, 0));
if (ret) {
bch_err(c, "delete error (first) in test_delete: %s", bch2_err_str(ret));
goto err;
}
pr_info("deleting twice");
ret = commit_do(&trans, NULL, NULL, 0,
bch2_btree_iter_traverse(&iter) ?:
bch2_btree_delete_at(&trans, &iter, 0));
if (ret) {
bch_err(c, "delete error (second) in test_delete: %s", bch2_err_str(ret));
goto err;
}
err:
bch2_trans_iter_exit(&trans, &iter);
bch2_trans_exit(&trans);
return ret;
}
static int test_delete_written(struct bch_fs *c, u64 nr)
{
struct btree_trans trans;
struct btree_iter iter;
struct bkey_i_cookie k;
int ret;
bkey_cookie_init(&k.k_i);
k.k.p.snapshot = U32_MAX;
bch2_trans_init(&trans, c, 0, 0);
bch2_trans_iter_init(&trans, &iter, BTREE_ID_xattrs, k.k.p,
BTREE_ITER_INTENT);
ret = commit_do(&trans, NULL, NULL, 0,
bch2_btree_iter_traverse(&iter) ?:
bch2_trans_update(&trans, &iter, &k.k_i, 0));
if (ret) {
bch_err(c, "update error in test_delete_written: %s", bch2_err_str(ret));
goto err;
}
bch2_trans_unlock(&trans);
bch2_journal_flush_all_pins(&c->journal);
ret = commit_do(&trans, NULL, NULL, 0,
bch2_btree_iter_traverse(&iter) ?:
bch2_btree_delete_at(&trans, &iter, 0));
if (ret) {
bch_err(c, "delete error in test_delete_written: %s", bch2_err_str(ret));
goto err;
}
err:
bch2_trans_iter_exit(&trans, &iter);
bch2_trans_exit(&trans);
return ret;
}
static int test_iterate(struct bch_fs *c, u64 nr)
{
struct btree_trans trans;
struct btree_iter iter = { NULL };
struct bkey_s_c k;
u64 i;
int ret = 0;
bch2_trans_init(&trans, c, 0, 0);
delete_test_keys(c);
pr_info("inserting test keys");
for (i = 0; i < nr; i++) {
struct bkey_i_cookie k;
bkey_cookie_init(&k.k_i);
k.k.p.offset = i;
k.k.p.snapshot = U32_MAX;
ret = bch2_btree_insert(c, BTREE_ID_xattrs, &k.k_i,
NULL, NULL, 0);
if (ret) {
bch_err(c, "insert error in test_iterate: %s", bch2_err_str(ret));
goto err;
}
}
pr_info("iterating forwards");
i = 0;
for_each_btree_key(&trans, iter, BTREE_ID_xattrs,
SPOS(0, 0, U32_MAX), 0, k, ret) {
if (k.k->p.inode)
break;
BUG_ON(k.k->p.offset != i++);
}
BUG_ON(i != nr);
pr_info("iterating backwards");
while (!IS_ERR_OR_NULL((k = bch2_btree_iter_prev(&iter)).k))
BUG_ON(k.k->p.offset != --i);
BUG_ON(i);
err:
bch2_trans_iter_exit(&trans, &iter);
bch2_trans_exit(&trans);
return ret;
}
static int test_iterate_extents(struct bch_fs *c, u64 nr)
{
struct btree_trans trans;
struct btree_iter iter = { NULL };
struct bkey_s_c k;
u64 i;
int ret = 0;
bch2_trans_init(&trans, c, 0, 0);
delete_test_keys(c);
pr_info("inserting test extents");
for (i = 0; i < nr; i +=