[PATCH net v2] net/smc: avoid recursive sk_callback_lock in listen data_ready
From: Runyu Xiao
Date: Fri Jun 19 2026 - 01:54:48 EST
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@xxxxxxxxxxxxxxx
Signed-off-by: Runyu Xiao <runyu.xiao@xxxxxxxxxx>
---
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