bcachefs: Version table now lists required recovery passes
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 10 Jul 2023 17:42:26 +0000 (13:42 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:10:06 +0000 (17:10 -0400)
Now that we've got forward compatibility sorted out, we should be doing
more frequent version upgrades in the future.

To avoid having to run a full fsck for every version upgrade, this
improves the BCH_METADATA_VERSIONS() table to explicitly specify a
bitmask of recovery passes to run when upgrading to or past a given
version.

This means we can also delete PASS_UPGRADE().

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs.h
fs/bcachefs/bcachefs_format.h
fs/bcachefs/recovery.c
fs/bcachefs/recovery.h
fs/bcachefs/super-io.c
fs/bcachefs/super-io.h

index 88a1782b2a0e7c5d48ca935483efbbb332a16573..d8c020644f54082f5bd1118abfe1ecdae572f6d6 100644 (file)
@@ -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]; }
 
index 78771d8d8a62771444a328237126ff5c8a0dfaf7..274e57740d74167ecad1ba2b352642ac8ac95309 100644 (file)
@@ -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
index 9ca6c236f5085c6fb970c1689c940794cf099d8e..0486ec9d281cf257d3f26041da90f1089746d5f6 100644 (file)
@@ -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,12 +1159,15 @@ 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");
 
-               bch_info(c, "%s", buf.buf);
+                       c->recovery_passes_explicit |= recovery_passes;
+                       c->opts.fix_errors = FSCK_OPT_YES;
+               }
 
-               c->opts.fsck            = true;
-               c->opts.fix_errors      = FSCK_OPT_YES;
+               bch_info(c, "%s", buf.buf);
 
                mutex_lock(&c->sb_lock);
                bch2_sb_upgrade(c, new_version);
@@ -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) {
index 8c0348e8b84cf00de92fcfebc588a64957d2253a..f8e796c0f8c86880f6f9b63017f4c0ef4f879ecc 100644 (file)
@@ -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 *);
 
index a06310492e79c059d5de864a894a9cc11740f059..6a97af0f5896af5016c0f32f53e7b4bab2d0d554 100644 (file)
@@ -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"
 #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 <linux/backing-dev.h>
 #include <linux/sort.h>
 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()
index b365f088ba41dd66ff5fb76ac60cfe44bd36354a..904adea6a0da20e9699a920c00e909fca4703ac5 100644 (file)
@@ -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);