Commit 2e984040 authored by Kent Overstreet's avatar Kent Overstreet

bcachefs: Improve btree node read error path

This ensures that failure to read a btree node error is treated as a
topology error, and returns the correct error so that the topology
repair pass will be run.
Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 464b4155
...@@ -536,10 +536,23 @@ static void btree_err_msg(struct printbuf *out, struct bch_fs *c, ...@@ -536,10 +536,23 @@ static void btree_err_msg(struct printbuf *out, struct bch_fs *c,
} }
enum btree_err_type { enum btree_err_type {
/*
* We can repair this locally, and we're after the checksum check so
* there's no need to try another replica:
*/
BTREE_ERR_FIXABLE, BTREE_ERR_FIXABLE,
/*
* We can repair this if we have to, but we should try reading another
* replica if we can:
*/
BTREE_ERR_WANT_RETRY, BTREE_ERR_WANT_RETRY,
/*
* Read another replica if we have one, otherwise consider the whole
* node bad:
*/
BTREE_ERR_MUST_RETRY, BTREE_ERR_MUST_RETRY,
BTREE_ERR_FATAL, BTREE_ERR_BAD_NODE,
BTREE_ERR_INCOMPATIBLE,
}; };
enum btree_validate_ret { enum btree_validate_ret {
...@@ -565,36 +578,40 @@ static int __btree_err(enum btree_err_type type, ...@@ -565,36 +578,40 @@ static int __btree_err(enum btree_err_type type,
prt_vprintf(&out, fmt, args); prt_vprintf(&out, fmt, args);
va_end(args); va_end(args);
if (write == READ &&
type == BTREE_ERR_FIXABLE &&
!test_bit(BCH_FS_FSCK_DONE, &c->flags)) {
mustfix_fsck_err(c, "%s", out.buf);
goto out;
}
bch2_print_string_as_lines(KERN_ERR, out.buf);
if (write == WRITE) { if (write == WRITE) {
bch2_print_string_as_lines(KERN_ERR, out.buf);
ret = c->opts.errors == BCH_ON_ERROR_continue ret = c->opts.errors == BCH_ON_ERROR_continue
? 0 ? 0
: -BCH_ERR_fsck_errors_not_fixed; : -BCH_ERR_fsck_errors_not_fixed;
goto out; goto out;
} }
if (!have_retry && type == BTREE_ERR_WANT_RETRY)
type = BTREE_ERR_FIXABLE;
if (!have_retry && type == BTREE_ERR_MUST_RETRY)
type = BTREE_ERR_BAD_NODE;
switch (type) { switch (type) {
case BTREE_ERR_FIXABLE: case BTREE_ERR_FIXABLE:
ret = -BCH_ERR_fsck_errors_not_fixed; mustfix_fsck_err(c, "%s", out.buf);
ret = -BCH_ERR_fsck_fix;
break; break;
case BTREE_ERR_WANT_RETRY: case BTREE_ERR_WANT_RETRY:
if (have_retry)
ret = BTREE_RETRY_READ;
break;
case BTREE_ERR_MUST_RETRY: case BTREE_ERR_MUST_RETRY:
bch2_print_string_as_lines(KERN_ERR, out.buf);
ret = BTREE_RETRY_READ; ret = BTREE_RETRY_READ;
break; break;
case BTREE_ERR_FATAL: case BTREE_ERR_BAD_NODE:
bch2_print_string_as_lines(KERN_ERR, out.buf);
bch2_topology_error(c);
ret = -BCH_ERR_need_topology_repair;
break;
case BTREE_ERR_INCOMPATIBLE:
bch2_print_string_as_lines(KERN_ERR, out.buf);
ret = -BCH_ERR_fsck_errors_not_fixed; ret = -BCH_ERR_fsck_errors_not_fixed;
break; break;
default:
BUG();
} }
out: out:
fsck_err: fsck_err:
...@@ -679,7 +696,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca, ...@@ -679,7 +696,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
btree_err_on((version != BCH_BSET_VERSION_OLD && btree_err_on((version != BCH_BSET_VERSION_OLD &&
version < bcachefs_metadata_version_min) || version < bcachefs_metadata_version_min) ||
version >= bcachefs_metadata_version_max, version >= bcachefs_metadata_version_max,
BTREE_ERR_FATAL, c, ca, b, i, BTREE_ERR_INCOMPATIBLE, c, ca, b, i,
"unsupported bset version"); "unsupported bset version");
if (btree_err_on(version < c->sb.version_min, if (btree_err_on(version < c->sb.version_min,
...@@ -703,7 +720,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca, ...@@ -703,7 +720,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
} }
btree_err_on(BSET_SEPARATE_WHITEOUTS(i), btree_err_on(BSET_SEPARATE_WHITEOUTS(i),
BTREE_ERR_FATAL, c, ca, b, i, BTREE_ERR_INCOMPATIBLE, c, ca, b, i,
"BSET_SEPARATE_WHITEOUTS no longer supported"); "BSET_SEPARATE_WHITEOUTS no longer supported");
if (btree_err_on(offset + sectors > btree_sectors(c), if (btree_err_on(offset + sectors > btree_sectors(c),
...@@ -780,7 +797,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca, ...@@ -780,7 +797,7 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
err = bch2_bkey_format_validate(&bn->format); err = bch2_bkey_format_validate(&bn->format);
btree_err_on(err, btree_err_on(err,
BTREE_ERR_FATAL, c, ca, b, i, BTREE_ERR_BAD_NODE, c, ca, b, i,
"invalid bkey format: %s", err); "invalid bkey format: %s", err);
compat_bformat(b->c.level, b->c.btree_id, version, compat_bformat(b->c.level, b->c.btree_id, version,
...@@ -969,7 +986,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca, ...@@ -969,7 +986,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
btree_err_on(btree_node_type_is_extents(btree_node_type(b)) && btree_err_on(btree_node_type_is_extents(btree_node_type(b)) &&
!BTREE_NODE_NEW_EXTENT_OVERWRITE(b->data), !BTREE_NODE_NEW_EXTENT_OVERWRITE(b->data),
BTREE_ERR_FATAL, c, NULL, b, NULL, BTREE_ERR_INCOMPATIBLE, c, NULL, b, NULL,
"btree node does not have NEW_EXTENT_OVERWRITE set"); "btree node does not have NEW_EXTENT_OVERWRITE set");
sectors = vstruct_sectors(b->data, c->block_bits); sectors = vstruct_sectors(b->data, c->block_bits);
...@@ -1151,12 +1168,10 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca, ...@@ -1151,12 +1168,10 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
printbuf_exit(&buf); printbuf_exit(&buf);
return retry_read; return retry_read;
fsck_err: fsck_err:
if (ret == BTREE_RETRY_READ) { if (ret == BTREE_RETRY_READ)
retry_read = 1; retry_read = 1;
} else { else
bch2_inconsistent_error(c);
set_btree_node_read_error(b); set_btree_node_read_error(b);
}
goto out; goto out;
} }
......
...@@ -27,8 +27,11 @@ bool bch2_inconsistent_error(struct bch_fs *c) ...@@ -27,8 +27,11 @@ bool bch2_inconsistent_error(struct bch_fs *c)
void bch2_topology_error(struct bch_fs *c) void bch2_topology_error(struct bch_fs *c)
{ {
if (!test_bit(BCH_FS_TOPOLOGY_REPAIR_DONE, &c->flags))
return;
set_bit(BCH_FS_TOPOLOGY_ERROR, &c->flags); set_bit(BCH_FS_TOPOLOGY_ERROR, &c->flags);
if (test_bit(BCH_FS_INITIAL_GC_DONE, &c->flags)) if (test_bit(BCH_FS_FSCK_DONE, &c->flags))
bch2_inconsistent_error(c); bch2_inconsistent_error(c);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment