Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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 u32 restart_count = trans->restart_count;
38 struct printbuf buf = PRINTBUF;
39 int ret = 0;
40
41 fsck_err_on(test_bit(BCH_FS_clean_recovery, &c->flags),
42 trans, logged_op_but_clean,
43 "filesystem marked as clean but have logged op\n%s",
44 (bch2_bkey_val_to_text(&buf, c, k),
45 buf.buf));
46
47 struct bkey_buf sk;
48 bch2_bkey_buf_init(&sk);
49 bch2_bkey_buf_reassemble(&sk, c, k);
50
51 const struct bch_logged_op_fn *fn = logged_op_fn(sk.k->k.type);
52 if (fn)
53 fn->resume(trans, sk.k);
54
55 ret = bch2_logged_op_finish(trans, sk.k);
56
57 bch2_bkey_buf_exit(&sk, c);
58fsck_err:
59 printbuf_exit(&buf);
60 return ret ?: trans_was_restarted(trans, restart_count);
61}
62
63int bch2_resume_logged_ops(struct bch_fs *c)
64{
65 int ret = bch2_trans_run(c,
66 for_each_btree_key(trans, iter,
67 BTREE_ID_logged_ops, POS_MIN,
68 BTREE_ITER_prefetch, k,
69 resume_logged_op(trans, &iter, k)));
70 bch_err_fn(c, ret);
71 return ret;
72}
73
74static int __bch2_logged_op_start(struct btree_trans *trans, struct bkey_i *k)
75{
76 struct btree_iter iter;
77 int ret;
78
79 ret = bch2_bkey_get_empty_slot(trans, &iter, BTREE_ID_logged_ops, POS_MAX);
80 if (ret)
81 return ret;
82
83 k->k.p = iter.pos;
84
85 ret = bch2_trans_update(trans, &iter, k, 0);
86 bch2_trans_iter_exit(trans, &iter);
87 return ret;
88}
89
90int bch2_logged_op_start(struct btree_trans *trans, struct bkey_i *k)
91{
92 return commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
93 __bch2_logged_op_start(trans, k));
94}
95
96int bch2_logged_op_finish(struct btree_trans *trans, struct bkey_i *k)
97{
98 int ret = commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
99 bch2_btree_delete(trans, BTREE_ID_logged_ops, k->k.p, 0));
100 /*
101 * This needs to be a fatal error because we've left an unfinished
102 * operation in the logged ops btree.
103 *
104 * We should only ever see an error here if the filesystem has already
105 * been shut down, but make sure of that here:
106 */
107 if (ret) {
108 struct bch_fs *c = trans->c;
109 struct printbuf buf = PRINTBUF;
110
111 bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(k));
112 bch2_fs_fatal_error(c, "deleting logged operation %s: %s",
113 buf.buf, bch2_err_str(ret));
114 printbuf_exit(&buf);
115 }
116
117 return ret;
118}