blob: 60e00702d1a46e41dc3cc0b5818fd108b8487ca6 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
#include "bcachefs.h"
#include "bkey_buf.h"
#include "btree_update.h"
#include "error.h"
#include "io_misc.h"
#include "logged_ops.h"
#include "super.h"
struct bch_logged_op_fn {
u8 type;
int (*resume)(struct btree_trans *, struct bkey_i *);
};
static const struct bch_logged_op_fn logged_op_fns[] = {
#define x(n) { \
.type = KEY_TYPE_logged_op_##n, \
.resume = bch2_resume_logged_op_##n, \
},
BCH_LOGGED_OPS()
#undef x
};
static const struct bch_logged_op_fn *logged_op_fn(enum bch_bkey_type type)
{
for (unsigned i = 0; i < ARRAY_SIZE(logged_op_fns); i++)
if (logged_op_fns[i].type == type)
return logged_op_fns + i;
return NULL;
}
static int resume_logged_op(struct btree_trans *trans, struct btree_iter *iter,
struct bkey_s_c k)
{
struct bch_fs *c = trans->c;
u32 restart_count = trans->restart_count;
struct printbuf buf = PRINTBUF;
int ret = 0;
fsck_err_on(test_bit(BCH_FS_clean_recovery, &c->flags),
trans, logged_op_but_clean,
"filesystem marked as clean but have logged op\n%s",
(bch2_bkey_val_to_text(&buf, c, k),
buf.buf));
struct bkey_buf sk;
bch2_bkey_buf_init(&sk);
bch2_bkey_buf_reassemble(&sk, c, k);
const struct bch_logged_op_fn *fn = logged_op_fn(sk.k->k.type);
if (fn)
fn->resume(trans, sk.k);
ret = bch2_logged_op_finish(trans, sk.k);
bch2_bkey_buf_exit(&sk, c);
fsck_err:
printbuf_exit(&buf);
return ret ?: trans_was_restarted(trans, restart_count);
}
int bch2_resume_logged_ops(struct bch_fs *c)
{
int ret = bch2_trans_run(c,
for_each_btree_key(trans, iter,
BTREE_ID_logged_ops, POS_MIN,
BTREE_ITER_prefetch, k,
resume_logged_op(trans, &iter, k)));
bch_err_fn(c, ret);
return ret;
}
static int __bch2_logged_op_start(struct btree_trans *trans, struct bkey_i *k)
{
struct btree_iter iter;
int ret;
ret = bch2_bkey_get_empty_slot(trans, &iter, BTREE_ID_logged_ops, POS_MAX);
if (ret)
return ret;
k->k.p = iter.pos;
ret = bch2_trans_update(trans, &iter, k, 0);
bch2_trans_iter_exit(trans, &iter);
return ret;
}
int bch2_logged_op_start(struct btree_trans *trans, struct bkey_i *k)
{
return commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
__bch2_logged_op_start(trans, k));
}
int bch2_logged_op_finish(struct btree_trans *trans, struct bkey_i *k)
{
int ret = commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
bch2_btree_delete(trans, BTREE_ID_logged_ops, k->k.p, 0));
/*
* This needs to be a fatal error because we've left an unfinished
* operation in the logged ops btree.
*
* We should only ever see an error here if the filesystem has already
* been shut down, but make sure of that here:
*/
if (ret) {
struct bch_fs *c = trans->c;
struct printbuf buf = PRINTBUF;
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(k));
bch2_fs_fatal_error(c, "deleting logged operation %s: %s",
buf.buf, bch2_err_str(ret));
printbuf_exit(&buf);
}
return ret;
}