summaryrefslogtreecommitdiff
path: root/net/rds/rds.h
diff options
context:
space:
mode:
authorGerd Rausch <gerd.rausch@oracle.com>2026-01-21 22:52:13 -0700
committerJakub Kicinski <kuba@kernel.org>2026-01-23 11:51:31 -0800
commitdb69e9b838c39f4fb17d0547aeb71d55a7f28061 (patch)
treec27ec11609a9761b3a08cdbc10d4478c7f517489 /net/rds/rds.h
parentad22d24be635c6beab6a1fdd3f8b1f3c478d15da (diff)
downloadlinux-next-db69e9b838c39f4fb17d0547aeb71d55a7f28061.tar.gz
linux-next-db69e9b838c39f4fb17d0547aeb71d55a7f28061.zip
net/rds: rds_tcp_accept_one ought to not discard messages
RDS/TCP differs from RDS/RDMA in that message acknowledgment is done based on TCP sequence numbers: As soon as the last byte of a message has been acknowledged by the TCP stack of a peer, "rds_tcp_write_space()" goes on to discard prior messages from the send queue. Which is fine, for as long as the receiver never throws any messages away. Unfortunately, that is *not* the case since the introduction of MPRDS: commit 1a0e100fb2c96 "RDS: TCP: Enable multipath RDS for TCP" A new function "rds_tcp_accept_one_path" was introduced, which is entitled to return "NULL", if no connection path is currently available. Unfortunately, this happens after the "->accept()" call, and the new socket often already contains messages, since the peer already transitioned to "RDS_CONN_UP" on behalf of "TCP_ESTABLISHED". That's also the case after this [1]: commit 1a0e100fb2c96 "RDS: TCP: Force every connection to be initiated by numerically smaller IP address" which tried to address the situation of pending data by only transitioning connections from a smaller IP address to "RDS_CONN_UP". But even in those cases, and in particular if the "RDS_EXTHDR_NPATHS" handshake has not occurred yet, and therefore we're working with "c_npaths <= 1", "c_conn[0]" may be in a state distinct from "RDS_CONN_DOWN", and therefore all messages on the just accepted socket will be tossed away. This fix changes "rds_tcp_accept_one": * If connected from a peer with a larger IP address, the new socket will continue to get closed right away. With commit [1] above, there should not be any messages in the socket receive buffer, since the peer never transitioned to "RDS_CONN_UP". Therefore it should be okay to not make any efforts to dispatch the socket receive buffer. * If connected from a peer with a smaller IP address, we call "rds_tcp_accept_one_path" to find a free slot/"path". If found, business goes on as usual. If none was found, we save/stash the newly accepted socket into "rds_tcp_accepted_sock", in order to not lose any messages that may have arrived already. We then return from "rds_tcp_accept_one" with "-ENOBUFS". Later on, when a slot/"path" does become available again (e.g. state transitioned to "RDS_CONN_DOWN", or HS extension header was received with "c_npaths > 1") we call "rds_tcp_conn_slots_available" that simply re-issues a "rds_tcp_accept_one_path" worker-callback and picks up the new socket from "rds_tcp_accepted_sock", and thereby continuing where it left with "-ENOBUFS" last time. Since a new slot has become available, those messages won't be lost, since processing proceeds as if that slot had been available the first time around. Signed-off-by: Gerd Rausch <gerd.rausch@oracle.com> Signed-off-by: Jack Vogel <jack.vogel@oracle.com> Signed-off-by: Allison Henderson <allison.henderson@oracle.com> Link: https://patch.msgid.link/20260122055213.83608-3-achender@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/rds/rds.h')
-rw-r--r--net/rds/rds.h66
1 files changed, 39 insertions, 27 deletions
diff --git a/net/rds/rds.h b/net/rds/rds.h
index b35afa2658cc..8a549fe687ac 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -506,33 +506,6 @@ struct rds_notifier {
*/
#define RDS_TRANS_LOOP 3
-/**
- * struct rds_transport - transport specific behavioural hooks
- *
- * @xmit: .xmit is called by rds_send_xmit() to tell the transport to send
- * part of a message. The caller serializes on the send_sem so this
- * doesn't need to be reentrant for a given conn. The header must be
- * sent before the data payload. .xmit must be prepared to send a
- * message with no data payload. .xmit should return the number of
- * bytes that were sent down the connection, including header bytes.
- * Returning 0 tells the caller that it doesn't need to perform any
- * additional work now. This is usually the case when the transport has
- * filled the sending queue for its connection and will handle
- * triggering the rds thread to continue the send when space becomes
- * available. Returning -EAGAIN tells the caller to retry the send
- * immediately. Returning -ENOMEM tells the caller to retry the send at
- * some point in the future.
- *
- * @conn_shutdown: conn_shutdown stops traffic on the given connection. Once
- * it returns the connection can not call rds_recv_incoming().
- * This will only be called once after conn_connect returns
- * non-zero success and will The caller serializes this with
- * the send and connecting paths (xmit_* and conn_*). The
- * transport is responsible for other serialization, including
- * rds_recv_incoming(). This is called in process context but
- * should try hard not to block.
- */
-
struct rds_transport {
char t_name[TRANSNAMSIZ];
struct list_head t_item;
@@ -545,10 +518,49 @@ struct rds_transport {
__u32 scope_id);
int (*conn_alloc)(struct rds_connection *conn, gfp_t gfp);
void (*conn_free)(void *data);
+
+ /*
+ * conn_slots_available is invoked when a previously unavailable
+ * connection slot becomes available again. rds_tcp_accept_one_path may
+ * return -ENOBUFS if it cannot find an available slot, and then stashes
+ * the new socket in "rds_tcp_accepted_sock". This function re-issues
+ * `rds_tcp_accept_one_path`, which picks up the stashed socket and
+ * continuing where it left with "-ENOBUFS" last time. This ensures
+ * messages received on the new socket are not discarded when no
+ * connection path was available at the time.
+ */
+ void (*conn_slots_available)(struct rds_connection *conn);
int (*conn_path_connect)(struct rds_conn_path *cp);
+
+ /*
+ * conn_shutdown stops traffic on the given connection. Once
+ * it returns the connection can not call rds_recv_incoming().
+ * This will only be called once after conn_connect returns
+ * non-zero success and will The caller serializes this with
+ * the send and connecting paths (xmit_* and conn_*). The
+ * transport is responsible for other serialization, including
+ * rds_recv_incoming(). This is called in process context but
+ * should try hard not to block.
+ */
void (*conn_path_shutdown)(struct rds_conn_path *conn);
void (*xmit_path_prepare)(struct rds_conn_path *cp);
void (*xmit_path_complete)(struct rds_conn_path *cp);
+
+ /*
+ * .xmit is called by rds_send_xmit() to tell the transport to send
+ * part of a message. The caller serializes on the send_sem so this
+ * doesn't need to be reentrant for a given conn. The header must be
+ * sent before the data payload. .xmit must be prepared to send a
+ * message with no data payload. .xmit should return the number of
+ * bytes that were sent down the connection, including header bytes.
+ * Returning 0 tells the caller that it doesn't need to perform any
+ * additional work now. This is usually the case when the transport has
+ * filled the sending queue for its connection and will handle
+ * triggering the rds thread to continue the send when space becomes
+ * available. Returning -EAGAIN tells the caller to retry the send
+ * immediately. Returning -ENOMEM tells the caller to retry the send at
+ * some point in the future.
+ */
int (*xmit)(struct rds_connection *conn, struct rds_message *rm,
unsigned int hdr_off, unsigned int sg, unsigned int off);
int (*xmit_rdma)(struct rds_connection *conn, struct rm_rdma_op *op);