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 | |
22 | const char * const bch2_recovery_passes[] = { |
23 | #define x(_fn, ...) #_fn, |
24 | BCH_RECOVERY_PASSES() |
25 | #undef x |
26 | NULL |
27 | }; |
28 | |
29 | static int bch2_check_allocations(struct bch_fs *c) |
30 | { |
31 | return bch2_gc(c, true, false); |
32 | } |
33 | |
34 | static 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 | |
52 | struct recovery_pass_fn { |
53 | int (*fn)(struct bch_fs *); |
54 | unsigned when; |
55 | }; |
56 | |
57 | static 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 | |
63 | static 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 | |
69 | static 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 | |
74 | u64 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 | |
83 | u64 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 | */ |
101 | int 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 | |
122 | int 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 | |
139 | static 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 | |
154 | u64 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 | |
164 | static 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 | |
179 | static 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 | |
196 | int 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 | |
218 | int 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 | |