bcachefs: Fix bch2_btree_node_upgrade()
authorKent Overstreet <kent.overstreet@gmail.com>
Fri, 5 Aug 2022 17:06:44 +0000 (13:06 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:41 +0000 (17:09 -0400)
Previously, if we were trying to upgrade from a read to an intent lock
but we held an additional read lock via another btree_path,
bch2_btree_node_upgrade() would always fail, in six_lock_tryupgrade().

This patch factors out the code that __bch2_btree_node_lock_write() uses
to temporarily drop extra read locks, so that six_lock_tryupgrade() can
succeed.

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

index 08dbc799bb35b6ce3ad3bba1d06469feb8f09a88..5b6d8184ea4522ef8f19585f723d42b8496ca820 100644 (file)
@@ -230,6 +230,7 @@ bool bch2_btree_node_upgrade(struct btree_trans *trans,
                             struct btree_path *path, unsigned level)
 {
        struct btree *b = path->l[level].b;
+       struct six_lock_count count = bch2_btree_node_lock_counts(trans, path, &b->c, level);
 
        if (!is_btree_node(path, level))
                return false;
@@ -253,11 +254,24 @@ bool bch2_btree_node_upgrade(struct btree_trans *trans,
        if (race_fault())
                return false;
 
-       if (btree_node_locked(path, level)
-           ? six_lock_tryupgrade(&b->c.lock)
-           : six_relock_type(&b->c.lock, SIX_LOCK_intent, path->l[level].lock_seq))
-               goto success;
+       if (btree_node_locked(path, level)) {
+               bool ret;
+
+               six_lock_readers_add(&b->c.lock, -count.n[SIX_LOCK_read]);
+               ret = six_lock_tryupgrade(&b->c.lock);
+               six_lock_readers_add(&b->c.lock, count.n[SIX_LOCK_read]);
+
+               if (ret)
+                       goto success;
+       } else {
+               if (six_relock_type(&b->c.lock, SIX_LOCK_intent, path->l[level].lock_seq))
+                       goto success;
+       }
 
+       /*
+        * Do we already have an intent lock via another path? If so, just bump
+        * lock count:
+        */
        if (btree_node_lock_seq_matches(path, b, level) &&
            btree_node_lock_increment(trans, &b->c, level, BTREE_NODE_INTENT_LOCKED)) {
                btree_node_unlock(trans, path, level);