1 /*
2 * Description: test if io_uring SQ poll kthread is stopped when the userspace
3 * process ended with or without closing the io_uring fd
4 *
5 */
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <pthread.h>
11 #include <stdbool.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <signal.h>
15 #include <sys/poll.h>
16 #include <sys/wait.h>
17 #include <sys/epoll.h>
18
19 #include "liburing.h"
20
21 #define SQ_THREAD_IDLE 2000
22 #define BUF_SIZE 128
23 #define KTHREAD_NAME "io_uring-sq"
24
25 enum {
26 TEST_OK = 0,
27 TEST_SKIPPED = 1,
28 TEST_FAILED = 2,
29 };
30
do_test_sq_poll_kthread_stopped(bool do_exit)31 static int do_test_sq_poll_kthread_stopped(bool do_exit)
32 {
33 int ret = 0, pipe1[2];
34 struct io_uring_params param;
35 struct io_uring ring;
36 struct io_uring_sqe *sqe;
37 struct io_uring_cqe *cqe;
38 uint8_t buf[BUF_SIZE];
39 struct iovec iov;
40
41 if (geteuid()) {
42 fprintf(stderr, "sqpoll requires root!\n");
43 return TEST_SKIPPED;
44 }
45
46 if (pipe(pipe1) != 0) {
47 perror("pipe");
48 return TEST_FAILED;
49 }
50
51 memset(¶m, 0, sizeof(param));
52
53 param.flags |= IORING_SETUP_SQPOLL;
54 param.sq_thread_idle = SQ_THREAD_IDLE;
55
56 ret = io_uring_queue_init_params(16, &ring, ¶m);
57 if (ret) {
58 fprintf(stderr, "ring setup failed\n");
59 ret = TEST_FAILED;
60 goto err_pipe;
61 }
62
63 ret = io_uring_register_files(&ring, &pipe1[1], 1);
64 if (ret) {
65 fprintf(stderr, "file reg failed: %d\n", ret);
66 ret = TEST_FAILED;
67 goto err_uring;
68 }
69
70 iov.iov_base = buf;
71 iov.iov_len = BUF_SIZE;
72
73 sqe = io_uring_get_sqe(&ring);
74 if (!sqe) {
75 fprintf(stderr, "io_uring_get_sqe failed\n");
76 ret = TEST_FAILED;
77 goto err_uring;
78 }
79
80 io_uring_prep_writev(sqe, 0, &iov, 1, 0);
81 sqe->flags |= IOSQE_FIXED_FILE;
82
83 ret = io_uring_submit(&ring);
84 if (ret < 0) {
85 fprintf(stderr, "io_uring_submit failed - ret: %d\n",
86 ret);
87 ret = TEST_FAILED;
88 goto err_uring;
89 }
90
91 ret = io_uring_wait_cqe(&ring, &cqe);
92 if (ret < 0) {
93 fprintf(stderr, "io_uring_wait_cqe - ret: %d\n",
94 ret);
95 ret = TEST_FAILED;
96 goto err_uring;
97 }
98
99 if (cqe->res != BUF_SIZE) {
100 fprintf(stderr, "unexpected cqe->res %d [expected %d]\n",
101 cqe->res, BUF_SIZE);
102 ret = TEST_FAILED;
103 goto err_uring;
104
105 }
106
107 io_uring_cqe_seen(&ring, cqe);
108
109 ret = TEST_OK;
110
111 err_uring:
112 if (do_exit)
113 io_uring_queue_exit(&ring);
114 err_pipe:
115 close(pipe1[0]);
116 close(pipe1[1]);
117
118 return ret;
119 }
120
test_sq_poll_kthread_stopped(bool do_exit)121 int test_sq_poll_kthread_stopped(bool do_exit)
122 {
123 pid_t pid;
124 int status = 0;
125
126 pid = fork();
127
128 if (pid == 0) {
129 int ret = do_test_sq_poll_kthread_stopped(do_exit);
130 exit(ret);
131 }
132
133 pid = wait(&status);
134 if (status != 0)
135 return WEXITSTATUS(status);
136
137 sleep(1);
138 if (system("ps --ppid 2 | grep " KTHREAD_NAME) == 0) {
139 fprintf(stderr, "%s kthread still running!\n", KTHREAD_NAME);
140 return TEST_FAILED;
141 }
142
143 return 0;
144 }
145
main(int argc,char * argv[])146 int main(int argc, char *argv[])
147 {
148 int ret;
149
150 if (argc > 1)
151 return 0;
152
153 ret = test_sq_poll_kthread_stopped(true);
154 if (ret == TEST_SKIPPED) {
155 printf("test_sq_poll_kthread_stopped_exit: skipped\n");
156 } else if (ret == TEST_FAILED) {
157 fprintf(stderr, "test_sq_poll_kthread_stopped_exit failed\n");
158 return ret;
159 }
160
161 ret = test_sq_poll_kthread_stopped(false);
162 if (ret == TEST_SKIPPED) {
163 printf("test_sq_poll_kthread_stopped_noexit: skipped\n");
164 } else if (ret == TEST_FAILED) {
165 fprintf(stderr, "test_sq_poll_kthread_stopped_noexit failed\n");
166 return ret;
167 }
168
169 return 0;
170 }
171