[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