Linux Test Project
 help / color / mirror / Atom feed
* [LTP] [PATCH v2 0/5] Even more epoll tests
@ 2026-07-15 12:36 Cyril Hrubis
  2026-07-15 12:36 ` [LTP] [PATCH v2 1/5] syscalls: Add epoll_ctl07 Cyril Hrubis
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Cyril Hrubis @ 2026-07-15 12:36 UTC (permalink / raw)
  To: ltp

Even after the patchset there were gaps in the coverage.

v2:
 - fixed compilation failure on missing EPOLLEXCLUSIVE
 - fixed the round vs round+1 in TFAIL message in epoll_wait16
 - fixed typos in commit messages

 - didn't add the tst_reap_children() in epoll_wait16 since the
   test library collects the children processes automatically

Cyril Hrubis (5):
  syscalls: Add epoll_ctl07
  syscalls: Add epoll_wait13
  syscalls: Add epoll_wait14
  syscalls: Add epoll_wait15
  syscalls: Add epoll_wait16 EPOLLEXCLUSIVE test

 include/lapi/epoll.h                          |   4 +
 runtest/syscalls                              |   5 +
 .../kernel/syscalls/epoll_ctl/.gitignore      |   1 +
 .../kernel/syscalls/epoll_ctl/epoll_ctl07.c   |  60 ++++++++
 .../kernel/syscalls/epoll_wait/.gitignore     |   4 +
 .../kernel/syscalls/epoll_wait/epoll_wait13.c | 143 ++++++++++++++++++
 .../kernel/syscalls/epoll_wait/epoll_wait14.c | 114 ++++++++++++++
 .../kernel/syscalls/epoll_wait/epoll_wait15.c | 102 +++++++++++++
 .../kernel/syscalls/epoll_wait/epoll_wait16.c | 115 ++++++++++++++
 9 files changed, 548 insertions(+)
 create mode 100644 testcases/kernel/syscalls/epoll_ctl/epoll_ctl07.c
 create mode 100644 testcases/kernel/syscalls/epoll_wait/epoll_wait13.c
 create mode 100644 testcases/kernel/syscalls/epoll_wait/epoll_wait14.c
 create mode 100644 testcases/kernel/syscalls/epoll_wait/epoll_wait15.c
 create mode 100644 testcases/kernel/syscalls/epoll_wait/epoll_wait16.c

-- 
2.54.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [LTP] [PATCH v2 1/5] syscalls: Add epoll_ctl07
  2026-07-15 12:36 [LTP] [PATCH v2 0/5] Even more epoll tests Cyril Hrubis
@ 2026-07-15 12:36 ` Cyril Hrubis
  2026-07-15 12:59   ` [LTP] " linuxtestproject.agent
  2026-07-30  8:49   ` [LTP] [PATCH v2 1/5] " Andrea Cervesato via ltp
  2026-07-15 12:36 ` [LTP] [PATCH v2 2/5] syscalls: Add epoll_wait13 Cyril Hrubis
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 12+ messages in thread
From: Cyril Hrubis @ 2026-07-15 12:36 UTC (permalink / raw)
  To: ltp

The epoll_ctl05 exercies the loop detector with a longer chain this
test 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] 12+ messages in thread

* [LTP] [PATCH v2 2/5] syscalls: Add epoll_wait13
  2026-07-15 12:36 [LTP] [PATCH v2 0/5] Even more epoll tests Cyril Hrubis
  2026-07-15 12:36 ` [LTP] [PATCH v2 1/5] syscalls: Add epoll_ctl07 Cyril Hrubis
@ 2026-07-15 12:36 ` Cyril Hrubis
  2026-07-30 11:10   ` Andrea Cervesato via ltp
  2026-07-15 12:36 ` [LTP] [PATCH v2 3/5] syscalls: Add epoll_wait14 Cyril Hrubis
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Cyril Hrubis @ 2026-07-15 12:36 UTC (permalink / raw)
  To: ltp

That checks that subsequent calls to epoll() return all events if there
were more events 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] 12+ messages in thread

* [LTP] [PATCH v2 3/5] syscalls: Add epoll_wait14
  2026-07-15 12:36 [LTP] [PATCH v2 0/5] Even more epoll tests Cyril Hrubis
  2026-07-15 12:36 ` [LTP] [PATCH v2 1/5] syscalls: Add epoll_ctl07 Cyril Hrubis
  2026-07-15 12:36 ` [LTP] [PATCH v2 2/5] syscalls: Add epoll_wait13 Cyril Hrubis
@ 2026-07-15 12:36 ` Cyril Hrubis
  2026-07-30 11:12   ` Andrea Cervesato via ltp
  2026-07-15 12:36 ` [LTP] [PATCH v2 4/5] syscalls: Add epoll_wait15 Cyril Hrubis
  2026-07-15 12:36 ` [LTP] [PATCH v2 5/5] syscalls: Add epoll_wait16 EPOLLEXCLUSIVE test Cyril Hrubis
  4 siblings, 1 reply; 12+ messages in thread
From: Cyril Hrubis @ 2026-07-15 12:36 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 never 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] 12+ messages in thread

* [LTP] [PATCH v2 4/5] syscalls: Add epoll_wait15
  2026-07-15 12:36 [LTP] [PATCH v2 0/5] Even more epoll tests Cyril Hrubis
                   ` (2 preceding siblings ...)
  2026-07-15 12:36 ` [LTP] [PATCH v2 3/5] syscalls: Add epoll_wait14 Cyril Hrubis
@ 2026-07-15 12:36 ` Cyril Hrubis
  2026-07-30 11:15   ` Andrea Cervesato via ltp
  2026-07-15 12:36 ` [LTP] [PATCH v2 5/5] syscalls: Add epoll_wait16 EPOLLEXCLUSIVE test Cyril Hrubis
  4 siblings, 1 reply; 12+ messages in thread
