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