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: 13+ 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 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 4:43 ` [PATCH net-next v12 03/12] net/rds: guard every work-requeueing site with rds_destroy_pending() Allison Henderson
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 4:43 ` [PATCH net-next v12 05/12] net/rds: split connection destroy into quiesce and kref-governed free Allison Henderson
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 4:43 ` [PATCH net-next v12 07/12] net/rds: wait for connections to be freed on transport unload Allison Henderson
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 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 4:43 ` [PATCH net-next v12 10/12] net/rds: hold connection references in lookup, sockets and c_passive Allison Henderson
2026-11-13 4:43 ` Allison Henderson [this message]
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
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.