* [LTP] [PATCH 1/5] syscalls: Add epoll_ctl07
2026-07-15 9:41 [LTP] [PATCH 0/5] Even more epoll tests Cyril Hrubis
@ 2026-07-15 9:41 ` Cyril Hrubis
2026-07-15 11:02 ` [LTP] " linuxtestproject.agent
2026-07-15 9:41 ` [LTP] [PATCH 2/5] syscalls: Add epoll_wait13 Cyril Hrubis
` (4 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Cyril Hrubis @ 2026-07-15 9:41 UTC (permalink / raw)
To: ltp
The epoll_ctl05 excercies the loop detector with a longer chain this
tests does the same but only with two epoll fds.
Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
runtest/syscalls | 1 +
.../kernel/syscalls/epoll_ctl/.gitignore | 1 +
.../kernel/syscalls/epoll_ctl/epoll_ctl07.c | 60 +++++++++++++++++++
3 files changed, 62 insertions(+)
create mode 100644 testcases/kernel/syscalls/epoll_ctl/epoll_ctl07.c
diff --git a/runtest/syscalls b/runtest/syscalls
index 789d1550d..9f46e28de 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -188,6 +188,7 @@ epoll_ctl03 epoll_ctl03
epoll_ctl04 epoll_ctl04
epoll_ctl05 epoll_ctl05
epoll_ctl06 epoll_ctl06
+epoll_ctl07 epoll_ctl07
epoll_wait01 epoll_wait01
epoll_wait02 epoll_wait02
diff --git a/testcases/kernel/syscalls/epoll_ctl/.gitignore b/testcases/kernel/syscalls/epoll_ctl/.gitignore
index 9c555f41b..66da2dbfb 100644
--- a/testcases/kernel/syscalls/epoll_ctl/.gitignore
+++ b/testcases/kernel/syscalls/epoll_ctl/.gitignore
@@ -4,3 +4,4 @@ epoll_ctl03
epoll_ctl04
epoll_ctl05
epoll_ctl06
+epoll_ctl07
diff --git a/testcases/kernel/syscalls/epoll_ctl/epoll_ctl07.c b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl07.c
new file mode 100644
index 000000000..6e9b3a1a2
--- /dev/null
+++ b/testcases/kernel/syscalls/epoll_ctl/epoll_ctl07.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+/*\
+ * Verify that :manpage:`epoll_ctl(2)` fails with ELOOP when an
+ * ``EPOLL_CTL_ADD`` operation would create a minimal two-instance cycle of
+ * epoll instances monitoring one another.
+ *
+ * This is the shortest path through the kernel loop detector. The existing
+ * epoll_ctl05 test exercises a long chain closed into a loop.
+ *
+ * [Algorithm]
+ *
+ * - create two epoll instances ep_a and ep_b
+ * - add ep_b into ep_a with EPOLL_CTL_ADD (succeeds)
+ * - add ep_a into ep_b with EPOLL_CTL_ADD, which must fail with ELOOP
+ */
+
+#include <sys/epoll.h>
+
+#include "tst_test.h"
+#include "tst_epoll.h"
+
+static int ep_a = -1, ep_b = -1;
+
+static void setup(void)
+{
+ struct epoll_event ev = {.events = EPOLLIN};
+
+ ep_a = SAFE_EPOLL_CREATE1(0);
+ ep_b = SAFE_EPOLL_CREATE1(0);
+
+ ev.data.fd = ep_b;
+ SAFE_EPOLL_CTL(ep_a, EPOLL_CTL_ADD, ep_b, &ev);
+}
+
+static void cleanup(void)
+{
+ if (ep_a != -1)
+ SAFE_CLOSE(ep_a);
+
+ if (ep_b != -1)
+ SAFE_CLOSE(ep_b);
+}
+
+static void run(void)
+{
+ struct epoll_event ev = {.events = EPOLLIN, .data.fd = ep_a};
+
+ TST_EXP_FAIL(epoll_ctl(ep_b, EPOLL_CTL_ADD, ep_a, &ev), ELOOP,
+ "epoll_ctl(EPOLL_CTL_ADD) closing a two-instance cycle");
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .cleanup = cleanup,
+ .test_all = run,
+};
--
2.54.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [LTP] syscalls: Add epoll_ctl07
2026-07-15 9:41 ` [LTP] [PATCH 1/5] syscalls: Add epoll_ctl07 Cyril Hrubis
@ 2026-07-15 11:02 ` linuxtestproject.agent
2026-07-15 11:07 ` Cyril Hrubis
0 siblings, 1 reply; 11+ messages in thread
From: linuxtestproject.agent @ 2026-07-15 11:02 UTC (permalink / raw)
To: Cyril Hrubis; +Cc: ltp
Hi Cyril,
On Wed, 15 Jul 2026 11:41:34 +0200, Cyril Hrubis wrote:
> syscalls: Add epoll_ctl07
--- [PATCH 1/5] ---
> The epoll_ctl05 excercies the loop detector with a longer chain this
> tests does the same but only with two epoll fds.
Two typos: "excercies" -> "exercises", "this tests" -> "this test".
--- [PATCH 2/5] ---
> That checks that subsequent calls to epoll() return all events if there
> were more events than than the size of the events array passed to epoll.
Duplicate word: "than than" -> "than".
--- [PATCH 3/5] ---
> This is another test for maxevents cap, but in this case we do not drain
> the fds so they are newer removed from the ready set.
Typo: "newer" -> "never".
--- [PATCH 4/5] ---
> + * This is the default (non-EPOLLEXCLUSIVE) behavior and serves as the
> + * contrast to the thundering-herd avoidance tested in epoll_wait16.
At the point this patch is applied, epoll_wait16 does not exist yet
(it is added in the next patch). The intermediate tree has a dangling
cross-reference. Consider adding this sentence with patch 5/5, or
rewording to not name the follow-up test explicitly.
--- [PATCH 5/5] ---
The commit subject says "syscalls/epoll_wait15: Add EPOLLEXCLUSIVE
test" but the diff adds epoll_wait16.c, registers epoll_wait16 in
runtest/syscalls, and adds epoll_wait16 to .gitignore. Should the
subject name epoll_wait16?
> + if (nwoken != round+1) {
> + tst_res(TFAIL,
> + "event %d woke %d waiters in total, expected %d",
> + round, nwoken, round);
The last argument is `round`, but the predicate checks `nwoken !=
round+1`, so the expected value is `round+1`. At round=0 the message
would print "expected 0" when the expected count is 1. Should the last
argument be `round+1`?
> +#include "lapi/epoll.h"
EPOLLEXCLUSIVE is used in this test but is not defined anywhere in
lapi/epoll.h (confirmed: the file defines EPOLL_CLOEXEC and epoll_pwait
fallbacks, but nothing for EPOLLEXCLUSIVE). EPOLLEXCLUSIVE was added to
glibc in 2.24; on older build environments the test will fail to
compile.
A fallback definition belongs in lapi/epoll.h:
#ifndef EPOLLEXCLUSIVE
#define EPOLLEXCLUSIVE (1u << 28)
#endif
> + if (round == NWAITERS)
> + tst_res(TPASS, "each event woke exactly one waiter");
> +
> + SAFE_CLOSE(fds[0]);
> + SAFE_CLOSE(fds[1]);
> +}
run() forks NWAITERS children but never calls tst_reap_children().
Each child calls exit(0) after the checkpoint wake, so they are all
finished by the time the loop exits, but the parent leaves them as
zombies. The structurally identical epoll_wait15.c calls
tst_reap_children() after its equivalent loop. Should tst_reap_children()
be called here before or after the result is reported?
Verdict - Needs revision
---
Note:
The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.
Regards,
LTP AI Reviewer
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [LTP] syscalls: Add epoll_ctl07
2026-07-15 11:02 ` [LTP] " linuxtestproject.agent
@ 2026-07-15 11:07 ` Cyril Hrubis
0 siblings, 0 replies; 11+ messages in thread
From: Cyril Hrubis @ 2026-07-15 11:07 UTC (permalink / raw)
To: linuxtestproject.agent; +Cc: ltp
Hi!
> A fallback definition belongs in lapi/epoll.h:
>
> #ifndef EPOLLEXCLUSIVE
> #define EPOLLEXCLUSIVE (1u << 28)
> #endif
Indeed it fails to compile on older distros, I will add the fallback.
> > + if (round == NWAITERS)
> > + tst_res(TPASS, "each event woke exactly one waiter");
> > +
> > + SAFE_CLOSE(fds[0]);
> > + SAFE_CLOSE(fds[1]);
> > +}
>
> run() forks NWAITERS children but never calls tst_reap_children().
> Each child calls exit(0) after the checkpoint wake, so they are all
> finished by the time the loop exits, but the parent leaves them as
> zombies. The structurally identical epoll_wait15.c calls
> tst_reap_children() after its equivalent loop. Should tst_reap_children()
> be called here before or after the result is reported?
Children that exit(0) are autocollected by the test library.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 11+ messages in thread
* [LTP] [PATCH 2/5] syscalls: Add epoll_wait13
2026-07-15 9:41 [LTP] [PATCH 0/5] Even more epoll tests Cyril Hrubis
2026-07-15 9:41 ` [LTP] [PATCH 1/5] syscalls: Add epoll_ctl07 Cyril Hrubis
@ 2026-07-15 9:41 ` Cyril Hrubis
2026-07-15 9:41 ` [LTP] [PATCH 3/5] syscalls: Add epoll_wait14 Cyril Hrubis
` (3 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Cyril Hrubis @ 2026-07-15 9:41 UTC (permalink / raw)
To: ltp
That checks that subsequent calls to epoll() return all events if there
were more events than than the size of the events array passed to epoll.
Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
runtest/syscalls | 1 +
.../kernel/syscalls/epoll_wait/.gitignore | 1 +
.../kernel/syscalls/epoll_wait/epoll_wait13.c | 143 ++++++++++++++++++
3 files changed, 145 insertions(+)
create mode 100644 testcases/kernel/syscalls/epoll_wait/epoll_wait13.c
diff --git a/runtest/syscalls b/runtest/syscalls
index 9f46e28de..1cbc13c82 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -202,6 +202,7 @@ epoll_wait09 epoll_wait09
epoll_wait10 epoll_wait10
epoll_wait11 epoll_wait11
epoll_wait12 epoll_wait12
+epoll_wait13 epoll_wait13
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 ad890d077..145e7a0bd 100644
--- a/testcases/kernel/syscalls/epoll_wait/.gitignore
+++ b/testcases/kernel/syscalls/epoll_wait/.gitignore
@@ -10,3 +10,4 @@ epoll_wait09
epoll_wait10
epoll_wait11
epoll_wait12
+epoll_wait13
diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait13.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait13.c
new file mode 100644
index 000000000..8a8446baa
--- /dev/null
+++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait13.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+/*\
+ * Verify that :manpage:`epoll_wait(2)` caps the number of reported events at
+ * ``maxevents`` and eventually reports every ready file descriptor while
+ * ignoring the registered descriptors that are not ready.
+ *
+ * When more file descriptors are ready than the ``maxevents`` limit passed to
+ * :manpage:`epoll_wait(2)`, a single call must return at most ``maxevents``
+ * events. The remaining ready descriptors have to be reported by subsequent
+ * calls. Descriptors that are registered but have no data ready must never be
+ * reported, so the test also registers a set of quiet descriptors and checks
+ * that none of them show up in the results.
+ */
+
+#include <sys/epoll.h>
+
+#include "tst_test.h"
+#include "tst_epoll.h"
+#include "tst_minmax.h"
+
+/* NREADY is deliberately not a multiple of MAXEVENTS. */
+#define NREADY 13
+#define NQUIET 5
+#define MAXEVENTS 3
+
+/*
+ * Ready fds are registered with data.u64 in the range [0, NREADY), quiet fds
+ * with data.u64 >= NREADY, so any reported quiet fd is trivially detected.
+ */
+#define QUIET_TAG NREADY
+
+static int epfd = -1;
+static int ready_fds[NREADY][2];
+static int quiet_fds[NQUIET][2];
+static struct epoll_event *events;
+
+static void register_fds(int fds[][2], int n, uint64_t tag)
+{
+ struct epoll_event ev = {.events = EPOLLIN};
+ int i;
+
+ for (i = 0; i < n; i++) {
+ SAFE_PIPE(fds[i]);
+
+ ev.data.u64 = tag + i;
+ SAFE_EPOLL_CTL(epfd, EPOLL_CTL_ADD, fds[i][0], &ev);
+ }
+}
+
+static void close_fds(int fds[][2], int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (fds[i][0] > 0) {
+ SAFE_CLOSE(fds[i][0]);
+ SAFE_CLOSE(fds[i][1]);
+ }
+ }
+}
+
+static void setup(void)
+{
+ epfd = SAFE_EPOLL_CREATE1(0);
+
+ register_fds(ready_fds, NREADY, 0);
+ register_fds(quiet_fds, NQUIET, QUIET_TAG);
+}
+
+static void cleanup(void)
+{
+ if (epfd != -1)
+ SAFE_CLOSE(epfd);
+
+ close_fds(ready_fds, NREADY);
+ close_fds(quiet_fds, NQUIET);
+}
+
+static void run(void)
+{
+ int seen[NREADY] = {};
+ int total = 0, calls = 0;
+ int i, ret, remaining, exp_events;
+ char c;
+
+ for (i = 0; i < NREADY; i++)
+ SAFE_WRITE(SAFE_WRITE_ALL, ready_fds[i][1], "x", 1);
+
+ while (total < NREADY) {
+ ret = SAFE_EPOLL_WAIT(epfd, events, MAXEVENTS, 0);
+ calls++;
+
+ remaining = NREADY - total;
+ exp_events = MIN(remaining, MAXEVENTS);
+
+ if (ret != exp_events) {
+ tst_res(TFAIL,
+ "epoll_wait() returned %d events, expected %d",
+ ret, exp_events);
+ return;
+ }
+
+ for (i = 0; i < ret; i++) {
+ uint64_t idx = events[i].data.u64;
+
+ if (idx >= NREADY) {
+ tst_res(TFAIL,
+ "epoll_wait() reported quiet fd (tag %llu)",
+ (unsigned long long)idx);
+ return;
+ }
+
+ if (seen[idx]) {
+ tst_res(TFAIL,
+ "epoll_wait() reported ready fd %llu twice",
+ (unsigned long long)idx);
+ return;
+ }
+
+ seen[idx] = 1;
+ total++;
+
+ SAFE_READ(1, ready_fds[idx][0], &c, 1);
+ }
+ }
+
+ tst_res(TPASS, "epoll_wait() reported all %d ready fds in %d calls",
+ NREADY, calls);
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .cleanup = cleanup,
+ .test_all = run,
+ .bufs = (struct tst_buffers []) {
+ {&events, .size = MAXEVENTS * sizeof(struct epoll_event)},
+ {},
+ },
+};
--
2.54.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 11+ messages in thread* [LTP] [PATCH 3/5] syscalls: Add epoll_wait14
2026-07-15 9:41 [LTP] [PATCH 0/5] Even more epoll tests Cyril Hrubis
2026-07-15 9:41 ` [LTP] [PATCH 1/5] syscalls: Add epoll_ctl07 Cyril Hrubis
2026-07-15 9:41 ` [LTP] [PATCH 2/5] syscalls: Add epoll_wait13 Cyril Hrubis
@ 2026-07-15 9:41 ` Cyril Hrubis
2026-07-15 9:41 ` [LTP] [PATCH 4/5] syscalls: Add epoll_wait15 Cyril Hrubis
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Cyril Hrubis @ 2026-07-15 9:41 UTC (permalink / raw)
To: ltp
This is another test for maxevents cap, but in this case we do not drain
the fds so they are newer removed from the ready set. We loop over the
epoll_wait() and make sure that the fds are reported round robin i.e. no
fd is starved.
Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
runtest/syscalls | 1 +
.../kernel/syscalls/epoll_wait/.gitignore | 1 +
.../kernel/syscalls/epoll_wait/epoll_wait14.c | 114 ++++++++++++++++++
3 files changed, 116 insertions(+)
create mode 100644 testcases/kernel/syscalls/epoll_wait/epoll_wait14.c
diff --git a/runtest/syscalls b/runtest/syscalls
index 1cbc13c82..7c7e4b6f6 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -203,6 +203,7 @@ epoll_wait10 epoll_wait10
epoll_wait11 epoll_wait11
epoll_wait12 epoll_wait12
epoll_wait13 epoll_wait13
+epoll_wait14 epoll_wait14
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 145e7a0bd..fadaf4faa 100644
--- a/testcases/kernel/syscalls/epoll_wait/.gitignore
+++ b/testcases/kernel/syscalls/epoll_wait/.gitignore
@@ -11,3 +11,4 @@ epoll_wait10
epoll_wait11
epoll_wait12
epoll_wait13
+epoll_wait14
diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait14.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait14.c
new file mode 100644
index 000000000..192db87b5
--- /dev/null
+++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait14.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+/*\
+ * Verify that :manpage:`epoll_wait(2)` rotates its ready list fairly.
+ *
+ * When more file descriptors are ready than the ``maxevents`` limit and the
+ * descriptors are kept ready (never drained), the kernel has to rotate the
+ * ready list so that all of them are reported in a round-robin fashion. Over
+ * several full rotations every descriptor must therefore be reported the same
+ * number of times, none may be starved and none may be favoured.
+ */
+
+#include <sys/epoll.h>
+
+#include "tst_test.h"
+#include "tst_epoll.h"
+
+/* NREADY is deliberately not a multiple of MAXEVENTS. */
+#define NREADY 13
+#define MAXEVENTS 3
+#define ROUNDS 4
+
+static int epfd = -1;
+static int ready_fds[NREADY][2];
+static struct epoll_event *events;
+
+static void setup(void)
+{
+ struct epoll_event ev = {.events = EPOLLIN};
+ int i;
+
+ epfd = SAFE_EPOLL_CREATE1(0);
+
+ for (i = 0; i < NREADY; i++) {
+ SAFE_PIPE(ready_fds[i]);
+ SAFE_WRITE(SAFE_WRITE_ALL, ready_fds[i][1], "x", 1);
+
+ ev.data.u64 = i;
+ SAFE_EPOLL_CTL(epfd, EPOLL_CTL_ADD, ready_fds[i][0], &ev);
+ }
+}
+
+static void cleanup(void)
+{
+ int i;
+
+ if (epfd != -1)
+ SAFE_CLOSE(epfd);
+
+ for (i = 0; i < NREADY; i++) {
+ if (ready_fds[i][0] > 0) {
+ SAFE_CLOSE(ready_fds[i][0]);
+ SAFE_CLOSE(ready_fds[i][1]);
+ }
+ }
+}
+
+static void run(void)
+{
+ int count[NREADY] = {};
+ int collected = 0, target = ROUNDS * NREADY;
+ int i, ret;
+
+ while (collected < target) {
+ ret = SAFE_EPOLL_WAIT(epfd, events, MAXEVENTS, 0);
+
+ if (ret != MAXEVENTS) {
+ tst_res(TFAIL,
+ "epoll_wait() returned %d events, expected %d",
+ ret, MAXEVENTS);
+ return;
+ }
+
+ for (i = 0; i < ret && collected < target; i++) {
+ uint64_t idx = events[i].data.u64;
+
+ if (idx >= NREADY) {
+ tst_res(TFAIL,
+ "epoll_wait() returned bogus index %llu",
+ (unsigned long long)idx);
+ return;
+ }
+
+ count[idx]++;
+ collected++;
+ }
+ }
+
+ for (i = 0; i < NREADY; i++) {
+ if (count[i] != ROUNDS) {
+ tst_res(TFAIL,
+ "fd %d reported %d times, expected %d",
+ i, count[i], ROUNDS);
+ return;
+ }
+ }
+
+ tst_res(TPASS,
+ "epoll_wait() reported each of %d fds exactly %d times",
+ NREADY, ROUNDS);
+}
+
+static struct tst_test test = {
+ .setup = setup,
+ .cleanup = cleanup,
+ .test_all = run,
+ .bufs = (struct tst_buffers []) {
+ {&events, .size = MAXEVENTS * sizeof(struct epoll_event)},
+ {},
+ },
+};
--
2.54.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 11+ messages in thread* [LTP] [PATCH 4/5] syscalls: Add epoll_wait15
2026-07-15 9:41 [LTP] [PATCH 0/5] Even more epoll tests Cyril Hrubis
` (2 preceding siblings ...)
2026-07-15 9:41 ` [LTP] [PATCH 3/5] syscalls: Add epoll_wait14 Cyril Hrubis
@ 2026-07-15 9:41 ` Cyril Hrubis
2026-07-15 9:41 ` [LTP] [PATCH 5/5] syscalls/epoll_wait15: Add EPOLLEXCLUSIVE test Cyril Hrubis
2026-07-15 11:25 ` [LTP] [PATCH 0/5] Even more epoll tests Andrea Cervesato via ltp
5 siblings, 0 replies; 11+ messages in thread
From: Cyril Hrubis @ 2026-07-15 9:41 UTC (permalink / raw)
To: ltp
A test that all epoll instances are woken up on a single event.
Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
runtest/syscalls | 1 +
.../kernel/syscalls/epoll_wait/.gitignore | 1 +
.../kernel/syscalls/epoll_wait/epoll_wait15.c | 102 ++++++++++++++++++
3 files changed, 104 insertions(+)
create mode 100644 testcases/kernel/syscalls/epoll_wait/epoll_wait15.c
diff --git a/runtest/syscalls b/runtest/syscalls
index 7c7e4b6f6..af04b430e 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -204,6 +204,7 @@ epoll_wait11 epoll_wait11
epoll_wait12 epoll_wait12
epoll_wait13 epoll_wait13
epoll_wait14 epoll_wait14
+epoll_wait15 epoll_wait15
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 fadaf4faa..5ba917bdb 100644
--- a/testcases/kernel/syscalls/epoll_wait/.gitignore
+++ b/testcases/kernel/syscalls/epoll_wait/.gitignore
@@ -12,3 +12,4 @@ epoll_wait11
epoll_wait12
epoll_wait13
epoll_wait14
+epoll_wait15
diff --git a/testcases/kernel/syscalls/epoll_wait/epoll_wait15.c b/testcases/kernel/syscalls/epoll_wait/epoll_wait15.c
new file mode 100644
index 000000000..5460a4ab5
--- /dev/null
+++ b/testcases/kernel/syscalls/epoll_wait/epoll_wait15.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+/*\
+ * Verify that a single event on a file descriptor monitored by several epoll
+ * instances wakes all of the waiters blocked in :manpage:`epoll_wait(2)`.
+ *
+ * This is the default (non-EPOLLEXCLUSIVE) behavior and serves as the contrast
+ * to the thundering-herd avoidance tested in epoll_wait16.
+ */
+
+#include <sys/epoll.h>
+#include <sys/mman.h>
+
+#include "tst_test.h"
+#include "tst_epoll.h"
+#include "tst_atomic.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};
+
+ efd = SAFE_EPOLL_CREATE1(0);
+ SAFE_EPOLL_CTL(efd, EPOLL_CTL_ADD, fds[0], &ev);
+
+ TST_CHECKPOINT_WAKE(0);
+
+ if (epoll_wait(efd, &ev, 1, 2000) == 1)
+ tst_atomic_add_return(1, woken);
+
+ SAFE_CLOSE(efd);
+ exit(0);
+}
+
+static void run(void)
+{
+ int i, nwoken;
+
+ tst_atomic_store(0, woken);
+
+ SAFE_PIPE(fds);
+
+ for (i = 0; i < NWAITERS; i++) {
+ pids[i] = SAFE_FORK();
+ if (!pids[i])
+ child();
+
+ TST_CHECKPOINT_WAIT(0);
+ }
+
+ for (i = 0; i < NWAITERS; i++)
+ TST_PROCESS_STATE_WAIT(pids[i], 'S', 0);
+
+ SAFE_WRITE(SAFE_WRITE_ALL, fds[1], "x", 1);
+
+ tst_reap_children();
+
+ nwoken = tst_atomic_load(woken);
+
+ if (nwoken == NWAITERS)
+ tst_res(TPASS, "single event woke all %d waiters", NWAITERS);
+ else
+ tst_res(TFAIL, "single event woke %d of %d waiters",
+ nwoken, NWAITERS);
+
+ 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,
+};
--
2.54.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 11+ messages in thread* [LTP] [PATCH 5/5] syscalls/epoll_wait15: Add EPOLLEXCLUSIVE test
2026-07-15 9:41 [LTP] [PATCH 0/5] Even more epoll tests Cyril Hrubis
` (3 preceding siblings ...)
2026-07-15 9:41 ` [LTP] [PATCH 4/5] syscalls: Add epoll_wait15 Cyril Hrubis
@ 2026-07-15 9:41 ` Cyril Hrubis
2026-07-15 11:25 ` [LTP] [PATCH 0/5] Even more epoll tests Andrea Cervesato via ltp
5 siblings, 0 replies; 11+ messages in thread
From: Cyril Hrubis @ 2026-07-15 9:41 UTC (permalink / raw)
To: ltp
Tests that with EPOLLEXCLUSIVE only single waiter is woken up.
Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
runtest/syscalls | 1 +
.../kernel/syscalls/epoll_wait/.gitignore | 1 +
.../kernel/syscalls/epoll_wait/epoll_wait16.c | 115 ++++++++++++++++++
3 files changed, 117 insertions(+)
create mode 100644 testcases/kernel/syscalls/epoll_wait/epoll_wait16.c
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..39d8ea259
--- /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, 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
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [LTP] [PATCH 0/5] Even more epoll tests
2026-07-15 9:41 [LTP] [PATCH 0/5] Even more epoll tests Cyril Hrubis
` (4 preceding siblings ...)
2026-07-15 9:41 ` [LTP] [PATCH 5/5] syscalls/epoll_wait15: Add EPOLLEXCLUSIVE test Cyril Hrubis
@ 2026-07-15 11:25 ` Andrea Cervesato via ltp
2026-07-15 11:30 ` Cyril Hrubis
5 siblings, 1 reply; 11+ messages in thread
From: Andrea Cervesato via ltp @ 2026-07-15 11:25 UTC (permalink / raw)
To: Cyril Hrubis; +Cc: ltp
Hi Cyril,
the patch doesn't compile in opensuse 42.2. We need to define
EPOLLEXCLUSIVE because it's undeclared.
https://github.com/linux-test-project/ltp/actions/runs/29408205713/job/87328746756
Moving the patch-set to changes request.
Regards,
--
Andrea Cervesato
SUSE QE Automation Engineer Linux
andrea.cervesato@suse.com
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 11+ messages in thread