mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 04:31:50 +00:00
bcachefs: Fix a deadlock on starting an interior btree update
Not legal to block on a journal prereservation with btree locks held. Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com> Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
1e3b1f9a22
commit
0f9dda478f
@ -935,7 +935,7 @@ static void bch2_coalesce_nodes(struct bch_fs *c, struct btree_iter *iter,
|
||||
return;
|
||||
}
|
||||
|
||||
as = bch2_btree_update_start(c, iter->btree_id,
|
||||
as = bch2_btree_update_start(iter->trans, iter->btree_id,
|
||||
btree_update_reserve_required(c, parent) + nr_old_nodes,
|
||||
BTREE_INSERT_NOFAIL|
|
||||
BTREE_INSERT_USE_RESERVE,
|
||||
|
@ -949,14 +949,34 @@ void bch2_btree_update_done(struct btree_update *as)
|
||||
}
|
||||
|
||||
struct btree_update *
|
||||
bch2_btree_update_start(struct bch_fs *c, enum btree_id id,
|
||||
bch2_btree_update_start(struct btree_trans *trans, enum btree_id id,
|
||||
unsigned nr_nodes, unsigned flags,
|
||||
struct closure *cl)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct journal_preres journal_preres = { 0 };
|
||||
struct btree_reserve *reserve;
|
||||
struct btree_update *as;
|
||||
int ret;
|
||||
|
||||
ret = bch2_journal_preres_get(&c->journal, &journal_preres,
|
||||
BTREE_UPDATE_JOURNAL_RES,
|
||||
JOURNAL_RES_GET_NONBLOCK);
|
||||
if (ret == -EAGAIN) {
|
||||
bch2_trans_unlock(trans);
|
||||
|
||||
ret = bch2_journal_preres_get(&c->journal, &journal_preres,
|
||||
BTREE_UPDATE_JOURNAL_RES,
|
||||
JOURNAL_RES_GET_NONBLOCK);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (!bch2_trans_relock(trans)) {
|
||||
bch2_journal_preres_put(&c->journal, &journal_preres);
|
||||
return ERR_PTR(-EINTR);
|
||||
}
|
||||
}
|
||||
|
||||
reserve = bch2_btree_reserve_get(c, nr_nodes, flags, cl);
|
||||
if (IS_ERR(reserve))
|
||||
return ERR_CAST(reserve);
|
||||
@ -969,18 +989,10 @@ bch2_btree_update_start(struct bch_fs *c, enum btree_id id,
|
||||
as->btree_id = id;
|
||||
as->reserve = reserve;
|
||||
INIT_LIST_HEAD(&as->write_blocked_list);
|
||||
as->journal_preres = journal_preres;
|
||||
|
||||
bch2_keylist_init(&as->parent_keys, as->inline_keys);
|
||||
|
||||
ret = bch2_journal_preres_get(&c->journal, &as->journal_preres,
|
||||
ARRAY_SIZE(as->journal_entries), 0);
|
||||
if (ret) {
|
||||
bch2_btree_reserve_put(c, reserve);
|
||||
closure_debug_destroy(&as->cl);
|
||||
mempool_free(as, &c->btree_interior_update_pool);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
mutex_lock(&c->btree_interior_update_lock);
|
||||
list_add_tail(&as->list, &c->btree_interior_update_list);
|
||||
mutex_unlock(&c->btree_interior_update_lock);
|
||||
@ -1551,7 +1563,7 @@ int bch2_btree_split_leaf(struct bch_fs *c, struct btree_iter *iter,
|
||||
goto out;
|
||||
}
|
||||
|
||||
as = bch2_btree_update_start(c, iter->btree_id,
|
||||
as = bch2_btree_update_start(trans, iter->btree_id,
|
||||
btree_update_reserve_required(c, b), flags,
|
||||
!(flags & BTREE_INSERT_NOUNLOCK) ? &cl : NULL);
|
||||
if (IS_ERR(as)) {
|
||||
@ -1663,7 +1675,7 @@ retry:
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
as = bch2_btree_update_start(c, iter->btree_id,
|
||||
as = bch2_btree_update_start(trans, iter->btree_id,
|
||||
btree_update_reserve_required(c, parent) + 1,
|
||||
BTREE_INSERT_NOFAIL|
|
||||
BTREE_INSERT_USE_RESERVE,
|
||||
@ -1776,7 +1788,7 @@ static int __btree_node_rewrite(struct bch_fs *c, struct btree_iter *iter,
|
||||
struct btree *n, *parent = btree_node_parent(iter, b);
|
||||
struct btree_update *as;
|
||||
|
||||
as = bch2_btree_update_start(c, iter->btree_id,
|
||||
as = bch2_btree_update_start(iter->trans, iter->btree_id,
|
||||
(parent
|
||||
? btree_update_reserve_required(c, parent)
|
||||
: 0) + 1,
|
||||
@ -2043,7 +2055,7 @@ int bch2_btree_node_update_key(struct bch_fs *c, struct btree_iter *iter,
|
||||
new_hash = bch2_btree_node_mem_alloc(c);
|
||||
}
|
||||
|
||||
as = bch2_btree_update_start(c, iter->btree_id,
|
||||
as = bch2_btree_update_start(iter->trans, iter->btree_id,
|
||||
parent ? btree_update_reserve_required(c, parent) : 0,
|
||||
BTREE_INSERT_NOFAIL|
|
||||
BTREE_INSERT_USE_RESERVE|
|
||||
|
@ -32,6 +32,9 @@ struct pending_btree_node_free {
|
||||
__BKEY_PADDED(key, BKEY_BTREE_PTR_VAL_U64s_MAX);
|
||||
};
|
||||
|
||||
#define BTREE_UPDATE_JOURNAL_RES \
|
||||
((BKEY_BTREE_PTR_U64s_MAX + 1) * (BTREE_MAX_DEPTH - 1) * 2)
|
||||
|
||||
/*
|
||||
* Tracks an in progress split/rewrite of a btree node and the update to the
|
||||
* parent node:
|
||||
@ -105,8 +108,7 @@ struct btree_update {
|
||||
unsigned nr_new_nodes;
|
||||
|
||||
unsigned journal_u64s;
|
||||
u64 journal_entries[
|
||||
(BKEY_BTREE_PTR_U64s_MAX + 1) * (BTREE_MAX_DEPTH - 1) * 2];
|
||||
u64 journal_entries[BTREE_UPDATE_JOURNAL_RES];
|
||||
|
||||
/* Only here to reduce stack usage on recursive splits: */
|
||||
struct keylist parent_keys;
|
||||
@ -132,7 +134,7 @@ struct btree *__bch2_btree_node_alloc_replacement(struct btree_update *,
|
||||
|
||||
void bch2_btree_update_done(struct btree_update *);
|
||||
struct btree_update *
|
||||
bch2_btree_update_start(struct bch_fs *, enum btree_id, unsigned,
|
||||
bch2_btree_update_start(struct btree_trans *, enum btree_id, unsigned,
|
||||
unsigned, struct closure *);
|
||||
|
||||
void bch2_btree_interior_update_will_free_node(struct btree_update *,
|
||||
|
Loading…
Reference in New Issue
Block a user