smc_listen() installs smc_clcsock_data_ready() as the underlying TCP listen socket's sk_data_ready callback. The callback takes sk_callback_lock before looking up the SMC listener and queuing smc_tcp_listen_work(). This can recurse when the underlying TCP listen socket is being closed. The close/flush path may invoke the installed sk_data_ready callback with sk_callback_lock already held, so smc_clcsock_data_ready() tries to take the same rwlock again in the same thread. This issue was found by our static analysis tool and then manually reviewed against the current tree. The reproducer keeps the SMC listen callback installation path: smc_listen() smc_clcsock_replace_cb() sk_data_ready = smc_clcsock_data_ready() It then models the close/flush carrier that invokes the installed sk_data_ready callback while sk_callback_lock is already held. Lockdep reports the same-thread recursive acquisition: WARNING: possible recursive locking detected kworker/u4:3/39 is trying to acquire lock: (sk_callback_lock) at smc_clcsock_data_ready+0xa/0x4d but task is already holding lock: (sk_callback_lock) at smc_close_flush_work+0xc/0x30 Possible unsafe locking scenario: CPU0 ---- lock(sk_callback_lock); lock(sk_callback_lock); *** DEADLOCK *** Workqueue: smc_close_wq smc_close_flush_work Call Trace: dump_stack_lvl __lock_acquire lock_acquire _raw_read_lock_bh smc_clcsock_data_ready smc_close_flush_work process_one_work worker_thread kthread ret_from_fork The same pattern was fixed for nvmet TCP by checking TCP_LISTEN before taking sk_callback_lock: commit 2fa8961d3a6a ("nvmet-tcp: fixup hang in nvmet_tcp_listen_data_ready()") Do the same for SMC. smc_clcsock_data_ready() is installed by smc_listen() on the underlying TCP listen socket and only queues smc_tcp_listen_work() for the SMC listen/accept path. Once that socket is no longer in TCP_LISTEN, there is no listen accept work to queue from this callback, and avoiding sk_callback_lock also avoids the recursive locking path. Fixes: 0558226cebee ("net/smc: Fix slab-out-of-bounds issue in fallback") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao --- v2: - Include the fuller Lockdep stack from the grounded reproducer. - Add the related nvmet TCP fix reference. - Explain why the TCP_LISTEN check is valid for the SMC listen callback. net/smc/af_smc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 6421c2e1c84d..1af4e3c333ff 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -2631,6 +2631,9 @@ static void smc_clcsock_data_ready(struct sock *listen_clcsock) { struct smc_sock *lsmc; + if (READ_ONCE(listen_clcsock->sk_state) != TCP_LISTEN) + return; + read_lock_bh(&listen_clcsock->sk_callback_lock); lsmc = smc_clcsock_user_data(listen_clcsock); if (!lsmc) -- 2.34.1