• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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