From d63b6086589589f297074b9ef1591e61899ef014 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 28 Apr 2026 13:31:45 -0700 Subject: [PATCH] Use spin_lock_bh on recinf->lock to fix softirq deadlock timer_callback() runs in softirq context and acquires recinf->lock, but the process-context callers (scoutfs_recov_prepare, _begin, _finish, _is_pending, _next_pending, _shutdown) were taking the same lock with plain spin_lock(), leaving softirqs enabled. Found by Lockdep: ``` ================================ WARNING: inconsistent lock state 5.14.0-427.35.1.el9_4.x86_64+debug #1 Tainted: G OE ------- --- -------------------------------- inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage. swapper/2/0 [HC0[0]:SC1[1]:HE1:SE0] takes: ffff88813cdd9c20 (&recinf->lock){+.?.}-{2:2}, at: timer_callback+0x26/0x380 [scoutfs] {SOFTIRQ-ON-W} state was registered at: __lock_acquire+0x7d0/0x1900 lock_acquire+0x1da/0x640 _raw_spin_lock+0x34/0x80 scoutfs_recov_finish+0x80/0x830 [scoutfs] server_greeting+0x244/0xe60 [scoutfs] scoutfs_net_proc_worker+0x28a/0xce0 [scoutfs] recv_one_message+0x7e3/0xd10 [scoutfs] scoutfs_net_recv_worker+0x441/0xe00 [scoutfs] process_one_work+0x8e5/0x1530 worker_thread+0x598/0xf70 kthread+0x2a4/0x350 ret_from_fork+0x29/0x50 irq event stamp: 549813370 hardirqs last enabled at (549813370): [] _raw_spin_unlock_irq+0x24/0x50 hardirqs last disabled at (549813369): [] _raw_spin_lock_irq+0x5e/0x90 softirqs last enabled at (549813356): [] __do_softirq+0x621/0x9c2 softirqs last disabled at (549813363): [] __irq_exit_rcu+0x185/0x230 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&recinf->lock); lock(&recinf->lock); *** DEADLOCK *** ``` Convert the six process-context sites to spin_lock_bh()/spin_unlock_bh(). Signed-off-by: Auke Kok --- kmod/src/recov.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/kmod/src/recov.c b/kmod/src/recov.c index d77e39cf..96c71395 100644 --- a/kmod/src/recov.c +++ b/kmod/src/recov.c @@ -103,7 +103,7 @@ int scoutfs_recov_prepare(struct super_block *sb, u64 rid, int which) if (!alloc) return -ENOMEM; - spin_lock(&recinf->lock); + spin_lock_bh(&recinf->lock); pend = lookup_pending(recinf, rid, SCOUTFS_RECOV_ALL); if (pend) { @@ -116,7 +116,7 @@ int scoutfs_recov_prepare(struct super_block *sb, u64 rid, int which) list_sort(NULL, &recinf->pending, cmp_pending_rid); } - spin_unlock(&recinf->lock); + spin_unlock_bh(&recinf->lock); kfree(alloc); return 0; @@ -153,7 +153,7 @@ int scoutfs_recov_begin(struct super_block *sb, void (*timeout_fn)(struct super_ DECLARE_RECOV_INFO(sb, recinf); int ret; - spin_lock(&recinf->lock); + spin_lock_bh(&recinf->lock); recinf->timeout_fn = timeout_fn; recinf->timer.expires = jiffies + msecs_to_jiffies(timeout_ms); @@ -161,7 +161,7 @@ int scoutfs_recov_begin(struct super_block *sb, void (*timeout_fn)(struct super_ ret = recov_finished(recinf); - spin_unlock(&recinf->lock); + spin_unlock_bh(&recinf->lock); if (ret > 0) del_timer_sync(&recinf->timer); @@ -183,7 +183,7 @@ int scoutfs_recov_finish(struct super_block *sb, u64 rid, int which) struct recov_pending *pend; int ret = 0; - spin_lock(&recinf->lock); + spin_lock_bh(&recinf->lock); pend = lookup_pending(recinf, rid, which); if (pend) { @@ -196,7 +196,7 @@ int scoutfs_recov_finish(struct super_block *sb, u64 rid, int which) } } - spin_unlock(&recinf->lock); + spin_unlock_bh(&recinf->lock); if (ret > 0) del_timer_sync(&recinf->timer); @@ -215,9 +215,9 @@ bool scoutfs_recov_is_pending(struct super_block *sb, u64 rid, int which) DECLARE_RECOV_INFO(sb, recinf); bool is_pending; - spin_lock(&recinf->lock); + spin_lock_bh(&recinf->lock); is_pending = lookup_pending(recinf, rid, which) != NULL; - spin_unlock(&recinf->lock); + spin_unlock_bh(&recinf->lock); return is_pending; } @@ -236,10 +236,10 @@ u64 scoutfs_recov_next_pending(struct super_block *sb, u64 rid, int which) DECLARE_RECOV_INFO(sb, recinf); struct recov_pending *pend; - spin_lock(&recinf->lock); + spin_lock_bh(&recinf->lock); pend = next_pending(recinf, rid, which); rid = pend ? pend->rid : 0; - spin_unlock(&recinf->lock); + spin_unlock_bh(&recinf->lock); return rid; } @@ -257,10 +257,10 @@ void scoutfs_recov_shutdown(struct super_block *sb) del_timer_sync(&recinf->timer); - spin_lock(&recinf->lock); + spin_lock_bh(&recinf->lock); list_splice_init(&recinf->pending, &list); recinf->timeout_fn = NULL; - spin_unlock(&recinf->lock); + spin_unlock_bh(&recinf->lock); list_for_each_entry_safe(pend, tmp, &list, head) { list_del(&pend->head);