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