This introduces major/minor versioning to the superblock version number.
Major version number changes indicate incompatible releases; we can move
forward to a new major version number, but not backwards. Minor version
numbers indicate compatible changes - these add features, but can still
be mounted and used by old versions.
With the recent patches that make it possible to roll out new btrees and
key types without breaking compatibility, we should be able to roll out
most new features without incompatible changes.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
BCH_FS_INITIAL_GC_UNFIXED, /* kill when we enumerate fsck errors */
BCH_FS_NEED_ANOTHER_GC,
- BCH_FS_VERSION_UPGRADE,
BCH_FS_HAVE_DELETED_SNAPSHOTS,
/* errors: */
* One common version number for all on disk data structures - superblock, btree
* nodes, journal entries
*/
+#define BCH_VERSION_MAJOR(_v) ((__u16) ((_v) >> 10))
+#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, 10) \
- x(inode_btree_change, 11) \
- x(snapshot, 12) \
- x(inode_backpointers, 13) \
- x(btree_ptr_sectors_written, 14) \
- x(snapshot_2, 15) \
- x(reflink_p_fix, 16) \
- x(subvol_dirent, 17) \
- x(inode_v2, 18) \
- x(freespace, 19) \
- x(alloc_v4, 20) \
- x(new_data_types, 21) \
- x(backpointers, 22) \
- x(inode_v3, 23) \
- x(unwritten_extents, 24) \
- x(bucket_gens, 25) \
- x(lru_v2, 26) \
- x(fragmentation_lru, 27) \
- x(no_bps_in_alloc_keys, 28) \
- x(snapshot_trees, 29)
+ 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))
enum bcachefs_metadata_version {
bcachefs_metadata_version_min = 9,
bcachefs_metadata_version_max
};
-static const unsigned bcachefs_metadata_required_upgrade_below = bcachefs_metadata_version_snapshot_trees;
+static const unsigned bcachefs_metadata_required_upgrade_below = bcachefs_metadata_version_major_minor;
#define bcachefs_metadata_version_current (bcachefs_metadata_version_max - 1)
btree_err_on(!bch2_version_compatible(version),
BTREE_ERR_INCOMPATIBLE, c, ca, b, i,
- "unsupported bset version %u", version);
+ "unsupported bset version %u.%u",
+ BCH_VERSION_MAJOR(version),
+ BCH_VERSION_MINOR(version));
if (btree_err_on(version < c->sb.version_min,
BTREE_ERR_FIXABLE, c, NULL, b, i,
mutex_unlock(&c->sb_lock);
}
- if (btree_err_on(version > c->sb.version,
+ if (btree_err_on(BCH_VERSION_MAJOR(version) >
+ BCH_VERSION_MAJOR(c->sb.version),
BTREE_ERR_FIXABLE, c, NULL, b, i,
"bset version %u newer than superblock version %u",
version, c->sb.version)) {
version = le32_to_cpu(jset->version);
if (journal_entry_err_on(!bch2_version_compatible(version), c, jset, NULL,
- "%s sector %llu seq %llu: incompatible journal entry version %u",
+ "%s sector %llu seq %llu: incompatible journal entry version %u.%u",
ca ? ca->name : c->name,
- sector, le64_to_cpu(jset->seq), version)) {
+ sector, le64_to_cpu(jset->seq),
+ BCH_VERSION_MAJOR(version),
+ BCH_VERSION_MINOR(version))) {
/* don't try to continue: */
return -EINVAL;
}
version = le32_to_cpu(jset->version);
if (journal_entry_err_on(!bch2_version_compatible(version), c, jset, NULL,
- "%s sector %llu seq %llu: unknown journal entry version %u",
+ "%s sector %llu seq %llu: unknown journal entry version %u.%u",
ca ? ca->name : c->name,
- sector, le64_to_cpu(jset->seq), version)) {
+ sector, le64_to_cpu(jset->seq),
+ BCH_VERSION_MAJOR(version),
+ BCH_VERSION_MINOR(version))) {
/* don't try to continue: */
return -EINVAL;
}
static void check_version_upgrade(struct bch_fs *c)
{
- unsigned version = c->sb.version_upgrade_complete ?: c->sb.version;
+ unsigned latest_compatible = bch2_version_compatible(c->sb.version);
+ unsigned latest_version = bcachefs_metadata_version_current;
+ unsigned old_version = c->sb.version_upgrade_complete ?: c->sb.version;
+ unsigned new_version = 0;
+
+ if (old_version < bcachefs_metadata_required_upgrade_below) {
+ if (c->opts.version_upgrade == BCH_VERSION_UPGRADE_incompatible ||
+ latest_compatible < bcachefs_metadata_required_upgrade_below)
+ new_version = latest_version;
+ else
+ new_version = latest_compatible;
+ } else {
+ switch (c->opts.version_upgrade) {
+ case BCH_VERSION_UPGRADE_compatible:
+ new_version = latest_compatible;
+ break;
+ case BCH_VERSION_UPGRADE_incompatible:
+ new_version = latest_version;
+ break;
+ case BCH_VERSION_UPGRADE_none:
+ new_version = old_version;
+ break;
+ }
+ }
- if (version < bcachefs_metadata_required_upgrade_below ||
- (version < bcachefs_metadata_version_current &&
- c->opts.version_upgrade != BCH_VERSION_UPGRADE_none)) {
+ if (new_version > old_version) {
struct printbuf buf = PRINTBUF;
- if (version != c->sb.version) {
- prt_str(&buf, "version upgrade to ");
+ if (old_version < bcachefs_metadata_required_upgrade_below)
+ prt_str(&buf, "Version upgrade required:\n");
+
+ if (old_version != c->sb.version) {
+ prt_str(&buf, "Version upgrade from ");
+ bch2_version_to_text(&buf, c->sb.version_upgrade_complete);
+ prt_str(&buf, " to ");
bch2_version_to_text(&buf, c->sb.version);
- prt_str(&buf, " incomplete:\n");
+ prt_str(&buf, " 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");
+ prt_printf(&buf, "Doing %s version upgrade from ",
+ BCH_VERSION_MAJOR(old_version) != BCH_VERSION_MAJOR(new_version)
+ ? "incompatible" : "compatible");
+ bch2_version_to_text(&buf, old_version);
+ prt_str(&buf, " to ");
+ bch2_version_to_text(&buf, new_version);
+ prt_newline(&buf);
+
+ prt_str(&buf, "fsck required");
bch_info(c, "%s", buf.buf);
- printbuf_exit(&buf);
c->opts.fsck = true;
c->opts.fix_errors = FSCK_OPT_YES;
- set_bit(BCH_FS_VERSION_UPGRADE, &c->flags);
+
+ mutex_lock(&c->sb_lock);
+ c->disk_sb.sb->version = cpu_to_le16(new_version);
+ c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_SB_FEATURES_ALL);
+ mutex_unlock(&c->sb_lock);
+
+ printbuf_exit(&buf);
}
}
static const struct blk_holder_ops bch2_sb_handle_bdev_ops = {
};
-static const char * const bch2_metadata_versions[] = {
-#define x(t, n) [n] = #t,
+struct bch2_metadata_version_str {
+ u16 version;
+ const char *name;
+};
+
+static const struct bch2_metadata_version_str bch2_metadata_versions[] = {
+#define x(n, v) { .version = v, .name = #n },
BCH_METADATA_VERSIONS()
#undef x
};
void bch2_version_to_text(struct printbuf *out, unsigned v)
{
- const char *str = v < ARRAY_SIZE(bch2_metadata_versions)
- ? bch2_metadata_versions[v]
- : "(unknown version)";
+ const char *str = "(unknown version)";
+
+ for (unsigned i = 0; i < ARRAY_SIZE(bch2_metadata_versions); i++)
+ if (bch2_metadata_versions[i].version == v) {
+ str = bch2_metadata_versions[i].name;
+ break;
+ }
+
+ prt_printf(out, "%u.%u: %s", BCH_VERSION_MAJOR(v), BCH_VERSION_MINOR(v), str);
+}
+
+unsigned bch2_latest_compatible_version(unsigned v)
+{
+ if (!BCH_VERSION_MAJOR(v))
+ return v;
+
+ for (unsigned i = 0; i < ARRAY_SIZE(bch2_metadata_versions); i++)
+ if (bch2_metadata_versions[i].version > v &&
+ BCH_VERSION_MAJOR(bch2_metadata_versions[i].version) ==
+ BCH_VERSION_MAJOR(v))
+ v = bch2_metadata_versions[i].version;
- prt_printf(out, "%u: %s", v, str);
+ return v;
}
const char * const bch2_sb_fields[] = {
closure_init_stack(cl);
memset(&sb_written, 0, sizeof(sb_written));
- if (test_bit(BCH_FS_VERSION_UPGRADE, &c->flags)) {
- c->disk_sb.sb->magic = BCHFS_MAGIC;
- c->disk_sb.sb->layout.magic = BCHFS_MAGIC;
- }
+ /* Make sure we're using the new magic numbers: */
+ c->disk_sb.sb->magic = BCHFS_MAGIC;
+ c->disk_sb.sb->layout.magic = BCHFS_MAGIC;
le64_add_cpu(&c->disk_sb.sb->seq, 1);
mutex_lock(&c->sb_lock);
SET_BCH_SB_CLEAN(c->disk_sb.sb, false);
+ /*
+ * Downgrade, if superblock is at a higher version than currently
+ * supported:
+ */
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 (test_bit(BCH_FS_VERSION_UPGRADE, &c->flags) ||
- c->sb.version > bcachefs_metadata_version_current)
+ if (c->sb.version > bcachefs_metadata_version_current)
c->disk_sb.sb->version = cpu_to_le16(bcachefs_metadata_version_current);
-
- if (test_bit(BCH_FS_VERSION_UPGRADE, &c->flags))
- c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_SB_FEATURES_ALL);
+ if (c->sb.version_min > bcachefs_metadata_version_current)
+ c->disk_sb.sb->version_min = cpu_to_le16(bcachefs_metadata_version_current);
+ c->disk_sb.sb->compat[0] &= cpu_to_le64((1ULL << BCH_COMPAT_NR) - 1);
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);
static inline bool bch2_version_compatible(u16 version)
{
- return version <= bcachefs_metadata_version_current &&
+ return BCH_VERSION_MAJOR(version) <= BCH_VERSION_MAJOR(bcachefs_metadata_version_current) &&
version >= bcachefs_metadata_version_min;
}
void bch2_version_to_text(struct printbuf *, unsigned);
+unsigned bch2_latest_compatible_version(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 *,