aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2024-05-21 13:02:01 -0600
committerJens Axboe <axboe@kernel.dk>2024-05-21 13:02:01 -0600
commit06c22ef6637284ab1f31ee64f1ee48a829958816 (patch)
tree11cbf706caf0cf17dcf2ae1e4ab8cbe568d681a4
parente06369fe55921f30ae10ed5619beb25eb48e4cc5 (diff)
downloadliburing-master.tar.gz
test/sqpoll-exec: add test case for file being immediately closedHEADmaster
Add test case for whether or not exec fails right after we've reaped a CQE for the final close of a file. If the file hasn't gone through it's final fput yet, then the exec will fail with ETXTBUSY. Link: https://lore.kernel.org/all/313824bc-799d-414f-96b7-e6de57c7e21d@gmail.com/ Link: https://lore.kernel.org/io-uring/40c7404a-f4ce-4a7d-86f3-313a9e9ee113@kernel.dk/ Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--test/Makefile1
-rw-r--r--test/sqpoll-exec.c131
2 files changed, 132 insertions, 0 deletions
diff --git a/test/Makefile b/test/Makefile
index 9a65ba6c..2521e123 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -186,6 +186,7 @@ test_srcs := \
sq-full.c \
sq-full-cpp.cc \
sqpoll-disable-exit.c \
+ sqpoll-exec.c \
sq-poll-dup.c \
sqpoll-exit-hang.c \
sq-poll-kthread.c \
diff --git a/test/sqpoll-exec.c b/test/sqpoll-exec.c
new file mode 100644
index 00000000..c5beb48f
--- /dev/null
+++ b/test/sqpoll-exec.c
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Check that closing a file with SQPOLL has it immediately closed
+ * upon receiving the CQE for the close. The 6.9 kernel had a bug
+ * where SQPOLL would not run kernel wide task_work when running the
+ * private task_work, which would defer the close if this was the
+ * final close of the file.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int fill_exec_target(char *dst, char *path)
+{
+ struct stat sb;
+
+ /*
+ * Should either be ./exec-target.t or test/exec-target.t
+ */
+ sprintf(dst, "%s", path);
+ return stat(dst, &sb);
+}
+
+static int test_exec(struct io_uring *ring, char * const argv[])
+{
+ char prog_path[PATH_MAX];
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, wstatus, fd;
+ pid_t p;
+
+ if (fill_exec_target(prog_path, "./exec-target.t") &&
+ fill_exec_target(prog_path, "test/exec-target.t")) {
+ fprintf(stdout, "Can't find exec-target, skipping\n");
+ return 0;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_openat(sqe, AT_FDCWD, prog_path, O_WRONLY, 0);
+ sqe->user_data = 0;
+
+ io_uring_submit(ring);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe %d\n", ret);
+ return 1;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "open: %d\n", cqe->res);
+ return 1;
+ }
+ fd = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_close(sqe, fd);
+ sqe->user_data = 1;
+
+ io_uring_submit(ring);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe %d\n", ret);
+ return 1;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "close: %d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ p = fork();
+ if (p == -1) {
+ fprintf(stderr, "fork() failed\n");
+ return 1;
+ }
+
+ if (p == 0) {
+ /* file should be closed, try exec'ing it */
+ ret = execve(prog_path, argv, NULL);
+ if (ret) {
+ fprintf(stderr, "exec failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+
+ if (waitpid(p, &wstatus, 0) == (pid_t)-1) {
+ perror("waitpid()");
+ return 1;
+ }
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus))
+ return 1;
+
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ struct io_uring ring;
+ int ret, i;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SQPOLL);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < 20; i++) {
+ ret = test_exec(&ring, argv);
+ if (ret) {
+ fprintf(stderr, "test_exec failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}