From: Cyril Hrubis @ 2026-07-15 12:36 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] 12+ messages in thread

* [LTP] [PATCH v2 5/5] syscalls: Add epoll_wait16 EPOLLEXCLUSIVE test
  2026-07-15 12:36 [LTP] [PATCH v2 0/5] Even more epoll tests Cyril Hrubis
                   ` (3 preceding siblings ...)
  2026-07-15 12:36 ` [LTP] [PATCH v2 4/5] syscalls: Add epoll_wait15 Cyril Hrubis
@ 2026-07-15 12:36 ` Cyril Hrubis
  2026-07-30 11:17   ` Andrea Cervesato via ltp
  4 siblings, 1 reply; 12+ messages in thread
From: Cyril Hrubis @ 2026-07-15 12:36 UTC (permalink / raw)
  To: ltp

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


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [LTP] syscalls: Add epoll_ctl07
  2026-07-15 12:36 ` [LTP] [PATCH v2 1/5] syscalls: Add epoll_ctl07 Cyril Hrubis
@ 2026-07-15 12:59   ` linuxtestproject.agent
  2026-07-30  8:49   ` [LTP] [PATCH v2 1/5] " Andrea Cervesato via ltp
  1 sibling, 0 replies; 12+ messages in thread
From: linuxtestproject.agent @ 2026-07-15 12:59 UTC (permalink / raw)
  To: Cyril Hrubis; +Cc: ltp

Hi Cyril,

On Wed, Jul 15, 2026, Cyril Hrubis wrote:
> syscalls: Add epoll_ctl07

--- [PATCH 1/5] ---

> The epoll_ctl05 exercies the loop detector with a longer chain this
> test does the same but only with two epoll fds.

Typo: "exercies" should be "exercises". Also the sentence runs on --
a period or semicolon is needed after "longer chain".

--- [PATCH 2/5] ---

> That checks that subsequent calls to epoll() return all events if there
> were more events than the size of the events array passed to epoll.

The syscall under test is epoll_wait(2), not epoll(). Should read
"subsequent calls to epoll_wait()".

> +static void close_fds(int fds[][2], int n)
> +{
> +	int i;
> +
> +	for (i = 0; i < n; i++) {
> +		if (fds[i][0] > 0) {

The check `> 0` uses 0 as the "not opened" sentinel, but fd 0 is a
valid file descriptor. If SAFE_PIPE() returns fd 0 for fds[i][0]
(which happens when stdin has been closed), both pipe ends would leak.

The convention in this file is already `!= -1` (see epfd). Initialise
the arrays to {-1, -1} and guard with `!= -1`.

--- [PATCH 3/5] ---

> +	for (i = 0; i < NREADY; i++) {
> +		if (ready_fds[i][0] > 0) {

Same `> 0` sentinel issue as in epoll_wait13. The static array is
zero-initialised, but fd 0 is a valid descriptor. Use {-1, -1}
initialisation and guard with `!= -1`.

--- [PATCH 5/5] ---

> +		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);

The condition tests `nwoken != round+1`, so the expected count is
`round+1`, but the third format argument is `round`. When round=0 and
nwoken=2 the message reads "expected 0" instead of "expected 1".
The last argument should be `round+1`.

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] 12+ messages in thread

* Re: [LTP] [PATCH v2 1/5] syscalls: Add epoll_ctl07
  2026-07-15 12:36 ` [LTP] [PATCH v2 1/5] syscalls: Add epoll_ctl07 Cyril Hrubis
  2026-07-15 12:59   ` [LTP] " linuxtestproject.agent
@ 2026-07-30  8:49   ` Andrea Cervesato via ltp
  1 sibling, 0 replies; 12+ messages in thread
From: Andrea Cervesato via ltp @ 2026-07-30  8:49 UTC (permalink / raw)
  To: Cyril Hrubis; +Cc: ltp

Reviewed-by: Andrea Cervesato <andrea.cervesato@suse.com>

