if (rw == WRITE &&
            !(flags & BKEY_INVALID_JOURNAL) &&
-           test_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags)) {
+           c->curr_recovery_pass > BCH_RECOVERY_PASS_check_btree_backpointers) {
                unsigned i, bp_len = 0;
 
                for (i = 0; i < BCH_ALLOC_V4_NR_BACKPOINTERS(a.v); i++)
                        }
 
                        if (!a.v->io_time[READ] &&
-                           test_bit(BCH_FS_CHECK_ALLOC_TO_LRU_REFS_DONE, &c->flags)) {
+                           c->curr_recovery_pass > BCH_RECOVERY_PASS_check_alloc_to_lru_refs) {
                                prt_printf(err, "cached bucket with read_time == 0");
                                return -BCH_ERR_invalid_bkey;
                        }
                return ret;
 
        if (ca->mi.freespace_initialized &&
-           test_bit(BCH_FS_CHECK_ALLOC_DONE, &c->flags) &&
+           c->curr_recovery_pass > BCH_RECOVERY_PASS_check_alloc_info &&
            bch2_trans_inconsistent_on(old.k->type != old_type, trans,
                        "incorrect key when %s %s:%llu:%llu:0 (got %s should be %s)\n"
                        "  for %s",
        }
 
        if (a->v.journal_seq > c->journal.flushed_seq_ondisk) {
-               if (test_bit(BCH_FS_CHECK_ALLOC_DONE, &c->flags)) {
+               if (c->curr_recovery_pass > BCH_RECOVERY_PASS_check_alloc_info) {
                        bch2_trans_inconsistent(trans,
                                "clearing need_discard but journal_seq %llu > flushed_seq %llu\n"
                                "%s",
        }
 
        if (a->v.data_type != BCH_DATA_need_discard) {
-               if (test_bit(BCH_FS_CHECK_ALLOC_DONE, &c->flags)) {
+               if (c->curr_recovery_pass > BCH_RECOVERY_PASS_check_alloc_info) {
                        bch2_trans_inconsistent(trans,
                                "bucket incorrectly set in need_discard btree\n"
                                "%s",
                bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&a->k_i));
 
        bch_err(c, "%s", buf.buf);
-       if (test_bit(BCH_FS_CHECK_LRUS_DONE, &c->flags)) {
+       if (c->curr_recovery_pass > BCH_RECOVERY_PASS_check_lrus) {
                bch2_inconsistent_error(c);
                ret = -EINVAL;
        }
 
        a = bch2_alloc_to_v4(k, &a_convert);
 
        if (a->data_type != BCH_DATA_free) {
-               if (!test_bit(BCH_FS_CHECK_ALLOC_DONE, &c->flags)) {
+               if (c->curr_recovery_pass <= BCH_RECOVERY_PASS_check_alloc_info) {
                        ob = NULL;
                        goto err;
                }
        }
 
        if (genbits != (alloc_freespace_genbits(*a) >> 56) &&
-           test_bit(BCH_FS_CHECK_ALLOC_DONE, &c->flags)) {
+           c->curr_recovery_pass > BCH_RECOVERY_PASS_check_alloc_info) {
                prt_printf(&buf, "bucket in freespace btree with wrong genbits (got %u should be %llu)\n"
                       "  freespace key ",
                       genbits, alloc_freespace_genbits(*a) >> 56);
                bch2_trans_inconsistent(trans, "%s", buf.buf);
                ob = ERR_PTR(-EIO);
                goto err;
-
        }
 
-       if (!test_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags)) {
+       if (c->curr_recovery_pass <= BCH_RECOVERY_PASS_check_extents_to_backpointers) {
                struct bch_backpointer bp;
                struct bpos bp_pos = POS_MIN;
 
        if (s.skipped_need_journal_commit * 2 > avail)
                bch2_journal_flush_async(&c->journal, NULL);
 
-       if (!ob && freespace && !test_bit(BCH_FS_CHECK_ALLOC_DONE, &c->flags)) {
+       if (!ob && freespace && c->curr_recovery_pass <= BCH_RECOVERY_PASS_check_alloc_info) {
                freespace = false;
                goto alloc;
        }
 
                bch2_bkey_val_to_text(&buf, c, orig_k);
 
                bch_err(c, "%s", buf.buf);
-       } else if (test_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags)) {
+       } else if (c->curr_recovery_pass > BCH_RECOVERY_PASS_check_extents_to_backpointers) {
                prt_printf(&buf, "backpointer not found when deleting");
                prt_newline(&buf);
                printbuf_indent_add(&buf, 2);
 
        printbuf_exit(&buf);
 
-       if (test_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags)) {
+       if (c->curr_recovery_pass > BCH_RECOVERY_PASS_check_extents_to_backpointers) {
                bch2_inconsistent_error(c);
                return -EIO;
        } else {
        bch2_backpointer_to_text(&buf, &bp);
        prt_printf(&buf, "\n  ");
        bch2_bkey_val_to_text(&buf, c, k);
-       if (!test_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags))
+       if (c->curr_recovery_pass >= BCH_RECOVERY_PASS_check_extents_to_backpointers)
                bch_err_ratelimited(c, "%s", buf.buf);
        else
                bch2_trans_inconsistent(trans, "%s", buf.buf);
 
 
        /* fsck passes: */
        BCH_FS_TOPOLOGY_REPAIR_DONE,
-       BCH_FS_INITIAL_GC_DONE,         /* kill when we enumerate fsck passes */
-       BCH_FS_CHECK_ALLOC_DONE,
-       BCH_FS_CHECK_LRUS_DONE,
-       BCH_FS_CHECK_BACKPOINTERS_DONE,
-       BCH_FS_CHECK_ALLOC_TO_LRU_REFS_DONE,
        BCH_FS_FSCK_DONE,
        BCH_FS_INITIAL_GC_UNFIXED,      /* kill when we enumerate fsck errors */
        BCH_FS_NEED_ANOTHER_GC,
        BCH_WRITE_REF_NR,
 };
 
+#define PASS_SILENT            BIT(0)
+#define PASS_FSCK              BIT(1)
+#define PASS_UNCLEAN           BIT(2)
+#define PASS_ALWAYS            BIT(3)
+#define PASS_UPGRADE(v)                ((v) << 4)
+
+#define BCH_RECOVERY_PASSES()                                                                  \
+       x(alloc_read,                   PASS_ALWAYS)                                            \
+       x(stripes_read,                 PASS_ALWAYS)                                            \
+       x(initialize_subvolumes,        PASS_UPGRADE(bcachefs_metadata_version_snapshot_2))     \
+       x(snapshots_read,               PASS_ALWAYS)                                            \
+       x(check_allocations,            PASS_FSCK)                                              \
+       x(set_may_go_rw,                PASS_ALWAYS|PASS_SILENT)                                \
+       x(journal_replay,               PASS_ALWAYS)                                            \
+       x(check_alloc_info,             PASS_FSCK)                                              \
+       x(check_lrus,                   PASS_FSCK)                                              \
+       x(check_btree_backpointers,     PASS_FSCK)                                              \
+       x(check_backpointers_to_extents,PASS_FSCK)                                              \
+       x(check_extents_to_backpointers,PASS_FSCK)                                              \
+       x(check_alloc_to_lru_refs,      PASS_FSCK)                                              \
+       x(fs_freespace_init,            PASS_ALWAYS|PASS_SILENT)                                \
+       x(bucket_gens_init,             PASS_UPGRADE(bcachefs_metadata_version_bucket_gens))    \
+       x(fs_upgrade_for_subvolumes,    PASS_UPGRADE(bcachefs_metadata_version_snapshot_2))     \
+       x(check_snapshot_trees,         PASS_FSCK)                                              \
+       x(check_snapshots,              PASS_FSCK)                                              \
+       x(check_subvols,                PASS_FSCK)                                              \
+       x(delete_dead_snapshots,        PASS_FSCK|PASS_UNCLEAN|PASS_SILENT)                     \
+       x(check_inodes,                 PASS_FSCK|PASS_UNCLEAN)                                 \
+       x(check_extents,                PASS_FSCK)                                              \
+       x(check_dirents,                PASS_FSCK)                                              \
+       x(check_xattrs,                 PASS_FSCK)                                              \
+       x(check_root,                   PASS_FSCK)                                              \
+       x(check_directory_structure,    PASS_FSCK)                                              \
+       x(check_nlinks,                 PASS_FSCK)                                              \
+       x(fix_reflink_p,                PASS_UPGRADE(bcachefs_metadata_version_reflink_p_fix))  \
+
+enum bch_recovery_pass {
+#define x(n, when)     BCH_RECOVERY_PASS_##n,
+       BCH_RECOVERY_PASSES()
+#undef x
+};
+
 struct bch_fs {
        struct closure          cl;
 
        /* RECOVERY */
        u64                     journal_replay_seq_start;
        u64                     journal_replay_seq_end;
+       enum bch_recovery_pass  curr_recovery_pass;
+
        /* DEBUG JUNK */
        struct dentry           *fs_debug_dir;
        struct dentry           *btree_debug_dir;
 
 {
        struct printbuf buf = PRINTBUF;
 
-       if (!test_bit(BCH_FS_INITIAL_GC_DONE, &c->flags))
+       if (c->curr_recovery_pass <= BCH_RECOVERY_PASS_check_allocations)
                return;
 
        prt_printf(&buf,
 
 
        if (IS_ENABLED(CONFIG_BCACHEFS_DEBUG) ||
            (BCH_SB_HAS_TOPOLOGY_ERRORS(c->disk_sb.sb) &&
-            !test_bit(BCH_FS_INITIAL_GC_DONE, &c->flags) &&
+            c->curr_recovery_pass <= BCH_RECOVERY_PASS_check_allocations &&
             c->opts.fix_errors != FSCK_OPT_NO)) {
                bch_info(c, "Starting topology repair pass");
                ret = bch2_repair_topology(c);
 
        if (ret == -BCH_ERR_need_topology_repair &&
            !test_bit(BCH_FS_TOPOLOGY_REPAIR_DONE, &c->flags) &&
-           !test_bit(BCH_FS_INITIAL_GC_DONE, &c->flags)) {
+           c->curr_recovery_pass <= BCH_RECOVERY_PASS_check_allocations) {
                set_bit(BCH_FS_NEED_ANOTHER_GC, &c->flags);
                SET_BCH_SB_HAS_TOPOLOGY_ERRORS(c->disk_sb.sb, true);
                ret = 0;
 
        }
 
        /*
-        * The check_dirents pass has already run, dangling dirents
+        * The bch2_check_dirents pass has already run, dangling dirents
         * shouldn't exist here:
         */
        return __lookup_inode(trans, inum, lostfound, &snapshot);
 }
 
 noinline_for_stack
-static int check_inodes(struct bch_fs *c, bool full)
+int bch2_check_inodes(struct bch_fs *c)
 {
+       bool full = c->opts.fsck;
        struct btree_trans trans;
        struct btree_iter iter;
        struct bch_inode_unpacked prev = { 0 };
  * Walk extents: verify that extents have a corresponding S_ISREG inode, and
  * that i_size an i_sectors are consistent
  */
-noinline_for_stack
-static int check_extents(struct bch_fs *c)
+int bch2_check_extents(struct bch_fs *c)
 {
        struct inode_walker w = inode_walker_init();
        struct snapshots_seen s;
        snapshots_seen_init(&s);
        bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
 
-       bch_verbose(c, "checking extents");
-
        ret = for_each_btree_key_commit(&trans, iter, BTREE_ID_extents,
                        POS(BCACHEFS_ROOT_INO, 0),
                        BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k,
  * Walk dirents: verify that they all have a corresponding S_ISDIR inode,
  * validate d_type
  */
-noinline_for_stack
-static int check_dirents(struct bch_fs *c)
+int bch2_check_dirents(struct bch_fs *c)
 {
        struct inode_walker dir = inode_walker_init();
        struct inode_walker target = inode_walker_init();
        struct bkey_s_c k;
        int ret = 0;
 
-       bch_verbose(c, "checking dirents");
-
        snapshots_seen_init(&s);
        bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
 
 /*
  * Walk xattrs: verify that they all have a corresponding inode
  */
-noinline_for_stack
-static int check_xattrs(struct bch_fs *c)
+int bch2_check_xattrs(struct bch_fs *c)
 {
        struct inode_walker inode = inode_walker_init();
        struct bch_hash_info hash_info;
        struct bkey_s_c k;
        int ret = 0;
 
-       bch_verbose(c, "checking xattrs");
-
        bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
 
        ret = for_each_btree_key_commit(&trans, iter, BTREE_ID_xattrs,
 }
 
 /* Get root directory, create if it doesn't exist: */
-noinline_for_stack
-static int check_root(struct bch_fs *c)
+int bch2_check_root(struct bch_fs *c)
 {
        int ret;
 
-       bch_verbose(c, "checking root directory");
-
        ret = bch2_trans_do(c, NULL, NULL,
                             BTREE_INSERT_NOFAIL|
                             BTREE_INSERT_LAZY_RW,
 
 /*
  * Check for unreachable inodes, as well as loops in the directory structure:
- * After check_dirents(), if an inode backpointer doesn't exist that means it's
+ * After bch2_check_dirents(), if an inode backpointer doesn't exist that means it's
  * unreachable:
  */
-noinline_for_stack
-static int check_directory_structure(struct bch_fs *c)
+int bch2_check_directory_structure(struct bch_fs *c)
 {
        struct btree_trans trans;
        struct btree_iter iter;
        return 0;
 }
 
-noinline_for_stack
-static int check_nlinks(struct bch_fs *c)
+int bch2_check_nlinks(struct bch_fs *c)
 {
        struct nlink_table links = { 0 };
        u64 this_iter_range_start, next_iter_range_start = 0;
        int ret = 0;
 
-       bch_verbose(c, "checking inode nlinks");
-
        do {
                this_iter_range_start = next_iter_range_start;
                next_iter_range_start = U64_MAX;
        return bch2_trans_update(trans, iter, &u->k_i, BTREE_TRIGGER_NORUN);
 }
 
-noinline_for_stack
-static int fix_reflink_p(struct bch_fs *c)
+int bch2_fix_reflink_p(struct bch_fs *c)
 {
        struct btree_iter iter;
        struct bkey_s_c k;
        if (c->sb.version >= bcachefs_metadata_version_reflink_p_fix)
                return 0;
 
-       bch_verbose(c, "fixing reflink_p keys");
-
        ret = bch2_trans_run(c,
                for_each_btree_key_commit(&trans, iter,
                                BTREE_ID_extents, POS_MIN,
                bch_err_fn(c, ret);
        return ret;
 }
-
-/*
- * Checks for inconsistencies that shouldn't happen, unless we have a bug.
- * Doesn't fix them yet, mainly because they haven't yet been observed:
- */
-int bch2_fsck_full(struct bch_fs *c)
-{
-       int ret;
-again:
-       ret =   bch2_fs_check_snapshot_trees(c);
-               bch2_fs_check_snapshots(c) ?:
-               bch2_fs_check_subvols(c) ?:
-               bch2_delete_dead_snapshots(c) ?:
-               check_inodes(c, true) ?:
-               check_extents(c) ?:
-               check_dirents(c) ?:
-               check_xattrs(c) ?:
-               check_root(c) ?:
-               check_directory_structure(c) ?:
-               check_nlinks(c) ?:
-               fix_reflink_p(c);
-
-       if (bch2_err_matches(ret, BCH_ERR_need_snapshot_cleanup)) {
-               set_bit(BCH_FS_HAVE_DELETED_SNAPSHOTS, &c->flags);
-               goto again;
-       }
-
-       return ret;
-}
-
-int bch2_fsck_walk_inodes_only(struct bch_fs *c)
-{
-       return  bch2_fs_check_snapshots(c) ?:
-               bch2_fs_check_subvols(c) ?:
-               bch2_delete_dead_snapshots(c) ?:
-               check_inodes(c, false);
-}
 
 #ifndef _BCACHEFS_FSCK_H
 #define _BCACHEFS_FSCK_H
 
-int bch2_fsck_full(struct bch_fs *);
-int bch2_fsck_walk_inodes_only(struct bch_fs *);
+int bch2_check_inodes(struct bch_fs *);
+int bch2_check_extents(struct bch_fs *);
+int bch2_check_dirents(struct bch_fs *);
+int bch2_check_xattrs(struct bch_fs *);
+int bch2_check_root(struct bch_fs *);
+int bch2_check_directory_structure(struct bch_fs *);
+int bch2_check_nlinks(struct bch_fs *);
+int bch2_fix_reflink_p(struct bch_fs *);
 
 #endif /* _BCACHEFS_FSCK_H */
 
        return ret;
 }
 
-static int bch2_fs_initialize_subvolumes(struct bch_fs *c)
+static int bch2_initialize_subvolumes(struct bch_fs *c)
 {
        struct bkey_i_snapshot_tree     root_tree;
        struct bkey_i_snapshot          root_snapshot;
        }
 }
 
+static int bch2_check_allocations(struct bch_fs *c)
+{
+       return bch2_gc(c, true, c->opts.norecovery);
+}
+
+static int bch2_set_may_go_rw(struct bch_fs *c)
+{
+       set_bit(BCH_FS_MAY_GO_RW, &c->flags);
+       return 0;
+}
+
+struct recovery_pass_fn {
+       int             (*fn)(struct bch_fs *);
+       const char      *name;
+       unsigned        when;
+};
+
+static struct recovery_pass_fn recovery_passes[] = {
+#define x(_fn, _when)  { .fn = bch2_##_fn, .name = #_fn, .when = _when },
+       BCH_RECOVERY_PASSES()
+#undef x
+};
+
+static bool should_run_recovery_pass(struct bch_fs *c, enum bch_recovery_pass pass)
+{
+       struct recovery_pass_fn *p = recovery_passes + c->curr_recovery_pass;
+
+       if (c->opts.norecovery && pass > BCH_RECOVERY_PASS_snapshots_read)
+               return false;
+       if ((p->when & PASS_FSCK) && c->opts.fsck)
+               return true;
+       if ((p->when & PASS_UNCLEAN) && !c->sb.clean)
+               return true;
+       if (p->when & PASS_ALWAYS)
+               return true;
+       if (p->when >= PASS_UPGRADE(0) &&
+           bch2_version_upgrading_to(c, p->when >> 4))
+               return true;
+       return false;
+}
+
+static int bch2_run_recovery_pass(struct bch_fs *c, enum bch_recovery_pass pass)
+{
+       int ret;
+
+       c->curr_recovery_pass = pass;
+
+       if (should_run_recovery_pass(c, pass)) {
+               struct recovery_pass_fn *p = recovery_passes + pass;
+
+               if (!(p->when & PASS_SILENT))
+                       printk(KERN_INFO bch2_log_msg(c, "%s..."), p->name);
+               ret = p->fn(c);
+               if (ret)
+                       return ret;
+               if (!(p->when & PASS_SILENT))
+                       printk(KERN_CONT " done\n");
+       }
+
+       return 0;
+}
+
+static int bch2_run_recovery_passes(struct bch_fs *c)
+{
+       int ret = 0;
+again:
+       while (c->curr_recovery_pass < ARRAY_SIZE(recovery_passes)) {
+               ret = bch2_run_recovery_pass(c, c->curr_recovery_pass);
+               if (ret)
+                       break;
+               c->curr_recovery_pass++;
+       }
+
+       if (bch2_err_matches(ret, BCH_ERR_need_snapshot_cleanup)) {
+               set_bit(BCH_FS_HAVE_DELETED_SNAPSHOTS, &c->flags);
+               c->curr_recovery_pass = BCH_RECOVERY_PASS_delete_dead_snapshots;
+               goto again;
+       }
+
+       return ret;
+}
+
 int bch2_fs_recovery(struct bch_fs *c)
 {
        struct bch_sb_field_clean *clean = NULL;
        if (ret)
                goto err;
 
-       bch_verbose(c, "starting alloc read");
-       ret = bch2_alloc_read(c);
-       if (ret)
-               goto err;
-       bch_verbose(c, "alloc read done");
-
-       bch_verbose(c, "starting stripes_read");
-       ret = bch2_stripes_read(c);
+       ret = bch2_run_recovery_passes(c);
        if (ret)
                goto err;
-       bch_verbose(c, "stripes_read done");
-
-       if (c->sb.version < bcachefs_metadata_version_snapshot_2) {
-               ret = bch2_fs_initialize_subvolumes(c);
-               if (ret)
-                       goto err;
-       }
-
-       bch_verbose(c, "reading snapshots table");
-       ret = bch2_fs_snapshots_start(c);
-       if (ret)
-               goto err;
-       bch_verbose(c, "reading snapshots done");
-
-       if (c->opts.fsck) {
-               bool metadata_only = c->opts.norecovery;
-
-               bch_info(c, "checking allocations");
-               ret = bch2_gc(c, true, metadata_only);
-               if (ret)
-                       goto err;
-               bch_verbose(c, "done checking allocations");
-
-               set_bit(BCH_FS_INITIAL_GC_DONE, &c->flags);
-
-               set_bit(BCH_FS_MAY_GO_RW, &c->flags);
-
-               bch_info(c, "starting journal replay, %zu keys", c->journal_keys.nr);
-               ret = bch2_journal_replay(c);
-               if (ret)
-                       goto err;
-               if (c->opts.verbose || !c->sb.clean)
-                       bch_info(c, "journal replay done");
-
-               bch_info(c, "checking need_discard and freespace btrees");
-               ret = bch2_check_alloc_info(c);
-               if (ret)
-                       goto err;
-               bch_verbose(c, "done checking need_discard and freespace btrees");
-
-               set_bit(BCH_FS_CHECK_ALLOC_DONE, &c->flags);
-
-               bch_info(c, "checking lrus");
-               ret = bch2_check_lrus(c);
-               if (ret)
-                       goto err;
-               bch_verbose(c, "done checking lrus");
-               set_bit(BCH_FS_CHECK_LRUS_DONE, &c->flags);
-
-               bch_info(c, "checking backpointers to alloc keys");
-               ret = bch2_check_btree_backpointers(c);
-               if (ret)
-                       goto err;
-               bch_verbose(c, "done checking backpointers to alloc keys");
-
-               bch_info(c, "checking backpointers to extents");
-               ret = bch2_check_backpointers_to_extents(c);
-               if (ret)
-                       goto err;
-               bch_verbose(c, "done checking backpointers to extents");
-
-               bch_info(c, "checking extents to backpointers");
-               ret = bch2_check_extents_to_backpointers(c);
-               if (ret)
-                       goto err;
-               bch_verbose(c, "done checking extents to backpointers");
-               set_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags);
-
-               bch_info(c, "checking alloc to lru refs");
-               ret = bch2_check_alloc_to_lru_refs(c);
-               if (ret)
-                       goto err;
-               bch_verbose(c, "done checking alloc to lru refs");
-               set_bit(BCH_FS_CHECK_ALLOC_TO_LRU_REFS_DONE, &c->flags);
-       } else {
-               set_bit(BCH_FS_INITIAL_GC_DONE, &c->flags);
-               set_bit(BCH_FS_CHECK_ALLOC_DONE, &c->flags);
-               set_bit(BCH_FS_CHECK_LRUS_DONE, &c->flags);
-               set_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags);
-               set_bit(BCH_FS_CHECK_ALLOC_TO_LRU_REFS_DONE, &c->flags);
-               set_bit(BCH_FS_FSCK_DONE, &c->flags);
-
-               if (c->opts.norecovery)
-                       goto out;
-
-               set_bit(BCH_FS_MAY_GO_RW, &c->flags);
-
-               bch_verbose(c, "starting journal replay, %zu keys", c->journal_keys.nr);
-               ret = bch2_journal_replay(c);
-               if (ret)
-                       goto err;
-               if (c->opts.verbose || !c->sb.clean)
-                       bch_info(c, "journal replay done");
-       }
-
-       ret = bch2_fs_freespace_init(c);
-       if (ret)
-               goto err;
-
-       if (bch2_version_upgrading_to(c, bcachefs_metadata_version_bucket_gens)) {
-               bch_info(c, "initializing bucket_gens");
-               ret = bch2_bucket_gens_init(c);
-               if (ret)
-                       goto err;
-               bch_verbose(c, "bucket_gens init done");
-       }
-
-       if (bch2_version_upgrading_to(c, bcachefs_metadata_version_snapshot_2)) {
-               ret = bch2_fs_upgrade_for_subvolumes(c);
-               if (ret)
-                       goto err;
-       }
-
-       if (c->opts.fsck) {
-               ret = bch2_fsck_full(c);
-               if (ret)
-                       goto err;
-               bch_verbose(c, "fsck done");
-       } else if (!c->sb.clean) {
-               bch_verbose(c, "checking for deleted inodes");
-               ret = bch2_fsck_walk_inodes_only(c);
-               if (ret)
-                       goto err;
-               bch_verbose(c, "check inodes done");
-       }
 
        if (enabled_qtypes(c)) {
                bch_verbose(c, "reading quotas");
        }
        mutex_unlock(&c->sb_lock);
 
-       set_bit(BCH_FS_INITIAL_GC_DONE, &c->flags);
-       set_bit(BCH_FS_CHECK_LRUS_DONE, &c->flags);
-       set_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags);
-       set_bit(BCH_FS_CHECK_ALLOC_TO_LRU_REFS_DONE, &c->flags);
+       c->curr_recovery_pass = ARRAY_SIZE(recovery_passes);
        set_bit(BCH_FS_MAY_GO_RW, &c->flags);
        set_bit(BCH_FS_FSCK_DONE, &c->flags);
 
        if (ret)
                goto err;
 
-       ret = bch2_fs_initialize_subvolumes(c);
+       ret = bch2_initialize_subvolumes(c);
        if (ret)
                goto err;
 
        bch_verbose(c, "reading snapshots table");
-       ret = bch2_fs_snapshots_start(c);
+       ret = bch2_snapshots_read(c);
        if (ret)
                goto err;
        bch_verbose(c, "reading snapshots done");
 
  * And, make sure it points to a subvolume within that snapshot tree, or correct
  * it to point to the oldest subvolume within that snapshot tree.
  */
-int bch2_fs_check_snapshot_trees(struct bch_fs *c)
+int bch2_check_snapshot_trees(struct bch_fs *c)
 {
        struct btree_iter iter;
        struct bkey_s_c k;
        return ret;
 }
 
-int bch2_fs_check_snapshots(struct bch_fs *c)
+int bch2_check_snapshots(struct bch_fs *c)
 {
        struct btree_iter iter;
        struct bkey_s_c k;
        return ret;
 }
 
-int bch2_fs_check_subvols(struct bch_fs *c)
+int bch2_check_subvols(struct bch_fs *c)
 {
        struct btree_iter iter;
        struct bkey_s_c k;
        genradix_free(&c->snapshots);
 }
 
-int bch2_fs_snapshots_start(struct bch_fs *c)
+int bch2_snapshots_read(struct bch_fs *c)
 {
        struct btree_iter iter;
        struct bkey_s_c k;
 
        set_bit(BCH_FS_HAVE_DELETED_SNAPSHOTS, &c->flags);
 
-       if (!test_bit(BCH_FS_FSCK_DONE, &c->flags))
+       if (c->curr_recovery_pass <= BCH_RECOVERY_PASS_delete_dead_snapshots)
                return 0;
 
        bch2_delete_dead_snapshots_async(c);
 
        return ret;
 }
 
-int bch2_fs_check_snapshot_trees(struct bch_fs *);
-int bch2_fs_check_snapshots(struct bch_fs *);
-int bch2_fs_check_subvols(struct bch_fs *);
+int bch2_check_snapshot_trees(struct bch_fs *);
+int bch2_check_snapshots(struct bch_fs *);
+int bch2_check_subvols(struct bch_fs *);
 
 void bch2_fs_snapshots_exit(struct bch_fs *);
-int bch2_fs_snapshots_start(struct bch_fs *);
+int bch2_snapshots_read(struct bch_fs *);
 
 int bch2_subvolume_invalid(const struct bch_fs *, struct bkey_s_c,
                           unsigned, struct printbuf *);