summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2022-08-12 17:01:13 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2022-08-17 14:40:23 -0400
commit939dcaf2dcd500ea5f5a1df893c64be791f2b04a (patch)
tree41136cbcfa7c08498375a4692dd0650c91d9757c
parent276734dd5c9c5b65030bf4aa67550cf3a88a7f81 (diff)
bcachefs: Fix spurious "backpointer doesn't match" error in fsck
New btree nodes dont't have allocation information written until the node has been written, which means there's a race where the backpointer for the old node points to a node that no longer exists. bch2_backpointer_get_node() checks for this, but fsck uses bch2_backpointer_get_key(); this patch updates bch2_backpointer_get_key() to call get_node() on not found to Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r--fs/bcachefs/backpointers.c39
-rw-r--r--fs/bcachefs/btree_iter.c8
-rw-r--r--fs/bcachefs/errcode.h1
-rw-r--r--fs/bcachefs/move.c2
4 files changed, 34 insertions, 16 deletions
diff --git a/fs/bcachefs/backpointers.c b/fs/bcachefs/backpointers.c
index d0b6b4f71b4c..029b1ec14283 100644
--- a/fs/bcachefs/backpointers.c
+++ b/fs/bcachefs/backpointers.c
@@ -526,9 +526,21 @@ struct bkey_s_c bch2_backpointer_get_key(struct btree_trans *trans,
if (extent_matches_bp(c, bp.btree_id, bp.level, k, bucket, bp))
return k;
- backpointer_not_found(trans, bucket, bp_offset, bp, k, "extent");
-
bch2_trans_iter_exit(trans, iter);
+
+ if (bp.level) {
+ /*
+ * If a backpointer for a btree node wasn't found, it may be
+ * because it was overwritten by a new btree node that hasn't
+ * been written out yet - backpointer_get_node() checks for
+ * this:
+ */
+ bch2_backpointer_get_node(trans, iter, bucket, bp_offset, bp);
+ bch2_trans_iter_exit(trans, iter);
+ return bkey_s_c_null;
+ }
+
+ backpointer_not_found(trans, bucket, bp_offset, bp, k, "extent");
return bkey_s_c_null;
}
@@ -540,7 +552,6 @@ struct btree *bch2_backpointer_get_node(struct btree_trans *trans,
{
struct bch_fs *c = trans->c;
struct btree *b;
- struct bkey_s_c k;
BUG_ON(!bp.level);
@@ -551,22 +562,24 @@ struct btree *bch2_backpointer_get_node(struct btree_trans *trans,
bp.level - 1,
0);
b = bch2_btree_iter_peek_node(iter);
- if (IS_ERR(b)) {
- bch2_trans_iter_exit(trans, iter);
- return b;
- }
+ if (IS_ERR(b))
+ goto err;
if (extent_matches_bp(c, bp.btree_id, bp.level,
bkey_i_to_s_c(&b->key),
bucket, bp))
return b;
- if (!btree_node_will_make_reachable(b))
- backpointer_not_found(trans, bucket, bp_offset,
- bp, k, "btree node");
-
+ if (btree_node_will_make_reachable(b)) {
+ b = ERR_PTR(-BCH_ERR_backpointer_to_overwritten_btree_node);
+ } else {
+ backpointer_not_found(trans, bucket, bp_offset, bp,
+ bkey_i_to_s_c(&b->key), "btree node");
+ b = NULL;
+ }
+err:
bch2_trans_iter_exit(trans, iter);
- return NULL;
+ return b;
}
static int bch2_check_btree_backpointer(struct btree_trans *trans, struct btree_iter *bp_iter,
@@ -829,6 +842,8 @@ static int check_one_backpointer(struct btree_trans *trans,
k = bch2_backpointer_get_key(trans, &iter, bucket, *bp_offset, bp);
ret = bkey_err(k);
+ if (ret == -BCH_ERR_backpointer_to_overwritten_btree_node)
+ return 0;
if (ret)
return ret;
diff --git a/fs/bcachefs/btree_iter.c b/fs/bcachefs/btree_iter.c
index efc26397360b..16a7736cd6fa 100644
--- a/fs/bcachefs/btree_iter.c
+++ b/fs/bcachefs/btree_iter.c
@@ -2043,12 +2043,13 @@ inline struct bkey_s_c bch2_btree_path_peek_slot(struct btree_path *path, struct
struct bkey_s_c k;
+ EBUG_ON(path->uptodate != BTREE_ITER_UPTODATE);
+ EBUG_ON(!btree_node_locked(path, path->level));
+
if (!path->cached) {
struct btree_path_level *l = path_l(path);
struct bkey_packed *_k;
- EBUG_ON(path->uptodate != BTREE_ITER_UPTODATE);
-
_k = bch2_btree_node_iter_peek_all(&l->iter, l->b);
k = _k ? bkey_disassemble(l->b, _k, u) : bkey_s_c_null;
@@ -2063,7 +2064,6 @@ inline struct bkey_s_c bch2_btree_path_peek_slot(struct btree_path *path, struct
(path->btree_id != ck->key.btree_id ||
bkey_cmp(path->pos, ck->key.pos)));
EBUG_ON(!ck || !ck->valid);
- EBUG_ON(path->uptodate != BTREE_ITER_UPTODATE);
*u = ck->k->k;
k = bkey_i_to_s_c(ck->k);
@@ -2896,7 +2896,7 @@ struct bkey_s_c bch2_btree_iter_peek_slot(struct btree_iter *iter)
}
if (unlikely(bkey_err(k)))
- return k;
+ goto out_no_locked;
next = k.k ? bkey_start_pos(k.k) : POS_MAX;
diff --git a/fs/bcachefs/errcode.h b/fs/bcachefs/errcode.h
index 15a1be2fcc84..232f7c7999f6 100644
--- a/fs/bcachefs/errcode.h
+++ b/fs/bcachefs/errcode.h
@@ -37,6 +37,7 @@
x(no_btree_node, no_btree_node_down) \
x(no_btree_node, no_btree_node_init) \
x(no_btree_node, no_btree_node_cached) \
+ x(0, backpointer_to_overwritten_btree_node) \
x(0, lock_fail_node_reused) \
x(0, lock_fail_root_changed) \
x(0, journal_reclaim_would_deadlock) \
diff --git a/fs/bcachefs/move.c b/fs/bcachefs/move.c
index 2fc247451390..224700675604 100644
--- a/fs/bcachefs/move.c
+++ b/fs/bcachefs/move.c
@@ -636,6 +636,8 @@ int __bch2_evacuate_bucket(struct moving_context *ctxt,
b = bch2_backpointer_get_node(&trans, &iter,
bucket, bp_offset, bp);
ret = PTR_ERR_OR_ZERO(b);
+ if (ret == -BCH_ERR_backpointer_to_overwritten_btree_node)
+ continue;
if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
continue;
if (ret)