Module Name:    src
Committed By:   christos
Date:           Fri Jul 28 18:19:01 UTC 2023

Modified Files:
        src/distrib/sets/lists/comp: mi
        src/distrib/sets/lists/debug: mi
        src/distrib/sets/lists/tests: mi
        src/lib/libc/compat/sys: compat_kevent.c
        src/lib/libc/sys: Makefile.inc kqueue.2
        src/lib/libpthread: pthread_cancelstub.c
        src/lib/librumpclient: rumpclient.c
        src/lib/librumphijack: hijack.c
        src/sys/compat/common: compat_100_mod.c compat_mod.h files.common
            kern_select_50.c
        src/sys/compat/linux/arch/amd64: syscalls.master
        src/sys/compat/linux/common: linux_misc.c linux_misc.h
        src/sys/compat/sys: event.h
        src/sys/kern: files.kern kern_event.c makesyscalls.sh syscalls.conf
            syscalls.master
        src/sys/rump: Makefile.rump rump.sysmap
        src/sys/sys: Makefile event.h syscall.h
        src/tests/kernel: Makefile
Added Files:
        src/lib/libc/sys: epoll.2 epoll.c
        src/sys/compat/common: kern_event_100.c
        src/sys/kern: sys_epoll.c
        src/sys/sys: epoll.h
        src/tests/kernel: t_epoll.c

Log Message:
Add epoll(2) from Theodore Preduta as part of GSoC 2023


To generate a diff of this commit:
cvs rdiff -u -r1.2437 -r1.2438 src/distrib/sets/lists/comp/mi
cvs rdiff -u -r1.406 -r1.407 src/distrib/sets/lists/debug/mi
cvs rdiff -u -r1.1277 -r1.1278 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.2 -r1.3 src/lib/libc/compat/sys/compat_kevent.c
cvs rdiff -u -r1.251 -r1.252 src/lib/libc/sys/Makefile.inc
cvs rdiff -u -r0 -r1.1 src/lib/libc/sys/epoll.2 src/lib/libc/sys/epoll.c
cvs rdiff -u -r1.58 -r1.59 src/lib/libc/sys/kqueue.2
cvs rdiff -u -r1.43 -r1.44 src/lib/libpthread/pthread_cancelstub.c
cvs rdiff -u -r1.69 -r1.70 src/lib/librumpclient/rumpclient.c
cvs rdiff -u -r1.136 -r1.137 src/lib/librumphijack/hijack.c
cvs rdiff -u -r1.1 -r1.2 src/sys/compat/common/compat_100_mod.c
cvs rdiff -u -r1.7 -r1.8 src/sys/compat/common/compat_mod.h
cvs rdiff -u -r1.8 -r1.9 src/sys/compat/common/files.common
cvs rdiff -u -r0 -r1.1 src/sys/compat/common/kern_event_100.c
cvs rdiff -u -r1.3 -r1.4 src/sys/compat/common/kern_select_50.c
cvs rdiff -u -r1.68 -r1.69 src/sys/compat/linux/arch/amd64/syscalls.master
cvs rdiff -u -r1.257 -r1.258 src/sys/compat/linux/common/linux_misc.c
cvs rdiff -u -r1.26 -r1.27 src/sys/compat/linux/common/linux_misc.h
cvs rdiff -u -r1.2 -r1.3 src/sys/compat/sys/event.h
cvs rdiff -u -r1.59 -r1.60 src/sys/kern/files.kern
cvs rdiff -u -r1.148 -r1.149 src/sys/kern/kern_event.c
cvs rdiff -u -r1.186 -r1.187 src/sys/kern/makesyscalls.sh
cvs rdiff -u -r0 -r1.1 src/sys/kern/sys_epoll.c
cvs rdiff -u -r1.31 -r1.32 src/sys/kern/syscalls.conf
cvs rdiff -u -r1.310 -r1.311 src/sys/kern/syscalls.master
cvs rdiff -u -r1.134 -r1.135 src/sys/rump/Makefile.rump
cvs rdiff -u -r1.9 -r1.10 src/sys/rump/rump.sysmap
cvs rdiff -u -r1.180 -r1.181 src/sys/sys/Makefile
cvs rdiff -u -r0 -r1.1 src/sys/sys/epoll.h
cvs rdiff -u -r1.54 -r1.55 src/sys/sys/event.h
cvs rdiff -u -r1.322 -r1.323 src/sys/sys/syscall.h
cvs rdiff -u -r1.72 -r1.73 src/tests/kernel/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/kernel/t_epoll.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/comp/mi
diff -u src/distrib/sets/lists/comp/mi:1.2437 src/distrib/sets/lists/comp/mi:1.2438
--- src/distrib/sets/lists/comp/mi:1.2437	Sun Jul  9 22:31:54 2023
+++ src/distrib/sets/lists/comp/mi	Fri Jul 28 14:18:59 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: mi,v 1.2437 2023/07/10 02:31:54 christos Exp $
+#	$NetBSD: mi,v 1.2438 2023/07/28 18:18:59 christos Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 ./etc/mtree/set.comp				comp-sys-root
@@ -3328,6 +3328,7 @@
 ./usr/include/sys/elfdefinitions.h		comp-c-include
 ./usr/include/sys/endian.h			comp-c-include
 ./usr/include/sys/envsys.h			comp-c-include
+./usr/include/sys/epoll.h			comp-c-include
 ./usr/include/sys/errno.h			comp-c-include
 ./usr/include/sys/evcnt.h			comp-c-include
 ./usr/include/sys/event.h			comp-c-include
@@ -13211,6 +13212,16 @@
 ./usr/share/man/html2/dup.html			comp-c-htmlman		html
 ./usr/share/man/html2/dup2.html			comp-c-htmlman		html
 ./usr/share/man/html2/dup3.html			comp-c-htmlman		html
+./usr/share/man/html2/epoll.html		comp-c-htmlman		html
+./usr/share/man/html2/epoll_create.html		comp-c-htmlman		html
+./usr/share/man/html2/epoll_create1.html	comp-c-htmlman		html
+./usr/share/man/html2/epoll_ctl.html		comp-c-htmlman		html
+./usr/share/man/html2/epoll_data.html		comp-c-htmlman		html
+./usr/share/man/html2/epoll_data_t.html		comp-c-htmlman		html
+./usr/share/man/html2/epoll_event.html		comp-c-htmlman		html
+./usr/share/man/html2/epoll_pwait.html		comp-c-htmlman		html
+./usr/share/man/html2/epoll_pwait2.html		comp-c-htmlman		html
+./usr/share/man/html2/epoll_wait.html		comp-c-htmlman		html
 ./usr/share/man/html2/errno.html		comp-c-htmlman		html
 ./usr/share/man/html2/eventfd.html		comp-c-htmlman		html
 ./usr/share/man/html2/eventfd_read.html		comp-c-htmlman		html
@@ -21524,6 +21535,16 @@
 ./usr/share/man/man2/dup.2			comp-c-man		.man
 ./usr/share/man/man2/dup2.2			comp-c-man		.man
 ./usr/share/man/man2/dup3.2			comp-c-man		.man
+./usr/share/man/man2/epoll.2			comp-c-man		.man
+./usr/share/man/man2/epoll_create.2		comp-c-man		.man
+./usr/share/man/man2/epoll_create1.2		comp-c-man		.man
+./usr/share/man/man2/epoll_ctl.2		comp-c-man		.man
+./usr/share/man/man2/epoll_data.2		comp-c-man		.man
+./usr/share/man/man2/epoll_data_t.2		comp-c-man		.man
+./usr/share/man/man2/epoll_event.2		comp-c-man		.man
+./usr/share/man/man2/epoll_pwait.2		comp-c-man		.man
+./usr/share/man/man2/epoll_pwait2.2		comp-c-man		.man
+./usr/share/man/man2/epoll_wait.2		comp-c-man		.man
 ./usr/share/man/man2/errno.2			comp-c-man		.man
 ./usr/share/man/man2/eventfd.2			comp-c-man		.man
 ./usr/share/man/man2/eventfd_read.2		comp-c-man		.man

Index: src/distrib/sets/lists/debug/mi
diff -u src/distrib/sets/lists/debug/mi:1.406 src/distrib/sets/lists/debug/mi:1.407
--- src/distrib/sets/lists/debug/mi:1.406	Wed Jul  5 18:42:46 2023
+++ src/distrib/sets/lists/debug/mi	Fri Jul 28 14:18:59 2023
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.406 2023/07/05 22:42:46 riastradh Exp $
+# $NetBSD: mi,v 1.407 2023/07/28 18:18:59 christos Exp $
 ./etc/mtree/set.debug                           comp-sys-root
 ./usr/lib					comp-sys-usr		compatdir
 ./usr/lib/i18n/libBIG5_g.a			comp-c-debuglib		debuglib,compatfile
@@ -1790,6 +1790,7 @@
 ./usr/libdata/debug/usr/tests/kernel/posix_spawn/t_fileactions.debug	tests-obsolete	obsolete,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/posix_spawn/t_spawn.debug		tests-obsolete	obsolete,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/posix_spawn/t_spawnattr.debug	tests-obsolete	obsolete,compattestfile
+./usr/libdata/debug/usr/tests/kernel/t_epoll.debug			tests-kernel-tests	debug,atf
 ./usr/libdata/debug/usr/tests/kernel/t_extattrctl.debug			tests-kernel-tests	debug,atf,rump
 ./usr/libdata/debug/usr/tests/kernel/t_extent.debug			tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_fcntl.debug			tests-kernel-tests	debug,atf

Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.1277 src/distrib/sets/lists/tests/mi:1.1278
--- src/distrib/sets/lists/tests/mi:1.1277	Sat Jul 15 08:24:57 2023
+++ src/distrib/sets/lists/tests/mi	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1277 2023/07/15 12:24:57 rillig Exp $
+# $NetBSD: mi,v 1.1278 2023/07/28 18:19:00 christos Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -2296,6 +2296,7 @@
 ./usr/tests/kernel/posix_spawn/t_fileactions		tests-obsolete		obsolete
 ./usr/tests/kernel/posix_spawn/t_spawn			tests-obsolete		obsolete
 ./usr/tests/kernel/posix_spawn/t_spawnattr		tests-obsolete		obsolete
+./usr/tests/kernel/t_epoll				tests-kernel-tests	atf
 ./usr/tests/kernel/t_extattrctl				tests-kernel-tests	atf,rump
 ./usr/tests/kernel/t_extent				tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/t_fcntl				tests-kernel-tests	atf

Index: src/lib/libc/compat/sys/compat_kevent.c
diff -u src/lib/libc/compat/sys/compat_kevent.c:1.2 src/lib/libc/compat/sys/compat_kevent.c:1.3
--- src/lib/libc/compat/sys/compat_kevent.c:1.2	Sat Jan 10 21:46:26 2009
+++ src/lib/libc/compat/sys/compat_kevent.c	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: compat_kevent.c,v 1.2 2009/01/11 02:46:26 christos Exp $ */
+/*	$NetBSD: compat_kevent.c,v 1.3 2023/07/28 18:19:00 christos Exp $ */
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
  */
 #include <sys/cdefs.h>
 #if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: compat_kevent.c,v 1.2 2009/01/11 02:46:26 christos Exp $");
+__RCSID("$NetBSD: compat_kevent.c,v 1.3 2023/07/28 18:19:00 christos Exp $");
 #endif /* LIBC_SCCS and not lint */
 
 #include "namespace.h"
