git: afeb885b4ae4 - main - linux: Preserve partial results from recvmmsg()

From: Pouria Mousavizadeh Tehrani <pouria_at_FreeBSD.org>
Date: Wed, 28 Oct 2026 15:25:27 UTC
The branch main has been updated by pouria:

URL: https://cgit.FreeBSD.org/src/commit/?id=afeb885b4ae41357a648f547d3835a5e0dcde81e

commit afeb885b4ae41357a648f547d3835a5e0dcde81e
Author:     Vladimir Grebenshchikov <vova@zote.me>
AuthorDate: 2026-10-21 22:14:33 +0000
Commit:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
CommitDate: 2026-10-28 15:15:51 +0000

    linux: Preserve partial results from recvmmsg()
    
    linux_recvmmsg_common() currently returns the error from the next
    recvmsg() attempt even when one or more datagrams have already been
    received and copied to userspace.
    
    This is especially visible with non-blocking UDP sockets.  After
    receiving the first datagram, the next iteration commonly returns
    EAGAIN.  The syscall then reports EAGAIN instead of the number of
    messages already stored in the mmsghdr array, causing applications to
    discard successfully received packets.
    
    This differs from Linux recvmmsg() semantics, where an error encountered
    after receiving at least one message is deferred and the number of
    received messages is returned.
    
    Return the accumulated datagram count and clear the error whenever at
    least one message was received.  Preserve the original error when no
    messages were received.
    
    This fixes packet loss in applications such as wireguard-go, whose
    non-blocking receive path uses recvmmsg() for batches of UDP packets.
    In the observed failure, WireGuard handshake responses reached the UDP
    socket, but a subsequent EAGAIN caused recvmmsg() to hide the received
    response and the handshake timed out.
    
    Reviewed by:            kib
    Pull Request:           https://github.com/freebsd/freebsd-src/pull/2484
    Differential Revision:  https://reviews.freebsd.org/D60961
---
 sys/compat/linux/linux_socket.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c
index 9c94079e0e0f..f2ed584d1f4c 100644
--- a/sys/compat/linux/linux_socket.c
+++ b/sys/compat/linux/linux_socket.c
@@ -2053,8 +2053,10 @@ linux_recvmmsg_common(struct thread *td, l_int s, struct l_mmsghdr *msg,
 		if (bsd_msg.msg_flags & MSG_OOB)
 			break;
 	}
-	if (error == 0)
+	if (datagrams != 0) {
 		td->td_retval[0] = datagrams;
+		error = 0;
+	}
 	fdrop(fp, td);
 	return (error);
 }