[PATCH net 0/2] tcp: fix listener wakeup after reuseport migration
From: Zhenzhong Wu
Date: Sat Apr 18 2026 - 00:17:43 EST
Hi,
this small series fixes a missing wakeup after listener migration in
the SO_REUSEPORT close path and adds regression selftests.
The issue shows up when a fully established child has already been
queued on listener A, userspace has not accepted it yet, and
listener A is then closed. The kernel migrates that child to
listener B in the same SO_REUSEPORT group via
inet_csk_reqsk_queue_add(), but the target listener's waiters are
not notified.
As a result, a nonblocking accept() still succeeds because it checks
the accept queue directly, but waiters that sleep for listener
readiness can remain asleep until another connection generates a
wakeup. This affects poll()/epoll_wait()-based waiters, and can also
leave a blocking accept() asleep after migration even though the
child is already in the target listener's accept queue.
The fix is to notify the target listener after a successful
inet_csk_reqsk_queue_add() in inet_csk_listen_stop().
I also checked the half-open migration path in
reqsk_timer_handler(). That path does not need an extra wakeup here
because the listener becomes readable only after the final ACK
completes the handshake, and tcp_child_process() already wakes the
parent listener at that point.
The series adds selftests under tools/testing/selftests/net/ that
reproduce the regression for both IPv4 and IPv6. They cover both
epoll-based waiters and a blocking accept() waiter.
Patch 1 contains only the runtime fix so it can stand on its own and
be considered for stable backporting. Patch 2 adds the selftest
coverage.
Testing:
On an unpatched host kernel:
unshare -Ur sh -c \
'./tools/testing/selftests/net/reuseport_migrate_epoll'
unshare -Ur sh -c \
'./tools/testing/selftests/net/reuseport_migrate_accept'
The epoll selftest fails for both IPv4 and IPv6 with:
accept queue was populated, but epoll_wait() timed out
The blocking accept selftest fails for both IPv4 and IPv6, for example
with:
blocking accept() completed only in cleanup
On a patched kernel booted under QEMU with a minimal initramfs, both
selftests pass:
ok 1 ipv4 epoll wake after reuseport migration
ok 2 ipv6 epoll wake after reuseport migration
reuseport_migrate_epoll_RC=0
ok 1 ipv4 blocking accept wake after reuseport migration
ok 2 ipv6 blocking accept wake after reuseport migration
reuseport_migrate_accept_RC=0
Zhenzhong Wu (2):
tcp: call sk_data_ready() after listener migration
selftests: net: add reuseport migration wakeup regression tests
net/ipv4/inet_connection_sock.c | 1 +
tools/testing/selftests/net/Makefile | 3 +
.../selftests/net/reuseport_migrate_accept.c | 533 ++++++++++++++++++
.../selftests/net/reuseport_migrate_epoll.c | 353 ++++++++++++
4 files changed, 890 insertions(+)
create mode 100644 tools/testing/selftests/net/reuseport_migrate_accept.c
create mode 100644 tools/testing/selftests/net/reuseport_migrate_epoll.c
base-commit: 52bcb57a4e8a0865a76c587c2451906342ae1b2d
--
2.43.0