// 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; }