[PATCH 4/8] SUNRPC: resume receiving after a TLS control record
Chuck Lever <[email protected]> Wed, 05 Aug 2026 14:30:55 -0400
| Newsgroups | gmane.linux.nfs,gmane.linux.network |
|---|---|
| Message-ID | <[email protected]> |
A TLS control record delivers no payload to the RPC layer.
svc_tcp_recvfrom() clears XPT_DATA before the receive, and
svc_tcp_sock_recv_cmsg() returns -EAGAIN for the record it consumed.
Nothing marks the transport ready again. kTLS raises data_ready for
arriving TCP segments, not for records it has already decrypted. An
RPC Call queued behind an alert or a KeyUpdate waits until the client
sends more. The client blocks until its RPC timeout expires.
The receive takes only the first two octets of the record. kTLS holds
the remainder on its receive list, where each later receive takes two
octets more.
Drain a record that is not an alert, then mark the transport ready
once a control record has been consumed.
Fixes: 5e052dda121e ("SUNRPC: Recognize control messages in server-side TCP socket code")
Signed-off-by: Chuck Lever <[email protected]>
---
net/sunrpc/svcsock.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 52 insertions(+), 3 deletions(-)
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 756db84e4aec..d8e836e0832e 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -238,6 +238,39 @@ static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining)
return len;
}
+/*
+ * kTLS delivers a record only up to the caller's buffer and keeps
+ * the remainder on its receive list, where no further data_ready
+ * announces it. Consume the whole record.
+ */
+static void
+svc_tcp_sock_drain_record(struct socket *sock)
+{
+ union {
+ struct cmsghdr cmsg;
+ u8 buf[CMSG_SPACE(sizeof(u8))];
+ } u;
+ u8 discard[64];
+ struct kvec discard_kvec = {
+ .iov_base = discard,
+ .iov_len = sizeof(discard),
+ };
+
+ for (;;) {
+ struct msghdr msg = {
+ .msg_control = &u,
+ .msg_controllen = sizeof(u),
+ };
+
+ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &discard_kvec, 1,
+ discard_kvec.iov_len);
+ if (sock_recvmsg(sock, &msg, MSG_DONTWAIT) <= 0)
+ break;
+ if (msg.msg_flags & MSG_EOR)
+ break;
+ }
+}
+
static int
svc_tcp_sock_process_cmsg(struct socket *sock, struct msghdr *msg,
struct cmsghdr *cmsg, int ret)
@@ -303,12 +336,20 @@ svc_tcp_sock_recv_cmsg(struct socket *sock, unsigned int *msg_flags)
* kTLS filled in u.cmsg.
*/
if (ret >= 0 && msg.msg_controllen < sizeof(u)) {
+ u8 content_type = tls_get_record_type(sock->sk, &u.cmsg);
+
/* Returning the count would credit the RPC stream with
* octets that never reached the caller's buffer.
*/
- if (tls_get_record_type(sock->sk, &u.cmsg) !=
- TLS_RECORD_TYPE_ALERT)
+ if (content_type != TLS_RECORD_TYPE_ALERT) {
+ /* Draining an application data record would
+ * discard the RPC stream.
+ */
+ if (content_type != TLS_RECORD_TYPE_DATA &&
+ !(msg.msg_flags & MSG_EOR))
+ svc_tcp_sock_drain_record(sock);
return -EAGAIN;
+ }
/* An Alert record carries exactly one two-octet message
* (RFC 8446 Section 5.1). alert_kvec caps the receive at two,
* so a longer record produces the same count. MSG_EOR appears
@@ -331,8 +372,16 @@ svc_tcp_sock_recvmsg(struct svc_sock *svsk, struct msghdr *msg)
ret = sock_recvmsg(sock, msg, MSG_DONTWAIT);
if (msg->msg_flags & MSG_CTRUNC) {
msg->msg_flags &= ~(MSG_CTRUNC | MSG_EOR);
- if (ret == 0 || ret == -EIO)
+ if (ret == 0 || ret == -EIO) {
ret = svc_tcp_sock_recv_cmsg(sock, &msg->msg_flags);
+ /* A control record delivers nothing to the caller,
+ * and kTLS announces no data_ready for records it
+ * already holds. Mark the transport ready so that
+ * the records behind this one are received.
+ */
+ if (ret == -EAGAIN)
+ set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+ }
}
return ret;
}
--
2.54.0