/* 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 #include #include #include #include #include #include #include #include #include #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_params p = { .flags = IORING_SETUP_SQPOLL, }; struct io_uring ring; int ret, i; if (argc > 1) return T_EXIT_SKIP; ret = t_create_ring_params(8, &ring, &p); if (ret == T_SETUP_SKIP) return T_EXIT_SKIP; else if (ret != T_SETUP_OK) return T_EXIT_FAIL; for (i = 0; i < 20; i++) { ret = test_exec(&ring, argv); if (ret) { fprintf(stderr, "test_exec failed\n"); return ret; } } return T_EXIT_PASS; }