--
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] 12+ messages in thread

* Re: [LTP] [PATCH v2 2/5] syscalls: Add epoll_wait13
  2026-07-15 12:36 ` [LTP] [PATCH v2 2/5] syscalls: Add epoll_wait13 Cyril Hrubis
@ 2026-07-30 11:10   ` Andrea Cervesato via ltp
  0 siblings, 0 replies; 12+ messages in thread
From: Andrea Cervesato via ltp @ 2026-07-30 11:10 UTC (permalink / raw)
  To: Cyril Hrubis; +Cc: ltp

>		if (fds[i][0] > 0) {
>			  SAFE_CLOSE(fds[i][0]);
>			  SAFE_CLOSE(fds[i][1]);
>		}

It's worth to change the statement to check for fd != -1.
Otherwise:

Reviewed-by: Andrea Cervesato <andrea.cervesato@suse.com>

--
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] 12+ messages in thread

* Re: [LTP] [PATCH v2 3/5] syscalls: Add epoll_wait14
  2026-07-15 12:36 ` [LTP] [PATCH v2 3/5] syscalls: Add epoll_wait14 Cyril Hrubis
@ 2026-07-30 11:12   ` Andrea Cervesato via ltp
  0 siblings, 0 replies; 12+ messages in thread
From: Andrea Cervesato via ltp @ 2026-07-30 11:12 UTC (permalink / raw)
  To: Cyril Hrubis; +Cc: ltp

Same comment of epoll_wait13: we should use fd != -1 inside the
statement. Otherwise:

Reviewed-by: Andrea Cervesato <andrea.cervesato@suse.com>

--
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] 12+ messages in thread

* Re: [LTP] [PATCH v2 4/5] syscalls: Add epoll_wait15
  2026-07-15 12:36 ` [LTP] [PATCH v2 4/5] syscalls: Add epoll_wait15 Cyril Hrubis
@ 2026-07-30 11:15   ` Andrea Cervesato via ltp
  0 siblings, 0 replies; 12+ messages in thread
From: Andrea Cervesato via ltp @ 2026-07-30 11:15 UTC (permalink / raw)
  To: Cyril Hrubis; +Cc: ltp

> +	if (epoll_wait(efd, &ev, 1, 2000) == 1)
> +		tst_atomic_add_return(1, woken);

Maybe here we can track errors via TWARN just in case?

Otherwise:

Reviewed-by: Andrea Cervesato <andrea.cervesato@suse.com>

--
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] 12+ messages in thread

* Re: [LTP] [PATCH v2 5/5] syscalls: Add epoll_wait16 EPOLLEXCLUSIVE test
  2026-07-15 12:36 ` [LTP] [PATCH v2 5/5] syscalls: Add epoll_wait16 EPOLLEXCLUSIVE test Cyril Hrubis
@ 2026-07-30 11:17   ` Andrea Cervesato via ltp
  0 siblings, 0 replies; 12+ messages in thread
From: Andrea Cervesato via ltp @ 2026-07-30 11:17 UTC (permalink / raw)
  To: Cyril Hrubis; +Cc: ltp

When using `make check` i get:

CHECK testcases/kernel/syscalls/epoll_wait/epoll_wait16.c
epoll_wait16.c:91: CHECK: spaces preferred around that '+' (ctx:VxV)
epoll_wait16.c:94: CHECK: spaces preferred around that '+' (ctx:VxV)

Same comment of epoll_wait15. Otherwise:

Reviewed-by: Andrea Cervesato <andrea.cervesato@suse.com>

--
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] 12+ messages in thread

end of thread, other threads:[~2026-07-30 11:17 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-15 12:36 [LTP] [PATCH v2 0/5] Even more epoll tests Cyril Hrubis
2026-07-15 12:36 ` [LTP] [PATCH v2 1/5] syscalls: Add epoll_ctl07 Cyril Hrubis
2026-07-15 12:59   ` [LTP] " linuxtestproject.agent
2026-07-30  8:49   ` [LTP] [PATCH v2 1/5] " Andrea Cervesato via ltp
2026-07-15 12:36 ` [LTP] [PATCH v2 2/5] syscalls: Add epoll_wait13 Cyril Hrubis
2026-07-30 11:10   ` Andrea Cervesato via ltp
2026-07-15 12:36 ` [LTP] [PATCH v2 3/5] syscalls: Add epoll_wait14 Cyril Hrubis
2026-07-30 11:12   ` Andrea Cervesato via ltp
2026-07-15 12:36 ` [LTP] [PATCH v2 4/5] syscalls: Add epoll_wait15 Cyril Hrubis
2026-07-30 11:15   ` Andrea Cervesato via ltp
2026-07-15 12:36 ` [LTP] [PATCH v2 5/5] syscalls: Add epoll_wait16 EPOLLEXCLUSIVE test Cyril Hrubis
2026-07-30 11:17   ` Andrea Cervesato via ltp

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox