git: b3836322a1e6 - main - linux: preserve epoll descriptors across fork using KQUEUE_CPONFORK
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 16 Oct 2026 20:16:00 UTC
The branch main has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=b3836322a1e6863734427ac9c01381ab25458126
commit b3836322a1e6863734427ac9c01381ab25458126
Author: Vladimir Grebenshchikov <vova@zote.me>
AuthorDate: 2026-10-12 18:40:23 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-10-16 20:10:55 +0000
linux: preserve epoll descriptors across fork using KQUEUE_CPONFORK
Linuxulator implements epoll instances using kqueues. Currently,
epoll_create_common() creates the backing kqueue with copy-on-fork
disabled. When fork() copies the descriptor table, the kqueue is
omitted from the child's table.
This differs from Linux, where an epoll descriptor survives fork(),
including when it was created with EPOLL_CLOEXEC. That flag controls
descriptor lifetime across exec(), not fork().
The missing descriptor breaks dbus-daemon daemonization. The daemon
creates its epoll instance before forking and continues using the
saved descriptor number in the child. F_GETFD initially fails with
EBADF. Once another open reuses that number, subsequent epoll_ctl()
calls fail with EINVAL because the descriptor no longer refers to
an epoll instance. The daemon then exits, preventing applications
that depend on the session bus from starting correctly.
Enable KQUEUE_CPONFORK for epoll's backing kqueue. This preserves the
descriptor number and copies supported event registrations into the
child, allowing the daemonized process to continue using epoll.
This is a partial compatibility improvement, not a complete
implementation of Linux epoll inheritance. Linux parent and child
reference the same epoll instance, whereas KQUEUE_CPONFORK creates
independent queues. Consequently, registration changes and one-shot
event consumption in one process are not reflected in the other.
Applications relying on shared epoll state across fork still require
further work.
Validation:
- A static Linux x86-64 C reproducer fails under the original
Linuxulator and passes on a real Linux kernel under QEMU.
- Before this change, the child loses its epoll descriptor with
both EPOLL_CLOEXEC enabled and disabled, while ordinary pipe
descriptors survive.
- After this change, descriptor flags, inherited readiness and
event data remain available in the child. epoll_ctl() also
succeeds after allocating additional descriptors.
- Both dbus-daemon --session --fork and dbus-launch start
successfully and answer a ListNames request from a separate
client.
- Shared-state regression tests still fail for parent visibility
of child ADD/MOD/DEL operations and EPOLLONESHOT consumption,
documenting the remaining semantic differences.
Reviewed by: kib
MFC after: 1 week
Differential revision: https://reviews.freebsd.org/D60783
---
sys/compat/linux/linux_event.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sys/compat/linux/linux_event.c b/sys/compat/linux/linux_event.c
index de313aefff97..507ddf1cde6f 100644
--- a/sys/compat/linux/linux_event.c
+++ b/sys/compat/linux/linux_event.c
@@ -104,7 +104,7 @@ static int
epoll_create_common(struct thread *td, int flags)
{
- return (kern_kqueue(td, flags, false, NULL));
+ return (kern_kqueue(td, flags, true, NULL));
}
#ifdef LINUX_LEGACY_SYSCALLS