aboutsummaryrefslogtreecommitdiff
path: root/testcases/kernel/syscalls/eventfd/eventfd06.c
diff options
context:
space:
mode:
Diffstat (limited to 'testcases/kernel/syscalls/eventfd/eventfd06.c')
-rw-r--r--testcases/kernel/syscalls/eventfd/eventfd06.c174
1 files changed, 174 insertions, 0 deletions
diff --git a/testcases/kernel/syscalls/eventfd/eventfd06.c b/testcases/kernel/syscalls/eventfd/eventfd06.c
new file mode 100644
index 000000000..7339dd471
--- /dev/null
+++ b/testcases/kernel/syscalls/eventfd/eventfd06.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) International Business Machines Corp., 2001
+ * Copyright (c) 2008 Vijay Kumar B. <vijaykumar@bravegnu.org>
+ * Copyright (c) Linux Test Project, 2008-2022
+ * Copyright (C) 2023 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * [Description]
+ *
+ * Test whether counter overflow is detected and handled correctly.
+ *
+ * It is not possible to directly overflow the counter using the
+ * write() syscall. Overflows occur when the counter is incremented
+ * from kernel space, in an IRQ context, when it is not possible to
+ * block the calling thread of execution.
+ *
+ * The AIO subsystem internally uses eventfd mechanism for
+ * notification of completion of read or write requests. In this test
+ * we trigger a counter overflow, by setting the counter value to the
+ * max possible value initially. When the AIO subsystem notifies
+ * through the eventfd counter, the counter overflows.
+ *
+ * If the counter starts from an initial value of 0, it will
+ * take decades for an overflow to occur. But since we set the initial
+ * value to the max possible counter value, we are able to cause it to
+ * overflow with a single increment.
+ *
+ * When the counter overflows, the following is tested:
+ *
+ * - POLLERR event occurs in poll() for the eventfd
+ * - readfd_set/writefd_set is set in select() for the eventfd
+ * - the counter value is UINT64_MAX
+ */
+
+#include "tst_test.h"
+
+#ifdef HAVE_LIBAIO
+#ifdef HAVE_IO_SET_EVENTFD
+
+#include <poll.h>
+#include <libaio.h>
+#include <stdlib.h>
+#include <sys/eventfd.h>
+
+#define MAXEVENTS 16
+#define BUFSIZE 1024
+
+static int fd;
+static int evfd;
+static io_context_t ctx;
+
+static void async_write(void)
+{
+ struct iocb iocb;
+ struct iocb *iocbap[1];
+ struct io_event ioev;
+ static char buf[BUFSIZE];
+
+ memset(buf, 1, BUFSIZE);
+
+ io_prep_pwrite(&iocb, fd, buf, sizeof(buf), 0);
+ io_set_eventfd(&iocb, evfd);
+
+ iocbap[0] = &iocb;
+ TEST(io_submit(ctx, 1, iocbap));
+ if (TST_RET < 0)
+ tst_brk(TBROK, "io_submit() failed: %s", tst_strerrno(-TST_RET));
+
+ TEST(io_getevents(ctx, 1, 1, &ioev, NULL));
+ if (TST_RET < 0)
+ tst_brk(TBROK, "io_getevents() failed: %s", tst_strerrno(-TST_RET));
+}
+
+static void clear_counter(void)
+{
+ uint64_t val;
+ uint64_t max = UINT64_MAX - 1;
+
+ TEST(read(evfd, &val, sizeof(val)));
+ if (TST_RET == -1 && TST_ERR != EAGAIN)
+ tst_brk(TBROK | TERRNO, "read");
+
+ SAFE_WRITE(0, evfd, &max, sizeof(max));
+}
+
+static void test_select(void)
+{
+ fd_set readfds;
+ uint64_t count;
+ struct timeval timeout = { 10, 0 };
+
+ clear_counter();
+ async_write();
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ tst_res(TINFO, "Checking if select() detects counter overflow");
+
+ TEST(select(fd + 1, NULL, &readfds, NULL, &timeout));
+ if (TST_RET == -1)
+ tst_brk(TBROK | TERRNO, "select");
+
+ TST_EXP_EQ_LI(FD_ISSET(fd, &readfds), 1);
+
+ SAFE_READ(0, evfd, &count, sizeof(count));
+ TST_EXP_EQ_LI(count, UINT64_MAX);
+}
+
+static void test_poll(void)
+{
+ uint64_t count;
+ struct pollfd pollfd;
+
+ clear_counter();
+ async_write();
+
+ pollfd.fd = evfd;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
+
+ tst_res(TINFO, "Checking if poll() detects counter overflow");
+
+ TEST(poll(&pollfd, 1, 10000));
+ if (TST_RET == -1)
+ tst_brk(TBROK | TERRNO, "poll");
+
+ TST_EXP_EQ_LI(pollfd.revents & POLLERR, POLLERR);
+
+ SAFE_READ(0, evfd, &count, sizeof(count));
+ TST_EXP_EQ_LI(count, UINT64_MAX);
+}
+
+static void setup(void)
+{
+ TEST(io_setup(MAXEVENTS, &ctx));
+ if (TST_RET < 0)
+ tst_brk(TBROK, "io_setup() failed: %s", tst_strerrno(-TST_RET));
+
+ fd = SAFE_OPEN("testfile", O_RDWR | O_CREAT, 0644);
+ evfd = TST_EXP_FD(eventfd(0, EFD_NONBLOCK));
+}
+
+static void cleanup(void)
+{
+ SAFE_CLOSE(evfd);
+ io_destroy(ctx);
+}
+
+static void run(void)
+{
+ test_select();
+ test_poll();
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .cleanup = cleanup,
+ .needs_tmpdir = 1,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_EVENTFD",
+ NULL
+ },
+};
+
+#else /* HAVE_IO_SET_EVENTFD */
+TST_TEST_TCONF("eventfd support is not available in AIO subsystem");
+#endif
+#else /* HAVE_LIBAIO */
+TST_TEST_TCONF("libaio is not available");
+#endif