bcachefs: Fix deadlock
authorAlan Huang <mmpgouride@gmail.com>
Wed, 26 Feb 2025 09:33:22 +0000 (17:33 +0800)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 27 Feb 2025 00:31:05 +0000 (19:31 -0500)
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/66f97d9a.050a0220.6bad9.001d.GAE@google.com/T/

Reported-by: syzbot+fe63f377148a6371a9db@syzkaller.appspotmail.com
Tested-by: syzbot+fe63f377148a6371a9db@syzkaller.appspotmail.com
Signed-off-by: Alan Huang <mmpgouride@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/btree_cache.c
fs/bcachefs/btree_key_cache.c
fs/bcachefs/btree_locking.c
fs/bcachefs/btree_locking.h
fs/bcachefs/six.c
fs/bcachefs/six.h

index ca755e8d1a372a0c54ead036c52fda36d864ba49..1ec1f90e0eb38c94e8368d2c20731e2ffd931ccd 100644 (file)
@@ -203,7 +203,7 @@ struct btree *__bch2_btree_node_mem_alloc(struct bch_fs *c)
                return NULL;
        }
 
-       bch2_btree_lock_init(&b->c, 0);
+       bch2_btree_lock_init(&b->c, 0, GFP_KERNEL);
 
        __bch2_btree_node_to_freelist(bc, b);
        return b;
@@ -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 : 0, GFP_NOWAIT);
+       } 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, GFP_KERNEL);
                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));
 
index 1821f40c161a17fce81e6467dea3e7916738a813..edce594333756e05b891f2a7ac36095aa14ae595 100644 (file)
@@ -156,7 +156,7 @@ bkey_cached_alloc(struct btree_trans *trans, struct btree_path *path, unsigned k
        }
 
        if (ck) {
-               bch2_btree_lock_init(&ck->c, pcpu_readers ? SIX_LOCK_INIT_PCPU : 0);
+               bch2_btree_lock_init(&ck->c, pcpu_readers ? SIX_LOCK_INIT_PCPU : 0, GFP_KERNEL);
                ck->c.cached = true;
                goto lock;
        }
index 10b805a60f5269289a3a4e89abee39b36ea1c29d..caef65adeae4951df96aa17bb5db6d34d45a5af3 100644 (file)
@@ -7,9 +7,10 @@
 static struct lock_class_key bch2_btree_node_lock_key;
 
 void bch2_btree_lock_init(struct btree_bkey_cached_common *b,
-                         enum six_lock_init_flags flags)
+                         enum six_lock_init_flags flags,
+                         gfp_t gfp)
 {
-       __six_lock_init(&b->lock, "b->c.lock", &bch2_btree_node_lock_key, flags);
+       __six_lock_init(&b->lock, "b->c.lock", &bch2_btree_node_lock_key, flags, gfp);
        lockdep_set_notrack_class(&b->lock);
 }
 
index b54ef48eb8cc2728b14238b5bd7fe5ebeebd25d9..b33ab7af8440214629f39b93712ab1065e399e86 100644 (file)
@@ -13,7 +13,7 @@
 #include "btree_iter.h"
 #include "six.h"
 
-void bch2_btree_lock_init(struct btree_bkey_cached_common *, enum six_lock_init_flags);
+void bch2_btree_lock_init(struct btree_bkey_cached_common *, enum six_lock_init_flags, gfp_t gfp);
 
 void bch2_trans_unlock_noassert(struct btree_trans *);
 void bch2_trans_unlock_write(struct btree_trans *);
index 7e7c66a1e1a6baae2c981c53588c95fc7ce88b98..7c403427fbdb885421227a60567808dc323052cd 100644 (file)
@@ -850,7 +850,8 @@ void six_lock_exit(struct six_lock *lock)
 EXPORT_SYMBOL_GPL(six_lock_exit);
 
 void __six_lock_init(struct six_lock *lock, const char *name,
-                    struct lock_class_key *key, enum six_lock_init_flags flags)
+                    struct lock_class_key *key, enum six_lock_init_flags flags,
+                    gfp_t gfp)
 {
        atomic_set(&lock->state, 0);
        raw_spin_lock_init(&lock->wait_lock);
@@ -873,7 +874,7 @@ 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);
+               lock->readers = alloc_percpu_gfp(unsigned, gfp);
        }
 #endif
 }
index c142e06b7a3a7c3ee73f1205a0a0408f3393e297..59b851cf8bacc4283373aad1398ed49991b86260 100644 (file)
@@ -164,18 +164,19 @@ enum six_lock_init_flags {
 };
 
 void __six_lock_init(struct six_lock *lock, const char *name,
-                    struct lock_class_key *key, enum six_lock_init_flags flags);
+                    struct lock_class_key *key, enum six_lock_init_flags flags,
+                    gfp_t gfp);
 
 /**
  * six_lock_init - initialize a six lock
  * @lock:      lock to initialize
  * @flags:     optional flags, i.e. SIX_LOCK_INIT_PCPU
  */
-#define six_lock_init(lock, flags)                                     \
+#define six_lock_init(lock, flags, gfp)                                        \
 do {                                                                   \
        static struct lock_class_key __key;                             \
                                                                        \
-       __six_lock_init((lock), #lock, &__key, flags);                  \
+       __six_lock_init((lock), #lock, &__key, flags, gfp);                     \
 } while (0)
 
 /**