/* * Description: test if io_uring SQ poll kthread is stopped when the userspace * process ended with or without closing the io_uring fd * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "liburing.h" #define SQ_THREAD_IDLE 2000 #define BUF_SIZE 128 #define KTHREAD_NAME "io_uring-sq" enum { TEST_OK = 0, TEST_SKIPPED = 1, TEST_FAILED = 2, }; static int do_test_sq_poll_kthread_stopped(bool do_exit) { int ret = 0, pipe1[2]; struct io_uring_params param; struct io_uring ring; struct io_uring_sqe *sqe; struct io_uring_cqe *cqe; uint8_t buf[BUF_SIZE]; struct iovec iov; if (geteuid()) { fprintf(stderr, "sqpoll requires root!\n"); return TEST_SKIPPED; } if (pipe(pipe1) != 0) { perror("pipe"); return TEST_FAILED; } memset(¶m, 0, sizeof(param)); param.flags |= IORING_SETUP_SQPOLL; param.sq_thread_idle = SQ_THREAD_IDLE; ret = io_uring_queue_init_params(16, &ring, ¶m); if (ret) { fprintf(stderr, "ring setup failed\n"); ret = TEST_FAILED; goto err_pipe; } ret = io_uring_register_files(&ring, &pipe1[1], 1); if (ret) { fprintf(stderr, "file reg failed: %d\n", ret); ret = TEST_FAILED; goto err_uring; } iov.iov_base = buf; iov.iov_len = BUF_SIZE; sqe = io_uring_get_sqe(&ring); if (!sqe) { fprintf(stderr, "io_uring_get_sqe failed\n"); ret = TEST_FAILED; goto err_uring; } io_uring_prep_writev(sqe, 0, &iov, 1, 0); sqe->flags |= IOSQE_FIXED_FILE; ret = io_uring_submit(&ring); if (ret < 0) { fprintf(stderr, "io_uring_submit failed - ret: %d\n", ret); ret = TEST_FAILED; goto err_uring; } ret = io_uring_wait_cqe(&ring, &cqe); if (ret < 0) { fprintf(stderr, "io_uring_wait_cqe - ret: %d\n", ret); ret = TEST_FAILED; goto err_uring; } if (cqe->res != BUF_SIZE) { fprintf(stderr, "unexpected cqe->res %d [expected %d]\n", cqe->res, BUF_SIZE); ret = TEST_FAILED; goto err_uring; } io_uring_cqe_seen(&ring, cqe); ret = TEST_OK; err_uring: if (do_exit) io_uring_queue_exit(&ring); err_pipe: close(pipe1[0]); close(pipe1[1]); return ret; } int test_sq_poll_kthread_stopped(bool do_exit) { pid_t pid; int status = 0; pid = fork(); if (pid == 0) { int ret = do_test_sq_poll_kthread_stopped(do_exit); exit(ret); } pid = wait(&status); if (status != 0) return WEXITSTATUS(status); sleep(1); if (system("ps --ppid 2 | grep " KTHREAD_NAME) == 0) { fprintf(stderr, "%s kthread still running!\n", KTHREAD_NAME); return TEST_FAILED; } return 0; } int main(int argc, char *argv[]) { int ret; if (argc > 1) return 0; ret = test_sq_poll_kthread_stopped(true); if (ret == TEST_SKIPPED) { printf("test_sq_poll_kthread_stopped_exit: skipped\n"); } else if (ret == TEST_FAILED) { fprintf(stderr, "test_sq_poll_kthread_stopped_exit failed\n"); return ret; } ret = test_sq_poll_kthread_stopped(false); if (ret == TEST_SKIPPED) { printf("test_sq_poll_kthread_stopped_noexit: skipped\n"); } else if (ret == TEST_FAILED) { fprintf(stderr, "test_sq_poll_kthread_stopped_noexit failed\n"); return ret; } return 0; }