diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2023-06-27 22:09:35 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-06-28 02:38:47 -0400 |
commit | 51c4540ad42380cd4f76f260bf4f9ce752e9ffa6 (patch) | |
tree | 631a1aaca1a098c01e6c6ef1a9e00331148114a9 | |
parent | c362d6993fe0f3e60e556f1b2d7eec97a3b9809f (diff) |
bcachefs: bcachefs_metadata_version_major_minorbcachefs_major_minor
XXX: verify that we ignore/do not fail if unknown
- superblock sections
- journal entry types
- btree ids
- key types
are present
also verify keys can be extended
also, change how "allowed keys for this btree" works - we _filter_ out
non-allowed keys when reading, only assert that keys we're writing match
filter
also: verion upgrade becomes an enum, not a bool
- none
- compatible
- incompatible
- change tools to use modparamater and scream loudly about doing a
major version upgrade when not supported
- version table needs "version upgrade requires fsck" field
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r-- | fs/bcachefs/bcachefs.h | 1 | ||||
-rw-r--r-- | fs/bcachefs/bcachefs_format.h | 53 | ||||
-rw-r--r-- | fs/bcachefs/btree_io.c | 12 | ||||
-rw-r--r-- | fs/bcachefs/journal_io.c | 24 | ||||
-rw-r--r-- | fs/bcachefs/opts.c | 5 | ||||
-rw-r--r-- | fs/bcachefs/opts.h | 1 | ||||
-rw-r--r-- | fs/bcachefs/recovery.c | 18 | ||||
-rw-r--r-- | fs/bcachefs/super-io.c | 132 | ||||
-rw-r--r-- | fs/bcachefs/super-io.h | 8 | ||||
-rw-r--r-- | fs/bcachefs/super.c | 3 |
10 files changed, 155 insertions, 102 deletions
diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h index ba8cd35bfd35..96d881d38915 100644 --- a/fs/bcachefs/bcachefs.h +++ b/fs/bcachefs/bcachefs.h @@ -713,6 +713,7 @@ struct bch_fs { u16 version; u16 version_min; + u16 version_upgrade_complete; u8 nr_devices; u8 clean; diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h index b2d210b865cf..5b3e206c2aeb 100644 --- a/fs/bcachefs/bcachefs_format.h +++ b/fs/bcachefs/bcachefs_format.h @@ -1574,30 +1574,32 @@ struct bch_sb_field_journal_seq_blacklist { * One common version number for all on disk data structures - superblock, btree * nodes, journal entries */ -#define BCH_JSET_VERSION_OLD 2 -#define BCH_BSET_VERSION_OLD 3 +#define BCH_VERSION_MAJOR(_v) ((__u8) ((_v) >> 10)) +#define BCH_VERSION_MINOR(_v) ((__u8) ((_v) >> 0)) +#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, @@ -1607,7 +1609,7 @@ enum bcachefs_metadata_version { 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) @@ -1751,6 +1753,11 @@ 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); LE64_BITMASK(BCH_SB_VERSION_UPGRADE, struct bch_sb, flags[4], 54, 56); +/* flags[4] 56-64 unused: */ + +LE64_BITMASK(BCH_SB_VERSION_UPGRADE_COMPLETE, + struct bch_sb, flags[5], 0, 16); + /* * Features: * diff --git a/fs/bcachefs/btree_io.c b/fs/bcachefs/btree_io.c index 990c2fa28114..54f8c8c76fbb 100644 --- a/fs/bcachefs/btree_io.c +++ b/fs/bcachefs/btree_io.c @@ -699,11 +699,11 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca, struct printbuf buf2 = PRINTBUF; int ret = 0; - btree_err_on((version != BCH_BSET_VERSION_OLD && - version < bcachefs_metadata_version_min) || - version >= bcachefs_metadata_version_max, + btree_err_on(!bch2_version_compatible(version), BTREE_ERR_INCOMPATIBLE, c, ca, b, i, - "unsupported bset 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, @@ -2019,9 +2019,7 @@ do_write: BUG_ON(BSET_BIG_ENDIAN(i) != CPU_BIG_ENDIAN); BUG_ON(i->seq != b->data->keys.seq); - i->version = c->sb.version < bcachefs_metadata_version_bkey_renumber - ? cpu_to_le16(BCH_BSET_VERSION_OLD) - : cpu_to_le16(c->sb.version); + i->version = cpu_to_le16(c->sb.version); SET_BSET_OFFSET(i, b->written); SET_BSET_CSUM_TYPE(i, bch2_meta_checksum_type(c)); diff --git a/fs/bcachefs/journal_io.c b/fs/bcachefs/journal_io.c index 7d0dd1b1d5cf..49f2fe413d4f 100644 --- a/fs/bcachefs/journal_io.c +++ b/fs/bcachefs/journal_io.c @@ -745,14 +745,12 @@ static int jset_validate(struct bch_fs *c, return JOURNAL_ENTRY_NONE; version = le32_to_cpu(jset->version); - if (journal_entry_err_on((version != BCH_JSET_VERSION_OLD && - version < bcachefs_metadata_version_min) || - version >= bcachefs_metadata_version_max, - c, jset, NULL, - "%s sector %llu seq %llu: unknown journal entry version %u", + if (journal_entry_err_on(!bch2_version_compatible(version), c, jset, NULL, + "%s sector %llu seq %llu: incompatible journal entry version %u.%u", ca ? ca->name : c->name, sector, le64_to_cpu(jset->seq), - version)) { + BCH_VERSION_MAJOR(version), + BCH_VERSION_MINOR(version))) { /* don't try to continue: */ return -EINVAL; } @@ -796,14 +794,12 @@ static int jset_validate_early(struct bch_fs *c, return JOURNAL_ENTRY_NONE; version = le32_to_cpu(jset->version); - if (journal_entry_err_on((version != BCH_JSET_VERSION_OLD && - version < bcachefs_metadata_version_min) || - version >= bcachefs_metadata_version_max, - c, jset, NULL, - "%s sector %llu seq %llu: unknown journal entry version %u", + if (journal_entry_err_on(!bch2_version_compatible(version), c, jset, NULL, + "%s sector %llu seq %llu: unknown journal entry version %u.%u", ca ? ca->name : c->name, sector, le64_to_cpu(jset->seq), - version)) { + BCH_VERSION_MAJOR(version), + BCH_VERSION_MINOR(version))) { /* don't try to continue: */ return -EINVAL; } @@ -1755,9 +1751,7 @@ void bch2_journal_write(struct closure *cl) } jset->magic = cpu_to_le64(jset_magic(c)); - jset->version = c->sb.version < bcachefs_metadata_version_bkey_renumber - ? cpu_to_le32(BCH_JSET_VERSION_OLD) - : cpu_to_le32(c->sb.version); + jset->version = cpu_to_le32(c->sb.version); SET_JSET_BIG_ENDIAN(jset, CPU_BIG_ENDIAN); SET_JSET_CSUM_TYPE(jset, bch2_meta_checksum_type(c)); diff --git a/fs/bcachefs/opts.c b/fs/bcachefs/opts.c index 647b2338acc9..0c0c83fa4264 100644 --- a/fs/bcachefs/opts.c +++ b/fs/bcachefs/opts.c @@ -11,11 +11,6 @@ #define x(t, n) [n] = #t, -const char * const bch2_metadata_versions[] = { - BCH_METADATA_VERSIONS() - NULL -}; - const char * const bch2_error_actions[] = { BCH_ERROR_ACTIONS() NULL diff --git a/fs/bcachefs/opts.h b/fs/bcachefs/opts.h index 9727e29f7766..e105a742fd44 100644 --- a/fs/bcachefs/opts.h +++ b/fs/bcachefs/opts.h @@ -8,7 +8,6 @@ #include <linux/sysfs.h> #include "bcachefs_format.h" -extern const char * const bch2_metadata_versions[]; extern const char * const bch2_error_actions[]; extern const char * const bch2_version_upgrade_opts[]; extern const char * const bch2_sb_features[]; diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c index deb1c4f37007..0ed3f60b878c 100644 --- a/fs/bcachefs/recovery.c +++ b/fs/bcachefs/recovery.c @@ -1114,11 +1114,16 @@ static bool should_do_version_upgrade(struct bch_fs *c) if (!c->opts.nochanges && c->sb.version < bcachefs_metadata_required_upgrade_below) { - bch_info(c, "version %s (%u) prior to %s (%u), upgrade and fsck required", - bch2_metadata_versions[c->sb.version], - c->sb.version, - bch2_metadata_versions[bcachefs_metadata_required_upgrade_below], - bcachefs_metadata_required_upgrade_below); + struct printbuf buf = PRINTBUF; + + prt_str(&buf, "version "); + bch2_version_to_text(&buf, c->sb.version_upgrade_complete ?: 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.fsck = true; c->opts.fix_errors = FSCK_OPT_YES; return true; @@ -1449,8 +1454,7 @@ use_clean: mutex_lock(&c->sb_lock); if (test_bit(BCH_FS_VERSION_UPGRADE, &c->flags)) { - 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); + SET_BCH_SB_VERSION_UPGRADE_COMPLETE(c->disk_sb.sb, bcachefs_metadata_version_current); write_sb = true; } diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c index 0acdcbfcec5c..ce3f100a2fab 100644 --- a/fs/bcachefs/super-io.c +++ b/fs/bcachefs/super-io.c @@ -23,6 +23,30 @@ #include <linux/backing-dev.h> #include <linux/sort.h> +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 = "(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); +} + const char * const bch2_sb_fields[] = { #define x(name, nr) #name, BCH_SB_FIELDS() @@ -250,40 +274,58 @@ static int validate_sb_layout(struct bch_sb_layout *layout, struct printbuf *out return 0; } -static int bch2_sb_validate(struct bch_sb_handle *disk_sb, struct printbuf *out, - int rw) +static int bch2_sb_compatible(struct bch_sb *sb, struct printbuf *out) { - struct bch_sb *sb = disk_sb->sb; - struct bch_sb_field *f; - struct bch_sb_field_members *mi; - enum bch_opt_id opt_id; - u32 version, version_min; - u16 block_size; - int ret; - - version = le16_to_cpu(sb->version); - version_min = version >= bcachefs_metadata_version_bkey_renumber - ? le16_to_cpu(sb->version_min) - : version; - - if (version >= bcachefs_metadata_version_max) { - prt_printf(out, "Unsupported superblock version %u (min %u, max %u)", - version, bcachefs_metadata_version_min, bcachefs_metadata_version_max); + u16 version = le16_to_cpu(sb->version); + u16 version_min = le16_to_cpu(sb->version_min); + + if (!bch2_version_compatible(version)) { + prt_str(out, "Unsupported superblock version "); + bch2_version_to_text(out, version); + prt_str(out, " (min "); + bch2_version_to_text(out, bcachefs_metadata_version_min); + prt_str(out, ", max "); + bch2_version_to_text(out, bcachefs_metadata_version_current); + prt_str(out, ")"); return -BCH_ERR_invalid_sb_version; } - if (version_min < bcachefs_metadata_version_min) { - prt_printf(out, "Unsupported superblock version %u (min %u, max %u)", - version_min, bcachefs_metadata_version_min, bcachefs_metadata_version_max); + if (!bch2_version_compatible(version_min)) { + prt_str(out, "Unsupported superblock version_min "); + bch2_version_to_text(out, version_min); + prt_str(out, " (min "); + bch2_version_to_text(out, bcachefs_metadata_version_min); + prt_str(out, ", max "); + bch2_version_to_text(out, bcachefs_metadata_version_current); + prt_str(out, ")"); return -BCH_ERR_invalid_sb_version; } if (version_min > version) { - prt_printf(out, "Bad minimum version %u, greater than version field %u", - version_min, version); + prt_str(out, "Bad minimum version "); + bch2_version_to_text(out, version_min); + prt_str(out, ", greater than version field "); + bch2_version_to_text(out, version); return -BCH_ERR_invalid_sb_version; } + return 0; +} + +static int bch2_sb_validate(struct bch_sb_handle *disk_sb, struct printbuf *out, + int rw) +{ + struct bch_sb *sb = disk_sb->sb; + struct bch_sb_field *f; + struct bch_sb_field_members *mi; + enum bch_opt_id opt_id; + u16 block_size; + int ret; + + ret = bch2_sb_compatible(sb, out); + if (ret) + return ret; + if (sb->features[1] || (le64_to_cpu(sb->features[0]) & (~0ULL << BCH_FEATURE_NR))) { prt_printf(out, "Filesystem has incompatible features"); @@ -331,7 +373,7 @@ static int bch2_sb_validate(struct bch_sb_handle *disk_sb, struct printbuf *out, if (rw == READ) { /* * Been seeing a bug where these are getting inexplicably - * zeroed, so we'r now validating them, but we have to be + * zeroed, so we're now validating them, but we have to be * careful not to preven people's filesystems from mounting: */ if (!BCH_SB_JOURNAL_FLUSH_DELAY(sb)) @@ -412,6 +454,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.nr_devices = src->nr_devices; c->sb.clean = BCH_SB_CLEAN(src); c->sb.encryption_type = BCH_SB_ENCRYPTION_TYPE(src); @@ -512,7 +555,6 @@ int bch2_sb_from_fs(struct bch_fs *c, struct bch_dev *ca) static int read_one_super(struct bch_sb_handle *sb, u64 offset, struct printbuf *err) { struct bch_csum csum; - u32 version, version_min; size_t bytes; int ret; reread: @@ -532,22 +574,9 @@ reread: return -BCH_ERR_invalid_sb_magic; } - version = le16_to_cpu(sb->sb->version); - version_min = version >= bcachefs_metadata_version_bkey_renumber - ? le16_to_cpu(sb->sb->version_min) - : version; - - if (version >= bcachefs_metadata_version_max) { - prt_printf(err, "Unsupported superblock version %u (min %u, max %u)", - version, bcachefs_metadata_version_min, bcachefs_metadata_version_max); - return -BCH_ERR_invalid_sb_version; - } - - if (version_min < bcachefs_metadata_version_min) { - prt_printf(err, "Unsupported superblock version %u (min %u, max %u)", - version_min, bcachefs_metadata_version_min, bcachefs_metadata_version_max); - return -BCH_ERR_invalid_sb_version; - } + ret = bch2_sb_compatible(sb->sb, err); + if (ret) + return ret; bytes = vstruct_bytes(sb->sb); @@ -1169,7 +1198,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 (test_bit(BCH_FS_VERSION_UPGRADE, &c->flags) || + 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); + 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); @@ -1503,12 +1544,17 @@ void bch2_sb_to_text(struct printbuf *out, struct bch_sb *sb, prt_str(out, "Version:"); prt_tab(out); - prt_printf(out, "%s", bch2_metadata_versions[le16_to_cpu(sb->version)]); + 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); - prt_printf(out, "%s", bch2_metadata_versions[le16_to_cpu(sb->version_min)]); + bch2_version_to_text(out, le16_to_cpu(sb->version_min)); prt_newline(out); prt_printf(out, "Created:"); diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h index ab0ad3248e8f..a5a80c481242 100644 --- a/fs/bcachefs/super-io.h +++ b/fs/bcachefs/super-io.h @@ -9,6 +9,14 @@ #include <asm/byteorder.h> +static inline bool bch2_version_compatible(u16 version) +{ + 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); + 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); diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c index bb648cadd712..d342e3ba91d9 100644 --- a/fs/bcachefs/super.c +++ b/fs/bcachefs/super.c @@ -876,7 +876,8 @@ static void print_mount_opts(struct bch_fs *c) struct printbuf p = PRINTBUF; bool first = true; - prt_printf(&p, "mounted version=%s", bch2_metadata_versions[c->sb.version]); + prt_str(&p, "mounted version="); + bch2_version_to_text(&p, c->sb.version); if (c->opts.read_only) { prt_str(&p, " opts="); |