diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2022-08-12 17:01:13 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2022-08-17 14:40:23 -0400 |
commit | 939dcaf2dcd500ea5f5a1df893c64be791f2b04a (patch) | |
tree | 41136cbcfa7c08498375a4692dd0650c91d9757c | |
parent | 276734dd5c9c5b65030bf4aa67550cf3a88a7f81 (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.c | 39 | ||||
-rw-r--r-- | fs/bcachefs/btree_iter.c | 8 | ||||
-rw-r--r-- | fs/bcachefs/errcode.h | 1 | ||||
-rw-r--r-- | fs/bcachefs/move.c | 2 |
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) |