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>
This commit is contained in:
Kent Overstreet 2023-07-10 13:42:26 -04:00
parent 6619d84626
commit 065bd3356c
6 changed files with 110 additions and 46 deletions

View File

@ -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]; }

View File

@ -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

View File

@ -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,13 +1159,16 @@ 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");
c->recovery_passes_explicit |= recovery_passes;
c->opts.fix_errors = FSCK_OPT_YES;
}
bch_info(c, "%s", buf.buf);
c->opts.fsck = true;
c->opts.fix_errors = FSCK_OPT_YES;
mutex_lock(&c->sb_lock);
bch2_sb_upgrade(c, new_version);
mutex_unlock(&c->sb_lock);
@ -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) {

View File

@ -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 *);

View File

@ -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()

View File

@ -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);