@@ -46,13 +46,16 @@ __RCSID("$NetBSD: compat_kevent.c,v 1.2 
 #include <compat/sys/time.h>
 #include <sys/event.h>
 #include <compat/sys/event.h>
+#include <stdlib.h>
 
 __warn_references(kevent,
     "warning: reference to compatibility kevent(); include <sys/event.h> to generate correct reference")
+__warn_references(kevent,
+    "warning: reference to compatibility __kevent50(); use kevent()")
 
 int
-kevent(int kq, const struct kevent *changelist, size_t nchanges,
-    struct kevent *eventlist, size_t nevents, const struct timespec50 *ts50)
+kevent(int kq, const struct kevent100 *changelist, size_t nchanges,
+    struct kevent100 *eventlist, size_t nevents, const struct timespec50 *ts50)
 {
 	struct timespec ts, *tsp;
 
@@ -60,5 +63,41 @@ kevent(int kq, const struct kevent *chan
 		timespec50_to_timespec(ts50, tsp = &ts);
 	else
 		tsp = NULL;
-	return __kevent50(kq, changelist, nchanges, eventlist, nevents, tsp);
+        return __kevent50(kq, changelist, nchanges, eventlist, nevents, tsp);
+}
+
+int
+__kevent50(int kq, const struct kevent100 *changelist100, size_t nchanges,
+    struct kevent100 *eventlist100, size_t nevents, const struct timespec *tsp)
+{
+	int retval;
+	struct kevent *changelist;
+	struct kevent *eventlist;
+
+	changelist = malloc(sizeof(*changelist) * nchanges);
+	if (changelist == NULL) {
+		return -1;
+	}
+
+	eventlist = malloc(sizeof(*eventlist) * nevents);
+        if (eventlist == NULL) {
+		retval = -1;
+		goto leave0;
+	}
+
+	for (size_t i = 0; i < nchanges; i++)
+		kevent100_to_kevent(changelist100 + i, changelist + i);
+
+	retval = __kevent100(kq, changelist, nchanges, eventlist, nevents, tsp);
+	if (retval == -1)
+		goto leave1;
+
+	for (int i = 0; i < retval; i++)
+		kevent_to_kevent100(eventlist + i, eventlist100 + i);
+
+leave1:
+	free(eventlist);
+leave0:
+	free(changelist);
+	return retval;
 }

Index: src/lib/libc/sys/Makefile.inc
diff -u src/lib/libc/sys/Makefile.inc:1.251 src/lib/libc/sys/Makefile.inc:1.252
--- src/lib/libc/sys/Makefile.inc:1.251	Sun Jul  9 22:31:54 2023
+++ src/lib/libc/sys/Makefile.inc	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile.inc,v 1.251 2023/07/10 02:31:54 christos Exp $
+#	$NetBSD: Makefile.inc,v 1.252 2023/07/28 18:19:00 christos Exp $
 #	@(#)Makefile.inc	8.3 (Berkeley) 10/24/94
 
 # sys sources
@@ -7,10 +7,10 @@
 # other sources shared with the kernel, used in syscalls
 SRCS+=	cpuset.c
 # glue to offer userland wrappers for some syscalls
-SRCS+=	accept4.c clock_getcpuclockid.c eventfd_read.c eventfd_write.c \
-	posix_fadvise.c posix_madvise.c ppoll.c sched.c sigqueue.c \
-	sigtimedwait.c sigwait.c sigwaitinfo.c statvfs.c swapon.c semctl.c \
-	vadvise.c
+SRCS+=	accept4.c clock_getcpuclockid.c epoll.c eventfd_read.c \
+	eventfd_write.c posix_fadvise.c posix_madvise.c ppoll.c sched.c \
+	sigqueue.c sigtimedwait.c sigwait.c sigwaitinfo.c statvfs.c swapon.c \
+	semctl.c vadvise.c
 
 .if ${RUMPRUN} != "yes"
 # modules with non-default implementations on at least one architecture:
@@ -103,6 +103,7 @@ ASM=\
 		clock_getcpuclockid2.S \
 		__clock_getres50.S __clock_gettime50.S \
 	dup.S dup2.S dup3.S \
+	epoll_create1.S epoll_ctl.S epoll_pwait2.S \
 	eventfd.S \
 	extattrctl.S \
 		extattr_delete_fd.S extattr_delete_file.S \
@@ -180,7 +181,7 @@ ASM_MD=	_lwp_getprivate.S mremap.S 
 WEAKASM= accept.S __aio_suspend50.S clock_nanosleep.S close.S connect.S \
 	execve.S \
 	fcntl.S fdatasync.S fsync.S \
-	fsync_range.S __kevent50.S \
+	fsync_range.S __kevent100.S \
 	kill.S mq_receive.S mq_send.S __mq_timedreceive50.S __mq_timedsend50.S \
 	msgrcv.S msgsnd.S __msync13.S  __nanosleep50.S open.S openat.S \
 	paccept.S poll.S \
@@ -260,7 +261,7 @@ LintSysPseudoNoerr.c: ${LIBCDIR}/sys/mak
 MAN+=	accept.2 access.2 acct.2 adjtime.2 bind.2 brk.2 chdir.2 \
 	chflags.2 chmod.2 chown.2 chroot.2 clock_getcpuclockid2.2 \
 	clock_settime.2 clone.2 close.2 \
-	connect.2 dup.2 eventfd.2 execve.2 _exit.2 extattr_get_file.2 \
+	connect.2 dup.2 epoll.2 eventfd.2 execve.2 _exit.2 extattr_get_file.2 \
 	fcntl.2 fdatasync.2 fdiscard.2 fhopen.2 \
 	flock.2 fork.2 fsync.2 getcontext.2 getdents.2 \
 	getfh.2 getvfsstat.2 getgid.2 getgroups.2 \
@@ -308,6 +309,15 @@ MLINKS+=chown.2 fchown.2 chown.2 lchown.
 MLINKS+=chroot.2 fchroot.2
 MLINKS+=clock_settime.2 clock_gettime.2
 MLINKS+=clock_settime.2 clock_getres.2
+MLINKS+=epoll.2 epoll_event.2 \
+	epoll.2 epoll_data.2 \
+	epoll.2 epoll_data_t.2 \
+	epoll.2 epoll_create.2 \
+	epoll.2 epoll_create1.2 \
+	epoll.2 epoll_ctl.2 \
+	epoll.2 epoll_wait.2 \
+	epoll.2 epoll_pwait.2 \
+	epoll.2 epoll_pwait2.2
 MLINKS+=eventfd.2 eventfd_read.2 \
 	eventfd.2 eventfd_write.2
 MLINKS+=extattr_get_file.2 extattr_set_file.2 \

Index: src/lib/libc/sys/kqueue.2
diff -u src/lib/libc/sys/kqueue.2:1.58 src/lib/libc/sys/kqueue.2:1.59
--- src/lib/libc/sys/kqueue.2:1.58	Sun Feb 13 11:51:56 2022
+++ src/lib/libc/sys/kqueue.2	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-.\"	$NetBSD: kqueue.2,v 1.58 2022/02/13 16:51:56 pgoyette Exp $
+.\"	$NetBSD: kqueue.2,v 1.59 2023/07/28 18:19:00 christos Exp $
 .\"
 .\" Copyright (c) 2000 Jonathan Lemon
 .\" All rights reserved.
@@ -32,7 +32,7 @@
 .\"
 .\" $FreeBSD: src/lib/libc/sys/kqueue.2,v 1.22 2001/06/27 19:55:57 dd Exp $
 .\"
-.Dd February 13, 2022
+.Dd June 19, 2023
 .Dt KQUEUE 2
 .Os
 .Sh NAME
@@ -192,6 +192,7 @@ struct kevent {
 	uint32_t  fflags;	/* filter flag value */
 	int64_t   data;		/* filter data value */
 	void     *udata;	/* opaque user data identifier */
+	uint64_t  ext[4];	/* extensions */
 };
 .Ed
 .Pp
@@ -215,6 +216,20 @@ Filter-specific flags.
 Filter-specific data value.
 .It Fa udata
 Opaque user-defined value passed through the kernel unchanged.
+.It Fa ext
+Extended data passed to and from kernel.
+The
+.Fa ext[0]
+and
+.Fa ext[1]
+members use is defined by the filter.
+If the filter does not use them, the members are copied unchanged.
+The
+.Fa ext[2]
+and
+.Fa ext[3]
+members are always passed through the kernel as-is,
+making additional context available to application.
 .El
 .Pp
 The

Index: src/lib/libpthread/pthread_cancelstub.c
diff -u src/lib/libpthread/pthread_cancelstub.c:1.43 src/lib/libpthread/pthread_cancelstub.c:1.44
--- src/lib/libpthread/pthread_cancelstub.c:1.43	Tue Apr 19 16:32:17 2022
+++ src/lib/libpthread/pthread_cancelstub.c	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: pthread_cancelstub.c,v 1.43 2022/04/19 20:32:17 rillig Exp $	*/
+/*	$NetBSD: pthread_cancelstub.c,v 1.44 2023/07/28 18:19:00 christos Exp $	*/
 
 /*-
  * Copyright (c) 2002, 2007 The NetBSD Foundation, Inc.
@@ -33,7 +33,7 @@
 #undef _FORTIFY_SOURCE
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: pthread_cancelstub.c,v 1.43 2022/04/19 20:32:17 rillig Exp $");
+__RCSID("$NetBSD: pthread_cancelstub.c,v 1.44 2023/07/28 18:19:00 christos Exp $");
 
 /* Need to use libc-private names for atomic operations. */
 #include "../../common/lib/libc/atomic/atomic_op_namespace.h"
@@ -101,7 +101,7 @@ int	_sys_fcntl(int, int, ...);
 int	_sys_fdatasync(int);
 int	_sys_fsync(int);
 int	_sys_fsync_range(int, int, off_t, off_t);
-int	_sys___kevent50(int, const struct kevent *, size_t, struct kevent *,
+int	_sys___kevent100(int, const struct kevent *, size_t, struct kevent *,
 	    size_t, const struct timespec *);
 int	_sys_mq_send(mqd_t, const char *, size_t, unsigned);
 ssize_t	_sys_mq_receive(mqd_t, char *, size_t, unsigned *);
@@ -180,7 +180,7 @@ __aio_suspend50(const struct aiocb * con
 }
 
 int
-__kevent50(int fd, const struct kevent *ev, size_t nev, struct kevent *rev,
+__kevent100(int fd, const struct kevent *ev, size_t nev, struct kevent *rev,
     size_t nrev, const struct timespec *ts)
 {
 	int retval;
@@ -188,7 +188,7 @@ __kevent50(int fd, const struct kevent *
 
 	self = pthread__self();
 	TESTCANCEL(self);
-	retval = _sys___kevent50(fd, ev, nev, rev, nrev, ts);
+	retval = _sys___kevent100(fd, ev, nev, rev, nrev, ts);
 	TESTCANCEL(self);
 
 	return retval;

Index: src/lib/librumpclient/rumpclient.c
diff -u src/lib/librumpclient/rumpclient.c:1.69 src/lib/librumpclient/rumpclient.c:1.70
--- src/lib/librumpclient/rumpclient.c:1.69	Thu Sep 16 18:19:10 2021
+++ src/lib/librumpclient/rumpclient.c	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-/*      $NetBSD: rumpclient.c,v 1.69 2021/09/16 22:19:10 andvar Exp $	*/
+/*      $NetBSD: rumpclient.c,v 1.70 2023/07/28 18:19:00 christos Exp $	*/
 
 /*
  * Copyright (c) 2010, 2011 Antti Kantee.  All Rights Reserved.
@@ -50,7 +50,7 @@
 #define USE_SIGNALFD
 #endif
 
-__RCSID("$NetBSD: rumpclient.c,v 1.69 2021/09/16 22:19:10 andvar Exp $");
+__RCSID("$NetBSD: rumpclient.c,v 1.70 2023/07/28 18:19:00 christos Exp $");
 
 #include <sys/param.h>
 #include <sys/mman.h>
@@ -926,8 +926,10 @@ rumpclient_init(void)
 #ifdef __NetBSD__
 #if !__NetBSD_Prereq__(5,99,7)
 	FINDSYM(kevent)
-#else
+#elif !__NetBSD_Prereq__(10,99,4)
 	FINDSYM2(kevent,_sys___kevent50)
+#else
+	FINDSYM2(kevent,_sys___kevent100)
 #endif
 #else
 	FINDSYM(kevent)

Index: src/lib/librumphijack/hijack.c
diff -u src/lib/librumphijack/hijack.c:1.136 src/lib/librumphijack/hijack.c:1.137
--- src/lib/librumphijack/hijack.c:1.136	Sat Apr 16 14:15:20 2022
+++ src/lib/librumphijack/hijack.c	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-/*      $NetBSD: hijack.c,v 1.136 2022/04/16 18:15:20 andvar Exp $	*/
+/*      $NetBSD: hijack.c,v 1.137 2023/07/28 18:19:00 christos Exp $	*/
 
 /*-
  * Copyright (c) 2011 Antti Kantee.  All Rights Reserved.
@@ -34,7 +34,7 @@
 #include <rump/rumpuser_port.h>
 
 #if !defined(lint)
-__RCSID("$NetBSD: hijack.c,v 1.136 2022/04/16 18:15:20 andvar Exp $");
+__RCSID("$NetBSD: hijack.c,v 1.137 2023/07/28 18:19:00 christos Exp $");
 #endif
 
 #include <sys/param.h>
@@ -190,7 +190,6 @@ enum dualcall {
 #define REALPSELECT pselect
 #define REALSELECT select
 #define REALPOLLTS pollts
-#define REALKEVENT kevent
 #define REALSTAT __stat30
 #define REALLSTAT __lstat30
 #define REALFSTAT __fstat30
@@ -203,7 +202,6 @@ enum dualcall {
 #define REALPSELECT _sys___pselect50
 #define REALSELECT _sys___select50
 #define REALPOLLTS _sys___pollts50
-#define REALKEVENT _sys___kevent50
 #define REALSTAT __stat50
 #define REALLSTAT __lstat50
 #define REALFSTAT __fstat50
@@ -214,6 +212,14 @@ enum dualcall {
 #define REALFHSTAT __fhstat50
 #endif /* < 5.99.7 */
 
+#if !__NetBSD_Prereq__(5,99,7)
+#define REALKEVENT kevent
+#elif !__NetBSD_Prereq__(10,99,4)
+#define REALKEVENT _sys___kevent50
+#else
+#define REALKEVENT _sys___kevent100
+#endif
+
 #define REALREAD _sys_read
 #define REALPREAD _sys_pread
 #define REALPWRITE _sys_pwrite

Index: src/sys/compat/common/compat_100_mod.c
diff -u src/sys/compat/common/compat_100_mod.c:1.1 src/sys/compat/common/compat_100_mod.c:1.2
--- src/sys/compat/common/compat_100_mod.c:1.1	Mon Dec 19 18:19:51 2022
+++ src/sys/compat/common/compat_100_mod.c	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: compat_100_mod.c,v 1.1 2022/12/19 23:19:51 pgoyette Exp $ */
+/*	$NetBSD: compat_100_mod.c,v 1.2 2023/07/28 18:19:00 christos Exp $ */
 
 /*-
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -38,7 +38,7 @@
 #endif
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: compat_100_mod.c,v 1.1 2022/12/19 23:19:51 pgoyette Exp $");
+__KERNEL_RCSID(0, "$NetBSD: compat_100_mod.c,v 1.2 2023/07/28 18:19:00 christos Exp $");
 
 #include <sys/systm.h>
 #include <sys/module.h>
@@ -50,14 +50,14 @@ int
 compat_100_init(void)
 {
 
-	return 0;
+	return kern_event_100_init();
 }
 
 int
 compat_100_fini(void)
 {
 
-	return 0;
+	return kern_event_100_fini();
 }
 
 MODULE(MODULE_CLASS_EXEC, compat_100, NULL);

Index: src/sys/compat/common/compat_mod.h
diff -u src/sys/compat/common/compat_mod.h:1.7 src/sys/compat/common/compat_mod.h:1.8
--- src/sys/compat/common/compat_mod.h:1.7	Mon Dec 19 18:19:51 2022
+++ src/sys/compat/common/compat_mod.h	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: compat_mod.h,v 1.7 2022/12/19 23:19:51 pgoyette Exp $	*/
+/*	$NetBSD: compat_mod.h,v 1.8 2023/07/28 18:19:00 christos Exp $	*/
 
 /*-
  * Copyright (c) 2013, 2019 The NetBSD Foundation, Inc.
@@ -35,6 +35,8 @@
 #ifdef COMPAT_100
 int compat_100_init(void);
 int compat_100_fini(void);
+int kern_event_100_init(void);
+int kern_event_100_fini(void);
 #endif
 
 #ifdef COMPAT_90

Index: src/sys/compat/common/files.common
diff -u src/sys/compat/common/files.common:1.8 src/sys/compat/common/files.common:1.9
--- src/sys/compat/common/files.common:1.8	Mon Dec 19 18:19:51 2022
+++ src/sys/compat/common/files.common	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: files.common,v 1.8 2022/12/19 23:19:51 pgoyette Exp $
+#	$NetBSD: files.common,v 1.9 2023/07/28 18:19:00 christos Exp $
 
 #
 # Generic utility files, used by various compat options.
@@ -112,6 +112,7 @@ file	compat/common/vfs_syscalls_90.c		co
 
 # Compatibility code for NetBSD 10.0
 file	compat/common/compat_100_mod.c		compat_100
+file	compat/common/kern_event_100.c		compat_100
 
 #
 # Sources for sysv ipc compatibility across the versions.

Index: src/sys/compat/common/kern_select_50.c
diff -u src/sys/compat/common/kern_select_50.c:1.3 src/sys/compat/common/kern_select_50.c:1.4
--- src/sys/compat/common/kern_select_50.c:1.3	Fri Sep 20 11:05:22 2019
+++ src/sys/compat/common/kern_select_50.c	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_select_50.c,v 1.3 2019/09/20 15:05:22 kamil Exp $	*/
+/*	$NetBSD: kern_select_50.c,v 1.4 2023/07/28 18:19:00 christos Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -29,7 +29,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_select_50.c,v 1.3 2019/09/20 15:05:22 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_select_50.c,v 1.4 2023/07/28 18:19:00 christos Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_compat_netbsd.h"
@@ -44,6 +44,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_select_
 #include <sys/syscallvar.h>
 #include <sys/syscallargs.h>
 
+#include <compat/sys/event.h>
 #include <compat/sys/time.h>
 #include <compat/common/compat_mod.h>
 
@@ -76,21 +77,22 @@ compat_50_sys_kevent(struct lwp *l, cons
 {
 	/* {
 		syscallarg(int) fd;
-		syscallarg(keventp_t) changelist;
+		syscallarg(struct kevent100 *) changelist;
 		syscallarg(size_t) nchanges;
-		syscallarg(keventp_t) eventlist;
+		syscallarg(struct kevent100 *) eventlist;
 		syscallarg(size_t) nevents;
 		syscallarg(struct timespec50) timeout;
 	} */
 	static const struct kevent_ops compat_50_kevent_ops = {
 		.keo_private = NULL,
 		.keo_fetch_timeout = compat_50_kevent_fetch_timeout,
-		.keo_fetch_changes = kevent_fetch_changes,
-		.keo_put_events = kevent_put_events,
+		.keo_fetch_changes = compat_100___kevent50_fetch_changes,
+		.keo_put_events = compat_100___kevent50_put_events,
 	};
 
-	return kevent1(retval, SCARG(uap, fd), SCARG(uap, changelist),
-	    SCARG(uap, nchanges), SCARG(uap, eventlist), SCARG(uap, nevents),
+	return kevent1(retval, SCARG(uap, fd),
+	    (const struct kevent *)(const void *)SCARG(uap, changelist), SCARG(uap, nchanges),
+	    (struct kevent *)(void *)SCARG(uap, eventlist), SCARG(uap, nevents),
 	    (const struct timespec *)(const void *)SCARG(uap, timeout),
 	    &compat_50_kevent_ops);
 }

Index: src/sys/compat/linux/arch/amd64/syscalls.master
diff -u src/sys/compat/linux/arch/amd64/syscalls.master:1.68 src/sys/compat/linux/arch/amd64/syscalls.master:1.69
--- src/sys/compat/linux/arch/amd64/syscalls.master:1.68	Sun Jul  9 22:31:55 2023
+++ src/sys/compat/linux/arch/amd64/syscalls.master	Fri Jul 28 14:19:00 2023
@@ -1,4 +1,4 @@
-	$NetBSD: syscalls.master,v 1.68 2023/07/10 02:31:55 christos Exp $
+	$NetBSD: syscalls.master,v 1.69 2023/07/28 18:19:00 christos Exp $
 
 ;	@(#)syscalls.master	8.1 (Berkeley) 7/19/93
 
@@ -51,6 +51,7 @@
 #include <compat/sys/time.h>
 
 #include <compat/linux/common/linux_types.h>
+#include <compat/linux/common/linux_misc.h>
 #include <compat/linux/common/linux_mmap.h>
 #include <compat/linux/common/linux_ipc.h>
 #include <compat/linux/common/linux_msg.h>
@@ -412,7 +413,7 @@
 210	UNIMPL		io_cancel
 211	UNIMPL		get_thread_area
 212	UNIMPL		lookup_dcookie
-213	UNIMPL		epoll_create
+213	STD		{ int|linux_sys||epoll_create(int size); }
 214	UNIMPL		epoll_ctl_old
 215	UNIMPL		epoll_wait_old
 216	UNIMPL		remap_file_pages
@@ -442,8 +443,11 @@
 			    int flags, struct linux_timespec *rqtp, \
 			    struct linux_timespec *rmtp); }
 231	STD		{ int|linux_sys||exit_group(int error_code); }
-232	UNIMPL		epoll_wait
-233	UNIMPL		epoll_ctl
+232	STD		{ int|linux_sys||epoll_wait(int epfd, \
+			    struct linux_epoll_event *events, int maxevents, \
+			    int timeout); }
+233	STD		{ int|linux_sys||epoll_ctl(int epfd, int op, int fd, \
+			    struct linux_epoll_event *event); }
 234	STD		{ int|linux_sys||tgkill(int tgid, int tid, int sig); }
 235	NOARGS		{ int|compat_50_sys||utimes(const char *path, \
 			    const struct timeval50 *tptr); }
@@ -517,7 +521,9 @@
 279	UNIMPL		move_pages
 280	STD		{ int|linux_sys||utimensat(int fd, const char *path, \
 			    struct linux_timespec *times, int flag); }
-281	UNIMPL		epoll_pwait
+281	STD		{ int|linux_sys||epoll_pwait(int epfd, \
+			    struct linux_epoll_event *events, int maxevents, \
+			    int timeout, const linux_sigset_t *sigmask); }
 282	UNIMPL		signalfd
 283	STD		{ int|linux_sys||timerfd_create(clockid_t clock_id, \
 			    int flags); }
@@ -535,7 +541,7 @@
 289	UNIMPL		signalfd4
 290	STD		{ int|linux_sys||eventfd2(unsigned int initval, \
 			    int flags); }
-291	UNIMPL		epoll_create1
+291	STD		{ int|linux_sys||epoll_create1(int flags); }
 292	STD		{ int|linux_sys||dup3(int from, int to, int flags); }
 293	STD		{ int|linux_sys||pipe2(int *pfds, int flags); }
 294	UNIMPL		inotify_init1
@@ -696,7 +702,10 @@
 438	UNIMPL		pidfd_getfd
 439	UNIMPL		faccessat2
 440	UNIMPL		process_madvise
-441	UNIMPL		epoll_pwait2
+441	STD		{ int|linux_sys||epoll_pwait2(int epfd, \
+			    struct linux_epoll_event *events, int maxevents, \
+			    const struct linux_timespec *timeout, \
+			    const linux_sigset_t *sigmask); }
 442	UNIMPL		mount_setattr
 443	UNIMPL		quotactl_fd
 444	UNIMPL		landlock_create_ruleset

Index: src/sys/compat/linux/common/linux_misc.c
diff -u src/sys/compat/linux/common/linux_misc.c:1.257 src/sys/compat/linux/common/linux_misc.c:1.258
--- src/sys/compat/linux/common/linux_misc.c:1.257	Sun Jul  9 22:31:55 2023
+++ src/sys/compat/linux/common/linux_misc.c	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: linux_misc.c,v 1.257 2023/07/10 02:31:55 christos Exp $	*/
+/*	$NetBSD: linux_misc.c,v 1.258 2023/07/28 18:19:01 christos Exp $	*/
 
 /*-
  * Copyright (c) 1995, 1998, 1999, 2008 The NetBSD Foundation, Inc.
@@ -57,13 +57,14 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_misc.c,v 1.257 2023/07/10 02:31:55 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_misc.c,v 1.258 2023/07/28 18:19:01 christos Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/namei.h>
 #include <sys/proc.h>
 #include <sys/dirent.h>
+#include <sys/epoll.h>
 #include <sys/eventfd.h>
 #include <sys/file.h>
 #include <sys/stat.h>
@@ -1682,6 +1683,234 @@ linux_sys_eventfd2(struct lwp *l, const 
 				 retval);
 }
 
+/*
+ * epoll_create(2).  Check size and call sys_epoll_create1.
+ */
+int
+linux_sys_epoll_create(struct lwp *l,
+    const struct linux_sys_epoll_create_args *uap, register_t *retval)
+{
+	/* {
+		syscallarg(int) size;
+	} */
+	struct sys_epoll_create1_args ca;
+
+	/*
+	 * SCARG(uap, size) is unused.  Linux just tests it and then
+	 * forgets it as well.
+	 */
+	if (SCARG(uap, size) <= 0)
+		return EINVAL;
+
+	SCARG(&ca, flags) = 0;
+	return sys_epoll_create1(l, &ca, retval);
+}
+
+/*
+ * epoll_create1(2).  Translate the flags and call sys_epoll_create1.
+ */
+int
+linux_sys_epoll_create1(struct lwp *l,
+    const struct linux_sys_epoll_create1_args *uap, register_t *retval)
+{
+	/* {
+		syscallarg(int) flags;
+	} */
+	struct sys_epoll_create1_args ca;
+
+        if ((SCARG(uap, flags) & ~(LINUX_O_CLOEXEC)) != 0)
+		return EINVAL;
+
+	SCARG(&ca, flags) = 0;
+	if ((SCARG(uap, flags) & LINUX_O_CLOEXEC) != 0)
+		SCARG(&ca, flags) |= O_CLOEXEC;
+
+	return sys_epoll_create1(l, &ca, retval);
+}
+
+/*
+ * epoll_ctl(2).  Copyin event and translate it if necessary and then
+ * call epoll_ctl_common().
+ */
+int
+linux_sys_epoll_ctl(struct lwp *l, const struct linux_sys_epoll_ctl_args *uap,
+    register_t *retval)
+{
+	/* {
+		syscallarg(int) epfd;
+		syscallarg(int) op;
+		syscallarg(int) fd;
+		syscallarg(struct linux_epoll_event *) event;
+	} */
+	struct linux_epoll_event lee;
+	struct epoll_event ee;
+	struct epoll_event *eep;
+	int error;
+
+	if (SCARG(uap, op) != EPOLL_CTL_DEL) {
+		error = copyin(SCARG(uap, event), &lee, sizeof(lee));
+		if (error != 0)
+			return error;
+
+		/*
+		 * On some architectures, struct linux_epoll_event and
+		 * struct epoll_event are packed differently... but otherwise
+		 * the contents are the same.
+		 */
+		ee.events = lee.events;
+		ee.data = lee.data;
+
+		eep = &ee;
+	} else
+		eep = NULL;
+
+	return epoll_ctl_common(l, retval, SCARG(uap, epfd), SCARG(uap, op),
+	    SCARG(uap, fd), eep);
+}
+
+/*
+ * epoll_wait(2).  Call sys_epoll_pwait().
+ */
+int
+linux_sys_epoll_wait(struct lwp *l,
+    const struct linux_sys_epoll_wait_args *uap, register_t *retval)
+{
+	/* {
+		syscallarg(int) epfd;
+		syscallarg(struct linux_epoll_event *) events;
+		syscallarg(int) maxevents;
+		syscallarg(int) timeout;
+	} */
+	struct linux_sys_epoll_pwait_args ea;
+
+	SCARG(&ea, epfd) = SCARG(uap, epfd);
+	SCARG(&ea, events) = SCARG(uap, events);
+	SCARG(&ea, maxevents) = SCARG(uap, maxevents);
+	SCARG(&ea, timeout) = SCARG(uap, timeout);
+	SCARG(&ea, sigmask) = NULL;
+
+	return linux_sys_epoll_pwait(l, &ea, retval);
+}
+
+/*
+ * Main body of epoll_pwait2(2).  Translate timeout and sigmask and
+ * call epoll_wait_common.
+ */
+static int
+linux_epoll_pwait2_common(struct lwp *l, register_t *retval, int epfd,
+    struct linux_epoll_event *events, int maxevents,
+    struct linux_timespec *timeout, const linux_sigset_t *sigmask)
+{
+	struct timespec ts, *tsp;
+	linux_sigset_t lss;
+	sigset_t ss, *ssp;
+	struct epoll_event *eep;
+	struct linux_epoll_event *leep;
+	int i, error;
+
+	if (maxevents <= 0 || maxevents > EPOLL_MAX_EVENTS)
+		return EINVAL;
+
+	if (timeout != NULL) {
+		linux_to_native_timespec(&ts, timeout);
+		tsp = &ts;
+	} else
+		tsp = NULL;
+
+	if (sigmask != NULL) {
+		error = copyin(sigmask, &lss, sizeof(lss));
+		if (error != 0)
+			return error;
+
+		linux_to_native_sigset(&ss, &lss);
+		ssp = &ss;
+	} else
+		ssp = NULL;
+
+	eep = kmem_alloc(maxevents * sizeof(*eep), KM_SLEEP);
+
+	error = epoll_wait_common(l, retval, epfd, eep, maxevents, tsp,
+	    ssp);
+	if (error == 0 && *retval > 0) {
+		leep = kmem_alloc((*retval) * sizeof(*leep), KM_SLEEP);
+
+		/* Translate the events (because of packing). */
+		for (i = 0; i < *retval; i++) {
+			leep[i].events = eep[i].events;
+			leep[i].data = eep[i].data;
+		}
+
+		error = copyout(leep, events, (*retval) * sizeof(*leep));
+		kmem_free(leep, (*retval) * sizeof(*leep));
+	}
+
+	kmem_free(eep, maxevents * sizeof(*eep));
+	return error;
+}
+
+/*
+ * epoll_pwait(2).  Translate timeout and call sys_epoll_pwait2.
+ */
+int
+linux_sys_epoll_pwait(struct lwp *l,
+    const struct linux_sys_epoll_pwait_args *uap, register_t *retval)
+{
+	/* {
+		syscallarg(int) epfd;
+		syscallarg(struct linux_epoll_event *) events;
+		syscallarg(int) maxevents;
+		syscallarg(int) timeout;
+		syscallarg(linux_sigset_t *) sigmask;
+	} */
+        struct linux_timespec lts, *ltsp;
+	const int timeout = SCARG(uap, timeout);
+
+	if (timeout >= 0) {
+		/* Convert from milliseconds to timespec. */
+		lts.tv_sec = timeout / 1000;
+		lts.tv_nsec = (timeout % 1000) * 1000000;
+
+	        ltsp = &lts;
+	} else
+		ltsp = NULL;
+
+	return linux_epoll_pwait2_common(l, retval, SCARG(uap, epfd),
+	    SCARG(uap, events), SCARG(uap, maxevents), ltsp,
+	    SCARG(uap, sigmask));
+}
+
+
+/*
+ * epoll_pwait2(2).  Copyin timeout and call linux_epoll_pwait2_common().
+ */
+int
+linux_sys_epoll_pwait2(struct lwp *l,
+    const struct linux_sys_epoll_pwait2_args *uap, register_t *retval)
+{
+	/* {
+		syscallarg(int) epfd;
+		syscallarg(struct linux_epoll_event *) events;
+		syscallarg(int) maxevents;
+	        syscallarg(struct linux_timespec *) timeout;
+		syscallarg(linux_sigset_t *) sigmask;
+	} */
+	struct linux_timespec lts, *ltsp;
+	int error;
+
+	if (SCARG(uap, timeout) != NULL) {
+		error = copyin(SCARG(uap, timeout), &lts, sizeof(lts));
+		if (error != 0)
+			return error;
+
+		ltsp = &lts;
+	} else
+		ltsp = NULL;
+
+	return linux_epoll_pwait2_common(l, retval, SCARG(uap, epfd),
+	    SCARG(uap, events), SCARG(uap, maxevents), ltsp,
+	    SCARG(uap, sigmask));
+}
+
 #define	LINUX_MFD_CLOEXEC	0x0001U
 #define	LINUX_MFD_ALLOW_SEALING	0x0002U
 #define	LINUX_MFD_HUGETLB	0x0004U

Index: src/sys/compat/linux/common/linux_misc.h
diff -u src/sys/compat/linux/common/linux_misc.h:1.26 src/sys/compat/linux/common/linux_misc.h:1.27
--- src/sys/compat/linux/common/linux_misc.h:1.26	Sat May  2 21:06:56 2020
+++ src/sys/compat/linux/common/linux_misc.h	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: linux_misc.h,v 1.26 2020/05/03 01:06:56 thorpej Exp $	*/
+/*	$NetBSD: linux_misc.h,v 1.27 2023/07/28 18:19:01 christos Exp $	*/
 
 /*-
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -141,6 +141,15 @@ extern const int linux_fstypes_cnt;
  */
 #define linux_to_bsd_posix_fadv(advice) (advice)
 
+struct linux_epoll_event {
+	uint32_t	events;
+	uint64_t	data;
+}
+#if defined(__amd64__)
+__packed
+#endif
+;
+
 #ifdef _KERNEL
 __BEGIN_DECLS
 int bsd_to_linux_wstat(int);

Index: src/sys/compat/sys/event.h
diff -u src/sys/compat/sys/event.h:1.2 src/sys/compat/sys/event.h:1.3
--- src/sys/compat/sys/event.h:1.2	Sat Jan 10 21:45:50 2009
+++ src/sys/compat/sys/event.h	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: event.h,v 1.2 2009/01/11 02:45:50 christos Exp $	*/
+/*	$NetBSD: event.h,v 1.3 2023/07/28 18:19:01 christos Exp $	*/
 
 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jle...@freebsd.org>
@@ -34,11 +34,90 @@
 #include <sys/cdefs.h>
 struct timespec;
 
+#ifdef _KERNEL
+#include <lib/libkern/libkern.h>
+#else
+#include <string.h>
+#endif
+
+struct kevent100 {
+	uintptr_t	ident;		/* identifier for this event */
+	uint32_t	filter;		/* filter for event */
+	uint32_t	flags;		/* action flags for kqueue */
+	uint32_t	fflags;		/* filter flag value */
+	int64_t		data;		/* filter data value */
+	void		*udata;		/* opaque user data identifier */
+};
+
+static __inline void
+kevent100_to_kevent(const struct kevent100 *kev100, struct kevent *kev)
+{
+	memset(kev, 0, sizeof(*kev));
+	memcpy(kev, kev100, sizeof(*kev100));
+}
+
+static __inline void
+kevent_to_kevent100(const struct kevent *kev, struct kevent100 *kev100)
+{
+	memcpy(kev100, kev, sizeof(*kev100));
+}
+
+#ifdef _KERNEL
+static int
+compat_100___kevent50_fetch_changes(void *ctx, const struct kevent *changelist,
+    struct kevent *changes, size_t index, int n)
+{
+	int error, i;
+	struct kevent100 *buf;
+	const size_t buf_size = sizeof(*buf) * n;
+	const struct kevent100 *changelist100 = (const struct kevent100 *)changelist;
+
+	KASSERT(n >= 0);
+
+	buf = kmem_alloc(buf_size, KM_SLEEP);
+
+	error = copyin(changelist100 + index, buf, buf_size);
+	if (error != 0)
+		goto leave;
+
+	for (i = 0; i < n; i++)
+		kevent100_to_kevent(buf + i, changes + i);
+
+leave:
+	kmem_free(buf, buf_size);
+	return error;
+}
+
+static int
+compat_100___kevent50_put_events(void *ctx, struct kevent *events,
+    struct kevent *eventlist, size_t index, int n)
+{
+	int error, i;
+        struct kevent100 *buf;
+	const size_t buf_size = sizeof(*buf) * n;
+	struct kevent100 *eventlist100 = (struct kevent100 *)eventlist;
+
+	KASSERT(n >= 0);
+
+	buf = kmem_alloc(buf_size, KM_SLEEP);
+
+	for (i = 0; i < n; i++)
+	        kevent_to_kevent100(events + i, buf + i);
+
+	error = copyout(buf, eventlist100 + index, buf_size);
+
+	kmem_free(buf, buf_size);
+	return error;
+}
+#endif /* _KERNEL */
+
 __BEGIN_DECLS
-int	kevent(int, const struct kevent *, size_t, struct kevent *, size_t,
-    const struct timespec50 *);
-int	__kevent50(int, const struct kevent *, size_t, struct kevent *, size_t,
-    const struct timespec *);
+int	kevent(int, const struct kevent100 *, size_t, struct kevent100 *,
+    size_t, const struct timespec50 *);
+int	__kevent50(int, const struct kevent100 *, size_t, struct kevent100 *,
+    size_t, const struct timespec *);
+int	__kevent100(int, const struct kevent *, size_t, struct kevent *,
+    size_t, const struct timespec *);
 __END_DECLS
 
 #endif /* !_COMPAT_SYS_EVENT_H_ */

Index: src/sys/kern/files.kern
diff -u src/sys/kern/files.kern:1.59 src/sys/kern/files.kern:1.60
--- src/sys/kern/files.kern:1.59	Mon Jul 10 02:42:33 2023
+++ src/sys/kern/files.kern	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: files.kern,v 1.59 2023/07/10 06:42:33 mrg Exp $
+#	$NetBSD: files.kern,v 1.60 2023/07/28 18:19:01 christos Exp $
 
 #
 # kernel sources
@@ -161,6 +161,7 @@ file	kern/subr_workqueue.c		kern
 file	kern/subr_xcall.c		kern
 file	kern/sys_aio.c			aio
 file	kern/sys_descrip.c		kern
+file	kern/sys_epoll.c		kern
 file	kern/sys_eventfd.c		kern
 file	kern/sys_futex.c		kern
 file	kern/sys_generic.c		kern

Index: src/sys/kern/kern_event.c
diff -u src/sys/kern/kern_event.c:1.148 src/sys/kern/kern_event.c:1.149
--- src/sys/kern/kern_event.c:1.148	Sat Apr 22 09:52:54 2023
+++ src/sys/kern/kern_event.c	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_event.c,v 1.148 2023/04/22 13:52:54 riastradh Exp $	*/
+/*	$NetBSD: kern_event.c,v 1.149 2023/07/28 18:19:01 christos Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009, 2021 The NetBSD Foundation, Inc.
@@ -63,7 +63,7 @@
 #endif /* _KERNEL_OPT */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.148 2023/04/22 13:52:54 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.149 2023/07/28 18:19:01 christos Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -1785,7 +1785,7 @@ static const struct kevent_ops kevent_na
 };
 
 int
-sys___kevent50(struct lwp *l, const struct sys___kevent50_args *uap,
+sys___kevent100(struct lwp *l, const struct sys___kevent100_args *uap,
     register_t *retval)
 {
 	/* {

Index: src/sys/kern/makesyscalls.sh
diff -u src/sys/kern/makesyscalls.sh:1.186 src/sys/kern/makesyscalls.sh:1.187
--- src/sys/kern/makesyscalls.sh:1.186	Thu Oct 21 07:01:03 2021
+++ src/sys/kern/makesyscalls.sh	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: makesyscalls.sh,v 1.186 2021/10/21 11:01:03 andvar Exp $
+#	$NetBSD: makesyscalls.sh,v 1.187 2023/07/28 18:19:01 christos Exp $
 #
 # Copyright (c) 1994, 1996, 2000 Christopher G. Demetriou
 # All rights reserved.
@@ -419,6 +419,7 @@ NR == 1 {
 	uncompattypes["struct timeval50"] = "struct timeval";
 	uncompattypes["struct timespec50"] = "struct timespec";
 	uncompattypes["struct stat30"] = "struct stat";
+	uncompattypes["struct kevent100"] = "struct kevent";
 
 	next
 }

Index: src/sys/kern/syscalls.conf
diff -u src/sys/kern/syscalls.conf:1.31 src/sys/kern/syscalls.conf:1.32
--- src/sys/kern/syscalls.conf:1.31	Sat May 16 14:31:50 2020
+++ src/sys/kern/syscalls.conf	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: syscalls.conf,v 1.31 2020/05/16 18:31:50 christos Exp $
+#	$NetBSD: syscalls.conf,v 1.32 2023/07/28 18:19:01 christos Exp $
 
 sysnames="syscalls.c"
 sysnumhdr="../sys/syscall.h"
@@ -11,7 +11,7 @@ sysalign=1
 rumpcalls="../rump/librump/rumpkern/rump_syscalls.c"
 rumpcallshdr="../rump/include/rump/rump_syscalls.h"
 rumpsysmap="../rump/rump.sysmap"
-compatopts="compat_43 compat_09 compat_10 compat_11 compat_12 compat_13 compat_14 compat_15 compat_16 compat_20 compat_30 compat_40 compat_50 compat_60 compat_70 compat_80 compat_90"
+compatopts="compat_43 compat_09 compat_10 compat_11 compat_12 compat_13 compat_14 compat_15 compat_16 compat_20 compat_30 compat_40 compat_50 compat_60 compat_70 compat_80 compat_90 compat_100"
 libcompatopts=""
 
 switchname="sysent"

Index: src/sys/kern/syscalls.master
diff -u src/sys/kern/syscalls.master:1.310 src/sys/kern/syscalls.master:1.311
--- src/sys/kern/syscalls.master:1.310	Sun Jul  9 22:33:04 2023
+++ src/sys/kern/syscalls.master	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-	$NetBSD: syscalls.master,v 1.310 2023/07/10 02:33:04 christos Exp $
+	$NetBSD: syscalls.master,v 1.311 2023/07/28 18:19:01 christos Exp $
 
 ;	@(#)syscalls.master	8.2 (Berkeley) 1/13/94
 
@@ -705,8 +705,8 @@
 343	STD		{ int|sys||rasctl(void *addr, size_t len, int op); }
 344	STD	RUMP	{ int|sys||kqueue(void); }
 345	COMPAT_50 MODULAR compat_50 RUMP { int|sys||kevent(int fd, \
-			    const struct kevent *changelist, size_t nchanges, \
-			    struct kevent *eventlist, size_t nevents, \
+			    const struct kevent100 *changelist, size_t nchanges, \
+			    struct kevent100 *eventlist, size_t nevents, \
 			    const struct timespec50 *timeout); }
 
 ; Scheduling system calls.
@@ -912,9 +912,10 @@
 		{ int|sys||_lwp_park(const struct timespec *ts, \
 				lwpid_t unpark, const void *hint, \
 				const void *unparkhint); }
-435	STD	RUMP	{ int|sys|50|kevent(int fd, \
-			    const struct kevent *changelist, size_t nchanges, \
-			    struct kevent *eventlist, size_t nevents, \
+435     COMPAT_100 MODULAR compat_100 RUMP \
+		{ int|sys|50|kevent(int fd, \
+			    const struct kevent100 *changelist, size_t nchanges, \
+			    struct kevent100 *eventlist, size_t nevents, \
 			    const struct timespec *timeout); }
 436	STD	RUMP	{ int|sys|50|pselect(int nd, fd_set *in, fd_set *ou, \
 			    fd_set *ex, const struct timespec *ts, \
@@ -1051,3 +1052,14 @@
 499	STD	RUMP	{ long|sys||lpathconf(const char *path, int name); }
 500	STD		{ int|sys||memfd_create(const char *name, \
 			    unsigned int flags); }
+501	STD	RUMP	{ int|sys|100|kevent(int fd, \
+			    const struct kevent *changelist, size_t nchanges, \
+			    struct kevent *eventlist, size_t nevents, \
+			    const struct timespec *timeout); }
+502	STD		{ int|sys||epoll_create1(int flags); }
+503	STD		{ int|sys||epoll_ctl(int epfd, int op, int fd, \
+			    struct epoll_event *event); }
+504	STD		{ int|sys||epoll_pwait2(int epfd, \
+			    struct epoll_event *events, int maxevents, \
+			    const struct timespec *timeout, \
+			    const sigset_t *sigmask); }

Index: src/sys/rump/Makefile.rump
diff -u src/sys/rump/Makefile.rump:1.134 src/sys/rump/Makefile.rump:1.135
--- src/sys/rump/Makefile.rump:1.134	Tue May  3 04:34:00 2022
+++ src/sys/rump/Makefile.rump	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile.rump,v 1.134 2022/05/03 08:34:00 hannken Exp $
+#	$NetBSD: Makefile.rump,v 1.135 2023/07/28 18:19:01 christos Exp $
 #
 
 .if !defined(_RUMP_MK)
@@ -48,7 +48,7 @@ CPPFLAGS+=	-DMIPS1=1
 # which NetBSD compat to build
 RUMP_NBCOMPAT?=default
 .if ${RUMP_NBCOMPAT} == "all" || ${RUMP_NBCOMPAT} == "default"
-RUMP_NBCOMPAT=	50 60 70 80 90
+RUMP_NBCOMPAT=	50 60 70 80 90 100
 .endif
 .if ${RUMP_NBCOMPAT} == "none"
 RUMP_NBCOMPAT=

Index: src/sys/rump/rump.sysmap
diff -u src/sys/rump/rump.sysmap:1.9 src/sys/rump/rump.sysmap:1.10
--- src/sys/rump/rump.sysmap:1.9	Mon Nov  2 13:56:16 2020
+++ src/sys/rump/rump.sysmap	Fri Jul 28 14:19:01 2023
@@ -178,7 +178,7 @@
 428  sys___clock_settime50  __clock_settime50  rump___sysimpl_clock_settime50
 429  sys___clock_getres50   __clock_getres50   rump___sysimpl_clock_getres50
 430  sys___nanosleep50      __nanosleep50      rump___sysimpl_nanosleep50
-435  sys___kevent50         __kevent50         rump___sysimpl_kevent50
+435  sys_nomodule           __kevent50         rump___sysimpl_kevent50
 436  sys___pselect50        __pselect50        rump___sysimpl_pselect50
 437  sys___pollts50         __pollts50         rump___sysimpl_pollts50
 438  sys_nomodule           __aio_suspend50    rump___sysimpl_aio_suspend50
@@ -220,3 +220,4 @@
 485  sys___fstatvfs190      __fstatvfs190      rump___sysimpl_fstatvfs190
 486  sys___fhstatvfs190     __fhstatvfs190     rump___sysimpl_fhstatvfs190
 499  sys_lpathconf          lpathconf          rump___sysimpl_lpathconf
+501  sys___kevent100        __kevent100        rump___sysimpl_kevent100

Index: src/sys/sys/Makefile
diff -u src/sys/sys/Makefile:1.180 src/sys/sys/Makefile:1.181
--- src/sys/sys/Makefile:1.180	Sun Oct 10 09:03:10 2021
+++ src/sys/sys/Makefile	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.180 2021/10/10 13:03:10 jmcneill Exp $
+#	$NetBSD: Makefile,v 1.181 2023/07/28 18:19:01 christos Exp $
 
 .include <bsd.own.mk>
 
@@ -18,7 +18,7 @@ INCS=	acct.h acl.h agpio.h aio.h ansi.h 
 	dir.h dirent.h \
 	disk.h disklabel.h disklabel_acorn.h disklabel_gpt.h disklabel_rdb.h \
 	dkbad.h dkio.h dkstat.h domain.h drvctlio.h dvdio.h \
-	efiio.h endian.h envsys.h errno.h evcnt.h event.h eventfd.h exec.h \
+	efiio.h endian.h envsys.h epoll.h errno.h evcnt.h event.h eventfd.h exec.h \
 	exec_aout.h exec_coff.h exec_ecoff.h exec_elf.h exec_script.h \
 	extattr.h extent.h \
 	fault.h \

Index: src/sys/sys/event.h
diff -u src/sys/sys/event.h:1.54 src/sys/sys/event.h:1.55
--- src/sys/sys/event.h:1.54	Mon Jul 18 20:46:00 2022
+++ src/sys/sys/event.h	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: event.h,v 1.54 2022/07/19 00:46:00 thorpej Exp $	*/
+/*	$NetBSD: event.h,v 1.55 2023/07/28 18:19:01 christos Exp $	*/
 
 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jle...@freebsd.org>
@@ -70,6 +70,7 @@ struct kevent {
 	uint32_t	fflags;		/* filter flag value */
 	int64_t		data;		/* filter data value */
 	void		*udata;		/* opaque user data identifier */
+	uint64_t	ext[4];		/* extensions */
 };
 
 static __inline void
@@ -349,7 +350,7 @@ int	kqueue(void);
 int	kqueue1(int);
 #ifndef __LIBC12_SOURCE__
 int	kevent(int, const struct kevent *, size_t, struct kevent *, size_t,
-		    const struct timespec *) __RENAME(__kevent50);
+		    const struct timespec *) __RENAME(__kevent100);
 #endif
 #endif /* !_POSIX_C_SOURCE */
 __END_DECLS

Index: src/sys/sys/syscall.h
diff -u src/sys/sys/syscall.h:1.322 src/sys/sys/syscall.h:1.323
--- src/sys/sys/syscall.h:1.322	Sun Jul  9 22:37:05 2023
+++ src/sys/sys/syscall.h	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: syscall.h,v 1.322 2023/07/10 02:37:05 christos Exp $ */
+/* $NetBSD: syscall.h,v 1.323 2023/07/28 18:19:01 christos Exp $ */
 
 /*
  * System call numbers.
@@ -954,7 +954,7 @@
 /* syscall: "kqueue" ret: "int" args: */
 #define	SYS_kqueue	344
 
-/* syscall: "compat_50_kevent" ret: "int" args: "int" "const struct kevent *" "size_t" "struct kevent *" "size_t" "const struct timespec50 *" */
+/* syscall: "compat_50_kevent" ret: "int" args: "int" "const struct kevent100 *" "size_t" "struct kevent100 *" "size_t" "const struct timespec50 *" */
 #define	SYS_compat_50_kevent	345
 
 /* syscall: "_sched_setparam" ret: "int" args: "pid_t" "lwpid_t" "int" "const struct sched_param *" */
@@ -1207,8 +1207,8 @@
 /* syscall: "compat_60__lwp_park" ret: "int" args: "const struct timespec *" "lwpid_t" "const void *" "const void *" */
 #define	SYS_compat_60__lwp_park	434
 
-/* syscall: "__kevent50" ret: "int" args: "int" "const struct kevent *" "size_t" "struct kevent *" "size_t" "const struct timespec *" */
-#define	SYS___kevent50	435
+/* syscall: "compat_100___kevent50" ret: "int" args: "int" "const struct kevent100 *" "size_t" "struct kevent100 *" "size_t" "const struct timespec *" */
+#define	SYS_compat_100___kevent50	435
 
 /* syscall: "__pselect50" ret: "int" args: "int" "fd_set *" "fd_set *" "fd_set *" "const struct timespec *" "const sigset_t *" */
 #define	SYS___pselect50	436
@@ -1407,6 +1407,18 @@
 /* syscall: "memfd_create" ret: "int" args: "const char *" "unsigned int" */
 #define	SYS_memfd_create	500
 
-#define	SYS_MAXSYSCALL	501
+/* syscall: "__kevent100" ret: "int" args: "int" "const struct kevent *" "size_t" "struct kevent *" "size_t" "const struct timespec *" */
+#define	SYS___kevent100	501
+
+/* syscall: "epoll_create1" ret: "int" args: "int" */
+#define	SYS_epoll_create1	502
+
+/* syscall: "epoll_ctl" ret: "int" args: "int" "int" "int" "struct epoll_event *" */
+#define	SYS_epoll_ctl	503
+
+/* syscall: "epoll_pwait2" ret: "int" args: "int" "struct epoll_event *" "int" "const struct timespec *" "const sigset_t *" */
+#define	SYS_epoll_pwait2	504
+
+#define	SYS_MAXSYSCALL	505
 #define	SYS_NSYSENT	512
 #endif /* _SYS_SYSCALL_H_ */

Index: src/tests/kernel/Makefile
diff -u src/tests/kernel/Makefile:1.72 src/tests/kernel/Makefile:1.73
--- src/tests/kernel/Makefile:1.72	Sat Jun  3 17:28:52 2023
+++ src/tests/kernel/Makefile	Fri Jul 28 14:19:01 2023
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.72 2023/06/03 21:28:52 lukem Exp $
+# $NetBSD: Makefile,v 1.73 2023/07/28 18:19:01 christos Exp $
 
 NOMAN=		# defined
 
@@ -7,7 +7,8 @@ NOMAN=		# defined
 TESTSDIR=	${TESTSBASE}/kernel
 
 TESTS_SUBDIRS+=	kqueue
-TESTS_C=	t_fcntl
+TESTS_C=	t_epoll
+TESTS_C+=	t_fcntl
 TESTS_C+=	t_lock
 TESTS_C+=	t_lockf
 TESTS_C+=	t_pty

Added files:

Index: src/lib/libc/sys/epoll.2
diff -u /dev/null src/lib/libc/sys/epoll.2:1.1
--- /dev/null	Fri Jul 28 14:19:02 2023
+++ src/lib/libc/sys/epoll.2	Fri Jul 28 14:19:00 2023
@@ -0,0 +1,388 @@
+.\"	$NetBSD: epoll.2,v 1.1 2023/07/28 18:19:00 christos Exp $
+.\"
+.\" Copyright (c) 2023 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Theodore Preduta.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd July 19, 2023
+.Dt EPOLL 2
+.Os
+.Sh NAME
+.Nm epoll ,
+.Nm epoll_event ,
+.Nm epoll_data ,
+.Nm epoll_data_t ,
+.Nm epoll_create ,
+.Nm epoll_create1 ,
+.Nm epoll_ctl ,
+.Nm epoll_wait ,
+.Nm epoll_pwait ,
+.Nm epoll_pwait2
+.Nd event notification mechanism
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In sys/epoll.h
+.Bd -literal
+union epoll_data {
+	void		*ptr;
+	int		fd;
+	uint32_t	u32;
+	uint64_t	u64;
+};
+
+typedef union epoll_data	epoll_data_t;
+
+struct epoll_event {
+	uint32_t	events;
+	epoll_data_t	data;
+};
+.Ed
+.Pp
+.Ft int
+.Fn epoll_create "int size"
+.Ft int
+.Fn epoll_create1 "int flags"
+.Ft int
+.Fn epoll_ctl "int epfd" "int op" "int fd" "struct epoll_event *event"
+.Ft int
+.Fn epoll_wait "int epfd" "struct epoll_event *events" "int maxevents" "int timeout"
+.Ft int
+.Fn epoll_pwait "int epfd" "struct epoll_event *events" "int maxevents" "int timeout" "const sigset_t *sigmask"
+.Ft int
+.Fn epoll_pwait2 "int epfd" "struct epoll_event *events" "int maxevents" "const struct timespec *timeout" "const sigset_t *sigmask"
+.Sh DESCRIPTION
+.Nm
+provides a similar facility to both
+.Xr select 2
+and
+.Xr kqueue 2 :
+it allows for the examination of file descriptors to see if they are available
+for reading/writing.
+.Pp
+The
+.Va epoll_event
+structure consists of two fields,
+.Va events
+and
+.Va data .
+The
+.Va data
+field is passed through the kernel and is intended to be used to identify the
+event.
+When used with
+.Fa epoll_ctl ,
+the
+.Va events
+field consists of a mask of the events that the
+.Nm
+instance should watch for, and when being used with
+.Fa epoll_wait ,
+.Fa epoll_pwait ,
+and
+.Fa epoll_pwait2
+consists of a mask of the events that occurred.
+The following are possible values for
+.Va events :
+.Bl -tag -width EPOLLONESHOT
+.It Dv EPOLLIN
+Watch for
+.Xr read 2
+operations.
+.It Dv EPOLLOUT
+Watch for
+.Xr write 2
+operations.
+.It Dv EPOLLRDHUP
+Watch for a peer closed connection.
+.It Dv EPOLLERR
+Watch for error conditions.
+.It Dv EPOLLET
+This option modifies the other set bits of
+.Va events .
+When set, the events described by other bits in
+.Va events
+are only triggered when the state change.
+Otherwise the events are considered triggered whenever the condition is true.
+.It Dv EPOLLONESHOT
+Remove this event once it is retrieved once.
+.El
+.Pp
+.Fn epoll_create
+and
+.Fn epoll_create1
+both create an
+.Nm
+instance.
+The
+.Fa size
+argument specified for
+.Fn epoll_create
+exists so that the
+.Nx
+function has the same signature as the Linux system call of the same name.
+.Fa size
+must be positive, but is otherwise unused.
+Additionally, optionally,
+.Dv EPOLL_CLOEXEC
+may be specified in the
+.Fa flags
+of
+.Fn epoll_create1
+to set the
+.Xr close 2
+on
+.Xr exec 2
+flag.
+.Pp
+.Fn epoll_ctl
+is used to make changes to the given
+.Nm
+instance based on the provided
+.Fa op .
+Possible values for
+.Fa op
+are:
+.Bl -tag -width EPOLL_CTL_ADD
+.It Dv EPOLL_CTL_ADD
+Register interest of
+.Fa fd
+on the
+.Fa epfd
+for the events specified in
+.Fa event .
+.It Dv EPOLL_CTL_MOD
+Modify the events registered for
+.Fa fd
+to those specified in
+.Fa event .
+.It Dv EPOLL_CTL_DEL
+Deregister
+.Fa fd
+from the
+.Nm
+instance specified in
+.Fa epfd .
+Note that
+.Fa event
+is completely ignored in this case.
+.El
+.Pp
+.Fn epoll_wait ,
+.Fn epoll_pwait ,
+and
+.Fn epoll_pwait2
+provide the ability to wait for up to
+.Fa maxevents
+which are stored in the buffer pointed to by
+.Fa events .
+For
+.Fn epoll_wait
+and
+.Fn epoll_pwait ,
+a timeout may be specified in
+.Fa timeout
+in milliseconds.
+If no timeout is desired, -1 should be specified in
+.Fa timeout .
+For
+.Fn epoll_pwait2
+if no timeout is desired
+.Fa timeout
+should be specified as
+.Dv NULL .
+Additionally,
+a sigmask may be specified to
+.Fa epoll_pwait
+and
+.Fa epoll_pwait2
+in
+.Fa sigmask
+to set the sigmask while
+.Nm
+waits for events.
+.Pp
+Note that
+.Nm
+is not intended to be used by native
+.Nx
+applications.
+Instead it is only intended to used as a means to help port software originally
+written for Linux to
+.Nx .
+.Sh RETURN VALUES
+.Fn epoll_create
+and
+.Fn epoll_create1
+both return a file descriptor when successful.
+.Pp
+.Fn epoll_ctl
+returns zero when successful.
+.Pp
+.Fn epoll_wait ,
+.Fn epoll_pwait ,
+and
+.Fn epoll_pwait2
+return the number of events written to
+.Fa events
+when successful.
+Note that zero is written to when
+.Fa timeout
+expires and no events were available.
+.Pp
+When any of the above fail, -1 is returned and
+.Fa errno
+is set.
+.Sh ERRORS
+The
+.Fn epoll_create
+and
+.Fn epoll_create1
+functions fail if:
+.Bl -tag -width Er
+.It Bq Er EINVAL
+.Fa size
+is not positive.
+.Pp
+Bits other than
+.Dv EPOLL_CLOEXEC
+are provided in
+.Fa flags .
+.It Bq Er EMFILE
+The per-process descriptor table is full.
+.It Bq Er ENFILE
+The system file table is full.
+.It Bq Er ENOMEM
+The kernel failed to allocate enough memory for a
+.Nm
+instance.
+.El
+.Pp
+The
+.Fn epoll_ctl
+function fails if:
+.Bl -tag -width Er
+.It Bq Er EBADF
+.Fa epfd
+or
+.Fa fd
+is not a valid file descriptor.
+.It Bq Er EEXIST
+.Fa op
+is
+.Dv EPOLL_CTL_ADD
+and
+.Fa fd
+was already previously added via
+.Dv EPOLL_CTL_ADD .
+.It Bq Er EINVAL
+.Fa epfd
+is not a file descriptor for an
+.Nm
+instance.
+.Pp
+.Fa epfd
+and
+.Fa fd
+represent the same
+.Nm
+instance.
+.It Bq Er ELOOP
+.Fa op
+is
+.Dv EPOLL_CTL_ADD
+and adding
+.Fa fd
+would result in a circular loop of
+.Nm
+instances.
+.It Bq Er ENOENT
+.Fa op
+is
+.Dv EPOLL_CTL_MOD
+or
+.Dv EPOLL_CTL_DEL
+and
+.Fa fd
+was not previously added with
+.Dv EPOLL_CTL_ADD .
+.It Bq Er ENOMEM
+The kernel failed to allocate enough memory for
+.Fa op .
+.It Bq Er EPERM
+.Fa fd
+does not support
+.Nm epoll .
+.El
+.Pp
+The
+.Fn epoll_wait ,
+.Fn epoll_pwait ,
+and
+.Fn epoll_pwait2
+functions fail if:
+.Bl -tag -width Er
+.It Bq Er EBADF
+.Fa epfd
+is not a valid file descriptor.
+.It Bq Er EFAULT
+The area provided in
+.Fa events
+failed to be written to.
+.It Bq Er EINTR
+A signal was delivered before any events became available and
+.Fa timeout
+expired.
+.It Bq Er EINVAL
+.Fa epfd
+is not a valid
+.Nm
+file descriptor.
+.Pp
+.Fa maxevents
+is less than or equal to zero.
+.El
+.Sh SEE ALSO
+.Xr kqueue 2 ,
+.Xr poll 2 ,
+.Xr select 2
+.Sh HISTORY
+The
+.Nm
+functions and types are designed to be compatible with the Linux system calls of
+the same name.
+.Sh CAVEATS
+The
+.Nm
+facility is not intended to be used in conjunction with
+.Xr kqueue 2 .
+.Pp
+Unlike Linux's
+.Nm ,
+the
+.Nx
+version does not survive a
+.Xr fork 2 .
Index: src/lib/libc/sys/epoll.c
diff -u /dev/null src/lib/libc/sys/epoll.c:1.1
--- /dev/null	Fri Jul 28 14:19:02 2023
+++ src/lib/libc/sys/epoll.c	Fri Jul 28 14:19:00 2023
@@ -0,0 +1,69 @@
+/*	$NetBSD: epoll.c,v 1.1 2023/07/28 18:19:00 christos Exp $	*/
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: epoll.c,v 1.1 2023/07/28 18:19:00 christos Exp $");
+
+#include <sys/epoll.h>
+#include <sys/sigtypes.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <stddef.h>
+
+int
+epoll_create(int size)
+{
+	if (size <= 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	return epoll_create1(0);
+}
+
+int
+epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
+{
+	return epoll_pwait(epfd, events, maxevents, timeout, NULL);
+}
+
+int
+epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout,
+    const sigset_t *sigmask)
+{
+	struct timespec ts, *tsp;
+
+	if (timeout >= 0) {
+		ts.tv_sec = timeout / 1000;
+		ts.tv_nsec = (timeout % 1000) * 1000000;
+		tsp = &ts;
+	} else
+		tsp = NULL;
+
+	return epoll_pwait2(epfd, events, maxevents, tsp, sigmask);
+}

Index: src/sys/compat/common/kern_event_100.c
diff -u /dev/null src/sys/compat/common/kern_event_100.c:1.1
--- /dev/null	Fri Jul 28 14:19:02 2023
+++ src/sys/compat/common/kern_event_100.c	Fri Jul 28 14:19:00 2023
@@ -0,0 +1,88 @@
+/*	$NetBSD: kern_event_100.c,v 1.1 2023/07/28 18:19:00 christos Exp $	*/
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: kern_event_100.c,v 1.1 2023/07/28 18:19:00 christos Exp $");
+
+#if defined(_KERNEL_OPT)
+#include "opt_compat_netbsd.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/syscall.h>
+#include <sys/syscallvar.h>
+#include <sys/syscallargs.h>
+
+#include <compat/common/compat_mod.h>
+#include <compat/sys/event.h>
+
+static const struct syscall_package kern_event_100_syscalls[] = {
+	{ SYS_compat_100___kevent50, 0,
+	    (sy_call_t *)compat_100_sys___kevent50 },
+	{ 0, 0, NULL },
+};
+
+int
+compat_100_sys___kevent50(struct lwp *l,
+    const struct compat_100_sys___kevent50_args *uap,
+    register_t *retval)
+{
+	/* {
+		syscallarg(int) fd;
+		syscallarg(const struct kevent100 *) changelist;
+		syscallarg(size_t) nchanges;
+		syscallarg(struct kevent100 *) eventlist;
+		syscallarg(size_t) nevents;
+		syscallarg(const struct timespec *) timeout;
+	} */
+	static const struct kevent_ops compat_100_kevent_ops = {
+		.keo_private = NULL,
+		.keo_fetch_timeout = copyin,
+		.keo_fetch_changes = compat_100___kevent50_fetch_changes,
+		.keo_put_events = compat_100___kevent50_put_events,
+	};
+
+	return kevent1(retval, SCARG(uap, fd),
+	    (const struct kevent *)SCARG(uap, changelist), SCARG(uap, nchanges),
+	    (struct kevent *)SCARG(uap, eventlist), SCARG(uap, nevents),
+	    SCARG(uap, timeout), &compat_100_kevent_ops);
+}
+
+int
+kern_event_100_init(void)
+{
+
+	return syscall_establish(NULL, kern_event_100_syscalls);
+}
+
+int
+kern_event_100_fini(void)
+{
+
+	return syscall_disestablish(NULL, kern_event_100_syscalls);
+}

Index: src/sys/kern/sys_epoll.c
diff -u /dev/null src/sys/kern/sys_epoll.c:1.1
--- /dev/null	Fri Jul 28 14:19:02 2023
+++ src/sys/kern/sys_epoll.c	Fri Jul 28 14:19:01 2023
@@ -0,0 +1,680 @@
+/*	$NetBSD: sys_epoll.c,v 1.1 2023/07/28 18:19:01 christos Exp $	*/
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2007 Roman Divacky
+ * Copyright (c) 2014 Dmitry Chagin <dcha...@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: sys_epoll.c,v 1.1 2023/07/28 18:19:01 christos Exp $");
+
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/bitops.h>
+#include <sys/epoll.h>
+#include <sys/event.h>
+#include <sys/eventvar.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/fcntl.h>
+#include <sys/proc.h>
+#include <sys/signal.h>
+#include <sys/vnode.h>
+
+#include <sys/syscallargs.h>
+
+#define	EPOLL_MAX_DEPTH		5
+
+#define	EPOLL_EVRD	(EPOLLIN|EPOLLRDNORM)
+#define	EPOLL_EVWR	(EPOLLOUT|EPOLLWRNORM)
+#define	EPOLL_EVSUP	(EPOLLET|EPOLLONESHOT|EPOLLHUP|EPOLLERR|EPOLLPRI \
+			|EPOLL_EVRD|EPOLL_EVWR|EPOLLRDHUP)
+
+#define	kext_data	ext[0]
+#define	kext_epfd	ext[1]
+#define	kext_fd		ext[2]
+
+#if DEBUG
+#define	DPRINTF(x) uprintf x
+#else
+#define	DPRINTF(x) __nothing
+#endif
+
+struct epoll_edge {
+	int epfd;
+	int fd;
+};
+
+__BITMAP_TYPE(epoll_seen, char, 1);
+
+static int	epoll_to_kevent(int, int, struct epoll_event *, struct kevent *,
+    int *);
+static void	kevent_to_epoll(struct kevent *, struct epoll_event *);
+static int      epoll_kev_put_events(void *, struct kevent *, struct kevent *,
+    size_t, int);
+static int	epoll_kev_fetch_changes(void *, const struct kevent *,
+    struct kevent *, size_t, int);
+static int	epoll_kev_fetch_timeout(const void *, void *, size_t);
+static int	epoll_register_kevent(register_t *, int, int, int,
+    unsigned int);
+static int	epoll_fd_registered(register_t *, int, int);
+static int	epoll_delete_all_events(register_t *, int, int);
+static int	epoll_recover_watch_tree(struct epoll_edge *, size_t, size_t);
+static int	epoll_dfs(struct epoll_edge *, size_t, struct epoll_seen *,
+    size_t, int, int);
+static int	epoll_check_loop_and_depth(struct lwp *, int, int);
+
+/*
+ * epoll_create1(2).  Parse the flags and then create a kqueue instance.
+ */
+int
+sys_epoll_create1(struct lwp *l, const struct sys_epoll_create1_args *uap,
+    register_t *retval)
+{
+	/* {
+		syscallarg(int) flags;
+	} */
+	struct sys_kqueue1_args kqa;
+
+	if ((SCARG(uap, flags) & ~(O_CLOEXEC)) != 0)
+		return EINVAL;
+
+	SCARG(&kqa, flags) = SCARG(uap, flags);
+
+	return sys_kqueue1(l, &kqa, retval);
+}
+
+/*
+ * Structure converting function from epoll to kevent.
+ */
+static int
+epoll_to_kevent(int epfd, int fd, struct epoll_event *l_event,
+    struct kevent *kevent, int *nkevents)
+{
+	uint32_t levents = l_event->events;
+	uint32_t kev_flags = EV_ADD | EV_ENABLE;
+
+	/* flags related to how event is registered */
+	if ((levents & EPOLLONESHOT) != 0)
+		kev_flags |= EV_DISPATCH;
+	if ((levents & EPOLLET) != 0)
+		kev_flags |= EV_CLEAR;
+	if ((levents & EPOLLERR) != 0)
+		kev_flags |= EV_ERROR;
+	if ((levents & EPOLLRDHUP) != 0)
+		kev_flags |= EV_EOF;
+
+	/* flags related to what event is registered */
+	if ((levents & EPOLL_EVRD) != 0) {
+		EV_SET(kevent, fd, EVFILT_READ, kev_flags, 0, 0, 0);
+		kevent->kext_data = l_event->data;
+		kevent->kext_epfd = epfd;
+		kevent->kext_fd = fd;
+		++kevent;
+		++(*nkevents);
+	}
+	if ((levents & EPOLL_EVWR) != 0) {
+		EV_SET(kevent, fd, EVFILT_WRITE, kev_flags, 0, 0, 0);
+		kevent->kext_data = l_event->data;
+		kevent->kext_epfd = epfd;
+		kevent->kext_fd = fd;
+		++kevent;
+		++(*nkevents);
+	}
+	/* zero event mask is legal */
+	if ((levents & (EPOLL_EVRD | EPOLL_EVWR)) == 0) {
+		EV_SET(kevent++, fd, EVFILT_READ, EV_ADD|EV_DISABLE, 0, 0, 0);
+		++(*nkevents);
+	}
+
+	if ((levents & ~(EPOLL_EVSUP)) != 0) {
+		return EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Structure converting function from kevent to epoll. In a case
+ * this is called on error in registration we store the error in
+ * event->data and pick it up later in sys_epoll_ctl().
+ */
+static void
+kevent_to_epoll(struct kevent *kevent, struct epoll_event *l_event)
+{
+
+	l_event->data = kevent->kext_data;
+
+	if ((kevent->flags & EV_ERROR) != 0) {
+		l_event->events = EPOLLERR;
+		return;
+	}
+
+	/* XXX EPOLLPRI, EPOLLHUP */
+	switch (kevent->filter) {
+	case EVFILT_READ:
+		l_event->events = EPOLLIN;
+		if ((kevent->flags & EV_EOF) != 0)
+			l_event->events |= EPOLLRDHUP;
+		break;
+	case EVFILT_WRITE:
+		l_event->events = EPOLLOUT;
+		break;
+	default:
+		DPRINTF(("%s: unhandled kevent filter %d\n", __func__,
+		    kevent->filter));
+		break;
+	}
+}
+
+/*
+ * Copyout callback used by kevent.  This converts kevent events to
+ * epoll events that are located in args->eventlist.
+ */
+static int
+epoll_kev_put_events(void *ctx, struct kevent *events,
+    struct kevent *eventlist, size_t index, int n)
+{
+	int i;
+	struct epoll_event *eep = (struct epoll_event *)eventlist;
+
+	KASSERT(n >= 0 && n < EPOLL_MAX_EVENTS);
+
+	for (i = 0; i < n; i++)
+		kevent_to_epoll(events + i, eep + index + i);
+
+	return 0;
+}
+
+/*
+ * Copyin callback used by kevent. This copies already
+ * converted filters from kernel memory to the kevent
+ * internal kernel memory. Hence the memcpy instead of
+ * copyin.
+ */
+static int
+epoll_kev_fetch_changes(void *ctx, const struct kevent *changelist,
+    struct kevent *changes, size_t index, int n)
+{
+	KASSERT(n >= 0 && n < EPOLL_MAX_EVENTS);
+
+	memcpy(changes, changelist + index, n * sizeof(*changes));
+
+	return 0;
+}
+
+/*
+ * Timer copy callback used by kevent.  Copies a converted timeout
+ * from kernel memory to kevent memory.  Hence the memcpy instead of
+ * just using copyin.
+ */
+static int
+epoll_kev_fetch_timeout(const void *src, void *dest, size_t size)
+{
+	memcpy(dest, src, size);
+
+	return 0;
+}
+
+/*
+ * Load epoll filter, convert it to kevent filter and load it into
+ * kevent subsystem.
+ *
+ * event must point to kernel memory or be NULL.
+ */
+int
+epoll_ctl_common(struct lwp *l, register_t *retval, int epfd, int op, int fd,
+    struct epoll_event *event)
+{
+	struct kevent kev[2];
+        struct kevent_ops k_ops = {
+		.keo_private = NULL,
+		.keo_fetch_timeout = NULL,
+		.keo_fetch_changes = epoll_kev_fetch_changes,
+		.keo_put_events = NULL,
+	};
+	file_t *epfp, *fp;
+	int error = 0;
+	int nchanges = 0;
+
+	/*
+	 * Need to validate epfd and fd separately from kevent1 to match
+	 * Linux's errno behaviour.
+	 */
+	epfp = fd_getfile(epfd);
+	if (epfp == NULL)
+		return EBADF;
+	if (epfp->f_type != DTYPE_KQUEUE)
+		error = EINVAL;
+	fd_putfile(epfd);
+	if (error != 0)
+		return error;
+
+	fp = fd_getfile(fd);
+	if (fp == NULL)
+		return EBADF;
+	if (fp->f_type == DTYPE_VNODE) {
+		switch (fp->f_vnode->v_type) {
+		case VREG:
+		case VDIR:
+		case VBLK:
+		case VLNK:
+			error = EPERM;
+			break;
+
+		default:
+			break;
+		}
+	}
+	fd_putfile(fd);
+	if (error != 0)
+		return error;
+
+	/* Linux disallows spying on himself */
+	if (epfd == fd) {
+		return EINVAL;
+	}
+
+	if (op != EPOLL_CTL_DEL) {
+		error = epoll_to_kevent(epfd, fd, event, kev, &nchanges);
+		if (error != 0)
+			return error;
+	}
+
+	switch (op) {
+	case EPOLL_CTL_MOD:
+		error = epoll_delete_all_events(retval, epfd, fd);
+		if (error != 0)
+			return error;
+		break;
+
+	case EPOLL_CTL_ADD:
+		if (epoll_fd_registered(retval, epfd, fd))
+			return EEXIST;
+		error = epoll_check_loop_and_depth(l, epfd, fd);
+		if (error != 0)
+			return error;
+		break;
+
+	case EPOLL_CTL_DEL:
+		/* CTL_DEL means unregister this fd with this epoll */
+		return epoll_delete_all_events(retval, epfd, fd);
+
+	default:
+		DPRINTF(("%s: invalid op %d\n", ___func__, op));
+		return EINVAL;
+	}
+
+	error = kevent1(retval, epfd, kev, nchanges, NULL, 0, NULL, &k_ops);
+
+	if (error == EOPNOTSUPP) {
+		error = EPERM;
+	}
+
+	return error;
+}
+
+/*
+ * epoll_ctl(2).  Copyin event if necessary and then call
+ * epoll_ctl_common().
+ */
+int
+sys_epoll_ctl(struct lwp *l, const struct sys_epoll_ctl_args *uap,
+    register_t *retval)
+{
+	/* {
+		syscallarg(int) epfd;
+		syscallarg(int) op;
+		syscallarg(int) fd;
+		syscallarg(struct epoll_event *) event;
+	} */
+	struct epoll_event ee;
+	struct epoll_event *eep;
+	int error;
+
+	if (SCARG(uap, op) != EPOLL_CTL_DEL) {
+		error = copyin(SCARG(uap, event), &ee, sizeof(ee));
+		if (error != 0)
+			return error;
+
+		eep = &ee;
+	} else
+		eep = NULL;
+
+	return epoll_ctl_common(l, retval, SCARG(uap, epfd), SCARG(uap, op),
+	    SCARG(uap, fd), eep);
+}
+
+/*
+ * Wait for a filter to be triggered on the epoll file descriptor.
+ * All of the epoll_*wait* syscalls eventually end up here.
+ *
+ * events, nss, and ssp must point to kernel memory (or be NULL).
+ */
+int
+epoll_wait_common(struct lwp *l, register_t *retval, int epfd,
+    struct epoll_event *events, int maxevents, struct timespec *tsp,
+    const sigset_t *nssp)
+{
+	struct kevent_ops k_ops = {
+	        .keo_private = NULL,
+		.keo_fetch_timeout = epoll_kev_fetch_timeout,
+		.keo_fetch_changes = NULL,
+		.keo_put_events = epoll_kev_put_events,
+	};
+	struct proc *p = l->l_proc;
+	file_t *epfp;
+	sigset_t oss;
+	int error = 0;
+
+	if (maxevents <= 0 || maxevents > EPOLL_MAX_EVENTS)
+		return EINVAL;
+
+	/*
+	 * Need to validate epfd separately from kevent1 to match
+	 * Linux's errno behaviour.
+	 */
+	epfp = fd_getfile(epfd);
+	if (epfp == NULL)
+		return EBADF;
+	if (epfp->f_type != DTYPE_KQUEUE)
+		error = EINVAL;
+	fd_putfile(epfd);
+	if (error != 0)
+		return error;
+
+	if (nssp != NULL) {
+		mutex_enter(p->p_lock);
+		error = sigprocmask1(l, SIG_SETMASK, nssp, &oss);
+		mutex_exit(p->p_lock);
+		if (error != 0)
+			return error;
+	}
+
+	error = kevent1(retval, epfd, NULL, 0, (struct kevent *)events,
+	    maxevents, tsp, &k_ops);
+	/*
+	 * Since we're not registering nay events, ENOMEM should not
+	 * be possible for this specific kevent1 call.
+	 */
+	KASSERT(error != ENOMEM);
+
+	if (nssp != NULL) {
+	        mutex_enter(p->p_lock);
+		error = sigprocmask1(l, SIG_SETMASK, &oss, NULL);
+		mutex_exit(p->p_lock);
+	}
+
+	return error;
+}
+
+/*
+ * epoll_pwait2(2).
+ */
+int
+sys_epoll_pwait2(struct lwp *l, const struct sys_epoll_pwait2_args *uap,
+    register_t *retval)
+{
+	/* {
+		syscallarg(int) epfd;
+		syscallarg(struct epoll_event *) events;
+		syscallarg(int) maxevents;
+		syscallarg(struct timespec *) timeout;
+		syscallarg(sigset_t *) sigmask;
+	} */
+	struct epoll_event *events;
+	struct timespec ts, *tsp;
+	sigset_t ss, *ssp;
+	int error;
+	const int maxevents = SCARG(uap, maxevents);
+
+	if (maxevents <= 0 || maxevents >= EPOLL_MAX_EVENTS)
+		return EINVAL;
+
+	if (SCARG(uap, timeout) != NULL) {
+		error = copyin(SCARG(uap, timeout), &ts, sizeof(ts));
+		if (error != 0)
+			return error;
+
+		tsp = &ts;
+	} else
+		tsp = NULL;
+
+	if (SCARG(uap, sigmask) != NULL) {
+		error = copyin(SCARG(uap, sigmask), &ss, sizeof(ss));
+		if (error != 0)
+			return error;
+
+		ssp = &ss;
+	} else
+		ssp = NULL;
+
+	events = kmem_alloc(maxevents * sizeof(*events), KM_SLEEP);
+
+	error = epoll_wait_common(l, retval, SCARG(uap, epfd), events,
+	    maxevents, tsp, ssp);
+	if (error == 0)
+		error = copyout(events, SCARG(uap, events),
+		    *retval * sizeof(*events));
+
+	kmem_free(events, maxevents * sizeof(*events));
+	return error;
+}
+
+/*
+ * Helper that registers a single kevent.
+ */
+static int
+epoll_register_kevent(register_t *retval, int epfd, int fd, int filter,
+    unsigned int flags)
+{
+	struct kevent kev;
+	struct kevent_ops k_ops = {
+		.keo_private = NULL,
+		.keo_fetch_timeout = NULL,
+		.keo_fetch_changes = epoll_kev_fetch_changes,
+		.keo_put_events = NULL,
+	};
+
+	EV_SET(&kev, fd, filter, flags, 0, 0, 0);
+
+        return kevent1(retval, epfd, &kev, 1, NULL, 0, NULL, &k_ops);
+}
+
+/*
+ * Check if an fd is already registered in the kqueue referenced by epfd.
+ */
+static int
+epoll_fd_registered(register_t *retval, int epfd, int fd)
+{
+	/*
+	 * Set empty filter flags to avoid accidental modification of already
+	 * registered events. In the case of event re-registration:
+	 * 1. If event does not exists kevent() does nothing and returns ENOENT
+	 * 2. If event does exists, it's enabled/disabled state is preserved
+	 *    but fflags, data and udata fields are overwritten. So we can not
+	 *    set socket lowats and store user's context pointer in udata.
+	 */
+	if (epoll_register_kevent(retval, epfd, fd, EVFILT_READ, 0) != ENOENT ||
+	    epoll_register_kevent(retval, epfd, fd, EVFILT_WRITE, 0) != ENOENT)
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Remove all events in the kqueue referenced by epfd that depend on
+ * fd.
+ */
+static int
+epoll_delete_all_events(register_t *retval, int epfd, int fd)
+{
+	int error1, error2;
+
+	error1 = epoll_register_kevent(retval, epfd, fd, EVFILT_READ,
+	    EV_DELETE);
+	error2 = epoll_register_kevent(retval, epfd, fd, EVFILT_WRITE,
+	    EV_DELETE);
+
+	/* return 0 if at least one result positive */
+	return error1 == 0 ? 0 : error2;
+}
+
+/*
+ * Interate through all the knotes and recover a directed graph on
+ * which kqueues are watching each other.
+ *
+ * If edges is NULL, the number of edges is still counted but no graph
+ * is assembled.
+ */
+static int
+epoll_recover_watch_tree(struct epoll_edge *edges, size_t nedges, size_t nfds) {
+	file_t *currfp, *targetfp;
+	struct knote *kn, *tmpkn;
+	size_t i, nedges_so_far = 0;
+
+	for (i = 0; i < nfds && (edges == NULL || nedges_so_far < nedges); i++)
+	{
+		currfp = fd_getfile(i);
+		if (currfp == NULL)
+			continue;
+		if (currfp->f_type != DTYPE_KQUEUE)
+			goto continue_count_outer;
+
+		SLIST_FOREACH_SAFE(kn, &currfp->f_kqueue->kq_sel.sel_klist,
+		    kn_selnext, tmpkn) {
+			targetfp = fd_getfile(kn->kn_kevent.kext_epfd);
+			if (targetfp == NULL)
+				continue;
+			if (targetfp->f_type == DTYPE_KQUEUE) {
+				if (edges != NULL) {
+					edges[nedges_so_far].epfd =
+					    kn->kn_kevent.kext_epfd;
+					edges[nedges_so_far].fd =
+					    kn->kn_kevent.kext_fd;
+				}
+				nedges_so_far++;
+			}
+
+			fd_putfile(kn->kn_kevent.kext_epfd);
+		}
+
+continue_count_outer:
+		fd_putfile(i);
+	}
+
+	return nedges_so_far;
+}
+
+/*
+ * Run dfs on the graph described by edges, checking for loops and a
+ * depth greater than EPOLL_MAX_DEPTH.
+ */
+static int
+epoll_dfs(struct epoll_edge *edges, size_t nedges, struct epoll_seen *seen,
+    size_t nseen, int currfd, int depth)
+{
+	int error;
+	size_t i;
+
+	KASSERT(edges != NULL);
+	KASSERT(seen != NULL);
+	KASSERT(nedges > 0);
+	KASSERT(currfd < nseen);
+	KASSERT(0 <= depth && depth <= EPOLL_MAX_DEPTH + 1);
+
+	if (__BITMAP_ISSET(currfd, seen))
+		return ELOOP;
+
+	__BITMAP_SET(currfd, seen);
+
+	depth++;
+	if (depth > EPOLL_MAX_DEPTH)
+		return EINVAL;
+
+	for (i = 0; i < nedges; i++) {
+		if (edges[i].epfd != currfd)
+			continue;
+
+		error = epoll_dfs(edges, nedges, seen, nseen,
+		    edges[i].fd, depth);
+		if (error != 0)
+			return error;
+	}
+
+	return 0;
+}
+
+/*
+ * Check if adding fd to epfd would violate the maximum depth or
+ * create a loop.
+ */
+static int
+epoll_check_loop_and_depth(struct lwp *l, int epfd, int fd)
+{
+	int error;
+	file_t *fp;
+	struct epoll_edge *edges;
+	struct epoll_seen *seen;
+	size_t nedges, nfds, seen_size;
+	bool fdirrelevant;
+
+	/* If the target isn't another kqueue, we can skip this check */
+	fp = fd_getfile(fd);
+	if (fp == NULL)
+		return 0;
+	fdirrelevant = fp->f_type != DTYPE_KQUEUE;
+	fd_putfile(fd);
+	if (fdirrelevant)
+		return 0;
+
+	nfds = l->l_proc->p_fd->fd_lastfile + 1;
+
+	/*
+	 * We call epoll_recover_watch_tree twice, once to find the
+	 * number of edges, and once to actually fill them in.  We add one
+	 * because we want to include the edge epfd->fd.
+	 */
+        nedges = 1 + epoll_recover_watch_tree(NULL, 0, nfds);
+
+	edges = kmem_zalloc(nedges * sizeof(*edges), KM_SLEEP);
+
+	epoll_recover_watch_tree(edges + 1, nedges - 1, nfds);
+
+	edges[0].epfd = epfd;
+	edges[0].fd = fd;
+
+	seen_size = __BITMAP_SIZE(char, nfds);
+	seen = kmem_zalloc(seen_size, KM_SLEEP);
+
+	error = epoll_dfs(edges, nedges, seen, nfds, epfd, 0);
+
+	kmem_free(seen, seen_size);
+	kmem_free(edges, nedges * sizeof(*edges));
+
+	return error;
+}

Index: src/sys/sys/epoll.h
diff -u /dev/null src/sys/sys/epoll.h:1.1
--- /dev/null	Fri Jul 28 14:19:02 2023
+++ src/sys/sys/epoll.h	Fri Jul 28 14:19:01 2023
@@ -0,0 +1,98 @@
+/*	$NetBSD: epoll.h,v 1.1 2023/07/28 18:19:01 christos Exp $	*/
+
+/*-
+ * Copyright (c) 2007 Roman Divacky
+ * Copyright (c) 2014 Dmitry Chagin <dcha...@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_EPOLL_H_
+#define	_SYS_EPOLL_H_
+
+#include <sys/types.h>			/* for uint32_t, uint64_t */
+#include <sys/sigtypes.h>		/* for sigset_t */
+struct timespec;
+
+#define	EPOLLIN		0x00000001
+#define	EPOLLPRI	0x00000002
+#define	EPOLLOUT	0x00000004
+#define	EPOLLERR	0x00000008
+#define	EPOLLHUP	0x00000010
+#define	EPOLLRDNORM	0x00000040
+#define	EPOLLRDBAND	0x00000080
+#define	EPOLLWRNORM	0x00000100
+#define	EPOLLWRBAND	0x00000200
+#define	EPOLLMSG	0x00000400
+#define	EPOLLRDHUP	0x00002000
+#define	EPOLLWAKEUP	0x20000000
+#define	EPOLLONESHOT	0x40000000
+#define	EPOLLET		0x80000000
+
+#define	EPOLL_CTL_ADD	1
+#define	EPOLL_CTL_DEL	2
+#define	EPOLL_CTL_MOD	3
+
+#ifdef _KERNEL
+#define	EPOLL_MAX_EVENTS	(4 * 1024 * 1024)
+typedef uint64_t		epoll_data_t;
+#else
+union epoll_data {
+	void		*ptr;
+	int		fd;
+	uint32_t	u32;
+	uint64_t	u64;
+};
+
+typedef union epoll_data	epoll_data_t;
+#endif
+
+struct epoll_event {
+	uint32_t	events;
+	epoll_data_t	data;
+};
+
+#ifdef _KERNEL
+int	epoll_ctl_common(struct lwp *l, register_t *retval, int epfd, int op,
+	    int fd, struct epoll_event *event);
+int	epoll_wait_common(struct lwp *l, register_t *retval, int epfd,
+	    struct epoll_event *events, int maxevents, struct timespec *tsp,
+	    const sigset_t *nss);
+#else	/* !_KERNEL */
+__BEGIN_DECLS
+#ifdef _NETBSD_SOURCE
+int	epoll_create(int size);
+int	epoll_create1(int flags);
+int	epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
+int	epoll_wait(int epfd, struct epoll_event *events, int maxevents,
+	    int timeout);
+int	epoll_pwait(int epfd, struct epoll_event *events, int maxevents,
+	    int timeout, const sigset_t *sigmask);
+int	epoll_pwait2(int epfd, struct epoll_event *events, int maxevents,
+	    const struct timespec *timeout, const sigset_t *sigmask);
+#endif	/* _NETBSD_SOURCE */
+__END_DECLS
+#endif	/* !_KERNEL */
+
+#endif	/* !_SYS_EPOLL_H_ */

Index: src/tests/kernel/t_epoll.c
diff -u /dev/null src/tests/kernel/t_epoll.c:1.1
--- /dev/null	Fri Jul 28 14:19:02 2023
+++ src/tests/kernel/t_epoll.c	Fri Jul 28 14:19:01 2023
@@ -0,0 +1,225 @@
+/*	$NetBSD: t_epoll.c,v 1.1 2023/07/28 18:19:01 christos Exp $	*/
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Theodore Preduta.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_epoll.c,v 1.1 2023/07/28 18:19:01 christos Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/epoll.h>
+#include <errno.h>
+
+#include <atf-c.h>
+
+#include "h_macros.h"
+
+ATF_TC(create_size);
+ATF_TC_HEAD(create_size, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks that epoll_create requires a non-positive size");
+}
+ATF_TC_BODY(create_size, tc)
+{
+	ATF_REQUIRE_EQ_MSG(epoll_create(-1), -1,
+	    "epoll_create succeeded unexpectedly");
+	ATF_REQUIRE_ERRNO(EINVAL, true);
+
+	ATF_REQUIRE_EQ_MSG(epoll_create(0), -1,
+	    "epoll_create succeeded unexpectedly");
+	ATF_REQUIRE_ERRNO(EINVAL, true);
+
+	RL(epoll_create(1));
+}
+
+ATF_TC(bad_epfd);
+ATF_TC_HEAD(bad_epfd, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks that epoll_ctl detects an invalid epfd");
+}
+ATF_TC_BODY(bad_epfd, tc)
+{
+	int fd;
+	struct epoll_event event;
+
+	RL(fd = epoll_create1(0));
+	event.events = EPOLLIN;
+
+	ATF_REQUIRE_EQ_MSG(epoll_ctl(-1, EPOLL_CTL_ADD, fd, &event), -1,
+	    "epoll_ctl succeeded unexpectedly");
+	ATF_REQUIRE_ERRNO(EBADF, true);
+}
+
+ATF_TC(bad_fd);
+ATF_TC_HEAD(bad_fd, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks that epoll_ctl detects an invalid fd");
+}
+ATF_TC_BODY(bad_fd, tc)
+{
+	int epfd;
+	struct epoll_event event;
+
+	RL(epfd = epoll_create1(0));
+	event.events = EPOLLIN;
+
+	ATF_REQUIRE_EQ_MSG(epoll_ctl(epfd, EPOLL_CTL_ADD, -1, &event), -1,
+	    "epoll_ctl succeeded unexpectedly");
+	ATF_REQUIRE_ERRNO(EBADF, true);
+}
+
+ATF_TC(double_add);
+ATF_TC_HEAD(double_add, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks that epoll_ctl detects if a fd has already been added");
+}
+ATF_TC_BODY(double_add, tc)
+{
+	int epfd, fd;
+	struct epoll_event event;
+
+	RL(epfd = epoll_create1(0));
+	RL(fd = epoll_create1(0));
+	event.events = EPOLLIN;
+
+	RL(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event));
+
+	ATF_REQUIRE_EQ_MSG(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event), -1,
+	    "epoll_ctl succeeded unexpectedly");
+	ATF_REQUIRE_ERRNO(EEXIST, true);
+}
+
+ATF_TC(not_added);
+ATF_TC_HEAD(not_added, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks that epoll_ctl detects if a fd has not been added");
+}
+ATF_TC_BODY(not_added, tc)
+{
+	int epfd, fd;
+	struct epoll_event event;
+
+	RL(epfd = epoll_create1(0));
+	RL(fd = epoll_create1(0));
+	event.events = EPOLLIN;
+
+	ATF_REQUIRE_EQ_MSG(epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &event), -1,
+	    "epoll_ctl succeeded unexpectedly");
+	ATF_REQUIRE_ERRNO(ENOENT, true);
+
+	ATF_REQUIRE_EQ_MSG(epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL), -1,
+	    "epoll_ctl succeeded unexpectedly");
+	ATF_REQUIRE_ERRNO(ENOENT, true);
+}
+
+ATF_TC(watching_self);
+ATF_TC_HEAD(watching_self, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks that epoll disallows watching itself");
+}
+ATF_TC_BODY(watching_self, tc)
+{
+	int epfd;
+	struct epoll_event event;
+
+	RL(epfd = epoll_create1(0));
+	ATF_REQUIRE_EQ_MSG(epoll_ctl(epfd, EPOLL_CTL_ADD, epfd, &event), -1,
+	    "epoll_ctl succeeded unexpectedly");
+	ATF_REQUIRE_ERRNO(EINVAL, true);
+}
+
+ATF_TC(watch_loops);
+ATF_TC_HEAD(watch_loops, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr", "Checks that epoll disallows loops");
+}
+ATF_TC_BODY(watch_loops, tc)
+{
+        int epfd1, epfd2;
+	struct epoll_event event;
+
+	event.events = EPOLLIN;
+	RL(epfd1 = epoll_create1(0));
+	RL(epfd2 = epoll_create1(0));
+	RL(epoll_ctl(epfd1, EPOLL_CTL_ADD, epfd2, &event));
+	ATF_REQUIRE_EQ_MSG(epoll_ctl(epfd2, EPOLL_CTL_ADD, epfd1, &event), -1,
+	    "epoll_ctl succeeded unexpectedly");
+	ATF_REQUIRE_ERRNO(ELOOP, true);
+}
+
+ATF_TC(watch_depth);
+ATF_TC_HEAD(watch_depth, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks that epoll fails when the watch depth exceeds 5");
+}
+ATF_TC_BODY(watch_depth, tc)
+{
+	int epfd, tmp;
+	struct epoll_event event;
+
+	event.events = EPOLLIN;
+	RL(epfd = epoll_create1(0));
+	for (size_t i = 0; i < 4; i++) {
+		RL(tmp = epoll_create1(0));
+		RL(epoll_ctl(tmp, EPOLL_CTL_ADD, epfd, &event));
+		epfd = tmp;
+	}
+	RL(tmp = epoll_create1(0));
+	ATF_REQUIRE_EQ_MSG(epoll_ctl(tmp, EPOLL_CTL_ADD, epfd, &event), -1,
+	    "epoll_ctl succeeded unexpectedly");
+	ATF_REQUIRE_ERRNO(EINVAL, true);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	ATF_TP_ADD_TC(tp, create_size);
+	ATF_TP_ADD_TC(tp, bad_epfd);
+	ATF_TP_ADD_TC(tp, bad_fd);
+	ATF_TP_ADD_TC(tp, not_added);
+	ATF_TP_ADD_TC(tp, watching_self);
+	ATF_TP_ADD_TC(tp, watch_loops);
+	ATF_TP_ADD_TC(tp, watch_depth);
+
+	return atf_no_error();
+}

Reply via email to