public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
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


      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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox