(Added <passt-dev(a)passt.top> to distribution list. I found that my first approach was too simplistic, since it only moved the reading area forward in the receive buffer, but continue to fill in iov[0] with the indicated length. This commit exactly what we want: we indicate a NULL pointer in iov[0], but want the actually read bytes to end up in the remaining entries, and also the returned value to indicate the actually read length. I look forward to feedback to this, then I can hopefully post it to the netdev list next week. ///jon On 2023-06-22 22:12, Jon Maloy wrote:When reading received messages with MSG_PEEK, we sometines have to read the leading bytes of the stream several times, only to reach the bytes we really want. This is clearly non-optimal. What we would want is something similar to pread/preadv(), but working even for tcp sockets. At the same time, we obviously don't want to add any new arguments to the recv/recvmsg() calls. In this commit, we allow the user to set iovec.iov_base in the first vector entry to NULL. This tells the socket to skip the first entry, hence making the iov_len field of that entry indicate the offset value. This way, there is no need to add any new arguments. This change is simple and non-intrusive, and should be safe addition to the socket API. We have measured it to give a throughput improvement of 8-10 % for the protocol splicer 'passst', which is used in KubeVirt containers. Signed-off-by: Jon Maloy <jmaloy(a)redhat.com> works with original msghdr Signed-off-by: Jon Maloy <jmaloy(a)redhat.com> --- net/ipv4/tcp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 33f559f491c8..1d89337e89b6 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2428,6 +2428,7 @@ static int tcp_recvmsg_locked(struct sock *sk, struct msghdr *msg, size_t len, struct tcp_sock *tp = tcp_sk(sk); int copied = 0; u32 peek_seq; + u32 peek_offset; u32 *seq; unsigned long used; int err; @@ -2435,7 +2436,6 @@ static int tcp_recvmsg_locked(struct sock *sk, struct msghdr *msg, size_t len, long timeo; struct sk_buff *skb, *last; u32 urg_hole = 0; - err = -ENOTCONN; if (sk->sk_state == TCP_LISTEN) goto out; @@ -2469,6 +2469,14 @@ static int tcp_recvmsg_locked(struct sock *sk, struct msghdr *msg, size_t len, if (flags & MSG_PEEK) { peek_seq = tp->copied_seq; seq = &peek_seq; + if (msg->msg_iter.iov[0].iov_base == NULL) { + peek_offset = msg->msg_iter.iov[0].iov_len; + msg->msg_iter.iov = &msg->msg_iter.iov[1]; + msg->msg_iter.nr_segs -= 1; + msg->msg_iter.count -= peek_offset; + len -= peek_offset; + *seq += peek_offset; + } } target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);