bcachefs: BCH_SB_VERSION_UPGRADE_COMPLETE()
authorKent Overstreet <kent.overstreet@linux.dev>
Wed, 28 Jun 2023 23:59:56 +0000 (19:59 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:10:06 +0000 (17:10 -0400)
Version upgrades are not atomic operations: when we do a version upgrade
we need to update the superblock before we start using new features, and
then when the upgrade completes we need to update the superblock again.
This adds a new superblock field so we can detect and handle incomplete
version upgrades.

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

index b07c09a7708984415ce58b8c82e1910faad81d7e..0fc810ca5b6bd06ff3bae8d2bf0d32272f8803f8 100644 (file)
@@ -1232,8 +1232,7 @@ int bch2_check_alloc_hole_bucket_gens(struct btree_trans *trans,
        unsigned i, gens_offset, gens_end_offset;
        int ret;
 
-       if (c->sb.version < bcachefs_metadata_version_bucket_gens &&
-           !c->opts.version_upgrade)
+       if (c->sb.version < bcachefs_metadata_version_bucket_gens)
                return 0;
 
        bch2_btree_iter_set_pos(bucket_gens_iter, alloc_gens_pos(start, &gens_offset));
index a8488d4e18e8af4e0d2308a9f4f47fae7d810b06..d7f030aa30392ff9e7196bf2a65b6165117f77f0 100644 (file)
@@ -712,6 +712,7 @@ struct bch_fs {
 
                u16             version;
                u16             version_min;
+               u16             version_upgrade_complete;
 
                u8              nr_devices;
                u8              clean;
@@ -1134,6 +1135,12 @@ 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 49b86bfda76bfebe2695ac4a6452aacb2b54dc72..c397a3b96bd112cb55510fd16af53dbe18bb54e0 100644 (file)
@@ -1748,6 +1748,11 @@ LE64_BITMASK(BCH_SB_JOURNAL_TRANSACTION_NAMES,struct bch_sb, flags[4], 32, 33);
 LE64_BITMASK(BCH_SB_NOCOW,             struct bch_sb, flags[4], 33, 34);
 LE64_BITMASK(BCH_SB_WRITE_BUFFER_SIZE, struct bch_sb, flags[4], 34, 54);
 
+/* flags[4] 56-64 unused: */
+
+LE64_BITMASK(BCH_SB_VERSION_UPGRADE_COMPLETE,
+                                       struct bch_sb, flags[5],  0, 16);
+
 /*
  * Features:
  *
index 9ea85b097e8d517a17fa7f2895b9037bfa32d6fd..0173707cfd2ea70c9b147895add41ce15f2731e3 100644 (file)
@@ -1107,6 +1107,31 @@ static int bch2_fs_upgrade_for_subvolumes(struct bch_fs *c)
        return ret;
 }
 
+static void check_version_upgrade(struct bch_fs *c)
+{
+       unsigned version = c->sb.version_upgrade_complete ?: c->sb.version;
+
+       if (version < bcachefs_metadata_required_upgrade_below) {
+               struct printbuf buf = PRINTBUF;
+
+               if (version != c->sb.version)
+                       prt_str(&buf, "version upgrade incomplete:\n");
+
+               prt_str(&buf, "version ");
+               bch2_version_to_text(&buf, version);
+               prt_str(&buf, " prior to ");
+               bch2_version_to_text(&buf, bcachefs_metadata_required_upgrade_below);
+               prt_str(&buf, ", upgrade and fsck required");
+
+               bch_info(c, "%s", buf.buf);
+               printbuf_exit(&buf);
+
+               c->opts.version_upgrade = true;
+               c->opts.fsck            = true;
+               c->opts.fix_errors      = FSCK_OPT_YES;
+       }
+}
+
 int bch2_fs_recovery(struct bch_fs *c)
 {
        struct bch_sb_field_clean *clean = NULL;
@@ -1146,23 +1171,8 @@ int bch2_fs_recovery(struct bch_fs *c)
                goto err;
        }
 
-       if (!c->opts.nochanges &&
-           c->sb.version < bcachefs_metadata_required_upgrade_below) {
-               struct printbuf buf = PRINTBUF;
-
-               prt_str(&buf, "version ");
-               bch2_version_to_text(&buf, c->sb.version);
-               prt_str(&buf, " prior to ");
-               bch2_version_to_text(&buf, bcachefs_metadata_required_upgrade_below);
-               prt_str(&buf, ", upgrade and fsck required");
-
-               bch_info(c, "%s", buf.buf);
-               printbuf_exit(&buf);
-
-               c->opts.version_upgrade = true;
-               c->opts.fsck            = true;
-               c->opts.fix_errors      = FSCK_OPT_YES;
-       }
+       if (!c->opts.nochanges)
+               check_version_upgrade(c);
 
        if (c->opts.fsck && c->opts.norecovery) {
                bch_err(c, "cannot select both norecovery and fsck");
@@ -1406,8 +1416,7 @@ use_clean:
        if (ret)
                goto err;
 
-       if (c->sb.version < bcachefs_metadata_version_bucket_gens &&
-           c->opts.version_upgrade) {
+       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)
@@ -1415,7 +1424,7 @@ use_clean:
                bch_verbose(c, "bucket_gens init done");
        }
 
-       if (c->sb.version < bcachefs_metadata_version_snapshot_2) {
+       if (bch2_version_upgrading_to(c, bcachefs_metadata_version_snapshot_2)) {
                ret = bch2_fs_upgrade_for_subvolumes(c);
                if (ret)
                        goto err;
@@ -1443,9 +1452,8 @@ use_clean:
        }
 
        mutex_lock(&c->sb_lock);
-       if (c->opts.version_upgrade) {
-               c->disk_sb.sb->version = cpu_to_le16(bcachefs_metadata_version_current);
-               c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_SB_FEATURES_ALL);
+       if (BCH_SB_VERSION_UPGRADE_COMPLETE(c->disk_sb.sb) != c->sb.version) {
+               SET_BCH_SB_VERSION_UPGRADE_COMPLETE(c->disk_sb.sb, c->sb.version);
                write_sb = true;
        }
 
index 7f3e358d6203c15323c4b3e034ac21b3e48b0a31..71a1e2d76a15126b835c8bc73b2b06019f7591f6 100644 (file)
@@ -449,6 +449,7 @@ static void bch2_sb_update(struct bch_fs *c)
        c->sb.user_uuid         = src->user_uuid;
        c->sb.version           = le16_to_cpu(src->version);
        c->sb.version_min       = le16_to_cpu(src->version_min);
+       c->sb.version_upgrade_complete = BCH_SB_VERSION_UPGRADE_COMPLETE(src) ?: c->sb.version;
        c->sb.nr_devices        = src->nr_devices;
        c->sb.clean             = BCH_SB_CLEAN(src);
        c->sb.encryption_type   = BCH_SB_ENCRYPTION_TYPE(src);
@@ -1192,7 +1193,19 @@ int bch2_fs_mark_dirty(struct bch_fs *c)
 
        mutex_lock(&c->sb_lock);
        SET_BCH_SB_CLEAN(c->disk_sb.sb, false);
+
+       if (BCH_SB_VERSION_UPGRADE_COMPLETE(c->disk_sb.sb) > bcachefs_metadata_version_current)
+               SET_BCH_SB_VERSION_UPGRADE_COMPLETE(c->disk_sb.sb, bcachefs_metadata_version_current);
+
+       if (c->opts.version_upgrade ||
+           c->sb.version > bcachefs_metadata_version_current)
+               c->disk_sb.sb->version = cpu_to_le16(bcachefs_metadata_version_current);
+
+       if (c->opts.version_upgrade)
+               c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_SB_FEATURES_ALL);
+
        c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_SB_FEATURES_ALWAYS);
+
        c->disk_sb.sb->compat[0] &= cpu_to_le64((1ULL << BCH_COMPAT_NR) - 1);
        ret = bch2_write_super(c);
        mutex_unlock(&c->sb_lock);
@@ -1536,6 +1549,11 @@ void bch2_sb_to_text(struct printbuf *out, struct bch_sb *sb,
        bch2_version_to_text(out, le16_to_cpu(sb->version));
        prt_newline(out);
 
+       prt_str(out, "Version upgrade complete:");
+       prt_tab(out);
+       bch2_version_to_text(out, BCH_SB_VERSION_UPGRADE_COMPLETE(sb));
+       prt_newline(out);
+
        prt_printf(out, "Oldest version on disk:");
        prt_tab(out);
        bch2_version_to_text(out, le16_to_cpu(sb->version_min));