summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2023-07-10 19:42:26 +0200
committerKent Overstreet <kent.overstreet@linux.dev>2023-10-22 23:10:06 +0200
commit065bd3356ce490ae9454d8b3c98ff298e13d09ac (patch)
tree968bb0d1b3135ca9d51cdf43c2a963bede5552ab
parentbcachefs: bch2_sb_maybe_downgrade(), bch2_sb_upgrade() (diff)
downloadlinux-065bd3356ce490ae9454d8b3c98ff298e13d09ac.tar.xz
linux-065bd3356ce490ae9454d8b3c98ff298e13d09ac.zip
bcachefs: Version table now lists required recovery passes
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>
-rw-r--r--fs/bcachefs/bcachefs.h17
-rw-r--r--fs/bcachefs/bcachefs_format.h72
-rw-r--r--fs/bcachefs/recovery.c29
-rw-r--r--fs/bcachefs/recovery.h2
-rw-r--r--fs/bcachefs/super-io.c32
-rw-r--r--fs/bcachefs/super-io.h4
6 files changed, 110 insertions, 46 deletions
diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h
index 88a1782b2a0e..d8c020644f54 100644
--- a/fs/bcachefs/bcachefs.h
+++ b/fs/bcachefs/bcachefs.h
@@ -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]; }
diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h
index 78771d8d8a62..274e57740d74 100644
--- a/fs/bcachefs/bcachefs_format.h
+++ b/fs/bcachefs/bcachefs_format.h
@@ -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
diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c
index 9ca6c236f508..0486ec9d281c 100644
--- a/fs/bcachefs/recovery.c
+++ b/fs/bcachefs/recovery.c
@@ -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) {
diff --git a/fs/bcachefs/recovery.h b/fs/bcachefs/recovery.h
index 8c0348e8b84c..f8e796c0f8c8 100644
--- a/fs/bcachefs/recovery.h
+++ b/fs/bcachefs/recovery.h
@@ -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 *);
diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c
index a06310492e79..6a97af0f5896 100644
--- a/fs/bcachefs/super-io.c
+++ b/fs/bcachefs/super-io.c
@@ -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"
@@ -12,13 +13,13 @@
#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>
@@ -26,13 +27,18 @@
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()
diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h
index b365f088ba41..904adea6a0da 100644
--- a/fs/bcachefs/super-io.h
+++ b/fs/bcachefs/super-io.h
@@ -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);