[LTP] [PATCH v2 5/5] syscalls: Add epoll_wait16 EPOLLEXCLUSIVE test

Cyril Hrubis chrubis@suse.cz
Wed Jul 15 14:36:12 CEST 2026


Tests that with EPOLLEXCLUSIVE only single waiter is woken up.

Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
 include/lapi/epoll.h                          |   4 +
 runtest/syscalls                              |   1 +
 .../kernel/syscalls/epoll_wait/.gitignore     |   1 +
 .../kernel/syscalls/epoll_wait/epoll_wait16.c | 115 ++++++++++++++++++
 4 files changed, 121 insertions(+)
 create mode 100644 testcases/kernel/syscalls/epoll_wait/epoll_wait16.c

diff --git a/include/lapi/epoll.h b/include/lapi/epoll.h
index fc068ae20..15a954175 100644
--- a/include/lapi/epoll.h
+++ b/include/lapi/epoll.h
@@ -14,6 +14,10 @@
 #define EPOLL_CLOEXEC 02000000
 #endif
 
+#ifndef EPOLLEXCLUSIVE
+# define EPOLLEXCLUSIVE (1U << 28)
+#endif
+
 static inline void epoll_pwait_supported(void)
 {
 	/* allow the tests to fail early */
diff --git a/runtest/syscalls b/runtest/syscalls
index af04b430e..f50931f56 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -205,6 +205,7 @@ epoll_wait12 epoll_wait12
 epoll_wait13 epoll_wait13
 epoll_wait14 epoll_wait14
 epoll_wait15 epoll_wait15
+epoll_wait16 epoll_wait16
 
 epoll_pwait01 epoll_pwait01
 epoll_pwait02 epoll_pwait02
diff --git a/testcases/kernel/syscalls/epoll_wait/.gitignore b/testcases/kernel/syscalls/epoll_wait/.gitignore
index 5ba917bdb..1804efbce 100644
--- a/testcases/kernel/syscalls/epoll_wait/.gitignore
+++ b/testcases/kernel/syscalls/epoll_wait/.gitignore
@@ -13,3 +13,4 @@ epoll_wait12
 epoll_wait13
 epoll_wait14
 epoll_wait15
+epoll_wait16
diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait16.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait16.c
new file mode 100644
index 000000000..2d628c068
--- /dev/null
+++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait16.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+/*\
+ * Verify the EPOLLEXCLUSIVE wakeup semantics of :manpage:`epoll_ctl(2)` and
+ * :manpage:`epoll_wait(2)`.
+ *
+ * When a single target file descriptor is added with EPOLLEXCLUSIVE into
+ * several epoll instances that each have a waiter blocked in
+ * :manpage:`epoll_wait(2)`, one event on the target must wake exactly one
+ * waiter. This is the thundering-herd avoidance introduced in Linux 4.5.
+ *
+ * The test writes into a pipe in a loop and checks that on each write exactly
+ * one child process waiting on the fd is woken up.
+ */
+
+#include <sys/epoll.h>
+#include <sys/mman.h>
+
+#include "tst_test.h"
+#include "tst_epoll.h"
+#include "tst_atomic.h"
+#include "lapi/epoll.h"
+
+#define NWAITERS 8
+
+static int fds[2] = {-1, -1};
+static pid_t pids[NWAITERS];
+static tst_atomic_t *woken;
+
+static void setup(void)
+{
+	woken = SAFE_MMAP(NULL, sizeof(*woken), PROT_READ | PROT_WRITE,
+			  MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+}
+
+static void cleanup(void)
+{
+	if (fds[0] != -1) {
+		SAFE_CLOSE(fds[0]);
+		SAFE_CLOSE(fds[1]);
+	}
+
+	if (woken)
+		SAFE_MUNMAP(woken, sizeof(*woken));
+}
+
+static void child(void)
+{
+	int efd;
+	struct epoll_event ev = {.events = EPOLLIN | EPOLLET | EPOLLEXCLUSIVE};
+
+	efd = SAFE_EPOLL_CREATE1(0);
+	SAFE_EPOLL_CTL(efd, EPOLL_CTL_ADD, fds[0], &ev);
+
+	if (epoll_wait(efd, &ev, 1, 2000) == 1) {
+		tst_atomic_add_return(1, woken);
+		TST_CHECKPOINT_WAKE(0);
+	}
+
+	SAFE_CLOSE(efd);
+	exit(0);
+}
+
+static void run(void)
+{
+	int i, round, nwoken;
+	char c;
+
+	tst_atomic_store(0, woken);
+
+	SAFE_PIPE(fds);
+
+	for (i = 0; i < NWAITERS; i++) {
+		pids[i] = SAFE_FORK();
+		if (!pids[i])
+			child();
+	}
+
+	for (i = 0; i < NWAITERS; i++)
+		TST_PROCESS_STATE_WAIT(pids[i], 'S', 0);
+
+	for (round = 0; round < NWAITERS; round++) {
+		SAFE_WRITE(SAFE_WRITE_ALL, fds[1], "x", 1);
+
+		TST_CHECKPOINT_WAIT(0);
+
+		nwoken = tst_atomic_load(woken);
+		if (nwoken != round+1) {
+			tst_res(TFAIL,
+				"event %d woke %d waiters in total, expected %d",
+				round+1, nwoken, round);
+			break;
+		}
+
+		SAFE_READ(1, fds[0], &c, 1);
+	}
+
+	if (round == NWAITERS)
+		tst_res(TPASS, "each event woke exactly one waiter");
+
+	SAFE_CLOSE(fds[0]);
+	SAFE_CLOSE(fds[1]);
+}
+
+static struct tst_test test = {
+	.setup = setup,
+	.cleanup = cleanup,
+	.test_all = run,
+	.forks_child = 1,
+	.needs_checkpoints = 1,
+	.min_kver = "4.5",
+};
-- 
2.54.0



More information about the ltp mailing list