From: Allison Henderson <achender@kernel.org>
To: netdev@vger.kernel.org, linux-rdma@vger.kernel.org,
pabeni@redhat.com, edumazet@google.com, kuba@kernel.org,
horms@kernel.org
Cc: achender@kernel.org
Subject: [PATCH net-next v12 11/12] net/rds: give cm_id->context a reference on its connection
Date: Thu, 12 Nov 2026 21:43:54 -0700 [thread overview]
Message-ID: <20261113044355.2932029-12-achender@kernel.org> (raw)
In-Reply-To: <20261113044355.2932029-1-achender@kernel.org>
rds_rdma_cm_event_handler_cmn() picks the connection up from
cm_id->context and holds c_cm_lock - a mutex that lives in the
connection's path array - across the transport callbacks, but the
context pointer owns no reference. What keeps the connection alive
under the handler is ordering: rds_ib_conn_path_shutdown() destroys the
id, and rdma_destroy_id() waits for a running handler, before the
destroy drops the connection's initial reference, so no event is
handled on a connection that is being freed. Before this series that
ordering was broken from inside the handler, since rds_conn_destroy()
freed synchronously and the one callback that destroyed its
connection, rds_ib_cm_connect_complete() on a protocol version below
3.1, left the handler's mutex_unlock() running on freed memory;
commit f97d8c7bab78 ("rds: ib: use rds_conn_drop() on protocol version
mismatch") has since switched that path to rds_conn_drop(), which also
removed the deadlock that destroy took on c_cm_lock.
Now that connections are reference counted, give the id a reference of
its own rather than rely on that ordering. rds_ib_conn_path_connect()
takes it once rdma_create_id() has installed the connection as the
context, rds_ib_cm_handle_connect() takes it when it installs the id on
the passive side, and each is dropped after rdma_destroy_id() has
returned: in rds_ib_conn_path_shutdown(), and on the two failure paths
of rds_ib_conn_path_connect() that destroy the id straight away. With
that the handler cannot find its connection freed under it, whatever
the callbacks do to the other holders - the destroy's initial
reference, a socket's cache, a parent's c_passive, an inc. None of the
new puts can be the last reference: the shutdown runs on behalf of a
destroy, or of a drop on a connection still in the hash, and the
connect worker runs on a connection still in the hash, and each of
those holds a reference of its own.
rds_ib_cm_initiate_connect() and rds_ib_cm_handle_connect() get a
defensive check of the same kind. A destroy that began while the
address and route were resolving either waits on c_cm_lock behind the
ROUTE_RESOLVED handler - and its shutdown pass then tears down
whatever the handler set up, QP, cm_id and device reference alike - or
has already destroyed the id, in which case no event for it reaches
the handler. On the passive side a quiesced connection is unhashed,
so rds_conn_create() cannot hand it to a connect request, and the
listeners are stopped before any IB connection is destroyed at unload.
Neither path is reachable today, then; both functions nevertheless
decline to set up a QP, or install a new cm_id, on a connection whose
destroy has begun, so that the contract does not rest on that
ordering. rds_ib_cm_initiate_connect() returns success when it
declines: the id is still ic->i_cm_id, for the shutdown to destroy,
and a non-zero return would have the rdma_cm destroy it instead. An
unload that begins after the check has passed is covered by
rds_ib_add_conn() refusing a device that rds_ib_dev_shutdown() has
marked.
Assisted-by: Claude-Code:claude-fable-5
Signed-off-by: Allison Henderson <achender@kernel.org>
---
net/rds/ib_cm.c | 41 ++++++++++++++++++++++++++++++++++++----
net/rds/rdma_transport.c | 7 +++++++
2 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index 5bf6b22c10d6..a580a8c667ca 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -876,6 +876,15 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
* see the comment above rds_queue_reconnect()
*/
mutex_lock(&conn->c_cm_lock);
+ /* Defensive: a destroy that has quiesced this conn also unhashed
+ * it, so rds_conn_create() cannot have returned it, and the
+ * listeners are stopped before any IB connection is destroyed at
+ * unload. Should a request reach a conn in RDS_CONN_DOWN with
+ * its destroy begun all the same, do not install a new cm_id and
+ * QP that nothing would tear down.
+ */
+ if (rds_destroy_pending(conn))
+ goto out;
if (!rds_conn_transition(conn, RDS_CONN_DOWN, RDS_CONN_CONNECTING)) {
if (rds_conn_state(conn) == RDS_CONN_UP) {
rdsdebug("incoming connect while connecting\n");
@@ -904,6 +913,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
BUG_ON(ic->i_cm_id);
ic->i_cm_id = cm_id;
+ /* The id owns a reference on the connection it points at, put
+ * after rdma_destroy_id() in rds_ib_conn_path_shutdown().
+ */
+ rds_conn_get(conn);
cm_id->context = conn;
/* We got halfway through setting up the ib_connection, if we
@@ -928,10 +941,8 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
out:
if (conn) {
mutex_unlock(&conn->c_cm_lock);
- /* Drop the reference rds_conn_create() handed us. The
- * conn stays reachable through cm_id->context without a
- * reference of its own for now; the CM event handler is
- * given one of its own by a following patch.
+ /* Drop the reference rds_conn_create() handed us; the
+ * cm_id holds its own from here on.
*/
rds_conn_put(conn);
}
@@ -950,6 +961,18 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id, bool isv6)
union rds_ib_conn_priv dp;
int ret;
+ /* Defensive: a destroy that began while the address and route
+ * were being resolved either waits on c_cm_lock behind this
+ * handler, and its shutdown pass then tears down whatever is
+ * set up here, or has already destroyed the id this event
+ * arrived on, in which case the event never gets here. Decline
+ * to set up a QP all the same, and return success: the id is
+ * still ic->i_cm_id, for that shutdown to destroy, and a
+ * non-zero return would have the rdma_cm destroy it instead.
+ */
+ if (rds_destroy_pending(conn))
+ return 0;
+
/* If the peer doesn't do protocol negotiation, we must
* default to RDSv3.0 */
rds_ib_set_protocol(conn, RDS_PROTOCOL_4_1);
@@ -1007,6 +1030,10 @@ int rds_ib_conn_path_connect(struct rds_conn_path *cp)
rdsdebug("rdma_create_id() failed: %d\n", ret);
goto out;
}
+ /* The id owns a reference on the connection it points at, put
+ * after rdma_destroy_id() wherever the id is destroyed.
+ */
+ rds_conn_get(conn);
/* rds_ib_laddr_check() only vouched for the local address being
* on an IB device; the address resolution below picks the device
@@ -1017,6 +1044,7 @@ int rds_ib_conn_path_connect(struct rds_conn_path *cp)
rdsdebug("rdma_restrict_node_type() failed: %d\n", ret);
rdma_destroy_id(ic->i_cm_id);
ic->i_cm_id = NULL;
+ rds_conn_put(conn);
goto out;
}
@@ -1058,6 +1086,7 @@ int rds_ib_conn_path_connect(struct rds_conn_path *cp)
ret);
rdma_destroy_id(ic->i_cm_id);
ic->i_cm_id = NULL;
+ rds_conn_put(conn);
}
out:
@@ -1188,6 +1217,10 @@ void rds_ib_conn_path_shutdown(struct rds_conn_path *cp)
rds_ib_recv_clear_ring(ic);
rdma_destroy_id(ic->i_cm_id);
+ /* The id's reference on the connection. Never the last
+ * one: whoever runs this shutdown holds its own.
+ */
+ rds_conn_put(conn);
/*
* Move connection back to the nodev list.
diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c
index 75532734c054..5d7d41c2db29 100644
--- a/net/rds/rdma_transport.c
+++ b/net/rds/rdma_transport.c
@@ -60,6 +60,13 @@ static int rds_rdma_cm_event_handler_cmn(struct rdma_cm_id *cm_id,
rdsdebug("conn %p id %p handling event %u (%s)\n", conn, cm_id,
event->event, rdma_event_msg(event->event));
+ /* cm_id->context owns a reference on the connection, dropped
+ * only after rdma_destroy_id() has returned, and that waits for
+ * a running handler. The connection, and the mutex released at
+ * out: that lives in its path array, therefore outlive this
+ * handler whatever the callbacks below do to the other holders.
+ */
+
/* Prevent shutdown from tearing down the connection
* while we're executing. */
if (conn) {
--
2.50.1
next prev parent reply other threads:[~2026-11-13 4:44 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 4:43 [PATCH net-next v12 00/12] net/rds: make connection lifetime reference-counted Allison Henderson
2026-11-13 4:43 ` [PATCH net-next v12 01/12] net/rds: undo conn_alloc() the same way on every __rds_conn_create() exit Allison Henderson
2026-11-13 5:05 ` sashiko-bot
2026-11-13 4:43 ` [PATCH net-next v12 02/12] net/rds: ib: refuse to attach a connection to a device being removed Allison Henderson
2026-11-13 5:05 ` sashiko-bot
2026-11-13 4:43 ` [PATCH net-next v12 03/12] net/rds: guard every work-requeueing site with rds_destroy_pending() Allison Henderson
2026-11-13 5:05 ` sashiko-bot
2026-11-13 4:43 ` [PATCH net-next v12 04/12] net/rds: make rds_destroy_pending() report a connection's own destroy Allison Henderson
2026-11-13 5:05 ` sashiko-bot
2026-11-13 4:43 ` [PATCH net-next v12 05/12] net/rds: split connection destroy into quiesce and kref-governed free Allison Henderson
2026-11-13 5:05 ` sashiko-bot
2026-11-13 4:43 ` [PATCH net-next v12 06/12] net/rds: unlink transport nodes before a possibly deferred connection free Allison Henderson
2026-11-13 5:05 ` sashiko-bot
2026-11-13 4:43 ` [PATCH net-next v12 07/12] net/rds: wait for connections to be freed on transport unload Allison Henderson
2026-11-13 5:05 ` sashiko-bot
2026-11-13 4:43 ` [PATCH net-next v12 08/12] net/rds: hold a connection reference from struct rds_incoming Allison Henderson
2026-11-13 5:05 ` sashiko-bot
2026-11-13 4:43 ` [PATCH net-next v12 09/12] net/rds: take cp_lock to purge cp_send_queue in the quiesce Allison Henderson
2026-11-13 5:05 ` sashiko-bot
2026-11-13 4:43 ` [PATCH net-next v12 10/12] net/rds: hold connection references in lookup, sockets and c_passive Allison Henderson
2026-11-13 5:05 ` sashiko-bot
2026-11-13 4:43 ` Allison Henderson [this message]
2026-11-13 5:05 ` [PATCH net-next v12 11/12] net/rds: give cm_id->context a reference on its connection sashiko-bot
2026-11-13 4:43 ` [PATCH net-next v12 12/12] net/rds: drop rds_conn_count in favor of t_conn_count Allison Henderson
2026-11-13 5:05 ` sashiko-bot
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=20261113044355.2932029-12-achender@kernel.org \
--to=achender@kernel.org \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.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