* [PATCH net-next v2 1/6] net/tls: Bound consecutive no-data records in tls_sw_read_sock()
2026-07-20 14:27 [PATCH net-next v2 0/6] Deliver TLS control records to kernel read_sock consumers Chuck Lever
@ 2026-07-20 14:27 ` Chuck Lever
2026-07-20 14:27 ` [PATCH net-next v2 2/6] net: Introduce read_sock_rectype proto_ops for control record delivery Chuck Lever
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs,
Chuck Lever
A record that delivers no payload -- an empty TLS 1.3 data record
today, a control record once read_sock_rectype() lands -- leaves
tls_sw_read_sock() in its loop without advancing the caller's read
descriptor. A peer that streams such records keeps the receive loop
running, and the socket lock held, for as long as the records
arrive.
Cap the number of consecutive no-data records consumed per call. The
count resets on any record that delivers bytes, so a normal stream
is unaffected; a peer supplying only empty records is bounded to
TLS_RX_NODATA_LIMIT iterations before the call returns 0. read_sock
consumers treat that as "no progress, re-poll" rather than EOF, so
the connection stays up and makes progress once real data arrives.
Only tls_sw_read_sock() needs this cap. Its consumers drive the receive
loop from kernel context -- a work item or service thread holding the
socket lock across the whole call with no return to userspace -- so an
unbounded empty-record stream keeps that context and the lock pinned
for as long as the flood lasts. The cap supplies the return boundary
that a system call would otherwise provide. tls_sw_splice_read()
and tls_sw_recvmsg() already have one: they run in the calling task's
context, reschedule while draining the socket backlog (cond_resched()
in __release_sock()), and drop the socket lock when the call returns. A
flood there costs the caller only its own scheduler time, so the cap
would add nothing.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
net/tls/tls_sw.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index d4afc90fd796..087950ca639c 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2049,6 +2049,11 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
goto splice_read_end;
}
+/* Consecutive empty data records deliver no bytes; cap them per
+ * call so a peer streaming them cannot hold the socket lock here.
+ */
+#define TLS_RX_NODATA_LIMIT 16
+
int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t read_actor)
{
@@ -2057,6 +2062,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
struct tls_prot_info *prot = &tls_ctx->prot_info;
struct strp_msg *rxm = NULL;
struct sk_buff *skb = NULL;
+ unsigned int nodata_count = 0;
struct sk_psock *psock;
size_t flushed_at = 0;
bool released = true;
@@ -2122,7 +2128,13 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
* here instead.
*/
if (rxm->full_len == 0) {
+ err = 0;
consume_skb(skb);
+ /* tls_rx_reader_release() announces any parsed record
+ * on exit, so returning 0 here cannot strand it.
+ */
+ if (++nodata_count >= TLS_RX_NODATA_LIMIT)
+ break;
continue;
}
@@ -2133,6 +2145,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
goto read_sock_requeue;
}
copied += used;
+ nodata_count = 0;
if (used < rxm->full_len) {
rxm->offset += used;
rxm->full_len -= used;
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH net-next v2 2/6] net: Introduce read_sock_rectype proto_ops for control record delivery
2026-07-20 14:27 [PATCH net-next v2 0/6] Deliver TLS control records to kernel read_sock consumers Chuck Lever
2026-07-20 14:27 ` [PATCH net-next v2 1/6] net/tls: Bound consecutive no-data records in tls_sw_read_sock() Chuck Lever
@ 2026-07-20 14:27 ` Chuck Lever
2026-07-20 14:27 ` [PATCH net-next v2 3/6] tls: Implement read_sock_rectype for kTLS software path Chuck Lever
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs
From: Chuck Lever <chuck.lever@oracle.com>
Kernel TCP consumers that use the read_sock interface
(proto_ops.read_sock) cannot receive TLS control messages (Alerts,
Handshake records) when kTLS is active. The current
tls_sw_read_sock() method rejects non-data records with -EINVAL, and
the sk_read_actor_t callback has no channel for delivering record-
type metadata.
Four kernel subsystems are affected: NFSD (sunrpc svcsock), NFS
client (sunrpc xprtsock), NVMe target (nvmet-tcp), and NVMe host
(nvme-tcp). Each of these either falls back to the sock_recvmsg()
API or lacks TLS alert handling entirely.
A new read_sock_rectype method in struct proto_ops provides a
separate code path that delivers non-data TLS records to a callback,
without changing the behavior seen by existing read_sock consumers.
The new sk_read_rectype_actor_t callback type extends the
sk_read_actor_t signature with a rectype parameter carrying the
protocol-layer record type (for example, TLS_RECORD_TYPE_ALERT). The
record-type callback returns 0 to consume a record or a negative
value to requeue it and stop delivery; unlike the data callback, its
return value does not count bytes.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
include/linux/net.h | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/include/linux/net.h b/include/linux/net.h
index 277188a40c72..7a19a743a617 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -198,6 +198,13 @@ struct sk_buff;
struct proto_accept_arg;
typedef int (*sk_read_actor_t)(read_descriptor_t *, struct sk_buff *,
unsigned int, size_t);
+/* rectype carries the transport record type, for example a
+ * TLS_RECORD_TYPE_* value.
+ */
+typedef int (*sk_read_rectype_actor_t)(read_descriptor_t *,
+ struct sk_buff *,
+ unsigned int, size_t,
+ u8 rectype);
typedef int (*skb_read_actor_t)(struct sock *, struct sk_buff *);
@@ -264,6 +271,27 @@ struct proto_ops {
*/
int (*read_sock)(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t recv_actor);
+ /*
+ * read_sock_rectype splits delivery across two callbacks:
+ * recv_actor for data records, per the sk_read_actor_t
+ * convention, and rectype_actor for all other records,
+ * with rectype identifying each. A NULL rectype_actor
+ * leaves non-data records pending. rectype_actor returns 0
+ * to consume a record or negative to leave it pending for
+ * redelivery and stop delivery; the negative return is a
+ * backpressure signal, not a fatal error. Both callbacks
+ * report errors and early stop the way recv_actor does:
+ * by setting desc->count to 0 and recording the reason in
+ * desc->error, per the read_descriptor_t convention and
+ * independent of the return value. The return value reports
+ * only data bytes consumed by recv_actor; the caller
+ * detects an error or early stop via desc->count and
+ * desc->error.
+ */
+ int (*read_sock_rectype)(struct sock *sk,
+ read_descriptor_t *desc,
+ sk_read_actor_t recv_actor,
+ sk_read_rectype_actor_t rectype_actor);
/* This is different from read_sock(), it reads an entire skb at a time. */
int (*read_skb)(struct sock *sk, skb_read_actor_t recv_actor);
int (*sendmsg_locked)(struct sock *sk, struct msghdr *msg,
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH net-next v2 3/6] tls: Implement read_sock_rectype for kTLS software path
2026-07-20 14:27 [PATCH net-next v2 0/6] Deliver TLS control records to kernel read_sock consumers Chuck Lever
2026-07-20 14:27 ` [PATCH net-next v2 1/6] net/tls: Bound consecutive no-data records in tls_sw_read_sock() Chuck Lever
2026-07-20 14:27 ` [PATCH net-next v2 2/6] net: Introduce read_sock_rectype proto_ops for control record delivery Chuck Lever
@ 2026-07-20 14:27 ` Chuck Lever
2026-07-20 14:27 ` [PATCH net-next v2 4/6] selftests/tls: Add tests for data/control record interleaving Chuck Lever
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs
From: Chuck Lever <chuck.lever@oracle.com>
tls_sw_read_sock() rejects non-data records (alerts, handshake
messages) with -EINVAL. Kernel consumers that need TLS alert
delivery, such as NFSD, NFS client, and NVMe target, must fall back
to the sock_recvmsg() API to receive control messages via CMSG.
Implement the new read_sock_rectype() method for these consumers,
delivering non-data records to a callback through the kTLS software
receive path.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls.h | 3 +++
net/tls/tls_main.c | 5 +++++
net/tls/tls_sw.c | 41 ++++++++++++++++++++++++++++++++++++-----
3 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/net/tls/tls.h b/net/tls/tls.h
index 60a37bdaaa25..c21d2a985e13 100644
--- a/net/tls/tls.h
+++ b/net/tls/tls.h
@@ -168,6 +168,9 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
size_t len, unsigned int flags);
int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t read_actor);
+int tls_sw_read_sock_rectype(struct sock *sk, read_descriptor_t *desc,
+ sk_read_actor_t read_actor,
+ sk_read_rectype_actor_t rectype_actor);
int tls_device_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
void tls_device_splice_eof(struct socket *sock);
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index 8c588cdab733..4963e0caf6d5 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -949,12 +949,17 @@ static void build_proto_ops(struct proto_ops ops[TLS_NUM_CONFIG][TLS_NUM_CONFIG]
ops[TLS_BASE][TLS_SW ].splice_read = tls_sw_splice_read;
ops[TLS_BASE][TLS_SW ].poll = tls_sk_poll;
ops[TLS_BASE][TLS_SW ].read_sock = tls_sw_read_sock;
+ ops[TLS_BASE][TLS_SW ].read_sock_rectype = tls_sw_read_sock_rectype;
ops[TLS_SW ][TLS_SW ] = ops[TLS_SW ][TLS_BASE];
ops[TLS_SW ][TLS_SW ].splice_read = tls_sw_splice_read;
ops[TLS_SW ][TLS_SW ].poll = tls_sk_poll;
ops[TLS_SW ][TLS_SW ].read_sock = tls_sw_read_sock;
+ ops[TLS_SW ][TLS_SW ].read_sock_rectype = tls_sw_read_sock_rectype;
+ /* TLS_HW (device offload) RX entries inherit
+ * read_sock{,_rectype} from SW via the struct copies below.
+ */
#ifdef CONFIG_TLS_DEVICE
ops[TLS_HW ][TLS_BASE] = ops[TLS_BASE][TLS_BASE];
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 087950ca639c..af347b5b17fa 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2054,8 +2054,9 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
*/
#define TLS_RX_NODATA_LIMIT 16
-int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
- sk_read_actor_t read_actor)
+static int __tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
+ sk_read_actor_t read_actor,
+ sk_read_rectype_actor_t rectype_actor)
{
struct tls_context *tls_ctx = tls_get_ctx(sk);
struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx);
@@ -2115,10 +2116,27 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
tls_rx_rec_done(ctx);
}
- /* read_sock does not support reading control messages */
+ /* Control records (alerts, handshake) reach a consumer
+ * only through rectype_actor; without one, read_sock
+ * rejects them.
+ */
if (tlm->control != TLS_RECORD_TYPE_DATA) {
- err = -EINVAL;
- goto read_sock_requeue;
+ if (!rectype_actor) {
+ err = -EINVAL;
+ goto read_sock_requeue;
+ }
+ err = rectype_actor(desc, skb, rxm->offset,
+ rxm->full_len,
+ tlm->control);
+ if (err < 0)
+ goto read_sock_requeue;
+ err = 0;
+ /* rectype_actor consumes the whole record; no partial path */
+ consume_skb(skb);
+ skb = NULL;
+ if (++nodata_count >= TLS_RX_NODATA_LIMIT)
+ break;
+ continue;
}
/* An empty data record (legal in TLS 1.3) gives a zero
@@ -2164,6 +2182,19 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
goto read_sock_end;
}
+int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
+ sk_read_actor_t read_actor)
+{
+ return __tls_sw_read_sock(sk, desc, read_actor, NULL);
+}
+
+int tls_sw_read_sock_rectype(struct sock *sk, read_descriptor_t *desc,
+ sk_read_actor_t read_actor,
+ sk_read_rectype_actor_t rectype_actor)
+{
+ return __tls_sw_read_sock(sk, desc, read_actor, rectype_actor);
+}
+
bool tls_sw_sock_is_readable(struct sock *sk)
{
struct tls_context *tls_ctx = tls_get_ctx(sk);
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH net-next v2 4/6] selftests/tls: Add tests for data/control record interleaving
2026-07-20 14:27 [PATCH net-next v2 0/6] Deliver TLS control records to kernel read_sock consumers Chuck Lever
` (2 preceding siblings ...)
2026-07-20 14:27 ` [PATCH net-next v2 3/6] tls: Implement read_sock_rectype for kTLS software path Chuck Lever
@ 2026-07-20 14:27 ` Chuck Lever
2026-07-20 14:27 ` [PATCH net-next v2 5/6] SUNRPC: Use read_sock_rectype for svcsock TCP receives Chuck Lever
2026-07-20 14:28 ` [PATCH net-next v2 6/6] SUNRPC: Remove sock_recvmsg path from " Chuck Lever
5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs
From: Chuck Lever <chuck.lever@oracle.com>
The new read_sock_rectype proto_ops method delivers interleaved
data and control TLS records to kernel consumers through
separate callbacks. The existing selftest coverage for these
interleaving patterns is limited: data_control_data only
peeks, and splice_cmsg_to_pipe tests a single control record
in isolation.
Add seven tests that exercise the record patterns
read_sock_rectype is designed to handle:
- splice_data_cmsg_data: data-control-data via splice, with
the control record drained through recvmsg between the two
splice calls
- splice_multi_cmsg_data: data-control-control-data with
distinct content types, verifying that each control record
is independently drainable with its type preserved, and
splice resumes afterward
- recv_data_cmsg_data: complete consumption of a
data-control-data sequence through recv and recvmsg
- peek_cmsg_after_data: peek at an interleaved control
record after the preceding data record has been consumed,
then consume it
- cmsg_before_data: control record as the first record in the
stream, followed by data
- mixed_control_types: two different control record types
(distinct content_type values) interleaved with data,
verifying type preservation through delivery
- data_cmsg_eof: trailing control record followed by
connection close, verifying the receiver drains the
control record and then observes EOF
The fixture teardown is also updated to skip closing fd
when a test has already closed it (as data_cmsg_eof does).
These tests exercise the record decryption and rx_list
delivery pipeline shared by the recvmsg, splice, and
read_sock paths. read_sock_rectype itself is a kernel-internal
API without a direct userspace entry point, so the tests
validate through the userspace-accessible paths.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
tools/testing/selftests/net/tls.c | 298 +++++++++++++++++++++++++++++++++++++-
1 file changed, 297 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index cbdd3ea28b99..8136306b5caa 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -427,7 +427,8 @@ FIXTURE_SETUP(tls)
FIXTURE_TEARDOWN(tls)
{
- close(self->fd);
+ if (self->fd >= 0)
+ close(self->fd);
close(self->cfd);
}
@@ -897,6 +898,114 @@ TEST_F(tls, splice_dec_cmsg_to_pipe)
EXPECT_EQ(memcmp(test_str, buf, send_len), 0);
}
+/* Verify splice handles data-control-data: splice reads the data
+ * records successfully while the intervening control record must
+ * be drained via recvmsg before splice can continue.
+ */
+TEST_F(tls, splice_data_cmsg_data)
+{
+ char mem_send[TLS_PAYLOAD_MAX_LEN];
+ char mem_recv[TLS_PAYLOAD_MAX_LEN];
+ int send_len = 4096;
+ char *ctrl_str = "control";
+ int ctrl_len = strlen(ctrl_str) + 1;
+ char ctrl_buf[8];
+ int p[2];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ memrnd(mem_send, sizeof(mem_send));
+
+ ASSERT_GE(pipe(p), 0);
+
+ /* Send: data, control, data */
+ EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl_str, ctrl_len, 0),
+ ctrl_len);
+ EXPECT_EQ(send(self->fd, &mem_send[send_len], send_len, 0), send_len);
+
+ /* Splice first data record */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len);
+ EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+ EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
+
+ /* Splice hits control record, fails */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* Drain the control record via recvmsg */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ ctrl_buf, sizeof(ctrl_buf), MSG_WAITALL),
+ ctrl_len);
+ EXPECT_EQ(memcmp(ctrl_str, ctrl_buf, ctrl_len), 0);
+
+ /* Splice second data record */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len);
+ EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+ EXPECT_EQ(memcmp(&mem_send[send_len], mem_recv, send_len), 0);
+}
+
+/* Verify that multiple consecutive control records between data
+ * records can each be drained individually, and splice resumes
+ * afterward. The two control records use different content types
+ * to verify type preservation across the splice boundary.
+ */
+TEST_F(tls, splice_multi_cmsg_data)
+{
+ char mem_send[TLS_PAYLOAD_MAX_LEN];
+ char mem_recv[TLS_PAYLOAD_MAX_LEN];
+ int send_len = 4096;
+ char *ctrl1 = "alert1";
+ char *ctrl2 = "alert2";
+ int ctrl_len = strlen(ctrl1) + 1;
+ char ctrl_buf[7];
+ int p[2];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ memrnd(mem_send, sizeof(mem_send));
+
+ ASSERT_GE(pipe(p), 0);
+
+ /* Send: data, control(100), control(200), data */
+ EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl1, ctrl_len, 0), ctrl_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 200, ctrl2, ctrl_len, 0), ctrl_len);
+ EXPECT_EQ(send(self->fd, &mem_send[send_len], send_len, 0), send_len);
+
+ /* Splice first data */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len);
+ EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+ EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
+
+ /* Splice fails on first control record */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* Drain first control (type 100) */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ ctrl_buf, sizeof(ctrl_buf), MSG_WAITALL),
+ ctrl_len);
+ EXPECT_EQ(memcmp(ctrl1, ctrl_buf, ctrl_len), 0);
+
+ /* Splice fails on second control record */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* Drain second control (type 200) */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 200,
+ ctrl_buf, sizeof(ctrl_buf), MSG_WAITALL),
+ ctrl_len);
+ EXPECT_EQ(memcmp(ctrl2, ctrl_buf, ctrl_len), 0);
+
+ /* Splice second data */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len);
+ EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+ EXPECT_EQ(memcmp(&mem_send[send_len], mem_recv, send_len), 0);
+}
+
TEST_F(tls, recv_and_splice)
{
int send_len = TLS_PAYLOAD_MAX_LEN;
@@ -1682,6 +1791,193 @@ TEST_F(tls, data_control_data)
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_PEEK), send_len);
}
+/* Fully consume a data-control-data sequence. The existing
+ * data_control_data test only peeks; this exercises complete
+ * record delivery through recv and recvmsg.
+ */
+TEST_F(tls, recv_data_cmsg_data)
+{
+ char *data1 = "first_data";
+ char *ctrl = "ctrl_msg";
+ char *data2 = "second_data";
+ int d1_len = strlen(data1) + 1;
+ int c_len = strlen(ctrl) + 1;
+ int d2_len = strlen(data2) + 1;
+ char buf[20];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ EXPECT_EQ(send(self->fd, data1, d1_len, 0), d1_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl, c_len, 0), c_len);
+ EXPECT_EQ(send(self->fd, data2, d2_len, 0), d2_len);
+
+ /* First data record */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_WAITALL), d1_len);
+ EXPECT_EQ(memcmp(buf, data1, d1_len), 0);
+
+ /* recv without cmsg buffer fails on control record */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), 0), -1);
+ EXPECT_EQ(errno, EIO);
+
+ /* Drain control via recvmsg */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), MSG_WAITALL), c_len);
+ EXPECT_EQ(memcmp(buf, ctrl, c_len), 0);
+
+ /* Second data record */
+ EXPECT_EQ(recv(self->cfd, buf, d2_len, MSG_WAITALL), d2_len);
+ EXPECT_EQ(memcmp(buf, data2, d2_len), 0);
+}
+
+/* Peek at an interleaved control record after the preceding data
+ * record has been consumed, then consume it. MSG_PEEK exposes the
+ * control record's type without consuming it.
+ */
+TEST_F(tls, peek_cmsg_after_data)
+{
+ char *data = "leading";
+ char *ctrl = "middle";
+ char *tail = "trailing";
+ int d_len = strlen(data) + 1;
+ int c_len = strlen(ctrl) + 1;
+ int t_len = strlen(tail) + 1;
+ char buf[20];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ EXPECT_EQ(send(self->fd, data, d_len, 0), d_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl, c_len, 0), c_len);
+ EXPECT_EQ(send(self->fd, tail, t_len, 0), t_len);
+
+ /* Consume leading data */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_WAITALL), d_len);
+ EXPECT_EQ(memcmp(buf, data, d_len), 0);
+
+ /* Peek at the control record */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), MSG_PEEK), c_len);
+ EXPECT_EQ(memcmp(buf, ctrl, c_len), 0);
+
+ /* Consume the control record */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), 0), c_len);
+ EXPECT_EQ(memcmp(buf, ctrl, c_len), 0);
+
+ /* Trailing data */
+ EXPECT_EQ(recv(self->cfd, buf, t_len, MSG_WAITALL), t_len);
+ EXPECT_EQ(memcmp(buf, tail, t_len), 0);
+}
+
+/* Control record as the first record in the stream, followed by
+ * data. The control record must be drained before the data record
+ * becomes available.
+ */
+TEST_F(tls, cmsg_before_data)
+{
+ char *ctrl = "alert";
+ char *data = "payload";
+ int c_len = strlen(ctrl) + 1;
+ int d_len = strlen(data) + 1;
+ char buf[20];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl, c_len, 0), c_len);
+ EXPECT_EQ(send(self->fd, data, d_len, 0), d_len);
+
+ /* recv without cmsg fails */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), 0), -1);
+ EXPECT_EQ(errno, EIO);
+
+ /* Drain control via recvmsg */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), MSG_WAITALL), c_len);
+ EXPECT_EQ(memcmp(buf, ctrl, c_len), 0);
+
+ /* Data follows */
+ EXPECT_EQ(recv(self->cfd, buf, d_len, MSG_WAITALL), d_len);
+ EXPECT_EQ(memcmp(buf, data, d_len), 0);
+}
+
+/* Two different control record types interleaved with data.
+ * Each control record is delivered with its own type preserved;
+ * verify both types arrive intact.
+ */
+TEST_F(tls, mixed_control_types)
+{
+ char *data1 = "data1";
+ char *ctrl1 = "handshake";
+ char *ctrl2 = "alert_msg";
+ char *data2 = "data2";
+ int d1_len = strlen(data1) + 1;
+ int c1_len = strlen(ctrl1) + 1;
+ int c2_len = strlen(ctrl2) + 1;
+ int d2_len = strlen(data2) + 1;
+ char buf[20];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ EXPECT_EQ(send(self->fd, data1, d1_len, 0), d1_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl1, c1_len, 0), c1_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 200, ctrl2, c2_len, 0), c2_len);
+ EXPECT_EQ(send(self->fd, data2, d2_len, 0), d2_len);
+
+ /* First data */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_WAITALL), d1_len);
+ EXPECT_EQ(memcmp(buf, data1, d1_len), 0);
+
+ /* First control (type 100) */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), MSG_WAITALL), c1_len);
+ EXPECT_EQ(memcmp(buf, ctrl1, c1_len), 0);
+
+ /* Second control (type 200) */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 200,
+ buf, sizeof(buf), MSG_WAITALL), c2_len);
+ EXPECT_EQ(memcmp(buf, ctrl2, c2_len), 0);
+
+ /* Second data */
+ EXPECT_EQ(recv(self->cfd, buf, d2_len, MSG_WAITALL), d2_len);
+ EXPECT_EQ(memcmp(buf, data2, d2_len), 0);
+}
+
+/* Trailing control record with no data following it. The sender
+ * closes the connection after the control record; the receiver
+ * drains the control and then observes EOF.
+ */
+TEST_F(tls, data_cmsg_eof)
+{
+ char *data = "payload";
+ char *ctrl = "final";
+ int d_len = strlen(data) + 1;
+ int c_len = strlen(ctrl) + 1;
+ char buf[20];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ EXPECT_EQ(send(self->fd, data, d_len, 0), d_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl, c_len, 0), c_len);
+ EXPECT_EQ(close(self->fd), 0);
+ self->fd = -1;
+
+ /* Consume data */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_WAITALL), d_len);
+ EXPECT_EQ(memcmp(buf, data, d_len), 0);
+
+ /* Drain trailing control */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), 0), c_len);
+ EXPECT_EQ(memcmp(buf, ctrl, c_len), 0);
+
+ /* Next recv returns EOF */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), 0), 0);
+}
+
TEST_F(tls, shutdown)
{
char const *test_str = "test_read";
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH net-next v2 5/6] SUNRPC: Use read_sock_rectype for svcsock TCP receives
2026-07-20 14:27 [PATCH net-next v2 0/6] Deliver TLS control records to kernel read_sock consumers Chuck Lever
` (3 preceding siblings ...)
2026-07-20 14:27 ` [PATCH net-next v2 4/6] selftests/tls: Add tests for data/control record interleaving Chuck Lever
@ 2026-07-20 14:27 ` Chuck Lever
2026-07-20 14:28 ` [PATCH net-next v2 6/6] SUNRPC: Remove sock_recvmsg path from " Chuck Lever
5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs
From: Chuck Lever <chuck.lever@oracle.com>
The svcsock TCP receive path uses sock_recvmsg() with ancillary data
buffers to detect TLS alerts when kTLS is active. This CMSG-based
approach requires a MSG_CTRUNC recovery dance on every receive and
cannot deliver control records through the read_sock interface.
When the socket provides a read_sock_rectype method (now set by
kTLS), svc_tcp_recvfrom() now dispatches to a new
svc_tcp_recvfrom_readsock() path. Two actor callbacks handle the
data:
svc_tcp_recv_actor() parses the RPC record byte stream directly from
skbs. Fragment header bytes fill sk_marker first; subsequent body
bytes are copied into rq_pages at the position tracked by
sk_datalen. When the last fragment of a complete RPC message
arrives, the actor sets desc->count to zero, stopping the read loop.
svc_tcp_rectype_actor() handles non-data TLS records. For fatal
alerts, the transport is marked for deferred close and the read loop
is stopped via desc->count. All non-data records are consumed by
returning 0 so that subsequent data records remain deliverable.
Sockets without read_sock_rectype (plain TCP, non-kTLS) continue to
use the existing sock_recvmsg() path unchanged. A follow-up patch
retires that path in favor of read_sock.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/sunrpc/svcsock.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 244 insertions(+)
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 50e5e7f5b762..e40931d11491 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -1134,6 +1134,247 @@ static void svc_tcp_fragment_received(struct svc_sock *svsk)
svsk->sk_marker = xdr_zero;
}
+/*
+ * read_sock_rectype data actor: receives decrypted application data
+ * from the TLS layer, parsing the RPC record stream (fragment
+ * headers and message bodies) and assembling complete RPC messages
+ * into rqstp->rq_pages.
+ */
+static int svc_tcp_recv_actor(read_descriptor_t *desc,
+ struct sk_buff *skb,
+ unsigned int offset, size_t len)
+{
+ struct svc_rqst *rqstp = desc->arg.data;
+ struct svc_sock *svsk =
+ container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
+ size_t reclen, received, want, take, done, n;
+ size_t consumed = 0;
+
+ if (!desc->count)
+ return 0;
+
+ if (svsk->sk_tcplen < sizeof(rpc_fraghdr)) {
+ want = sizeof(rpc_fraghdr) - svsk->sk_tcplen;
+ n = min(want, len);
+
+ if (skb_copy_bits(skb, offset,
+ (char *)&svsk->sk_marker +
+ svsk->sk_tcplen, n))
+ goto fault;
+ svsk->sk_tcplen += n;
+ offset += n;
+ len -= n;
+ consumed += n;
+
+ if (svsk->sk_tcplen < sizeof(rpc_fraghdr))
+ return consumed;
+
+ trace_svcsock_marker(&svsk->sk_xprt, svsk->sk_marker);
+ if (svc_sock_reclen(svsk) + svsk->sk_datalen >
+ svsk->sk_xprt.xpt_server->sv_max_mesg) {
+ net_notice_ratelimited("svc: %s oversized RPC fragment (%u octets) from %pISpc\n",
+ svsk->sk_xprt.xpt_server->sv_name,
+ svc_sock_reclen(svsk),
+ (struct sockaddr *)&svsk->sk_xprt.xpt_remote);
+ desc->error = -EMSGSIZE;
+ desc->count = 0;
+ return consumed;
+ }
+ }
+
+ reclen = svc_sock_reclen(svsk);
+ received = svsk->sk_tcplen - sizeof(rpc_fraghdr);
+ want = reclen - received;
+ take = min(want, len);
+ done = 0;
+
+ while (done < take) {
+ unsigned int pg = svsk->sk_datalen >> PAGE_SHIFT;
+ unsigned int pg_off = svsk->sk_datalen & (PAGE_SIZE - 1);
+ size_t chunk = min(take - done,
+ PAGE_SIZE - (size_t)pg_off);
+
+ if (skb_copy_bits(skb, offset,
+ page_address(rqstp->rq_pages[pg]) + pg_off,
+ chunk))
+ goto fault;
+ flush_dcache_page(rqstp->rq_pages[pg]);
+ offset += chunk;
+ done += chunk;
+ svsk->sk_datalen += chunk;
+ }
+ svsk->sk_tcplen += take;
+ consumed += take;
+
+ if (svsk->sk_tcplen - sizeof(rpc_fraghdr) >= reclen) {
+ if (svc_sock_final_rec(svsk))
+ desc->count = 0;
+ else
+ svc_tcp_fragment_received(svsk);
+ }
+
+ return consumed;
+
+fault:
+ desc->error = -EFAULT;
+ desc->count = 0;
+ return consumed;
+}
+
+/*
+ * read_sock_rectype non-data record actor: receives non-data TLS
+ * records (alerts, handshake messages) and translates them into
+ * transport-level actions.
+ *
+ * Returns 0 to consume the record and allow the TLS layer to
+ * continue delivering subsequent records. A negative return
+ * causes the TLS layer to requeue the skb on its rx_list,
+ * blocking all further record delivery on this connection.
+ */
+static int svc_tcp_rectype_actor(read_descriptor_t *desc,
+ struct sk_buff *skb,
+ unsigned int offset, size_t len,
+ u8 rectype)
+{
+ struct svc_rqst *rqstp = desc->arg.data;
+ struct svc_sock *svsk =
+ container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
+
+ switch (rectype) {
+ case TLS_RECORD_TYPE_ALERT: {
+ u8 alert[2] = {}, level, description;
+ struct kvec kvec = {
+ .iov_base = alert,
+ .iov_len = sizeof(alert),
+ };
+ struct msghdr msg = {};
+
+ if (skb_copy_bits(skb, offset, alert, min(len, sizeof(alert))))
+ break;
+ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &kvec, 1, sizeof(alert));
+ tls_alert_recv(svsk->sk_sk, &msg, &level, &description);
+ if (level == TLS_ALERT_LEVEL_FATAL) {
+ svc_xprt_deferred_close(&svsk->sk_xprt);
+ desc->error = -ENOTCONN;
+ desc->count = 0;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int svc_tcp_recvfrom_readsock(struct svc_rqst *rqstp)
+{
+ struct svc_sock *svsk =
+ container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
+ struct svc_serv *serv = svsk->sk_xprt.xpt_server;
+ struct sock *sk = svsk->sk_sk;
+ read_descriptor_t desc = {
+ .arg.data = rqstp,
+ };
+ ssize_t len;
+ __be32 *p;
+ __be32 calldir;
+
+ clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+
+ svc_tcp_restore_pages(svsk, rqstp);
+ rqstp->rq_arg.head[0].iov_base = page_address(rqstp->rq_pages[0]);
+
+ desc.count = serv->sv_max_mesg;
+ lock_sock(sk);
+ len = svsk->sk_sock->ops->read_sock_rectype(sk, &desc,
+ svc_tcp_recv_actor,
+ svc_tcp_rectype_actor);
+ release_sock(sk);
+
+ if (desc.error < 0) {
+ len = desc.error;
+ goto err_discard;
+ }
+ if (desc.count != 0) {
+ if (len > 0)
+ set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+ goto err_incomplete;
+ }
+
+ if (svsk->sk_datalen < 8)
+ goto err_nuts;
+
+ rqstp->rq_arg.len = svsk->sk_datalen;
+ rqstp->rq_arg.page_base = 0;
+ if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) {
+ rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len;
+ rqstp->rq_arg.page_len = 0;
+ } else {
+ rqstp->rq_arg.page_len = rqstp->rq_arg.len -
+ rqstp->rq_arg.head[0].iov_len;
+ }
+
+ rqstp->rq_xprt_ctxt = NULL;
+ rqstp->rq_prot = IPPROTO_TCP;
+ if (test_bit(XPT_LOCAL, &svsk->sk_xprt.xpt_flags))
+ set_bit(RQ_LOCAL, &rqstp->rq_flags);
+ else
+ clear_bit(RQ_LOCAL, &rqstp->rq_flags);
+
+ p = (__be32 *)rqstp->rq_arg.head[0].iov_base;
+ calldir = p[1];
+ if (calldir)
+ len = receive_cb_reply(svsk, rqstp);
+
+ /* Reset TCP read info */
+ svsk->sk_datalen = 0;
+ svc_tcp_fragment_received(svsk);
+
+ if (len < 0)
+ goto error;
+
+ trace_svcsock_tcp_recv(&svsk->sk_xprt, rqstp->rq_arg.len);
+ svc_xprt_copy_addrs(rqstp, &svsk->sk_xprt);
+ if (serv->sv_stats)
+ serv->sv_stats->nettcpcnt++;
+
+ svc_sock_secure_port(rqstp);
+ set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+ svc_xprt_received(rqstp->rq_xprt);
+ return rqstp->rq_arg.len;
+
+err_incomplete:
+ svc_tcp_save_pages(svsk, rqstp);
+ if (len < 0 && len != -EAGAIN)
+ goto err_delete;
+ if (svsk->sk_tcplen >= sizeof(rpc_fraghdr))
+ trace_svcsock_tcp_recv_short(&svsk->sk_xprt,
+ svc_sock_reclen(svsk),
+ svsk->sk_tcplen - sizeof(rpc_fraghdr));
+ goto err_noclose;
+error:
+ if (len != -EAGAIN)
+ goto err_delete;
+ trace_svcsock_tcp_recv_eagain(&svsk->sk_xprt, 0);
+ goto err_noclose;
+err_nuts:
+ svsk->sk_datalen = 0;
+ goto err_delete;
+err_discard:
+ /*
+ * Clear sk_datalen so the teardown-time
+ * svc_tcp_clear_pages() does not walk the emptied
+ * svsk->sk_pages[].
+ */
+ svsk->sk_datalen = 0;
+err_delete:
+ trace_svcsock_tcp_recv_err(&svsk->sk_xprt, len);
+ svc_xprt_deferred_close(&svsk->sk_xprt);
+err_noclose:
+ svc_xprt_received(rqstp->rq_xprt);
+ return 0;
+}
+
/**
* svc_tcp_recvfrom - Receive data from a TCP socket
* @rqstp: request structure into which to receive an RPC Call
@@ -1162,6 +1403,9 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
__be32 *p;
__be32 calldir;
+ if (svsk->sk_sock->ops->read_sock_rectype)
+ return svc_tcp_recvfrom_readsock(rqstp);
+
clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
len = svc_tcp_read_marker(svsk, rqstp);
if (len < 0)
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH net-next v2 6/6] SUNRPC: Remove sock_recvmsg path from svcsock TCP receives
2026-07-20 14:27 [PATCH net-next v2 0/6] Deliver TLS control records to kernel read_sock consumers Chuck Lever
` (4 preceding siblings ...)
2026-07-20 14:27 ` [PATCH net-next v2 5/6] SUNRPC: Use read_sock_rectype for svcsock TCP receives Chuck Lever
@ 2026-07-20 14:28 ` Chuck Lever
5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-20 14:28 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs
From: Chuck Lever <chuck.lever@oracle.com>
The svcsock TCP receive path maintains two code paths: one
using read_sock/read_sock_rectype and a legacy path using
sock_recvmsg. Plain TCP sockets already provide read_sock
(tcp_read_sock) in their proto_ops, so a single
read_sock-based receive path handles all cases relevant to
NFSD, using read_sock_rectype under kTLS and read_sock
otherwise.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/sunrpc/svcsock.c | 329 ++++-----------------------------------------------
1 file changed, 26 insertions(+), 303 deletions(-)
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index e40931d11491..9b9e0da9e73c 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -8,15 +8,6 @@
* evenly when servicing a single client. May need to modify the
* svc_xprt_enqueue procedure...
*
- * TCP support is largely untested and may be a little slow. The problem
- * is that we currently do two separate recvfrom's, one for the 4-byte
- * record length, and the second for the actual record. This could possibly
- * be improved by always reading a minimum size of around 100 bytes and
- * tucking any superfluous bytes away in a temporary store. Still, that
- * leaves write requests out in the rain. An alternative may be to peek at
- * the first skb in the queue, and if it matches the next TCP sequence
- * number, to extract the record marker. Yuck.
- *
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
@@ -238,138 +229,6 @@ static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining)
return len;
}
-static int
-svc_tcp_sock_process_cmsg(struct socket *sock, struct msghdr *msg,
- struct cmsghdr *cmsg, int ret)
-{
- u8 content_type = tls_get_record_type(sock->sk, cmsg);
- u8 level, description;
-
- switch (content_type) {
- case 0:
- break;
- case TLS_RECORD_TYPE_DATA:
- /* TLS sets EOR at the end of each application data
- * record, even though there might be more frames
- * waiting to be decrypted.
- */
- msg->msg_flags &= ~MSG_EOR;
- break;
- case TLS_RECORD_TYPE_ALERT:
- tls_alert_recv(sock->sk, msg, &level, &description);
- ret = (level == TLS_ALERT_LEVEL_FATAL) ?
- -ENOTCONN : -EAGAIN;
- break;
- default:
- /* discard this record type */
- ret = -EAGAIN;
- }
- return ret;
-}
-
-static int
-svc_tcp_sock_recv_cmsg(struct socket *sock, unsigned int *msg_flags)
-{
- union {
- struct cmsghdr cmsg;
- u8 buf[CMSG_SPACE(sizeof(u8))];
- } u;
- u8 alert[2];
- struct kvec alert_kvec = {
- .iov_base = alert,
- .iov_len = sizeof(alert),
- };
- struct msghdr msg = {
- .msg_flags = *msg_flags,
- .msg_control = &u,
- .msg_controllen = sizeof(u),
- };
- int ret;
-
- iov_iter_kvec(&msg.msg_iter, ITER_DEST, &alert_kvec, 1,
- alert_kvec.iov_len);
- ret = sock_recvmsg(sock, &msg, MSG_DONTWAIT);
- if (ret > 0 &&
- tls_get_record_type(sock->sk, &u.cmsg) == TLS_RECORD_TYPE_ALERT) {
- iov_iter_revert(&msg.msg_iter, ret);
- ret = svc_tcp_sock_process_cmsg(sock, &msg, &u.cmsg, -EAGAIN);
- }
- return ret;
-}
-
-static int
-svc_tcp_sock_recvmsg(struct svc_sock *svsk, struct msghdr *msg)
-{
- int ret;
- struct socket *sock = svsk->sk_sock;
-
- 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)
- ret = svc_tcp_sock_recv_cmsg(sock, &msg->msg_flags);
- }
- return ret;
-}
-
-#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE
-static void svc_flush_bvec(const struct bio_vec *bvec, size_t size, size_t seek)
-{
- struct bvec_iter bi = {
- .bi_size = size + seek,
- };
- struct bio_vec bv;
-
- bvec_iter_advance(bvec, &bi, seek & PAGE_MASK);
- for_each_bvec(bv, bvec, bi, bi)
- flush_dcache_page(bv.bv_page);
-}
-#else
-static inline void svc_flush_bvec(const struct bio_vec *bvec, size_t size,
- size_t seek)
-{
-}
-#endif
-
-/*
- * Read from @rqstp's transport socket. The incoming message fills whole
- * pages in @rqstp's rq_pages array until the last page of the message
- * has been received into a partial page.
- */
-static ssize_t svc_tcp_read_msg(struct svc_rqst *rqstp, size_t buflen,
- size_t seek)
-{
- struct svc_sock *svsk =
- container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
- struct bio_vec *bvec = rqstp->rq_bvec;
- struct msghdr msg = { NULL };
- unsigned int i;
- ssize_t len;
- size_t t;
-
- clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
-
- for (i = 0, t = 0; t < buflen; i++, t += PAGE_SIZE)
- bvec_set_page(&bvec[i], rqstp->rq_pages[i], PAGE_SIZE, 0);
-
- iov_iter_bvec(&msg.msg_iter, ITER_DEST, bvec, i, buflen);
- if (seek) {
- iov_iter_advance(&msg.msg_iter, seek);
- buflen -= seek;
- }
- len = svc_tcp_sock_recvmsg(svsk, &msg);
- if (len > 0)
- svc_flush_bvec(bvec, len, seek);
-
- /* If we read a full record, then assume there may be more
- * data to read (stream based sockets only!)
- */
- if (len == buflen)
- set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
-
- return len;
-}
-
/*
* Set socket snd and rcv buffer lengths
*/
@@ -1048,50 +907,6 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk)
svsk->sk_datalen = 0;
}
-/*
- * Receive fragment record header into sk_marker.
- */
-static ssize_t svc_tcp_read_marker(struct svc_sock *svsk,
- struct svc_rqst *rqstp)
-{
- ssize_t want, len;
-
- /* If we haven't gotten the record length yet,
- * get the next four bytes.
- */
- if (svsk->sk_tcplen < sizeof(rpc_fraghdr)) {
- struct msghdr msg = { NULL };
- struct kvec iov;
-
- want = sizeof(rpc_fraghdr) - svsk->sk_tcplen;
- iov.iov_base = ((char *)&svsk->sk_marker) + svsk->sk_tcplen;
- iov.iov_len = want;
- iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, want);
- len = svc_tcp_sock_recvmsg(svsk, &msg);
- if (len < 0)
- return len;
- svsk->sk_tcplen += len;
- if (len < want) {
- /* call again to read the remaining bytes */
- goto err_short;
- }
- trace_svcsock_marker(&svsk->sk_xprt, svsk->sk_marker);
- if (svc_sock_reclen(svsk) + svsk->sk_datalen >
- svsk->sk_xprt.xpt_server->sv_max_mesg)
- goto err_too_large;
- }
- return svc_sock_reclen(svsk);
-
-err_too_large:
- net_notice_ratelimited("svc: %s oversized RPC fragment (%u octets) from %pISpc\n",
- svsk->sk_xprt.xpt_server->sv_name,
- svc_sock_reclen(svsk),
- (struct sockaddr *)&svsk->sk_xprt.xpt_remote);
- svc_xprt_deferred_close(&svsk->sk_xprt);
-err_short:
- return -EAGAIN;
-}
-
static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp)
{
struct rpc_xprt *bc_xprt = svsk->sk_xprt.xpt_bc_xprt;
@@ -1135,10 +950,10 @@ static void svc_tcp_fragment_received(struct svc_sock *svsk)
}
/*
- * read_sock_rectype data actor: receives decrypted application data
- * from the TLS layer, parsing the RPC record stream (fragment
- * headers and message bodies) and assembling complete RPC messages
- * into rqstp->rq_pages.
+ * read_sock data actor: receives application data from the
+ * transport socket, parsing the RPC record stream (fragment
+ * headers and message bodies) and assembling complete RPC
+ * messages into rqstp->rq_pages.
*/
static int svc_tcp_recv_actor(read_descriptor_t *desc,
struct sk_buff *skb,
@@ -1266,7 +1081,21 @@ static int svc_tcp_rectype_actor(read_descriptor_t *desc,
return 0;
}
-static int svc_tcp_recvfrom_readsock(struct svc_rqst *rqstp)
+/**
+ * svc_tcp_recvfrom - Receive data from a TCP socket
+ * @rqstp: request structure into which to receive an RPC Call
+ *
+ * Called in a loop when XPT_DATA has been set.
+ *
+ * Returns:
+ * On success, the number of bytes in a received RPC Call, or
+ * %0 if a complete RPC Call message was not ready to return
+ *
+ * The zero return case handles partial receives and callback Replies.
+ * The state of a partial receive is preserved in the svc_sock for
+ * the next call to svc_tcp_recvfrom.
+ */
+static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
{
struct svc_sock *svsk =
container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
@@ -1286,9 +1115,13 @@ static int svc_tcp_recvfrom_readsock(struct svc_rqst *rqstp)
desc.count = serv->sv_max_mesg;
lock_sock(sk);
- len = svsk->sk_sock->ops->read_sock_rectype(sk, &desc,
- svc_tcp_recv_actor,
- svc_tcp_rectype_actor);
+ if (svsk->sk_sock->ops->read_sock_rectype)
+ len = svsk->sk_sock->ops->read_sock_rectype(sk, &desc,
+ svc_tcp_recv_actor,
+ svc_tcp_rectype_actor);
+ else
+ len = svsk->sk_sock->ops->read_sock(sk, &desc,
+ svc_tcp_recv_actor);
release_sock(sk);
if (desc.error < 0) {
@@ -1375,116 +1208,6 @@ static int svc_tcp_recvfrom_readsock(struct svc_rqst *rqstp)
return 0;
}
-/**
- * svc_tcp_recvfrom - Receive data from a TCP socket
- * @rqstp: request structure into which to receive an RPC Call
- *
- * Called in a loop when XPT_DATA has been set.
- *
- * Read the 4-byte stream record marker, then use the record length
- * in that marker to set up exactly the resources needed to receive
- * the next RPC message into @rqstp.
- *
- * Returns:
- * On success, the number of bytes in a received RPC Call, or
- * %0 if a complete RPC Call message was not ready to return
- *
- * The zero return case handles partial receives and callback Replies.
- * The state of a partial receive is preserved in the svc_sock for
- * the next call to svc_tcp_recvfrom.
- */
-static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
-{
- struct svc_sock *svsk =
- container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
- struct svc_serv *serv = svsk->sk_xprt.xpt_server;
- size_t want, base;
- ssize_t len;
- __be32 *p;
- __be32 calldir;
-
- if (svsk->sk_sock->ops->read_sock_rectype)
- return svc_tcp_recvfrom_readsock(rqstp);
-
- clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
- len = svc_tcp_read_marker(svsk, rqstp);
- if (len < 0)
- goto error;
-
- base = svc_tcp_restore_pages(svsk, rqstp);
- want = len - (svsk->sk_tcplen - sizeof(rpc_fraghdr));
- len = svc_tcp_read_msg(rqstp, base + want, base);
- if (len >= 0) {
- trace_svcsock_tcp_recv(&svsk->sk_xprt, len);
- svsk->sk_tcplen += len;
- svsk->sk_datalen += len;
- }
- if (len != want || !svc_sock_final_rec(svsk))
- goto err_incomplete;
- if (svsk->sk_datalen < 8)
- goto err_nuts;
-
- rqstp->rq_arg.len = svsk->sk_datalen;
- rqstp->rq_arg.page_base = 0;
- if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) {
- rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len;
- rqstp->rq_arg.page_len = 0;
- } else
- rqstp->rq_arg.page_len = rqstp->rq_arg.len - rqstp->rq_arg.head[0].iov_len;
-
- rqstp->rq_xprt_ctxt = NULL;
- rqstp->rq_prot = IPPROTO_TCP;
- if (test_bit(XPT_LOCAL, &svsk->sk_xprt.xpt_flags))
- set_bit(RQ_LOCAL, &rqstp->rq_flags);
- else
- clear_bit(RQ_LOCAL, &rqstp->rq_flags);
-
- p = (__be32 *)rqstp->rq_arg.head[0].iov_base;
- calldir = p[1];
- if (calldir)
- len = receive_cb_reply(svsk, rqstp);
-
- /* Reset TCP read info */
- svsk->sk_datalen = 0;
- svc_tcp_fragment_received(svsk);
-
- if (len < 0)
- goto error;
-
- svc_xprt_copy_addrs(rqstp, &svsk->sk_xprt);
- if (serv->sv_stats)
- serv->sv_stats->nettcpcnt++;
-
- svc_sock_secure_port(rqstp);
- svc_xprt_received(rqstp->rq_xprt);
- return rqstp->rq_arg.len;
-
-err_incomplete:
- svc_tcp_save_pages(svsk, rqstp);
- if (len < 0 && len != -EAGAIN)
- goto err_delete;
- if (len == want)
- svc_tcp_fragment_received(svsk);
- else
- trace_svcsock_tcp_recv_short(&svsk->sk_xprt,
- svc_sock_reclen(svsk),
- svsk->sk_tcplen - sizeof(rpc_fraghdr));
- goto err_noclose;
-error:
- if (len != -EAGAIN)
- goto err_delete;
- trace_svcsock_tcp_recv_eagain(&svsk->sk_xprt, 0);
- goto err_noclose;
-err_nuts:
- svsk->sk_datalen = 0;
-err_delete:
- trace_svcsock_tcp_recv_err(&svsk->sk_xprt, len);
- svc_xprt_deferred_close(&svsk->sk_xprt);
-err_noclose:
- svc_xprt_received(rqstp->rq_xprt);
- return 0; /* record not complete */
-}
-
/*
* MSG_SPLICE_PAGES is used exclusively to reduce the number of
* copy operations in this path. Therefore the caller must ensure
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread