]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
bcachefs: Fix assertion popping in transaction commit path
authorKent Overstreet <kent.overstreet@gmail.com>
Wed, 16 Dec 2020 18:35:16 +0000 (13:35 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:44 +0000 (17:08 -0400)
We can't be holding read locks on btree nodes when we go to take write
locks: this would deadlock if another thread is holding an intent lock
on the node we have a read lock on, and it tries to commit and upgrade
to a write lock.

But instead of triggering an assertion, if this happens we can just
upgrade the read lock to an intent lock.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/btree_update_leaf.c

index 4a0e248f6f82a89aa9113f43ecd21138cc8d0a0e..a4a5e084aad3d9ba458789d8a68142b9cfaa40f6 100644 (file)
@@ -503,6 +503,10 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans,
 
        /*
         * Can't be holding any read locks when we go to take write locks:
+        * another thread could be holding an intent lock on the same node we
+        * have a read lock on, and it'll block trying to take a write lock
+        * (because we hold a read lock) and it could be blocking us by holding
+        * its own read lock (while we're trying to to take write locks).
         *
         * note - this must be done after bch2_trans_journal_preres_get_cold()
         * or anything else that might call bch2_trans_relock(), since that
@@ -510,9 +514,15 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans,
         */
        trans_for_each_iter(trans, iter) {
                if (iter->nodes_locked != iter->nodes_intent_locked) {
-                       EBUG_ON(iter->flags & BTREE_ITER_KEEP_UNTIL_COMMIT);
-                       EBUG_ON(trans->iters_live & (1ULL << iter->idx));
-                       bch2_btree_iter_unlock_noinline(iter);
+                       if ((iter->flags & BTREE_ITER_KEEP_UNTIL_COMMIT) ||
+                           (trans->iters_live & (1ULL << iter->idx))) {
+                               if (!bch2_btree_iter_upgrade(iter, 1)) {
+                                       trace_trans_restart_upgrade(trans->ip);
+                                       return -EINTR;
+                               }
+                       } else {
+                               bch2_btree_iter_unlock_noinline(iter);
+                       }
                }
        }