bcachefs: Fix a deadlock on starting an interior btree update
authorKent Overstreet <kent.overstreet@gmail.com>
Mon, 6 Apr 2020 01:49:17 +0000 (21:49 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:38 +0000 (17:08 -0400)
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>
fs/bcachefs/btree_gc.c
fs/bcachefs/btree_update_interior.c
fs/bcachefs/btree_update_interior.h

index 6220ec9b540b806a91a912135fc9167a0f84c639..e4c1b90f3cb59ec9bfecda643dbfa54604d6e355 100644 (file)
@@ -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,
index 677be20f79651e253dcf2b96d40163fff878a3ba..daa4c0716c05072a40e6d824aefc684f7cf6ca1a 100644 (file)
@@ -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|
index d3498aed145b8398b2a8946d002bd6746879e360..fb35be00f1bb53eac91b510a2b162e18dd31ee38 100644 (file)
@@ -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 *,