From: sashiko-bot@kernel.org
To: "Mahanta Jambigi" <mjambigi@linux.ibm.com>
Cc: linux-s390@vger.kernel.org, linux-rdma@vger.kernel.org,
"Heiko Carstens" <hca@linux.ibm.com>,
"Vasily Gorbik" <gor@linux.ibm.com>,
"Alexander Gordeev" <agordeev@linux.ibm.com>,
"Christian Borntraeger" <borntraeger@linux.ibm.com>
Subject: Re: [PATCH net] net/smc: fix handshake abort on a terminating link group
Date: Wed, 04 Nov 2026 12:40:12 +0000 [thread overview]
Message-ID: <sashiko-outbox-197280@kernel.org> (raw)
In-Reply-To: <20261103123955.47454-1-mjambigi@linux.ibm.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] net/smc: rbtree corruption in smc_conn_kill() on device fallback retry
- [High] net/smc: missing lgr->freeing check and set in smc_lgr_cleanup_early()
--
commit e77e324c5a7154d6e8cb873cd42b560cca1a0bfa
Author: Mahanta Jambigi <mjambigi@linux.ibm.com>
net/smc: fix handshake abort on a terminating link group
This commit avoids calling __smc_lgr_terminate() in smc_lgr_cleanup_early()
when lgr->list is already empty, and updates smc_conn_kill() to unregister
and skip connections that already have conn->freed set.
> diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c
> index 6021322253e93..4e10da5bd8142 100644
> --- a/net/smc/smc_core.c
> +++ b/net/smc/smc_core.c
> @@ -688,9 +688,15 @@ void smc_lgr_cleanup_early(struct smc_link_group *lgr)
>
> smc_lgr_list_head(lgr, &lgr_lock);
> spin_lock_bh(lgr_lock);
> + if (list_empty(&lgr->list)) {
> + /* another context already removed the link group from the
> + * list and will terminate it
> + */
> + spin_unlock_bh(lgr_lock);
> + return;
> + }
> /* do not use this link group for new connections */
> - if (!list_empty(&lgr->list))
> - list_del_init(&lgr->list);
> + list_del_init(&lgr->list);
> spin_unlock_bh(lgr_lock);
> __smc_lgr_terminate(lgr, true);
> }
[Severity: High]
Should smc_lgr_cleanup_early() also check lgr->freeing and lgr->terminating,
and set lgr->freeing = 1 before dropping lgr_lock, similar to
smc_lgr_terminate_sched()?
In smc_smcd_terminate(), smc_smcd_terminate_all(), and
smc_smcr_terminate_all(), link groups are moved onto a stack-local
lgr_free_list and marked with lgr->freeing = 1 under lgr_lock before the
lock is dropped:
net/smc/smc_core.c:smc_smcd_terminate() {
...
list_move(&lgr->list, &lgr_free_list);
lgr->freeing = 1;
}
}
spin_unlock_bh(&dev->lgr_lock);
/* cancel the regular free workers and actually free lgrs */
list_for_each_entry_safe(lgr, l, &lgr_free_list, list) {
list_del_init(&lgr->list);
schedule_work(&lgr->terminate_work);
}
}
If smc_lgr_cleanup_early() runs after dev->lgr_lock is released but before
the second loop runs list_del_init(&lgr->list), list_empty(&lgr->list) is
still false because lgr->list is linked into the other thread's stack-local
lgr_free_list. Wouldn't smc_lgr_cleanup_early() then corrupt lgr_free_list
with an unsynchronized list_del_init(&lgr->list) and proceed into
__smc_lgr_terminate() while holding the socket lock?
Conversely, when smc_lgr_cleanup_early() wins lgr_lock and unlinks
lgr->list without setting lgr->freeing = 1, __smc_lgr_terminate() only
calls non-synchronous cancel_delayed_work(&lgr->free_work), relying on
lgr->freeing to stop a concurrent smc_lgr_free_work():
net/smc/smc_core.c:smc_lgr_free_work() {
...
smc_lgr_list_head(lgr, &lgr_lock);
spin_lock_bh(lgr_lock);
if (lgr->freeing) {
spin_unlock_bh(lgr_lock);
return;
}
...
}
If smc_lgr_free_work() is already running and waiting on lgr_lock, or is
scheduled again before termination finishes, could it see lgr->freeing == 0
and call smc_lgr_free(lgr) concurrently with __smc_lgr_terminate()?
> @@ -1519,6 +1525,17 @@ static void smc_conn_kill(struct smc_connection *conn, bool soft)
> {
> struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
>
> + if (conn->freed) {
> + /* connection was already freed, e.g. by an aborted
> + * handshake, but kept its entry because smc_conn_free() skips
> + * the unregister for a link group that is no longer on the lgr
> + * list. Remove the stale entry, but do not kill a socket that
> + * may be an established fallback connection.
> + */
> + smc_lgr_unregister_conn(conn);
> + return;
> + }
[Severity: High]
Can deferring smc_lgr_unregister_conn(conn) until smc_conn_kill() corrupt
lgr->conns_all or cause an infinite loop in __smc_lgr_terminate() when a
server handshake retries another device or SMC version on the same
new_smc->conn?
During server handshake in smc_listen_work(), lock_sock(&new_smc->sk) is
held across smc_listen_find_device(), which tries candidate ISM and RDMA
devices and versions in sequence:
smc_listen_work()
lock_sock(&new_smc->sk)
smc_listen_find_device()
smc_find_ism_v2_device_serv()
smc_find_ism_v1_device_serv()
smc_find_rdma_v2_device_serv()
smc_find_rdma_v1_device_serv()
In each candidate helper, if smc_conn_create() registers new_smc->conn in
a link group (say lgr1) that is concurrently removed from lgr_list for
termination, the subsequent buffer creation or RMB registration (such as
smc_listen_rdma_reg() -> smcr_lgr_reg_rmbs() -> smc_llc_flow_initiate(),
which fails with -ENOLINK when list_empty(&lgr1->list) is true) fails and
calls smc_conn_abort(new_smc, ini->first_contact_local):
net/smc/af_smc.c:smc_find_rdma_v2_device_serv() {
...
rc = smc_listen_rdma_init(new_smc, ini);
if (!rc) {
rc = smc_listen_rdma_reg(new_smc, ini->first_contact_local);
if (rc)
smc_conn_abort(new_smc, ini->first_contact_local);
}
...
}
Because list_empty(&lgr1->list) is true, smc_conn_free() skips
smc_lgr_unregister_conn() and leaves new_smc->conn->alert_node in
lgr1->conns_all with a non-zero conn->alert_token_local, while
smc_lgr_cleanup_early() now returns immediately. Meanwhile,
__smc_lgr_terminate(lgr1) cannot run smc_conn_kill() yet because
smc_listen_work() still holds lock_sock(&new_smc->sk).
When smc_listen_find_device() moves on to the next candidate device or
version while still holding lock_sock(&new_smc->sk), it calls
smc_conn_create() -> smc_lgr_register_conn() again on the same
new_smc->conn:
net/smc/smc_core.c:smc_lgr_register_conn() {
...
if (!conn->lgr->is_smcd) {
rc = smcr_lgr_conn_assign_link(conn, first);
if (rc) {
conn->lgr = NULL;
return rc;
}
}
/* find a new alert_token_local value not yet used by some connection
* in this link group
*/
sock_hold(&smc->sk); /* sock_put in smc_lgr_unregister_conn() */
while (!conn->alert_token_local) {
conn->alert_token_local = atomic_inc_return(&nexttoken);
if (smc_lgr_find_conn(conn->alert_token_local, conn->lgr))
conn->alert_token_local = 0;
}
smc_lgr_add_alert_token(conn);
}
At that point:
1. If the second smc_lgr_register_conn() succeeds on another link group
lgr2, it sets conn->lgr = lgr2, takes a second sock_hold(&smc->sk),
reuses the stale conn->alert_token_local, inserts conn->alert_node into
lgr2->conns_all while it is still linked in lgr1->conns_all, and clears
conn->freed = 0. When lock_sock(&new_smc->sk) is later released,
__smc_lgr_terminate(lgr1) wakes up and runs smc_conn_kill(), which sees
conn->freed == 0, aborts the active connection on lgr2, and unregisters
conn from conn->lgr (lgr2) instead of lgr1.
2. If the second smc_lgr_register_conn() fails in
smcr_lgr_conn_assign_link(), it sets conn->lgr = NULL. When
__smc_lgr_terminate(lgr1) later acquires lock_sock(&new_smc->sk) and
calls smc_conn_kill() -> smc_lgr_unregister_conn(conn),
smc_conn_lgr_valid(conn) is false because conn->lgr is NULL, so
conn->alert_node is never removed from rb_first(&lgr1->conns_all) and
__smc_lgr_terminate(lgr1) loops forever.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261103123955.47454-1-mjambigi@linux.ibm.com?part=1
prev parent reply other threads:[~2026-11-04 12:40 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 12:39 [PATCH net] net/smc: fix handshake abort on a terminating link group Mahanta Jambigi
2026-11-03 12:44 ` netdev-bot+sinfo
2026-11-04 12:40 ` sashiko-bot [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-197280@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=agordeev@linux.ibm.com \
--cc=borntraeger@linux.ibm.com \
--cc=gor@linux.ibm.com \
--cc=hca@linux.ibm.com \
--cc=linux-rdma@vger.kernel.org \
--cc=linux-s390@vger.kernel.org \
--cc=mjambigi@linux.ibm.com \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox