From: wen.yang@linux.dev
To: Christian Brauner <brauner@kernel.org>, Jan Kara <jack@suse.cz>,
Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
Wen Yang <wen.yang@linux.dev>
Subject: [RFC PATCH v5 2/2] selftests/eventfd: add EFD_IOC_{SET,GET}_MAXIMUM tests
Date: Thu, 9 Apr 2026 01:24:49 +0800 [thread overview]
Message-ID: <183b5d6987f8a23b5912c3f8a8e618ef70f2fbd4.1775668339.git.wen.yang@linux.dev> (raw)
In-Reply-To: <cover.1775668339.git.wen.yang@linux.dev>
From: Wen Yang <wen.yang@linux.dev>
Add correctness tests for the flow-control ioctls introduced in the
preceding commit. Cover the GET/SET round-trip, EINVAL when the
requested maximum does not exceed the current counter, EAGAIN on an
O_NONBLOCK fd when a write would reach the configured maximum, EPOLLOUT
gating at maximum-1, /proc/self/fdinfo exposure of the "eventfd-maximum"
field, and ENOTTY for unrecognised ioctl commands.
Signed-off-by: Wen Yang <wen.yang@linux.dev>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
.../filesystems/eventfd/eventfd_test.c | 238 +++++++++++++++++-
1 file changed, 237 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/filesystems/eventfd/eventfd_test.c b/tools/testing/selftests/filesystems/eventfd/eventfd_test.c
index 1b48f267157d..9e33780f5330 100644
--- a/tools/testing/selftests/filesystems/eventfd/eventfd_test.c
+++ b/tools/testing/selftests/filesystems/eventfd/eventfd_test.c
@@ -5,12 +5,22 @@
#include <fcntl.h>
#include <asm/unistd.h>
#include <linux/time_types.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <signal.h>
#include <pthread.h>
#include <sys/epoll.h>
-#include <sys/eventfd.h>
+#include <sys/ioctl.h>
+/*
+ * Prevent <asm-generic/fcntl.h> (pulled in via <linux/eventfd.h> ->
+ * <linux/fcntl.h> -> <asm/fcntl.h>) from redefining struct flock and
+ * friends that are already provided by the system <fcntl.h> above.
+ */
+#define _ASM_GENERIC_FCNTL_H
+#include <linux/eventfd.h>
#include "kselftest_harness.h"
#define EVENTFD_TEST_ITERATIONS 100000UL
@@ -308,4 +318,230 @@ TEST(eventfd_check_read_with_semaphore)
close(fd);
}
+/*
+ * The default maximum is ULLONG_MAX, matching the original behaviour.
+ */
+TEST(eventfd_check_ioctl_get_maximum_default)
+{
+ uint64_t max;
+ int fd, ret;
+
+ fd = sys_eventfd2(0, EFD_NONBLOCK);
+ ASSERT_GE(fd, 0);
+
+ ret = ioctl(fd, EFD_IOC_GET_MAXIMUM, &max);
+ EXPECT_EQ(ret, 0);
+ EXPECT_EQ(max, UINT64_MAX);
+
+ close(fd);
+}
+
+/*
+ * EFD_IOC_SET_MAXIMUM and EFD_IOC_GET_MAXIMUM round-trip.
+ */
+TEST(eventfd_check_ioctl_set_get_maximum)
+{
+ uint64_t max;
+ int fd, ret;
+
+ fd = sys_eventfd2(0, EFD_NONBLOCK);
+ ASSERT_GE(fd, 0);
+
+ max = 1000;
+ ret = ioctl(fd, EFD_IOC_SET_MAXIMUM, &max);
+ EXPECT_EQ(ret, 0);
+
+ max = 0;
+ ret = ioctl(fd, EFD_IOC_GET_MAXIMUM, &max);
+ EXPECT_EQ(ret, 0);
+ EXPECT_EQ(max, 1000);
+
+ close(fd);
+}
+
+/*
+ * Setting a maximum that is less than or equal to the current counter
+ * must fail with EINVAL.
+ */
+TEST(eventfd_check_ioctl_set_maximum_invalid)
+{
+ uint64_t value = 5, max;
+ ssize_t size;
+ int fd, ret;
+
+ fd = sys_eventfd2(0, EFD_NONBLOCK);
+ ASSERT_GE(fd, 0);
+
+ /* write 5 into the counter */
+ size = write(fd, &value, sizeof(value));
+ EXPECT_EQ(size, (ssize_t)sizeof(value));
+
+ /* setting maximum == count (5) must fail */
+ max = 5;
+ ret = ioctl(fd, EFD_IOC_SET_MAXIMUM, &max);
+ EXPECT_EQ(ret, -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* setting maximum < count (3 < 5) must also fail */
+ max = 3;
+ ret = ioctl(fd, EFD_IOC_SET_MAXIMUM, &max);
+ EXPECT_EQ(ret, -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* setting maximum > count (10 > 5) must succeed */
+ max = 10;
+ ret = ioctl(fd, EFD_IOC_SET_MAXIMUM, &max);
+ EXPECT_EQ(ret, 0);
+
+ close(fd);
+}
+
+/*
+ * Writes that would push the counter to or beyond maximum must return
+ * EAGAIN on a non-blocking fd. After a read drains the counter the
+ * write should succeed again.
+ */
+TEST(eventfd_check_ioctl_write_blocked_at_maximum)
+{
+ uint64_t value, max_val = 5;
+ ssize_t size;
+ int fd, ret;
+
+ fd = sys_eventfd2(0, EFD_NONBLOCK);
+ ASSERT_GE(fd, 0);
+
+ ret = ioctl(fd, EFD_IOC_SET_MAXIMUM, &max_val);
+ ASSERT_EQ(ret, 0);
+
+ /* write 4 — counter becomes 4, one slot before maximum */
+ value = 4;
+ size = write(fd, &value, sizeof(value));
+ EXPECT_EQ(size, (ssize_t)sizeof(value));
+
+ /*
+ * Writing 1 more would reach maximum (4+1 == 5 == maximum), which
+ * is the overflow level. The write must block, i.e. return EAGAIN
+ * in non-blocking mode.
+ */
+ value = 1;
+ size = write(fd, &value, sizeof(value));
+ EXPECT_EQ(size, -1);
+ EXPECT_EQ(errno, EAGAIN);
+
+ /* drain the counter */
+ size = read(fd, &value, sizeof(value));
+ EXPECT_EQ(size, (ssize_t)sizeof(value));
+ EXPECT_EQ(value, 4);
+
+ /* now the write must succeed (counter was reset to 0) */
+ value = 1;
+ size = write(fd, &value, sizeof(value));
+ EXPECT_EQ(size, (ssize_t)sizeof(value));
+
+ close(fd);
+}
+
+/*
+ * Verify that EPOLLOUT is correctly gated by the configured maximum:
+ * it should be clear when count >= maximum - 1, and set again after a read.
+ */
+TEST(eventfd_check_ioctl_poll_epollout)
+{
+ struct epoll_event ev, events[2];
+ uint64_t value, max_val = 5;
+ ssize_t sz;
+ int fd, epfd, nfds, ret;
+
+ fd = sys_eventfd2(0, EFD_NONBLOCK);
+ ASSERT_GE(fd, 0);
+
+ epfd = epoll_create1(0);
+ ASSERT_GE(epfd, 0);
+
+ ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;
+ ev.data.fd = fd;
+ ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
+ ASSERT_EQ(ret, 0);
+
+ ret = ioctl(fd, EFD_IOC_SET_MAXIMUM, &max_val);
+ ASSERT_EQ(ret, 0);
+
+ /* fresh fd: EPOLLOUT must be set (count=0 < maximum-1=4) */
+ nfds = epoll_wait(epfd, events, 2, 0);
+ EXPECT_EQ(nfds, 1);
+ EXPECT_TRUE(!!(events[0].events & EPOLLOUT));
+
+ /* write 4 — count reaches maximum-1=4, EPOLLOUT must clear */
+ value = 4;
+ sz = write(fd, &value, sizeof(value));
+ EXPECT_EQ(sz, (ssize_t)sizeof(value));
+
+ nfds = epoll_wait(epfd, events, 2, 0);
+ EXPECT_EQ(nfds, 1);
+ EXPECT_FALSE(!!(events[0].events & EPOLLOUT));
+ EXPECT_TRUE(!!(events[0].events & EPOLLIN));
+
+ /* drain counter — EPOLLOUT must reappear */
+ sz = read(fd, &value, sizeof(value));
+ EXPECT_EQ(sz, (ssize_t)sizeof(value));
+
+ nfds = epoll_wait(epfd, events, 2, 0);
+ EXPECT_EQ(nfds, 1);
+ EXPECT_TRUE(!!(events[0].events & EPOLLOUT));
+
+ close(epfd);
+ close(fd);
+}
+
+/*
+ * /proc/self/fdinfo must expose the configured maximum.
+ */
+TEST(eventfd_check_fdinfo_maximum)
+{
+ struct error err = {0};
+ uint64_t max_val = 12345;
+ int fd, ret;
+
+ fd = sys_eventfd2(0, 0);
+ ASSERT_GE(fd, 0);
+
+ /* before setting: default should be ULLONG_MAX */
+ ret = verify_fdinfo(fd, &err, "eventfd-maximum: ", 17,
+ "%16llx\n", (unsigned long long)UINT64_MAX);
+ if (ret != 0)
+ ksft_print_msg("eventfd-maximum default check failed: %s\n",
+ err.msg);
+ EXPECT_EQ(ret, 0);
+
+ ret = ioctl(fd, EFD_IOC_SET_MAXIMUM, &max_val);
+ ASSERT_EQ(ret, 0);
+
+ memset(&err, 0, sizeof(err));
+ ret = verify_fdinfo(fd, &err, "eventfd-maximum: ", 17,
+ "%16llx\n", (unsigned long long)max_val);
+ if (ret != 0)
+ ksft_print_msg("eventfd-maximum after set check failed: %s\n",
+ err.msg);
+ EXPECT_EQ(ret, 0);
+
+ close(fd);
+}
+
+/*
+ * An unrecognised ioctl must return ENOTTY (not EINVAL or ENOENT).
+ */
+TEST(eventfd_check_ioctl_unknown)
+{
+ int fd, ret;
+
+ fd = sys_eventfd2(0, 0);
+ ASSERT_GE(fd, 0);
+
+ ret = ioctl(fd, _IO('J', 0xff));
+ EXPECT_EQ(ret, -1);
+ EXPECT_EQ(errno, ENOTTY);
+
+ close(fd);
+}
+
TEST_HARNESS_MAIN
--
2.25.1
prev parent reply other threads:[~2026-04-08 17:25 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-08 17:24 [RFC PATCH v5 0/2] eventfd: add configurable maximum counter value for flow control wen.yang
2026-04-08 17:24 ` [RFC PATCH v5 1/2] eventfd: add configurable per-fd counter maximum " wen.yang
2026-04-08 17:24 ` wen.yang [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=183b5d6987f8a23b5912c3f8a8e618ef70f2fbd4.1775668339.git.wen.yang@linux.dev \
--to=wen.yang@linux.dev \
--cc=brauner@kernel.org \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.