From: Jakub Kicinski <kuba@kernel.org>
To: achender@kernel.org
Cc: Jakub Kicinski <kuba@kernel.org>,
netdev@vger.kernel.org, linux-rdma@vger.kernel.org,
pabeni@redhat.com, edumazet@google.com, horms@kernel.org,
jhubbard@nvidia.com, woni9911@gmail.com, michal.kubiak@intel.com,
leon@kernel.org
Subject: Re: [PATCH net v4 4/7] net/rds: tcp: don't force RDS_CONN_RESETTING over a concurrent shutdown
Date: Thu, 27 Aug 2026 11:02:18 -0700 [thread overview]
Message-ID: <20260827180218.3950964-1-kuba@kernel.org> (raw)
In-Reply-To: <20260824003759.127353-5-achender@kernel.org>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net/rds: tcp: don't force RDS_CONN_RESETTING over a concurrent shutdown
This replaces the unconditional atomic_set(&cp->cp_state,
RDS_CONN_RESETTING) in rds_tcp_reset_callbacks() with two conditional
rds_conn_path_transition() attempts (CONNECTING -> RESETTING, and
RESETTING -> RESETTING), and drops the path with rds_conn_path_drop() if
neither succeeds. The intent is to avoid stomping on a shutdown that is
already in flight.
> diff --git a/net/rds/tcp.c b/net/rds/tcp.c
> index b263634ac750d..ad14217867a4d 100644
> --- a/net/rds/tcp.c
> +++ b/net/rds/tcp.c
> @@ -150,9 +150,22 @@ void rds_tcp_reset_callbacks(struct socket *sock,
> * end up deadlocking with tcp_sendmsg(), and the RDS_IN_XMIT
> * would not get set. As a result, we set c_state to
> * RDS_CONN_RESETTTING, to ensure that rds_tcp_state_change
> - * cannot mark rds_conn_path_up() in the window before lock_sock()
> + * cannot mark rds_conn_path_up() in the window before lock_sock().
> + *
> + * Only make that transition if the path is still connecting
> + * (or already resetting from an earlier duel). A path in any
> + * other state - typically RDS_CONN_DISCONNECTING or
> + * RDS_CONN_ERROR with a shutdown in flight - is dropped
> + * instead. That still replaces its state, with RDS_CONN_ERROR,
> + * and queues one more shutdown pass, but rds_conn_shutdown()
> + * accepts RDS_CONN_ERROR in its final transition to
> + * RDS_CONN_DOWN, so the shutdown in flight completes normally.
> */
> - atomic_set(&cp->cp_state, RDS_CONN_RESETTING);
> + if (!rds_conn_path_transition(cp, RDS_CONN_CONNECTING,
> + RDS_CONN_RESETTING) &&
> + !rds_conn_path_transition(cp, RDS_CONN_RESETTING,
> + RDS_CONN_RESETTING))
> + rds_conn_path_drop(cp, 0);
[Severity: Medium]
Does this break the post-condition that the only caller of
rds_tcp_reset_callbacks() encodes?
Previously the unconditional atomic_set() guaranteed the path was left in
RDS_CONN_RESETTING. Now, when neither transition succeeds,
rds_conn_path_drop() leaves cp_state at RDS_CONN_ERROR, and
rds_tcp_reset_callbacks() returns with the path in that state.
In rds_tcp_accept_one() the state check under t_conn_path_lock explicitly
admits RDS_CONN_ERROR:
net/rds/tcp_listen.c:rds_tcp_accept_one() {
conn_state = rds_conn_path_state(cp);
WARN_ON(conn_state == RDS_CONN_UP);
if (conn_state != RDS_CONN_CONNECTING && conn_state != RDS_CONN_ERROR) {
rds_conn_path_drop(cp, 0);
goto rst_nsk;
}
...
if (rs_tcp->t_sock) {
/* Duelling SYN has been handled in rds_tcp_accept_one() */
rds_tcp_reset_callbacks(new_sock, cp);
/* rds_connect_path_complete() marks RDS_CONN_UP */
rds_connect_path_complete(cp, RDS_CONN_RESETTING);
}
So for a path found in RDS_CONN_ERROR with osock != NULL, both cmpxchg
attempts fail, the drop re-stamps RDS_CONN_ERROR, and the following
rds_connect_path_complete(cp, RDS_CONN_RESETTING) can never make its
RESETTING -> UP transition.
That lands in the failure path:
net/rds/threads.c:rds_connect_path_complete() {
if (!rds_conn_path_transition(cp, curr, RDS_CONN_UP)) {
printk(KERN_WARNING "%s: Cannot transition to state UP, "
"current state is %d\n", __func__,
atomic_read(&cp->cp_state));
rds_conn_path_drop(cp, false);
return;
}
which is an unconditional, non-ratelimited KERN_WARNING plus another drop,
rather than bringing the freshly installed socket up. The same mismatch is
reachable when a peer FIN/RST turns the state into RDS_CONN_ERROR via
rds_tcp_state_change() -> rds_conn_path_drop() between the accept-side check
and these two cmpxchgs, since nothing serializes cp_state against
t_conn_path_lock.
Should the hard-coded RDS_CONN_RESETTING argument in rds_tcp_accept_one() and
the "rds_connect_path_complete() marks RDS_CONN_UP" comment be updated, or
should rds_tcp_reset_callbacks() report which branch it took so the caller
can skip the complete step?
[Severity: High]
This isn't a bug introduced by this patch, but the newly added
rds_conn_path_drop(cp, 0) here queues one more cp_down_w pass on cp->cp_wq
while the accept worker is still inside rds_tcp_reset_callbacks() and about
to release osock. Can that widen the existing double-teardown window on the
same struct socket?
rds_tcp_reset_callbacks() samples the socket at entry:
net/rds/tcp.c:rds_tcp_reset_callbacks() {
struct socket *osock = tc->t_sock;
if (!osock)
goto newsock;
and later, after the drop, unconditionally tears it down:
rds_tcp_restore_callbacks(osock, tc);
release_sock(osock->sk);
sock_release(osock);
The shutdown worker latches the same pointer and does the same teardown:
net/rds/tcp_connect.c:rds_tcp_conn_path_shutdown() {
struct socket *sock = tc->t_sock;
...
lock_sock()/rds_tcp_restore_callbacks()/sock_release()
with a drain loop of up to ~5 s in between. Neither path holds a common
lock: rds_conn_path_drop() and rds_conn_shutdown() do not take
t_conn_path_lock, and the transport shutdown runs outside cp_cm_lock, so both
can sock_release() the same socket.
The pre-existing part is the unsynchronized sampling of tc->t_sock at the top
of rds_tcp_reset_callbacks(). Checking the states in which the new drop
actually fires with osock != NULL, each already had a shutdown queued, in
flight, or completed (with tc->t_sock already cleared), so I could not
identify a state where this patch alone creates a teardown race that did not
exist before.
Also worth noting for the series ordering: later commits in this series close
the window structurally, "net/rds: acquire RDS_IN_XMIT in
rds_tcp_reset_callbacks()" reads tc->t_sock only while owning RDS_IN_XMIT,
and "net/rds: acquire the fastpath locks in rds_conn_shutdown()" holds
RDS_IN_XMIT across conn_path_shutdown(). At this commit the window is still
open, so a bisect landing here still carries it. Would it make sense to
order those two commits before this one?
--
pw-bot: cr
next prev parent reply other threads:[~2026-08-27 18:02 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-24 0:37 [PATCH net v4 0/7] net/rds: own the fastpath locks across connection teardown Allison Henderson
2026-08-24 0:37 ` [PATCH net v4 1/7] net/rds: use wq_has_sleeper() in release_in_xmit() Allison Henderson
2026-08-24 0:37 ` [PATCH net v4 2/7] net/rds: use clear_bit_unlock() in release_refill() Allison Henderson
2026-08-24 0:37 ` [PATCH net v4 3/7] net/rds: clear cp_flags bits individually in rds_conn_path_reset() Allison Henderson
2026-08-24 0:37 ` [PATCH net v4 4/7] net/rds: tcp: don't force RDS_CONN_RESETTING over a concurrent shutdown Allison Henderson
2026-08-27 18:02 ` Jakub Kicinski [this message]
2026-08-24 0:37 ` [PATCH net v4 5/7] net/rds: acquire RDS_IN_XMIT in rds_tcp_reset_callbacks() Allison Henderson
2026-08-24 0:37 ` [PATCH net v4 6/7] net/rds: acquire the fastpath locks in rds_conn_shutdown() Allison Henderson
2026-08-27 18:02 ` Jakub Kicinski
2026-08-28 22:36 ` Allison Henderson
2026-08-24 0:37 ` [PATCH net v4 7/7] net/rds: don't let rds_conn_shutdown() consume a concurrent drop Allison Henderson
2026-08-27 18:02 ` Jakub Kicinski
2026-08-28 22:38 ` Allison Henderson
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=20260827180218.3950964-1-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=achender@kernel.org \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=jhubbard@nvidia.com \
--cc=leon@kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=michal.kubiak@intel.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=woni9911@gmail.com \
/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