1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: test io_uring poll cancel handling
4 *
5 */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <inttypes.h>
12 #include <poll.h>
13 #include <sys/wait.h>
14 #include <signal.h>
15
16 #include "liburing.h"
17
18 struct poll_data {
19 unsigned is_poll;
20 unsigned is_cancel;
21 };
22
sig_alrm(int sig)23 static void sig_alrm(int sig)
24 {
25 fprintf(stderr, "Timed out!\n");
26 exit(1);
27 }
28
test_poll_cancel(void)29 static int test_poll_cancel(void)
30 {
31 struct io_uring ring;
32 int pipe1[2];
33 struct io_uring_cqe *cqe;
34 struct io_uring_sqe *sqe;
35 struct poll_data *pd, pds[2];
36 struct sigaction act;
37 int ret;
38
39 if (pipe(pipe1) != 0) {
40 perror("pipe");
41 return 1;
42 }
43
44 ret = io_uring_queue_init(2, &ring, 0);
45 if (ret) {
46 fprintf(stderr, "ring setup failed: %d\n", ret);
47 return 1;
48 }
49
50 memset(&act, 0, sizeof(act));
51 act.sa_handler = sig_alrm;
52 act.sa_flags = SA_RESTART;
53 sigaction(SIGALRM, &act, NULL);
54 alarm(1);
55
56 sqe = io_uring_get_sqe(&ring);
57 if (!sqe) {
58 fprintf(stderr, "get sqe failed\n");
59 return 1;
60 }
61
62 io_uring_prep_poll_add(sqe, pipe1[0], POLLIN);
63
64 pds[0].is_poll = 1;
65 pds[0].is_cancel = 0;
66 io_uring_sqe_set_data(sqe, &pds[0]);
67
68 ret = io_uring_submit(&ring);
69 if (ret <= 0) {
70 fprintf(stderr, "sqe submit failed\n");
71 return 1;
72 }
73
74 sqe = io_uring_get_sqe(&ring);
75 if (!sqe) {
76 fprintf(stderr, "get sqe failed\n");
77 return 1;
78 }
79
80 pds[1].is_poll = 0;
81 pds[1].is_cancel = 1;
82 io_uring_prep_poll_remove(sqe, (__u64)(uintptr_t)&pds[0]);
83 io_uring_sqe_set_data(sqe, &pds[1]);
84
85 ret = io_uring_submit(&ring);
86 if (ret <= 0) {
87 fprintf(stderr, "sqe submit failed: %d\n", ret);
88 return 1;
89 }
90
91 ret = io_uring_wait_cqe(&ring, &cqe);
92 if (ret < 0) {
93 fprintf(stderr, "wait cqe failed: %d\n", ret);
94 return 1;
95 }
96
97 pd = io_uring_cqe_get_data(cqe);
98 if (pd->is_poll && cqe->res != -ECANCELED) {
99 fprintf(stderr ,"sqe (add=%d/remove=%d) failed with %ld\n",
100 pd->is_poll, pd->is_cancel,
101 (long) cqe->res);
102 return 1;
103 } else if (pd->is_cancel && cqe->res) {
104 fprintf(stderr, "sqe (add=%d/remove=%d) failed with %ld\n",
105 pd->is_poll, pd->is_cancel,
106 (long) cqe->res);
107 return 1;
108 }
109 io_uring_cqe_seen(&ring, cqe);
110
111 ret = io_uring_wait_cqe(&ring, &cqe);
112 if (ret < 0) {
113 fprintf(stderr, "wait_cqe: %d\n", ret);
114 return 1;
115 }
116
117 pd = io_uring_cqe_get_data(cqe);
118 if (pd->is_poll && cqe->res != -ECANCELED) {
119 fprintf(stderr, "sqe (add=%d/remove=%d) failed with %ld\n",
120 pd->is_poll, pd->is_cancel,
121 (long) cqe->res);
122 return 1;
123 } else if (pd->is_cancel && cqe->res) {
124 fprintf(stderr, "sqe (add=%d/remove=%d) failed with %ld\n",
125 pd->is_poll, pd->is_cancel,
126 (long) cqe->res);
127 return 1;
128 }
129
130 close(pipe1[0]);
131 close(pipe1[1]);
132 io_uring_cqe_seen(&ring, cqe);
133 io_uring_queue_exit(&ring);
134 return 0;
135 }
136
137
__test_poll_cancel_with_timeouts(void)138 static int __test_poll_cancel_with_timeouts(void)
139 {
140 struct __kernel_timespec ts = { .tv_sec = 10, };
141 struct io_uring ring, ring2;
142 struct io_uring_sqe *sqe;
143 int ret, off_nr = 1000;
144
145 ret = io_uring_queue_init(8, &ring, 0);
146 if (ret) {
147 fprintf(stderr, "ring setup failed: %d\n", ret);
148 return 1;
149 }
150
151 ret = io_uring_queue_init(1, &ring2, 0);
152 if (ret) {
153 fprintf(stderr, "ring setup failed: %d\n", ret);
154 return 1;
155 }
156
157 /* test timeout-offset triggering path during cancelation */
158 sqe = io_uring_get_sqe(&ring);
159 io_uring_prep_timeout(sqe, &ts, off_nr, 0);
160
161 /* poll ring2 to trigger cancelation on exit() */
162 sqe = io_uring_get_sqe(&ring);
163 io_uring_prep_poll_add(sqe, ring2.ring_fd, POLLIN);
164 sqe->flags |= IOSQE_IO_LINK;
165
166 sqe = io_uring_get_sqe(&ring);
167 io_uring_prep_link_timeout(sqe, &ts, 0);
168
169 ret = io_uring_submit(&ring);
170 if (ret != 3) {
171 fprintf(stderr, "sqe submit failed\n");
172 return 1;
173 }
174
175 /* just drop all rings/etc. intact, exit() will clean them up */
176 return 0;
177 }
178
test_poll_cancel_with_timeouts(void)179 static int test_poll_cancel_with_timeouts(void)
180 {
181 int ret;
182 pid_t p;
183
184 p = fork();
185 if (p == -1) {
186 fprintf(stderr, "fork() failed\n");
187 return 1;
188 }
189
190 if (p == 0) {
191 ret = __test_poll_cancel_with_timeouts();
192 exit(ret);
193 } else {
194 int wstatus;
195
196 if (waitpid(p, &wstatus, 0) == (pid_t)-1) {
197 perror("waitpid()");
198 return 1;
199 }
200 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus)) {
201 fprintf(stderr, "child failed %i\n", WEXITSTATUS(wstatus));
202 return 1;
203 }
204 }
205 return 0;
206 }
207
main(int argc,char * argv[])208 int main(int argc, char *argv[])
209 {
210 int ret;
211
212 if (argc > 1)
213 return 0;
214
215 ret = test_poll_cancel();
216 if (ret) {
217 fprintf(stderr, "test_poll_cancel failed\n");
218 return -1;
219 }
220
221 ret = test_poll_cancel_with_timeouts();
222 if (ret) {
223 fprintf(stderr, "test_poll_cancel_with_timeouts failed\n");
224 return -1;
225 }
226
227 return 0;
228 }
229