1// SPDX-License-Identifier: GPL-2.0
2
3#include "bcachefs.h"
4#include "bkey_buf.h"
5#include "btree_update.h"
6#include "error.h"
7#include "io_misc.h"
8#include "logged_ops.h"
9#include "super.h"
10
11struct bch_logged_op_fn {
12	u8		type;
13	int		(*resume)(struct btree_trans *, struct bkey_i *);
14};
15
16static const struct bch_logged_op_fn logged_op_fns[] = {
17#define x(n)		{					\
18	.type		= KEY_TYPE_logged_op_##n,		\
19	.resume		= bch2_resume_logged_op_##n,		\
20},
21	BCH_LOGGED_OPS()
22#undef x
23};
24
25static const struct bch_logged_op_fn *logged_op_fn(enum bch_bkey_type type)
26{
27	for (unsigned i = 0; i < ARRAY_SIZE(logged_op_fns); i++)
28		if (logged_op_fns[i].type == type)
29			return logged_op_fns + i;
30	return NULL;
31}
32
33static int resume_logged_op(struct btree_trans *trans, struct btree_iter *iter,
34			    struct bkey_s_c k)
35{
36	struct bch_fs *c = trans->c;
37	const struct bch_logged_op_fn *fn = logged_op_fn(k.k->type);
38	struct bkey_buf sk;
39	u32 restart_count = trans->restart_count;
40
41	if (!fn)
42		return 0;
43
44	bch2_bkey_buf_init(&sk);
45	bch2_bkey_buf_reassemble(&sk, c, k);
46
47	fn->resume(trans, sk.k);
48
49	bch2_bkey_buf_exit(&sk, c);
50
51	return trans_was_restarted(trans, restart_count);
52}
53
54int bch2_resume_logged_ops(struct bch_fs *c)
55{
56	int ret = bch2_trans_run(c,
57		for_each_btree_key(trans, iter,
58				   BTREE_ID_logged_ops, POS_MIN,
59				   BTREE_ITER_PREFETCH, k,
60			resume_logged_op(trans, &iter, k)));
61	bch_err_fn(c, ret);
62	return ret;
63}
64
65static int __bch2_logged_op_start(struct btree_trans *trans, struct bkey_i *k)
66{
67	struct btree_iter iter;
68	int ret;
69
70	ret = bch2_bkey_get_empty_slot(trans, &iter, BTREE_ID_logged_ops, POS_MAX);
71	if (ret)
72		return ret;
73
74	k->k.p = iter.pos;
75
76	ret = bch2_trans_update(trans, &iter, k, 0);
77	bch2_trans_iter_exit(trans, &iter);
78	return ret;
79}
80
81int bch2_logged_op_start(struct btree_trans *trans, struct bkey_i *k)
82{
83	return commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
84			 __bch2_logged_op_start(trans, k));
85}
86
87void bch2_logged_op_finish(struct btree_trans *trans, struct bkey_i *k)
88{
89	int ret = commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
90			    bch2_btree_delete(trans, BTREE_ID_logged_ops, k->k.p, 0));
91	/*
92	 * This needs to be a fatal error because we've left an unfinished
93	 * operation in the logged ops btree.
94	 *
95	 * We should only ever see an error here if the filesystem has already
96	 * been shut down, but make sure of that here:
97	 */
98	if (ret) {
99		struct bch_fs *c = trans->c;
100		struct printbuf buf = PRINTBUF;
101
102		bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(k));
103		bch2_fs_fatal_error(c, "deleting logged operation %s: %s",
104				    buf.buf, bch2_err_str(ret));
105		printbuf_exit(&buf);
106	}
107}
108