* [BUG] bpf, sockmap: spurious wakeup by tcp_msg_wait_data() causing unexpected EAGAIN in recvfrom()
@ 2026-07-09 18:43 Nnamdi Onyeyiri
[not found] ` <20260709185526.E4DAB1F000E9@smtp.kernel.org>
0 siblings, 1 reply; 2+ messages in thread
From: Nnamdi Onyeyiri @ 2026-07-09 18:43 UTC (permalink / raw)
To: nnamdio, nnamdi.onyeyiri, john.fastabend, jakub, jiayuan.chen,
edumazet, ncardwell, kuniyu, davem, kuba, pabeni, horms
Cc: netdev, bpf
Hi,
We've encounted what appears to be a bug with bpf when invoking recvfrom() on
an ipv4 tcp socket that has been added to a sockmap. It results in unexpected
EAGAIN errors, that we've diagnosed as the result of spurious wakeups from
tcp_msg_wait_data().
This has been confirmed to still be present on the mainline kernel, and I have
written a reproducer at: https://github.com/Nnamdi/recvfrom_sockmap_eagain/
Attched is a patch that we found resolved this using kpatch locally. It just
causes spurious wakups to go round the loop again. I'd like to get a sense of
whether this is expected behaviour, or really is a bug, in which case, is this
the correct fix?
Thanks,
Nnamdi.
----8<----
From 0e0c342363b2e435297ab1feda402cde6ad54525 Mon Sep 17 00:00:00 2001
From: Nnamdi Onyeyiri <nnamdio@gmail.com>
Date: Thu, 9 Jul 2026 13:06:33 +0100
Subject: [PATCH] bpf, sockmap: handle spurious tcp_msg_wait_data() wakeup
recvfrom()/recv() are documented as only returning EAGAIN for blocking sockets
when they have a receive timeout configured. however, adding a blocking
ipv4 tcp socket without a receive timeout to a sockmap will cause EAGAIN errors
sporadically.
this happens when tcp_msg_wait_data() wakes spuriously (returning 0) in which
case, if no receive timeout is configured, we loop again instead of returning
-EAGAIN.
Signed-off-by: Nnamdi Onyeyiri <nnamdio@gmail.com>
---
net/ipv4/tcp_bpf.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
index cc0bd73f3..38fd391ff 100644
--- a/net/ipv4/tcp_bpf.c
+++ b/net/ipv4/tcp_bpf.c
@@ -317,6 +317,8 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
}
if (data && !sk_psock_queue_empty(psock))
goto msg_bytes_ready;
+ if (!data && timeo == MAX_SCHEDULE_TIMEOUT)
+ goto msg_bytes_ready;
copied = -EAGAIN;
}
out:
@@ -390,6 +392,8 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
sk_psock_put(sk, psock);
return tcp_recvmsg(sk, msg, len, flags);
}
+ if (!data && timeo == MAX_SCHEDULE_TIMEOUT)
+ goto msg_bytes_ready;
copied = -EAGAIN;
}
ret = copied;
--
2.52.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [BUG] bpf, sockmap: spurious wakeup by tcp_msg_wait_data() causing unexpected EAGAIN in recvfrom()
[not found] ` <20260709185526.E4DAB1F000E9@smtp.kernel.org>
@ 2026-07-10 20:17 ` Nnamdi Onyeyiri
0 siblings, 0 replies; 2+ messages in thread
From: Nnamdi Onyeyiri @ 2026-07-10 20:17 UTC (permalink / raw)
To: sashiko-reviews
Cc: bpf, netdev, john.fastabend, jakub, jiayuan.chen, edumazet,
ncardwell, kuniyu, davem, kuba, pabeni, horms
Hi,
The updated patch below addresses the issues raised by sashiko-bot. The closed
socket and signal handling code was added to tcp_bpf_recvmsg(), and the fix was
updated to work for sockets with SO_RCVTIMEO set.
Please let me know if any more changes are required, or if the patch would need
to be submitted some other way, I'm happy to adjust as necessary.
Thanks!
----8<----
From 20a1275aa5ecb927ff049971454b62682356ef0e Mon Sep 17 00:00:00 2001
From: Nnamdi Onyeyiri <nnamdio@gmail.com>
Date: Fri, 10 Jul 2026 20:47:36 +0100
Subject: [PATCH v2 1/1] bpf, sockmap: handle spurious tcp_msg_wait_data()
wakeup
recvfrom()/recv() are documented as only returning EAGAIN for blocking sockets
when they have a receive timeout configured. however, adding a blocking
ipv4 tcp socket without a receive timeout to a sockmap will cause EAGAIN errors
sporadically. a socket with a receive timeout may return EAGAIN before the
timeout expires.
this happens when tcp_msg_wait_data() wakes spuriously (returning 0) in which
case, if there is no timeout, or the timeout has not yet expired, we loop
again instead of returning.
Signed-off-by: Nnamdi Onyeyiri <nnamdio@gmail.com>
---
net/ipv4/tcp_bpf.c | 55 ++++++++++++++++++++++++++++++++++++++--------
1 file changed, 46 insertions(+), 9 deletions(-)
diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
index cc0bd73f36b6..e3109edb9621 100644
--- a/net/ipv4/tcp_bpf.c
+++ b/net/ipv4/tcp_bpf.c
@@ -179,7 +179,7 @@ EXPORT_SYMBOL_GPL(tcp_bpf_sendmsg_redir);
#ifdef CONFIG_BPF_SYSCALL
static int tcp_msg_wait_data(struct sock *sk, struct sk_psock *psock,
- long timeo)
+ long *timeo)
{
DEFINE_WAIT_FUNC(wait, woken_wake_function);
int ret = 0;
@@ -187,12 +187,12 @@ static int tcp_msg_wait_data(struct sock *sk, struct sk_psock *psock,
if (sk->sk_shutdown & RCV_SHUTDOWN)
return 1;
- if (!timeo)
+ if (!(*timeo))
return ret;
add_wait_queue(sk_sleep(sk), &wait);
sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
- ret = sk_wait_event(sk, &timeo,
+ ret = sk_wait_event(sk, timeo,
!list_empty(&psock->ingress_msg) ||
!skb_queue_empty_lockless(&sk->sk_receive_queue), &wait);
sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
@@ -229,6 +229,7 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
int copied_from_self = 0;
int copied = 0;
u32 seq;
+ long timeo;
if (unlikely(flags & MSG_ERRQUEUE))
return inet_recv_error(sk, msg, len);
@@ -262,6 +263,8 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
}
}
+ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
+
msg_bytes_ready:
copied = __sk_msg_recvmsg(sk, psock, msg, len, flags, &copied_from_self);
/* The typical case for EFAULT is the socket was gracefully
@@ -280,7 +283,6 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
}
seq += copied_from_self;
if (!copied) {
- long timeo;
int data;
if (sock_flag(sk, SOCK_DONE))
@@ -299,7 +301,6 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
goto out;
}
- timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
if (!timeo) {
copied = -EAGAIN;
goto out;
@@ -310,13 +311,15 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
goto out;
}
- data = tcp_msg_wait_data(sk, psock, timeo);
+ data = tcp_msg_wait_data(sk, psock, &timeo);
if (data < 0) {
copied = data;
goto unlock;
}
if (data && !sk_psock_queue_empty(psock))
goto msg_bytes_ready;
+ if (!data && timeo > 0)
+ goto msg_bytes_ready;
copied = -EAGAIN;
}
out:
@@ -355,6 +358,7 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
{
struct sk_psock *psock;
int copied, ret;
+ long timeo;
if (unlikely(flags & MSG_ERRQUEUE))
return inet_recv_error(sk, msg, len);
@@ -371,14 +375,45 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
return tcp_recvmsg(sk, msg, len, flags);
}
lock_sock(sk);
+
+ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
+
msg_bytes_ready:
copied = sk_msg_recvmsg(sk, psock, msg, len, flags);
if (!copied) {
- long timeo;
int data;
- timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
- data = tcp_msg_wait_data(sk, psock, timeo);
+ if (sock_flag(sk, SOCK_DONE)) {
+ ret = 0;
+ goto unlock;
+ }
+
+ if (sk->sk_err) {
+ ret = sock_error(sk);
+ goto unlock;
+ }
+
+ if (sk->sk_shutdown & RCV_SHUTDOWN) {
+ ret = 0;
+ goto unlock;
+ }
+
+ if (sk->sk_state == TCP_CLOSE) {
+ ret = -ENOTCONN;
+ goto unlock;
+ }
+
+ if (!timeo) {
+ ret = -EAGAIN;
+ goto unlock;
+ }
+
+ if (signal_pending(current)) {
+ ret = sock_intr_errno(timeo);
+ goto unlock;
+ }
+
+ data = tcp_msg_wait_data(sk, psock, &timeo);
if (data < 0) {
ret = data;
goto unlock;
@@ -390,6 +425,8 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
sk_psock_put(sk, psock);
return tcp_recvmsg(sk, msg, len, flags);
}
+ if (!data && timeo > 0)
+ goto msg_bytes_ready;
copied = -EAGAIN;
}
ret = copied;
--
2.52.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-10 20:17 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09 18:43 [BUG] bpf, sockmap: spurious wakeup by tcp_msg_wait_data() causing unexpected EAGAIN in recvfrom() Nnamdi Onyeyiri
[not found] ` <20260709185526.E4DAB1F000E9@smtp.kernel.org>
2026-07-10 20:17 ` Nnamdi Onyeyiri
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox