This fixes two deadlocks:

1.pcpu_alloc_mutex involved one as pointed by syzbot[1]
2.recursion deadlock.

The root cause is that we hold the bc lock during alloc_percpu, fix it
by following the pattern used by __btree_node_mem_alloc().

[1] https://lore.kernel.org/all/[email protected]/T/

Reported-by: [email protected]
Tested-by: [email protected]
Signed-off-by: Alan Huang <[email protected]>
---

syzbot test: 
https://lore.kernel.org/all/[email protected]/T/#m92500622db70f4e4974576c390a51697e9e46314

 fs/bcachefs/btree_cache.c | 7 ++++---
 fs/bcachefs/six.c         | 8 +++++++-
 fs/bcachefs/six.h         | 1 +
 3 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/fs/bcachefs/btree_cache.c b/fs/bcachefs/btree_cache.c
index ca755e8d1a37..87be42fc889c 100644
--- a/fs/bcachefs/btree_cache.c
+++ b/fs/bcachefs/btree_cache.c
@@ -795,17 +795,18 @@ struct btree *bch2_btree_node_mem_alloc(struct 
btree_trans *trans, bool pcpu_rea
                }
 
        b = __btree_node_mem_alloc(c, GFP_NOWAIT|__GFP_NOWARN);
-       if (!b) {
+       if (b) {
+               bch2_btree_lock_init(&b->c, pcpu_read_locks ? 
SIX_LOCK_INIT_PCPU|SIX_LOCK_INIT_NOWAIT : 0);
+       } else {
                mutex_unlock(&bc->lock);
                bch2_trans_unlock(trans);
                b = __btree_node_mem_alloc(c, GFP_KERNEL);
                if (!b)
                        goto err;
+               bch2_btree_lock_init(&b->c, pcpu_read_locks ? 
SIX_LOCK_INIT_PCPU : 0);
                mutex_lock(&bc->lock);
        }
 
-       bch2_btree_lock_init(&b->c, pcpu_read_locks ? SIX_LOCK_INIT_PCPU : 0);
-
        BUG_ON(!six_trylock_intent(&b->c.lock));
        BUG_ON(!six_trylock_write(&b->c.lock));
 
diff --git a/fs/bcachefs/six.c b/fs/bcachefs/six.c
index 7e7c66a1e1a6..81c832061331 100644
--- a/fs/bcachefs/six.c
+++ b/fs/bcachefs/six.c
@@ -865,6 +865,9 @@ void __six_lock_init(struct six_lock *lock, const char 
*name,
         * userspace:
         */
 #ifdef __KERNEL__
+       EBUG_ON(!(flags & SIX_LOCK_INIT_PCPU) &&
+               (flags & SIX_LOCK_INIT_NOWAIT));
+
        if (flags & SIX_LOCK_INIT_PCPU) {
                /*
                 * We don't return an error here on memory allocation failure
@@ -873,7 +876,10 @@ void __six_lock_init(struct six_lock *lock, const char 
*name,
                 * failure if they wish by checking lock->readers, but generally
                 * will not want to treat it as an error.
                 */
-               lock->readers = alloc_percpu(unsigned);
+               if (flags & SIX_LOCK_INIT_NOWAIT)
+                       lock->readers = alloc_percpu_gfp(unsigned, GFP_NOWAIT);
+               else
+                       lock->readers = alloc_percpu(unsigned);
        }
 #endif
 }
diff --git a/fs/bcachefs/six.h b/fs/bcachefs/six.h
index c142e06b7a3a..d188f699339f 100644
--- a/fs/bcachefs/six.h
+++ b/fs/bcachefs/six.h
@@ -161,6 +161,7 @@ void six_lock_exit(struct six_lock *lock);
 
 enum six_lock_init_flags {
        SIX_LOCK_INIT_PCPU      = 1U << 0,
+       SIX_LOCK_INIT_NOWAIT    = 1U << 1,
 };
 
 void __six_lock_init(struct six_lock *lock, const char *name,
-- 
2.48.1


Reply via email to