333 lines
12 KiB
Diff
333 lines
12 KiB
Diff
From a06f09e44c8c4e82680b58ed6c7c8048283a0466 Mon Sep 17 00:00:00 2001
|
|
From: Kent Overstreet <kent.overstreet@linux.dev>
|
|
Date: Mon, 11 Nov 2024 21:50:29 -0500
|
|
Subject: [PATCH 220/233] bcachefs: BCH_SB_VERSION_INCOMPAT
|
|
Content-Type: text/plain; charset="utf-8"
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
We've been getting away from feature bits: they don't have any kind of
|
|
ordering, and thus it's possible for people to enable weird combinations
|
|
of features that were never tested or intended to be run.
|
|
|
|
Much better to just give every new feature, compatible or incompatible,
|
|
a version number.
|
|
|
|
Additionally, we probably won't ever rev the major version number: major
|
|
version numbers represent incompatible versions, but that doesn't really
|
|
fit with how we actually roll out incompatible features - we need a
|
|
better way of rolling out incompatible features.
|
|
|
|
So, this patch adds two new superblock fields:
|
|
- BCH_SB_VERSION_INCOMPAT
|
|
- BCH_SB_VERSION_INCOMPAT_ALLOWED
|
|
|
|
BCH_SB_VERSION_INCOMPAT_ALLOWED indicates that incompatible features up
|
|
to version number x are allowed to be used without user prompting, but
|
|
it does not by itself deny old versions from mounting.
|
|
|
|
BCH_SB_VERSION_INCOMPAT does deny old versions from mounting, and must
|
|
be <= BCH_SB_VERSION_INCOMPAT_ALLOWED.
|
|
|
|
BCH_SB_VERSION_INCOMPAT will only be set when a codepath attempts to use
|
|
an incompatible feature, so as to not unnecessarily break compatibility
|
|
with old versions.
|
|
|
|
bch2_request_incompat_feature() is the new interface to check if an
|
|
incompatible feature may be used.
|
|
|
|
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
|
|
Signed-off-by: Alexander Miroshnichenko <alex@millerson.name>
|
|
---
|
|
fs/bcachefs/bcachefs.h | 2 ++
|
|
fs/bcachefs/bcachefs_format.h | 24 +++++++++-------
|
|
fs/bcachefs/recovery.c | 27 +++++++++++++++---
|
|
fs/bcachefs/super-io.c | 54 +++++++++++++++++++++++++++++++++--
|
|
fs/bcachefs/super-io.h | 19 ++++++++++--
|
|
5 files changed, 106 insertions(+), 20 deletions(-)
|
|
|
|
diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h
|
|
index 7b0959fb35dd..b749c4ecad1b 100644
|
|
--- a/fs/bcachefs/bcachefs.h
|
|
+++ b/fs/bcachefs/bcachefs.h
|
|
@@ -760,6 +760,8 @@ struct bch_fs {
|
|
__uuid_t user_uuid;
|
|
|
|
u16 version;
|
|
+ u16 version_incompat;
|
|
+ u16 version_incompat_allowed;
|
|
u16 version_min;
|
|
u16 version_upgrade_complete;
|
|
|
|
diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h
|
|
index cef22c15c256..0c6dfc4c1743 100644
|
|
--- a/fs/bcachefs/bcachefs_format.h
|
|
+++ b/fs/bcachefs/bcachefs_format.h
|
|
@@ -845,6 +845,9 @@ LE64_BITMASK(BCH_SB_VERSION_UPGRADE_COMPLETE,
|
|
struct bch_sb, flags[5], 0, 16);
|
|
LE64_BITMASK(BCH_SB_ALLOCATOR_STUCK_TIMEOUT,
|
|
struct bch_sb, flags[5], 16, 32);
|
|
+LE64_BITMASK(BCH_SB_VERSION_INCOMPAT, struct bch_sb, flags[5], 32, 48);
|
|
+LE64_BITMASK(BCH_SB_VERSION_INCOMPAT_ALLOWED,
|
|
+ struct bch_sb, flags[5], 48, 64);
|
|
|
|
static inline __u64 BCH_SB_COMPRESSION_TYPE(const struct bch_sb *sb)
|
|
{
|
|
@@ -897,21 +900,22 @@ static inline void SET_BCH_SB_BACKGROUND_COMPRESSION_TYPE(struct bch_sb *sb, __u
|
|
x(new_varint, 15) \
|
|
x(journal_no_flush, 16) \
|
|
x(alloc_v2, 17) \
|
|
- x(extents_across_btree_nodes, 18)
|
|
+ x(extents_across_btree_nodes, 18) \
|
|
+ x(incompat_version_field, 19)
|
|
|
|
#define BCH_SB_FEATURES_ALWAYS \
|
|
- ((1ULL << BCH_FEATURE_new_extent_overwrite)| \
|
|
- (1ULL << BCH_FEATURE_extents_above_btree_updates)|\
|
|
- (1ULL << BCH_FEATURE_btree_updates_journalled)|\
|
|
- (1ULL << BCH_FEATURE_alloc_v2)|\
|
|
- (1ULL << BCH_FEATURE_extents_across_btree_nodes))
|
|
+ (BIT_ULL(BCH_FEATURE_new_extent_overwrite)| \
|
|
+ BIT_ULL(BCH_FEATURE_extents_above_btree_updates)|\
|
|
+ BIT_ULL(BCH_FEATURE_btree_updates_journalled)|\
|
|
+ BIT_ULL(BCH_FEATURE_alloc_v2)|\
|
|
+ BIT_ULL(BCH_FEATURE_extents_across_btree_nodes))
|
|
|
|
#define BCH_SB_FEATURES_ALL \
|
|
(BCH_SB_FEATURES_ALWAYS| \
|
|
- (1ULL << BCH_FEATURE_new_siphash)| \
|
|
- (1ULL << BCH_FEATURE_btree_ptr_v2)| \
|
|
- (1ULL << BCH_FEATURE_new_varint)| \
|
|
- (1ULL << BCH_FEATURE_journal_no_flush))
|
|
+ BIT_ULL(BCH_FEATURE_new_siphash)| \
|
|
+ BIT_ULL(BCH_FEATURE_btree_ptr_v2)| \
|
|
+ BIT_ULL(BCH_FEATURE_new_varint)| \
|
|
+ BIT_ULL(BCH_FEATURE_journal_no_flush))
|
|
|
|
enum bch_sb_feature {
|
|
#define x(f, n) BCH_FEATURE_##f,
|
|
diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c
|
|
index fbef6579d884..383e03606d6e 100644
|
|
--- a/fs/bcachefs/recovery.c
|
|
+++ b/fs/bcachefs/recovery.c
|
|
@@ -612,6 +612,7 @@ static bool check_version_upgrade(struct bch_fs *c)
|
|
bch2_latest_compatible_version(c->sb.version));
|
|
unsigned old_version = c->sb.version_upgrade_complete ?: c->sb.version;
|
|
unsigned new_version = 0;
|
|
+ bool ret = false;
|
|
|
|
if (old_version < bcachefs_metadata_required_upgrade_below) {
|
|
if (c->opts.version_upgrade == BCH_VERSION_UPGRADE_incompatible ||
|
|
@@ -667,14 +668,32 @@ static bool check_version_upgrade(struct bch_fs *c)
|
|
}
|
|
|
|
bch_info(c, "%s", buf.buf);
|
|
+ printbuf_exit(&buf);
|
|
+
|
|
+ ret = true;
|
|
+ }
|
|
|
|
- bch2_sb_upgrade(c, new_version);
|
|
+ if (new_version > c->sb.version_incompat &&
|
|
+ c->opts.version_upgrade == BCH_VERSION_UPGRADE_incompatible) {
|
|
+ struct printbuf buf = PRINTBUF;
|
|
+
|
|
+ prt_str(&buf, "Now allowing incompatible features up to ");
|
|
+ bch2_version_to_text(&buf, new_version);
|
|
+ prt_str(&buf, ", previously allowed up to ");
|
|
+ bch2_version_to_text(&buf, c->sb.version_incompat_allowed);
|
|
+ prt_newline(&buf);
|
|
|
|
+ bch_info(c, "%s", buf.buf);
|
|
printbuf_exit(&buf);
|
|
- return true;
|
|
+
|
|
+ ret = true;
|
|
}
|
|
|
|
- return false;
|
|
+ if (ret)
|
|
+ bch2_sb_upgrade(c, new_version,
|
|
+ c->opts.version_upgrade == BCH_VERSION_UPGRADE_incompatible);
|
|
+
|
|
+ return ret;
|
|
}
|
|
|
|
int bch2_fs_recovery(struct bch_fs *c)
|
|
@@ -1074,7 +1093,7 @@ int bch2_fs_initialize(struct bch_fs *c)
|
|
bch2_check_version_downgrade(c);
|
|
|
|
if (c->opts.version_upgrade != BCH_VERSION_UPGRADE_none) {
|
|
- bch2_sb_upgrade(c, bcachefs_metadata_version_current);
|
|
+ bch2_sb_upgrade(c, bcachefs_metadata_version_current, false);
|
|
SET_BCH_SB_VERSION_UPGRADE_COMPLETE(c->disk_sb.sb, bcachefs_metadata_version_current);
|
|
bch2_write_super(c);
|
|
}
|
|
diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c
|
|
index 6a086c1c4b14..b0d52b6ccad4 100644
|
|
--- a/fs/bcachefs/super-io.c
|
|
+++ b/fs/bcachefs/super-io.c
|
|
@@ -42,7 +42,7 @@ static const struct bch2_metadata_version bch2_metadata_versions[] = {
|
|
#undef x
|
|
};
|
|
|
|
-void bch2_version_to_text(struct printbuf *out, unsigned v)
|
|
+void bch2_version_to_text(struct printbuf *out, enum bcachefs_metadata_version v)
|
|
{
|
|
const char *str = "(unknown version)";
|
|
|
|
@@ -55,7 +55,7 @@ void bch2_version_to_text(struct printbuf *out, unsigned v)
|
|
prt_printf(out, "%u.%u: %s", BCH_VERSION_MAJOR(v), BCH_VERSION_MINOR(v), str);
|
|
}
|
|
|
|
-unsigned bch2_latest_compatible_version(unsigned v)
|
|
+enum bcachefs_metadata_version bch2_latest_compatible_version(enum bcachefs_metadata_version v)
|
|
{
|
|
if (!BCH_VERSION_MAJOR(v))
|
|
return v;
|
|
@@ -69,6 +69,16 @@ unsigned bch2_latest_compatible_version(unsigned v)
|
|
return v;
|
|
}
|
|
|
|
+void bch2_set_version_incompat(struct bch_fs *c, enum bcachefs_metadata_version version)
|
|
+{
|
|
+ mutex_lock(&c->sb_lock);
|
|
+ SET_BCH_SB_VERSION_INCOMPAT(c->disk_sb.sb,
|
|
+ max(BCH_SB_VERSION_INCOMPAT(c->disk_sb.sb), version));
|
|
+ c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_FEATURE_incompat_version_field);
|
|
+ bch2_write_super(c);
|
|
+ mutex_unlock(&c->sb_lock);
|
|
+}
|
|
+
|
|
const char * const bch2_sb_fields[] = {
|
|
#define x(name, nr) #name,
|
|
BCH_SB_FIELDS()
|
|
@@ -369,6 +379,12 @@ static int bch2_sb_validate(struct bch_sb_handle *disk_sb,
|
|
return -BCH_ERR_invalid_sb_features;
|
|
}
|
|
|
|
+ if (BCH_VERSION_MAJOR(le16_to_cpu(sb->version)) > BCH_VERSION_MAJOR(bcachefs_metadata_version_current) ||
|
|
+ BCH_SB_VERSION_INCOMPAT(sb) > bcachefs_metadata_version_current) {
|
|
+ prt_printf(out, "Filesystem has incompatible version");
|
|
+ return -BCH_ERR_invalid_sb_features;
|
|
+ }
|
|
+
|
|
block_size = le16_to_cpu(sb->block_size);
|
|
|
|
if (block_size > PAGE_SECTORS) {
|
|
@@ -407,6 +423,21 @@ static int bch2_sb_validate(struct bch_sb_handle *disk_sb,
|
|
return -BCH_ERR_invalid_sb_time_precision;
|
|
}
|
|
|
|
+ /* old versions didn't know to downgrade this field */
|
|
+ if (BCH_SB_VERSION_INCOMPAT_ALLOWED(sb) > le16_to_cpu(sb->version))
|
|
+ SET_BCH_SB_VERSION_INCOMPAT_ALLOWED(sb, le16_to_cpu(sb->version));
|
|
+
|
|
+ if (BCH_SB_VERSION_INCOMPAT(sb) > BCH_SB_VERSION_INCOMPAT_ALLOWED(sb)) {
|
|
+ prt_printf(out, "Invalid version_incompat ");
|
|
+ bch2_version_to_text(out, BCH_SB_VERSION_INCOMPAT(sb));
|
|
+ prt_str(out, " > incompat_allowed ");
|
|
+ bch2_version_to_text(out, BCH_SB_VERSION_INCOMPAT_ALLOWED(sb));
|
|
+ if (flags & BCH_VALIDATE_write)
|
|
+ return -BCH_ERR_invalid_sb_version;
|
|
+ else
|
|
+ SET_BCH_SB_VERSION_INCOMPAT_ALLOWED(sb, BCH_SB_VERSION_INCOMPAT(sb));
|
|
+ }
|
|
+
|
|
if (!flags) {
|
|
/*
|
|
* Been seeing a bug where these are getting inexplicably
|
|
@@ -520,6 +551,9 @@ static void bch2_sb_update(struct bch_fs *c)
|
|
c->sb.uuid = src->uuid;
|
|
c->sb.user_uuid = src->user_uuid;
|
|
c->sb.version = le16_to_cpu(src->version);
|
|
+ c->sb.version_incompat = BCH_SB_VERSION_INCOMPAT(src);
|
|
+ c->sb.version_incompat_allowed
|
|
+ = BCH_SB_VERSION_INCOMPAT_ALLOWED(src);
|
|
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;
|
|
@@ -1152,6 +1186,8 @@ bool bch2_check_version_downgrade(struct bch_fs *c)
|
|
*/
|
|
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 (BCH_SB_VERSION_INCOMPAT_ALLOWED(c->disk_sb.sb) > bcachefs_metadata_version_current)
|
|
+ SET_BCH_SB_VERSION_INCOMPAT_ALLOWED(c->disk_sb.sb, 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 (c->sb.version_min > bcachefs_metadata_version_current)
|
|
@@ -1160,7 +1196,7 @@ bool bch2_check_version_downgrade(struct bch_fs *c)
|
|
return ret;
|
|
}
|
|
|
|
-void bch2_sb_upgrade(struct bch_fs *c, unsigned new_version)
|
|
+void bch2_sb_upgrade(struct bch_fs *c, unsigned new_version, bool incompat)
|
|
{
|
|
lockdep_assert_held(&c->sb_lock);
|
|
|
|
@@ -1170,6 +1206,10 @@ void bch2_sb_upgrade(struct bch_fs *c, unsigned new_version)
|
|
|
|
c->disk_sb.sb->version = cpu_to_le16(new_version);
|
|
c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_SB_FEATURES_ALL);
|
|
+
|
|
+ if (incompat)
|
|
+ SET_BCH_SB_VERSION_INCOMPAT_ALLOWED(c->disk_sb.sb,
|
|
+ max(BCH_SB_VERSION_INCOMPAT_ALLOWED(c->disk_sb.sb), new_version));
|
|
}
|
|
|
|
static int bch2_sb_ext_validate(struct bch_sb *sb, struct bch_sb_field *f,
|
|
@@ -1334,6 +1374,14 @@ 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_printf(out, "Incompatible features allowed:\t");
|
|
+ bch2_version_to_text(out, BCH_SB_VERSION_INCOMPAT_ALLOWED(sb));
|
|
+ prt_newline(out);
|
|
+
|
|
+ prt_printf(out, "Incompatible features in use:\t");
|
|
+ bch2_version_to_text(out, BCH_SB_VERSION_INCOMPAT(sb));
|
|
+ prt_newline(out);
|
|
+
|
|
prt_printf(out, "Version upgrade complete:\t");
|
|
bch2_version_to_text(out, BCH_SB_VERSION_UPGRADE_COMPLETE(sb));
|
|
prt_newline(out);
|
|
diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h
|
|
index 90e7b176cdd0..f1ab4f943720 100644
|
|
--- a/fs/bcachefs/super-io.h
|
|
+++ b/fs/bcachefs/super-io.h
|
|
@@ -18,8 +18,21 @@ static inline bool bch2_version_compatible(u16 version)
|
|
version >= bcachefs_metadata_version_min;
|
|
}
|
|
|
|
-void bch2_version_to_text(struct printbuf *, unsigned);
|
|
-unsigned bch2_latest_compatible_version(unsigned);
|
|
+void bch2_version_to_text(struct printbuf *, enum bcachefs_metadata_version);
|
|
+enum bcachefs_metadata_version bch2_latest_compatible_version(enum bcachefs_metadata_version);
|
|
+
|
|
+void bch2_set_version_incompat(struct bch_fs *, enum bcachefs_metadata_version);
|
|
+
|
|
+static inline bool bch2_request_incompat_feature(struct bch_fs *c,
|
|
+ enum bcachefs_metadata_version version)
|
|
+{
|
|
+ if (unlikely(version > c->sb.version_incompat)) {
|
|
+ if (version > c->sb.version_incompat_allowed)
|
|
+ return false;
|
|
+ bch2_set_version_incompat(c, version);
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
|
|
static inline size_t bch2_sb_field_bytes(struct bch_sb_field *f)
|
|
{
|
|
@@ -94,7 +107,7 @@ static inline void bch2_check_set_feature(struct bch_fs *c, unsigned feat)
|
|
}
|
|
|
|
bool bch2_check_version_downgrade(struct bch_fs *);
|
|
-void bch2_sb_upgrade(struct bch_fs *, unsigned);
|
|
+void bch2_sb_upgrade(struct bch_fs *, unsigned, bool);
|
|
|
|
void __bch2_sb_field_to_text(struct printbuf *, struct bch_sb *,
|
|
struct bch_sb_field *);
|
|
--
|
|
2.45.2
|
|
|