diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h index 88a1782b2a0e..d8c020644f54 100644 --- a/fs/bcachefs/bcachefs.h +++ b/fs/bcachefs/bcachefs.h @@ -660,12 +660,11 @@ enum bch_write_ref { #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(initialize_subvolumes, 0) \ x(snapshots_read, PASS_ALWAYS) \ x(check_allocations, PASS_FSCK) \ x(set_may_go_rw, PASS_ALWAYS|PASS_SILENT) \ @@ -677,8 +676,8 @@ enum bch_write_ref { 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(bucket_gens_init, 0) \ + x(fs_upgrade_for_subvolumes, 0) \ x(check_snapshot_trees, PASS_FSCK) \ x(check_snapshots, PASS_FSCK) \ x(check_subvols, PASS_FSCK) \ @@ -690,7 +689,7 @@ enum bch_write_ref { 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)) \ + x(fix_reflink_p, 0) \ enum bch_recovery_pass { #define x(n, when) BCH_RECOVERY_PASS_##n, @@ -1033,6 +1032,8 @@ struct bch_fs { u64 journal_replay_seq_start; u64 journal_replay_seq_end; enum bch_recovery_pass curr_recovery_pass; + /* bitmap of explicitly enabled recovery passes: */ + u64 recovery_passes_explicit; /* DEBUG JUNK */ struct dentry *fs_debug_dir; @@ -1177,12 +1178,6 @@ static inline bool bch2_dev_exists2(const struct bch_fs *c, unsigned dev) return dev < c->sb.nr_devices && c->devs[dev]; } -static inline bool bch2_version_upgrading_to(const struct bch_fs *c, unsigned new_version) -{ - return c->sb.version_upgrade_complete < new_version && - c->sb.version >= new_version; -} - #define BKEY_PADDED_ONSTACK(key, pad) \ struct { struct bkey_i key; __u64 key ## _pad[pad]; } diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h index 78771d8d8a62..274e57740d74 100644 --- a/fs/bcachefs/bcachefs_format.h +++ b/fs/bcachefs/bcachefs_format.h @@ -1578,32 +1578,58 @@ struct bch_sb_field_journal_seq_blacklist { #define BCH_VERSION_MINOR(_v) ((__u16) ((_v) & ~(~0U << 10))) #define BCH_VERSION(_major, _minor) (((_major) << 10)|(_minor) << 0) -#define BCH_METADATA_VERSIONS() \ - x(bkey_renumber, BCH_VERSION(0, 10)) \ - x(inode_btree_change, BCH_VERSION(0, 11)) \ - x(snapshot, BCH_VERSION(0, 12)) \ - x(inode_backpointers, BCH_VERSION(0, 13)) \ - x(btree_ptr_sectors_written, BCH_VERSION(0, 14)) \ - x(snapshot_2, BCH_VERSION(0, 15)) \ - x(reflink_p_fix, BCH_VERSION(0, 16)) \ - x(subvol_dirent, BCH_VERSION(0, 17)) \ - x(inode_v2, BCH_VERSION(0, 18)) \ - x(freespace, BCH_VERSION(0, 19)) \ - x(alloc_v4, BCH_VERSION(0, 20)) \ - x(new_data_types, BCH_VERSION(0, 21)) \ - x(backpointers, BCH_VERSION(0, 22)) \ - x(inode_v3, BCH_VERSION(0, 23)) \ - x(unwritten_extents, BCH_VERSION(0, 24)) \ - x(bucket_gens, BCH_VERSION(0, 25)) \ - x(lru_v2, BCH_VERSION(0, 26)) \ - x(fragmentation_lru, BCH_VERSION(0, 27)) \ - x(no_bps_in_alloc_keys, BCH_VERSION(0, 28)) \ - x(snapshot_trees, BCH_VERSION(0, 29)) \ - x(major_minor, BCH_VERSION(1, 0)) +#define RECOVERY_PASS_ALL_FSCK (1ULL << 63) + +#define BCH_METADATA_VERSIONS() \ + x(bkey_renumber, BCH_VERSION(0, 10), \ + RECOVERY_PASS_ALL_FSCK) \ + x(inode_btree_change, BCH_VERSION(0, 11), \ + RECOVERY_PASS_ALL_FSCK) \ + x(snapshot, BCH_VERSION(0, 12), \ + RECOVERY_PASS_ALL_FSCK) \ + x(inode_backpointers, BCH_VERSION(0, 13), \ + RECOVERY_PASS_ALL_FSCK) \ + x(btree_ptr_sectors_written, BCH_VERSION(0, 14), \ + RECOVERY_PASS_ALL_FSCK) \ + x(snapshot_2, BCH_VERSION(0, 15), \ + BIT_ULL(BCH_RECOVERY_PASS_fs_upgrade_for_subvolumes)| \ + BIT_ULL(BCH_RECOVERY_PASS_initialize_subvolumes)| \ + RECOVERY_PASS_ALL_FSCK) \ + x(reflink_p_fix, BCH_VERSION(0, 16), \ + BIT_ULL(BCH_RECOVERY_PASS_fix_reflink_p)) \ + x(subvol_dirent, BCH_VERSION(0, 17), \ + RECOVERY_PASS_ALL_FSCK) \ + x(inode_v2, BCH_VERSION(0, 18), \ + RECOVERY_PASS_ALL_FSCK) \ + x(freespace, BCH_VERSION(0, 19), \ + RECOVERY_PASS_ALL_FSCK) \ + x(alloc_v4, BCH_VERSION(0, 20), \ + RECOVERY_PASS_ALL_FSCK) \ + x(new_data_types, BCH_VERSION(0, 21), \ + RECOVERY_PASS_ALL_FSCK) \ + x(backpointers, BCH_VERSION(0, 22), \ + RECOVERY_PASS_ALL_FSCK) \ + x(inode_v3, BCH_VERSION(0, 23), \ + RECOVERY_PASS_ALL_FSCK) \ + x(unwritten_extents, BCH_VERSION(0, 24), \ + RECOVERY_PASS_ALL_FSCK) \ + x(bucket_gens, BCH_VERSION(0, 25), \ + BIT_ULL(BCH_RECOVERY_PASS_bucket_gens_init)| \ + RECOVERY_PASS_ALL_FSCK) \ + x(lru_v2, BCH_VERSION(0, 26), \ + RECOVERY_PASS_ALL_FSCK) \ + x(fragmentation_lru, BCH_VERSION(0, 27), \ + RECOVERY_PASS_ALL_FSCK) \ + x(no_bps_in_alloc_keys, BCH_VERSION(0, 28), \ + RECOVERY_PASS_ALL_FSCK) \ + x(snapshot_trees, BCH_VERSION(0, 29), \ + RECOVERY_PASS_ALL_FSCK) \ + x(major_minor, BCH_VERSION(1, 0), \ + 0) enum bcachefs_metadata_version { bcachefs_metadata_version_min = 9, -#define x(t, n) bcachefs_metadata_version_##t = n, +#define x(t, n, upgrade_passes) bcachefs_metadata_version_##t = n, BCH_METADATA_VERSIONS() #undef x bcachefs_metadata_version_max diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c index 9ca6c236f508..0486ec9d281c 100644 --- a/fs/bcachefs/recovery.c +++ b/fs/bcachefs/recovery.c @@ -1115,6 +1115,7 @@ static void check_version_upgrade(struct bch_fs *c) unsigned latest_version = bcachefs_metadata_version_current; unsigned old_version = c->sb.version_upgrade_complete ?: c->sb.version; unsigned new_version = 0; + u64 recovery_passes; if (old_version < bcachefs_metadata_required_upgrade_below) { if (c->opts.version_upgrade == BCH_VERSION_UPGRADE_incompatible || @@ -1158,13 +1159,16 @@ static void check_version_upgrade(struct bch_fs *c) bch2_version_to_text(&buf, new_version); prt_newline(&buf); - prt_str(&buf, "fsck required"); + recovery_passes = bch2_upgrade_recovery_passes(c, old_version, new_version); + if (recovery_passes) { + prt_str(&buf, "fsck required"); + + c->recovery_passes_explicit |= recovery_passes; + c->opts.fix_errors = FSCK_OPT_YES; + } bch_info(c, "%s", buf.buf); - c->opts.fsck = true; - c->opts.fix_errors = FSCK_OPT_YES; - mutex_lock(&c->sb_lock); bch2_sb_upgrade(c, new_version); mutex_unlock(&c->sb_lock); @@ -1196,21 +1200,30 @@ static struct recovery_pass_fn recovery_passes[] = { #undef x }; +u64 bch2_fsck_recovery_passes(void) +{ + u64 ret = 0; + + for (unsigned i = 0; i < ARRAY_SIZE(recovery_passes); i++) + if (recovery_passes[i].when & PASS_FSCK) + ret |= BIT_ULL(i); + return ret; +} + 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 (c->recovery_passes_explicit & BIT_ULL(pass)) + return true; 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; } @@ -1294,7 +1307,7 @@ int bch2_fs_recovery(struct bch_fs *c) goto err; } - if (!c->opts.nochanges) + if (c->opts.fsck || !(c->opts.nochanges && c->opts.norecovery)) check_version_upgrade(c); if (c->opts.fsck && c->opts.norecovery) { diff --git a/fs/bcachefs/recovery.h b/fs/bcachefs/recovery.h index 8c0348e8b84c..f8e796c0f8c8 100644 --- a/fs/bcachefs/recovery.h +++ b/fs/bcachefs/recovery.h @@ -52,6 +52,8 @@ void bch2_btree_and_journal_iter_init_node_iter(struct btree_and_journal_iter *, void bch2_journal_keys_free(struct journal_keys *); void bch2_journal_entries_free(struct bch_fs *); +u64 bch2_fsck_recovery_passes(void); + int bch2_fs_recovery(struct bch_fs *); int bch2_fs_initialize(struct bch_fs *); diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c index a06310492e79..6a97af0f5896 100644 --- a/fs/bcachefs/super-io.c +++ b/fs/bcachefs/super-io.c @@ -4,6 +4,7 @@ #include "btree_update_interior.h" #include "buckets.h" #include "checksum.h" +#include "counters.h" #include "disk_groups.h" #include "ec.h" #include "error.h" @@ -12,13 +13,13 @@ #include "journal_io.h" #include "journal_sb.h" #include "journal_seq_blacklist.h" +#include "recovery.h" #include "replicas.h" #include "quota.h" #include "super-io.h" #include "super.h" #include "trace.h" #include "vstructs.h" -#include "counters.h" #include #include @@ -26,13 +27,18 @@ static const struct blk_holder_ops bch2_sb_handle_bdev_ops = { }; -struct bch2_metadata_version_str { +struct bch2_metadata_version { u16 version; const char *name; + u64 recovery_passes; }; -static const struct bch2_metadata_version_str bch2_metadata_versions[] = { -#define x(n, v) { .version = v, .name = #n }, +static const struct bch2_metadata_version bch2_metadata_versions[] = { +#define x(n, v, _recovery_passes) { \ + .version = v, \ + .name = #n, \ + .recovery_passes = _recovery_passes, \ +}, BCH_METADATA_VERSIONS() #undef x }; @@ -64,6 +70,24 @@ unsigned bch2_latest_compatible_version(unsigned v) return v; } +u64 bch2_upgrade_recovery_passes(struct bch_fs *c, + unsigned old_version, + unsigned new_version) +{ + u64 ret = 0; + + for (const struct bch2_metadata_version *i = bch2_metadata_versions; + i < bch2_metadata_versions + ARRAY_SIZE(bch2_metadata_versions); + i++) + if (i->version > old_version && i->version <= new_version) { + if (i->recovery_passes & RECOVERY_PASS_ALL_FSCK) + ret |= bch2_fsck_recovery_passes(); + ret |= i->recovery_passes; + } + + return ret &= ~RECOVERY_PASS_ALL_FSCK; +} + const char * const bch2_sb_fields[] = { #define x(name, nr) #name, BCH_SB_FIELDS() diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h index b365f088ba41..904adea6a0da 100644 --- a/fs/bcachefs/super-io.h +++ b/fs/bcachefs/super-io.h @@ -18,6 +18,10 @@ static inline bool bch2_version_compatible(u16 version) void bch2_version_to_text(struct printbuf *, unsigned); unsigned bch2_latest_compatible_version(unsigned); +u64 bch2_upgrade_recovery_passes(struct bch_fs *c, + unsigned, + unsigned); + struct bch_sb_field *bch2_sb_field_get(struct bch_sb *, enum bch_sb_field_type); struct bch_sb_field *bch2_sb_field_resize(struct bch_sb_handle *, enum bch_sb_field_type, unsigned);