Skip to content

Commit d1041d8

Browse files
author
Kent Overstreet
committed
bcachefs: Fix missing commit in backpointer to missing target
Fsck wants to do transaction commits from an outer context; it may have other repair to do (i.e. duplicate backpointers). But when calling backpointer_not_found() from runtime code, i.e. runtime self healing, we should be doing the commit - the outer context expects to just be doing lookups. This fixes bugs where we get stuck spinning, reported as "RCU lock hold time warnings. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
1 parent a12cb6f commit d1041d8

File tree

1 file changed

+79
-38
lines changed

1 file changed

+79
-38
lines changed

fs/bcachefs/backpointers.c

Lines changed: 79 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ static inline int bch2_backpointers_maybe_flush(struct btree_trans *trans,
192192
static int backpointer_target_not_found(struct btree_trans *trans,
193193
struct bkey_s_c_backpointer bp,
194194
struct bkey_s_c target_k,
195-
struct bkey_buf *last_flushed)
195+
struct bkey_buf *last_flushed,
196+
bool commit)
196197
{
197198
struct bch_fs *c = trans->c;
198199
struct printbuf buf = PRINTBUF;
@@ -228,18 +229,77 @@ static int backpointer_target_not_found(struct btree_trans *trans,
228229
}
229230

230231
if (fsck_err(trans, backpointer_to_missing_ptr,
231-
"%s", buf.buf))
232+
"%s", buf.buf)) {
232233
ret = bch2_backpointer_del(trans, bp.k->p);
234+
if (ret || !commit)
235+
goto out;
236+
237+
/*
238+
* Normally, on transaction commit from inside a transaction,
239+
* we'll return -BCH_ERR_transaction_restart_nested, since a
240+
* transaction commit invalidates pointers given out by peek().
241+
*
242+
* However, since we're updating a write buffer btree, if we
243+
* return a transaction restart and loop we won't see that the
244+
* backpointer has been deleted without an additional write
245+
* buffer flush - and those are expensive.
246+
*
247+
* So we're relying on the caller immediately advancing to the
248+
* next backpointer and starting a new transaction immediately
249+
* after backpointer_get_key() returns NULL:
250+
*/
251+
ret = bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
252+
}
253+
out:
233254
fsck_err:
234255
printbuf_exit(&buf);
235256
return ret;
236257
}
237258

