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