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 | |
11 | struct bch_logged_op_fn { |
12 | u8 type; |
13 | int (*resume)(struct btree_trans *, struct bkey_i *); |
14 | }; |
15 | |
16 | static 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 | |
25 | static 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 | |
33 | static 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(type: 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(s: &sk); |
45 | bch2_bkey_buf_reassemble(s: &sk, c, k); |
46 | |
47 | fn->resume(trans, sk.k); |
48 | |
49 | bch2_bkey_buf_exit(s: &sk, c); |
50 | |
51 | return trans_was_restarted(trans, restart_count); |
52 | } |
53 | |
54 | int 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 | |
65 | static 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 | |
81 | int 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 | |
87 | void 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 | |