1// SPDX-License-Identifier: GPL-2.0
2
3#include "bcachefs.h"
4#include "alloc_background.h"
5#include "backpointers.h"
6#include "btree_gc.h"
7#include "btree_node_scan.h"
8#include "ec.h"
9#include "fsck.h"
10#include "inode.h"
11#include "journal.h"
12#include "lru.h"
13#include "logged_ops.h"
14#include "rebalance.h"
15#include "recovery.h"
16#include "recovery_passes.h"
17#include "snapshot.h"
18#include "subvolume.h"
19#include "super.h"
20#include "super-io.h"
21
22const char * const bch2_recovery_passes[] = {
23#define x(_fn, ...) #_fn,
24 BCH_RECOVERY_PASSES()
25#undef x
26 NULL
27};
28
29static int bch2_check_allocations(struct bch_fs *c)
30{
31 return bch2_gc(c, true, false);
32}
33
34static int bch2_set_may_go_rw(struct bch_fs *c)
35{
36 struct journal_keys *keys = &c->journal_keys;
37
38 /*
39 * After we go RW, the journal keys buffer can't be modified (except for
40 * setting journal_key->overwritten: it will be accessed by multiple
41 * threads
42 */
43 move_gap(keys, keys->nr);
44
45 set_bit(nr: BCH_FS_may_go_rw, addr: &c->flags);
46
47 if (keys->nr || c->opts.fsck || !c->sb.clean || c->recovery_passes_explicit)
48 return bch2_fs_read_write_early(c);
49 return 0;
50}
51
52struct recovery_pass_fn {
53 int (*fn)(struct bch_fs *);
54 unsigned when;
55};
56
57static struct recovery_pass_fn recovery_pass_fns[] = {
58#define x(_fn, _id, _when) { .fn = bch2_##_fn, .when = _when },
59 BCH_RECOVERY_PASSES()
60#undef x
61};
62
63static const u8 passes_to_stable_map[] = {
64#define x(n, id, ...) [BCH_RECOVERY_PASS_##n] = BCH_RECOVERY_PASS_STABLE_##n,
65 BCH_RECOVERY_PASSES()
66#undef x
67};
68
69static enum bch_recovery_pass_stable bch2_recovery_pass_to_stable(enum bch_recovery_pass pass)
70{
71 return passes_to_stable_map[pass];
72}
73
74u64 bch2_recovery_passes_to_stable(u64 v)
75{
76 u64 ret = 0;
77 for (unsigned i = 0; i < ARRAY_SIZE(passes_to_stable_map); i++)
78 if (v & BIT_ULL(i))
79 ret |= BIT_ULL(passes_to_stable_map[i]);
80 return ret;
81}
82
83u64 bch2_recovery_passes_from_stable(u64 v)
84{
85 static const u8 map[] = {
86#define x(n, id, ...) [BCH_RECOVERY_PASS_STABLE_##n] = BCH_RECOVERY_PASS_##n,
87 BCH_RECOVERY_PASSES()
88#undef x
89 };
90
91 u64 ret = 0;
92 for (unsigned i = 0; i < ARRAY_SIZE(map); i++)
93 if (v & BIT_ULL(i))
94 ret |= BIT_ULL(map[i]);
95 return ret;
96}
97
98/*
99 * For when we need to rewind recovery passes and run a pass we skipped:
100 */
101int bch2_run_explicit_recovery_pass(struct bch_fs *c,
102 enum bch_recovery_pass pass)
103{
104 if (c->recovery_passes_explicit & BIT_ULL(pass))
105 return 0;
106
107 bch_info(c, "running explicit recovery pass %s (%u), currently at %s (%u)",
108 bch2_recovery_passes[pass], pass,
109 bch2_recovery_passes[c->curr_recovery_pass], c->curr_recovery_pass);
110
111 c->recovery_passes_explicit |= BIT_ULL(pass);
112
113 if (c->curr_recovery_pass >= pass) {
114 c->curr_recovery_pass = pass;
115 c->recovery_passes_complete &= (1ULL << pass) >> 1;
116 return -BCH_ERR_restart_recovery;
117 } else {
118 return 0;
119 }
120}
121
122int bch2_run_explicit_recovery_pass_persistent(struct bch_fs *c,
123 enum bch_recovery_pass pass)
124{
125 enum bch_recovery_pass_stable s = bch2_recovery_pass_to_stable(pass);
126
127 mutex_lock(&c->sb_lock);
128 struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
129
130 if (!test_bit_le64(bit: s, addr: ext->recovery_passes_required)) {
131 __set_bit_le64(bit: s, addr: ext->recovery_passes_required);
132 bch2_write_super(c);
133 }
134 mutex_unlock(lock: &c->sb_lock);
135
136 return bch2_run_explicit_recovery_pass(c, pass);
137}
138
139static void bch2_clear_recovery_pass_required(struct bch_fs *c,
140 enum bch_recovery_pass pass)
141{
142 enum bch_recovery_pass_stable s = bch2_recovery_pass_to_stable(pass);
143
144 mutex_lock(&c->sb_lock);
145 struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
146
147 if (test_bit_le64(bit: s, addr: ext->recovery_passes_required)) {
148 __clear_bit_le64(bit: s, addr: ext->recovery_passes_required);
149 bch2_write_super(c);
150 }
151 mutex_unlock(lock: &c->sb_lock);
152}
153
154u64 bch2_fsck_recovery_passes(void)
155{
156 u64 ret = 0;
157
158 for (unsigned i = 0; i < ARRAY_SIZE(recovery_pass_fns); i++)
159 if (recovery_pass_fns[i].when & PASS_FSCK)
160 ret |= BIT_ULL(i);
161 return ret;
162}
163
164static bool should_run_recovery_pass(struct bch_fs *c, enum bch_recovery_pass pass)
165{
166 struct recovery_pass_fn *p = recovery_pass_fns + pass;
167
168 if (c->recovery_passes_explicit & BIT_ULL(pass))
169 return true;
170 if ((p->when & PASS_FSCK) && c->opts.fsck)
171 return true;
172 if ((p->when & PASS_UNCLEAN) && !c->sb.clean)
173 return true;
174 if (p->when & PASS_ALWAYS)
175 return true;
176 return false;
177}
178
179static int bch2_run_recovery_pass(struct bch_fs *c, enum bch_recovery_pass pass)
180{
181 struct recovery_pass_fn *p = recovery_pass_fns + pass;
182 int ret;
183
184 if (!(p->when & PASS_SILENT))
185 bch2_print(c, KERN_INFO bch2_log_msg(c, "%s..."),
186 bch2_recovery_passes[pass]);
187 ret = p->fn(c);
188 if (ret)
189 return ret;
190 if (!(p->when & PASS_SILENT))
191 bch2_print(c, KERN_CONT " done\n");
192
193 return 0;
194}
195
196int bch2_run_online_recovery_passes(struct bch_fs *c)
197{
198 int ret = 0;
199
200 for (unsigned i = 0; i < ARRAY_SIZE(recovery_pass_fns); i++) {
201 struct recovery_pass_fn *p = recovery_pass_fns + i;
202
203 if (!(p->when & PASS_ONLINE))
204 continue;
205
206 ret = bch2_run_recovery_pass(c, pass: i);
207 if (bch2_err_matches(ret, BCH_ERR_restart_recovery)) {
208 i = c->curr_recovery_pass;
209 continue;
210 }
211 if (ret)
212 break;
213 }
214
215 return ret;
216}
217
218int bch2_run_recovery_passes(struct bch_fs *c)
219{
220 int ret = 0;
221
222 while (c->curr_recovery_pass < ARRAY_SIZE(recovery_pass_fns)) {
223 if (c->opts.recovery_pass_last &&
224 c->curr_recovery_pass > c->opts.recovery_pass_last)
225 break;
226
227 if (should_run_recovery_pass(c, pass: c->curr_recovery_pass)) {
228 unsigned pass = c->curr_recovery_pass;
229
230 ret = bch2_run_recovery_pass(c, pass: c->curr_recovery_pass);
231 if (bch2_err_matches(ret, BCH_ERR_restart_recovery) ||
232 (ret && c->curr_recovery_pass < pass))
233 continue;
234 if (ret)
235 break;
236
237 c->recovery_passes_complete |= BIT_ULL(c->curr_recovery_pass);
238 }
239
240 c->recovery_pass_done = max(c->recovery_pass_done, c->curr_recovery_pass);
241
242 if (!test_bit(BCH_FS_error, &c->flags))
243 bch2_clear_recovery_pass_required(c, pass: c->curr_recovery_pass);
244
245 c->curr_recovery_pass++;
246 }
247
248 return ret;
249}
250

source code of linux/fs/bcachefs/recovery_passes.c