Commit 8b16413c authored by Kent Overstreet's avatar Kent Overstreet
Browse files

bcachefs: bch_sb.recovery_passes_required



Add two new superblock fields. Since the main section of the superblock
is now fully, we have to add a new variable length section for them -
bch_sb_field_ext.

 - recovery_passes_requried: recovery passes that must be run on the
   next mount
 - errors_silent: errors that will be silently fixed

These are to improve upgrading and dwongrading: these fields won't be
cleared until after recovery successfully completes, so there won't be
any issues with crashing partway through an upgrade or a downgrade.

Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 808c680f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -737,6 +737,7 @@ struct bch_fs {
		unsigned	nsec_per_time_unit;
		u64		features;
		u64		compat;
		unsigned long	errors_silent[BITS_TO_LONGS(BCH_SB_ERR_MAX)];
	}			sb;


+20 −13
Original line number Diff line number Diff line
@@ -1219,7 +1219,8 @@ struct bch_sb_field {
	x(journal_v2,			9)	\
	x(counters,			10)	\
	x(members_v2,			11)	\
	x(errors,	12)
	x(errors,			12)	\
	x(ext,				13)

enum bch_sb_field_type {
#define x(f, nr)	BCH_SB_FIELD_##f = nr,
@@ -1631,6 +1632,12 @@ struct bch_sb_field_errors {
LE64_BITMASK(BCH_SB_ERROR_ENTRY_ID,	struct bch_sb_field_error_entry, v,  0, 16);
LE64_BITMASK(BCH_SB_ERROR_ENTRY_NR,	struct bch_sb_field_error_entry, v, 16, 64);

struct bch_sb_field_ext {
	struct bch_sb_field	field;
	__le64			recovery_passes_required[2];
	__le64			errors_silent[8];
};

/* Superblock: */

/*
+1 −0
Original line number Diff line number Diff line
@@ -218,6 +218,7 @@
	x(BCH_ERR_invalid_sb,		invalid_sb_quota)			\
	x(BCH_ERR_invalid_sb,		invalid_sb_errors)			\
	x(BCH_ERR_invalid_sb,		invalid_sb_opt_compression)		\
	x(BCH_ERR_invalid_sb,		invalid_sb_ext)				\
	x(BCH_ERR_invalid,		invalid_bkey)				\
	x(BCH_ERR_operation_blocked,    nocow_lock_blocked)			\
	x(EIO,				btree_node_read_err)			\
+3 −0
Original line number Diff line number Diff line
@@ -152,6 +152,9 @@ int bch2_fsck_err(struct bch_fs *c,
	struct printbuf buf = PRINTBUF, *out = &buf;
	int ret = -BCH_ERR_fsck_ignore;

	if (test_bit(err, c->sb.errors_silent))
		return -BCH_ERR_fsck_fix;

	bch2_sb_error_count(c, err);

	va_start(args, fmt);
+60 −15
Original line number Diff line number Diff line
@@ -539,13 +539,12 @@ u64 bch2_recovery_passes_from_stable(u64 v)
	return ret;
}

static void check_version_upgrade(struct bch_fs *c)
static bool check_version_upgrade(struct bch_fs *c)
{
	unsigned latest_compatible = bch2_latest_compatible_version(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;
	u64 recovery_passes;

	if (old_version < bcachefs_metadata_required_upgrade_below) {
		if (c->opts.version_upgrade == BCH_VERSION_UPGRADE_incompatible ||
@@ -589,7 +588,7 @@ static void check_version_upgrade(struct bch_fs *c)
		bch2_version_to_text(&buf, new_version);
		prt_newline(&buf);

		recovery_passes = bch2_upgrade_recovery_passes(c, old_version, new_version);
		u64 recovery_passes = bch2_upgrade_recovery_passes(c, old_version, new_version);
		if (recovery_passes) {
			if ((recovery_passes & RECOVERY_PASS_ALL_FSCK) == RECOVERY_PASS_ALL_FSCK)
				prt_str(&buf, "fsck required");
@@ -604,12 +603,13 @@ static void check_version_upgrade(struct bch_fs *c)

		bch_info(c, "%s", buf.buf);

		mutex_lock(&c->sb_lock);
		bch2_sb_upgrade(c, new_version);
		mutex_unlock(&c->sb_lock);

		printbuf_exit(&buf);
		return true;
	}

	return false;
}

u64 bch2_fsck_recovery_passes(void)
@@ -684,7 +684,6 @@ int bch2_fs_recovery(struct bch_fs *c)
	struct bch_sb_field_clean *clean = NULL;
	struct jset *last_journal_entry = NULL;
	u64 last_seq = 0, blacklist_seq, journal_seq;
	bool write_sb = false;
	int ret = 0;

	if (c->sb.clean) {
@@ -712,15 +711,52 @@ int bch2_fs_recovery(struct bch_fs *c)
		goto err;
	}

	if (c->opts.fsck || !(c->opts.nochanges && c->opts.norecovery))
		check_version_upgrade(c);

	if (c->opts.fsck && c->opts.norecovery) {
		bch_err(c, "cannot select both norecovery and fsck");
		ret = -EINVAL;
		goto err;
	}

	if (!(c->opts.nochanges && c->opts.norecovery)) {
		mutex_lock(&c->sb_lock);
		bool write_sb = false;

		struct bch_sb_field_ext *ext =
			bch2_sb_field_get_minsize(&c->disk_sb, ext, sizeof(*ext) / sizeof(u64));
		if (!ext) {
			ret = -BCH_ERR_ENOSPC_sb;
			mutex_unlock(&c->sb_lock);
			goto err;
		}

		if (BCH_SB_HAS_TOPOLOGY_ERRORS(c->disk_sb.sb)) {
			ext->recovery_passes_required[0] |=
				cpu_to_le64(bch2_recovery_passes_to_stable(BIT_ULL(BCH_RECOVERY_PASS_check_topology)));
			write_sb = true;
		}

		u64 sb_passes = bch2_recovery_passes_from_stable(le64_to_cpu(ext->recovery_passes_required[0]));
		if (sb_passes) {
			struct printbuf buf = PRINTBUF;
			prt_str(&buf, "superblock requires following recovery passes to be run:\n  ");
			prt_bitflags(&buf, bch2_recovery_passes, sb_passes);
			bch_info(c, "%s", buf.buf);
			printbuf_exit(&buf);
		}

		if (check_version_upgrade(c))
			write_sb = true;

		if (write_sb)
			bch2_write_super(c);

		c->recovery_passes_explicit |= bch2_recovery_passes_from_stable(le64_to_cpu(ext->recovery_passes_required[0]));
		mutex_unlock(&c->sb_lock);
	}

	if (c->opts.fsck && IS_ENABLED(CONFIG_BCACHEFS_DEBUG))
		c->recovery_passes_explicit |= BIT_ULL(BCH_RECOVERY_PASS_check_topology);

	ret = bch2_blacklist_table_initialize(c);
	if (ret) {
		bch_err(c, "error initializing blacklist table");
@@ -857,11 +893,6 @@ int bch2_fs_recovery(struct bch_fs *c)
	if (ret)
		goto err;

	if (c->opts.fsck &&
	    (IS_ENABLED(CONFIG_BCACHEFS_DEBUG) ||
	     BCH_SB_HAS_TOPOLOGY_ERRORS(c->disk_sb.sb)))
		c->recovery_passes_explicit |= BIT_ULL(BCH_RECOVERY_PASS_check_topology);

	ret = bch2_run_recovery_passes(c);
	if (ret)
		goto err;
@@ -898,16 +929,30 @@ int bch2_fs_recovery(struct bch_fs *c)
	}

	mutex_lock(&c->sb_lock);
	bool write_sb = false;

	if (BCH_SB_VERSION_UPGRADE_COMPLETE(c->disk_sb.sb) != le16_to_cpu(c->disk_sb.sb->version)) {
		SET_BCH_SB_VERSION_UPGRADE_COMPLETE(c->disk_sb.sb, le16_to_cpu(c->disk_sb.sb->version));
		write_sb = true;
	}

	if (!test_bit(BCH_FS_ERROR, &c->flags)) {
	if (!test_bit(BCH_FS_ERROR, &c->flags) &&
	    !(c->disk_sb.sb->compat[0] & cpu_to_le64(1ULL << BCH_COMPAT_alloc_info))) {
		c->disk_sb.sb->compat[0] |= cpu_to_le64(1ULL << BCH_COMPAT_alloc_info);
		write_sb = true;
	}

	if (!test_bit(BCH_FS_ERROR, &c->flags)) {
		struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
		if (ext &&
		    (!bch2_is_zero(ext->recovery_passes_required, sizeof(ext->recovery_passes_required)) ||
		     !bch2_is_zero(ext->errors_silent, sizeof(ext->errors_silent)))) {
			memset(ext->recovery_passes_required, 0, sizeof(ext->recovery_passes_required));
			memset(ext->errors_silent, 0, sizeof(ext->errors_silent));
			write_sb = true;
		}
	}

	if (c->opts.fsck &&
	    !test_bit(BCH_FS_ERROR, &c->flags) &&
	    !test_bit(BCH_FS_ERRORS_NOT_FIXED, &c->flags)) {
Loading