238-
struct bkey_s_c bch2_backpointer_get_key(struct btree_trans *trans,
239-
struct bkey_s_c_backpointer bp,
240-
struct btree_iter *iter,
241-
unsigned iter_flags,
242-
struct bkey_buf *last_flushed)
259+
static struct btree *__bch2_backpointer_get_node(struct btree_trans *trans,
260+
struct bkey_s_c_backpointer bp,
261+
struct btree_iter *iter,
262+
struct bkey_buf *last_flushed,
263+
bool commit)
264+
{
265+
struct bch_fs *c = trans->c;
266+
267+
BUG_ON(!bp.v->level);
268+
269+
bch2_trans_node_iter_init(trans, iter,
270+
bp.v->btree_id,
271+
bp.v->pos,
272+
0,
273+
bp.v->level - 1,
274+
0);
275+
struct btree *b = bch2_btree_iter_peek_node(trans, iter);
276+
if (IS_ERR_OR_NULL(b))
277+
goto err;
278+
279+
BUG_ON(b->c.level != bp.v->level - 1);
280+
281+
if (extent_matches_bp(c, bp.v->btree_id, bp.v->level,
282+
bkey_i_to_s_c(&b->key), bp))
283+
return b;
284+
285+
if (btree_node_will_make_reachable(b)) {
286+
b = ERR_PTR(-BCH_ERR_backpointer_to_overwritten_btree_node);
287+
} else {
288+
int ret = backpointer_target_not_found(trans, bp, bkey_i_to_s_c(&b->key),
289+
last_flushed, commit);
290+
b = ret ? ERR_PTR(ret) : NULL;
291+
}
292+
err:
293+
bch2_trans_iter_exit(trans, iter);
294+
return b;
295+
}
296+
297+
static struct bkey_s_c __bch2_backpointer_get_key(struct btree_trans *trans,
298+
struct bkey_s_c_backpointer bp,
299+
struct btree_iter *iter,
300+
unsigned iter_flags,
301+
struct bkey_buf *last_flushed,
302+
bool commit)
243303
{
244304
struct bch_fs *c = trans->c;
245305

@@ -277,10 +337,10 @@ struct bkey_s_c bch2_backpointer_get_key(struct btree_trans *trans,
277337
bch2_trans_iter_exit(trans, iter);
278338

279339
if (!bp.v->level) {
280-
int ret = backpointer_target_not_found(trans, bp, k, last_flushed);
340+
int ret = backpointer_target_not_found(trans, bp, k, last_flushed, commit);
281341
return ret ? bkey_s_c_err(ret) : bkey_s_c_null;
282342
} else {
283-
struct btree *b = bch2_backpointer_get_node(trans, bp, iter, last_flushed);
343+
struct btree *b = __bch2_backpointer_get_node(trans, bp, iter, last_flushed, commit);
284344
if (b == ERR_PTR(-BCH_ERR_backpointer_to_overwritten_btree_node))
285345
return bkey_s_c_null;
286346
if (IS_ERR_OR_NULL(b))
@@ -295,35 +355,16 @@ struct btree *bch2_backpointer_get_node(struct btree_trans *trans,
295355
struct btree_iter *iter,
296356
struct bkey_buf *last_flushed)
297357
{
298-
struct bch_fs *c = trans->c;
299-
300-
BUG_ON(!bp.v->level);
301-
302-
bch2_trans_node_iter_init(trans, iter,
303-
bp.v->btree_id,
304-
bp.v->pos,
305-
0,
306-
bp.v->level - 1,
307-
0);
308-
struct btree *b = bch2_btree_iter_peek_node(trans, iter);
309-
if (IS_ERR_OR_NULL(b))
310-
goto err;
311-
312-
BUG_ON(b->c.level != bp.v->level - 1);
313-
314-
if (extent_matches_bp(c, bp.v->btree_id, bp.v->level,
315-
bkey_i_to_s_c(&b->key), bp))
316-
return b;
358+
return __bch2_backpointer_get_node(trans, bp, iter, last_flushed, true);
359+
}
317360

318-
if (btree_node_will_make_reachable(b)) {
319-
b = ERR_PTR(-BCH_ERR_backpointer_to_overwritten_btree_node);
320-
} else {
321-
int ret = backpointer_target_not_found(trans, bp, bkey_i_to_s_c(&b->key), last_flushed);
322-
b = ret ? ERR_PTR(ret) : NULL;
323-
}
324-
err:
325-
bch2_trans_iter_exit(trans, iter);
326-
return b;
361+
struct bkey_s_c bch2_backpointer_get_key(struct btree_trans *trans,
362+
struct bkey_s_c_backpointer bp,
363+
struct btree_iter *iter,
364+
unsigned iter_flags,
365+
struct bkey_buf *last_flushed)
366+
{
367+
return __bch2_backpointer_get_key(trans, bp, iter, iter_flags, last_flushed, true);
327368
}
328369

329370
static int bch2_check_backpointer_has_valid_bucket(struct btree_trans *trans, struct bkey_s_c k,
@@ -521,7 +562,7 @@ static int check_bp_exists(struct btree_trans *trans,
521562
struct bkey_s_c_backpointer other_bp = bkey_s_c_to_backpointer(bp_k);
522563

523564
struct bkey_s_c other_extent =
524-
bch2_backpointer_get_key(trans, other_bp, &other_extent_iter, 0, NULL);
565+
__bch2_backpointer_get_key(trans, other_bp, &other_extent_iter, 0, NULL, false);
525566
ret = bkey_err(other_extent);
526567
if (ret == -BCH_ERR_backpointer_to_overwritten_btree_node)
527568
ret = 0;

0 commit comments

Comments
 (0)