• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Description: run various min_timeout tests
4  *
5  */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <fcntl.h>
12 #include <sys/time.h>
13 #include <pthread.h>
14 
15 #include "liburing.h"
16 #include "helpers.h"
17 
18 struct data {
19 	pthread_barrier_t startup;
20 	unsigned long usec_sleep;
21 	int out_fds[8];
22 	int nr_fds;
23 };
24 
time_pass(struct timeval * start,unsigned long min_t,unsigned long max_t,const char * name)25 static int time_pass(struct timeval *start, unsigned long min_t,
26 		     unsigned long max_t, const char *name)
27 {
28 	unsigned long elapsed;
29 
30 	elapsed = mtime_since_now(start);
31 	if (elapsed < min_t || elapsed > max_t) {
32 		fprintf(stderr, "%s fails time check\n", name);
33 		fprintf(stderr, " elapsed=%lu, min=%lu, max=%lu\n", elapsed,
34 				min_t, max_t);
35 		return T_EXIT_FAIL;
36 	}
37 	return T_EXIT_PASS;
38 }
39 
pipe_write(void * data)40 static void *pipe_write(void *data)
41 {
42 	struct data *d = data;
43 	char buf[32];
44 	int i;
45 
46 	memset(buf, 0x55, sizeof(buf));
47 
48 	pthread_barrier_wait(&d->startup);
49 
50 	if (d->usec_sleep)
51 		usleep(d->usec_sleep);
52 
53 	for (i = 0; i < d->nr_fds; i++) {
54 		int ret;
55 
56 		ret = write(d->out_fds[i], buf, sizeof(buf));
57 		if (ret < 0) {
58 			perror("write");
59 			return NULL;
60 		}
61 	}
62 
63 	return NULL;
64 }
65 
__test_writes(struct io_uring * ring,int npipes,int usec_sleep,int usec_wait,int min_t,int max_t,const char * name)66 static int __test_writes(struct io_uring *ring, int npipes, int usec_sleep,
67 			 int usec_wait, int min_t, int max_t, const char *name)
68 {
69 	struct __kernel_timespec ts;
70 	struct io_uring_cqe *cqe;
71 	struct io_uring_sqe *sqe;
72 	struct timeval tv;
73 	int ret, i, fds[4][2];
74 	pthread_t thread;
75 	struct data d;
76 	char buf[32];
77 	void *tret;
78 
79 	for (i = 0; i < npipes; i++) {
80 		if (pipe(fds[i]) < 0) {
81 			perror("pipe");
82 			return T_EXIT_FAIL;
83 		}
84 		d.out_fds[i] = fds[i][1];
85 	}
86 	d.nr_fds = npipes;
87 
88 	pthread_barrier_init(&d.startup, NULL, 2);
89 	d.usec_sleep = usec_sleep;
90 
91 	pthread_create(&thread, NULL, pipe_write, &d);
92 	pthread_barrier_wait(&d.startup);
93 
94 	for (i = 0; i < npipes; i++) {
95 		sqe = io_uring_get_sqe(ring);
96 		io_uring_prep_read(sqe, fds[i][0], buf, sizeof(buf), 0);
97 	}
98 
99 	io_uring_submit(ring);
100 
101 	ts.tv_sec = 1;
102 	ts.tv_nsec = 0;
103 	gettimeofday(&tv, NULL);
104 	ret = io_uring_wait_cqes_min_timeout(ring, &cqe, 4, &ts, usec_wait, NULL);
105 	if (ret) {
106 		fprintf(stderr, "wait_cqes: %d\n", ret);
107 		return T_EXIT_FAIL;
108 	}
109 
110 	ret = time_pass(&tv, min_t, max_t, name);
111 
112 	io_uring_cq_advance(ring, npipes);
113 
114 	pthread_join(thread, &tret);
115 	for (i = 0; i < npipes; i++) {
116 		close(fds[i][0]);
117 		close(fds[i][1]);
118 	}
119 	return ret;
120 }
121 /*
122  * Test doing min_wait for N events, where 0 events are already available
123  * on wait enter but N/2 are posted within the min_wait window. We'll expect to
124  * return when the min_wait window expires.
125  */
test_some_wait(struct io_uring * ring)126 static int test_some_wait(struct io_uring *ring)
127 {
128 	return __test_writes(ring, 2, 1000, 100000, 95, 120, __FUNCTION__);
129 }
130 
131 /*
132  * Test doing min_wait for N events, where 0 events are already available
133  * on wait enter but N are posted within the min_wait window. We'll expect to
134  * return upon arrival of the N events, not the full min_wait window.
135  */
test_post_wait(struct io_uring * ring)136 static int test_post_wait(struct io_uring *ring)
137 {
138 	return __test_writes(ring, 4, 10000, 200000, 9, 12, __FUNCTION__);
139 }
140 
141 /*
142  * Test doing min_wait for N events, where 0 events are already available
143  * on wait enter and one is posted after the min_wait timeout has expired.
144  * That first event should cause wait to abort, even if the task has asked
145  * for more to wait on.
146  */
test_late(struct io_uring * ring)147 static int test_late(struct io_uring *ring)
148 {
149 	return __test_writes(ring, 1, 100000, 10000, 95, 120, __FUNCTION__);
150 }
151 
__test_nop(struct io_uring * ring,int nr_nops,int min_t,int max_t,unsigned long long_wait,const char * name)152 static int __test_nop(struct io_uring *ring, int nr_nops, int min_t, int max_t,
153 		      unsigned long long_wait, const char *name)
154 {
155 	struct __kernel_timespec ts;
156 	struct io_uring_cqe *cqe;
157 	struct timeval tv;
158 	int i, ret;
159 
160 	for (i = 0; i < nr_nops; i++) {
161 		struct io_uring_sqe *sqe;
162 
163 		sqe = io_uring_get_sqe(ring);
164 		io_uring_prep_nop(sqe);
165 	}
166 
167 	if (nr_nops)
168 		io_uring_submit(ring);
169 
170 	ts.tv_sec = 0;
171 	ts.tv_nsec = long_wait * 1000;
172 	gettimeofday(&tv, NULL);
173 	ret = io_uring_wait_cqes_min_timeout(ring, &cqe, 4, &ts, 50000, NULL);
174 	io_uring_cq_advance(ring, nr_nops);
175 	if (nr_nops) {
176 		if (ret) {
177 			fprintf(stderr, "wait_cqes: %d\n", ret);
178 			return T_EXIT_FAIL;
179 		}
180 	} else {
181 		if (ret != -ETIME) {
182 			fprintf(stderr, "wait_cqes: %d\n", ret);
183 			return T_EXIT_FAIL;
184 		}
185 	}
186 
187 	return time_pass(&tv, min_t, max_t, name);
188 }
189 
190 /*
191  * Test doing min_wait for N events, where N/2 events are already available
192  * on wait enter. This should abort waiting after min_wait, not do the full
193  * wait.
194  */
test_some(struct io_uring * ring)195 static int test_some(struct io_uring *ring)
196 {
197 	return __test_nop(ring, 2, 45, 55, 100000, __FUNCTION__);
198 }
199 
200 /*
201  * Test doing min_wait for N events, where N events are already available
202  * on wait enter.
203  */
test_already(struct io_uring * ring)204 static int test_already(struct io_uring *ring)
205 {
206 	return __test_nop(ring, 4, 0, 1, 100000, __FUNCTION__);
207 }
208 
209 /*
210  * Test doing min_wait for N events, and nothing ever gets posted. We'd
211  * expect the time to be the normal wait time, not the min_wait time.
212  */
test_nothing(struct io_uring * ring)213 static int test_nothing(struct io_uring *ring)
214 {
215 	return __test_nop(ring, 0, 95, 110, 100000, __FUNCTION__);
216 }
217 
218 /*
219  * Test doing min_wait for N events, and nothing ever gets posted, and use
220  * a min_wait time that's bigger than the total wait. We only expect the
221  * min_wait to elapse.
222  */
test_min_wait_biggest(struct io_uring * ring)223 static int test_min_wait_biggest(struct io_uring *ring)
224 {
225 	return __test_nop(ring, 0, 45, 55, 20000, __FUNCTION__);
226 }
227 
228 /*
229  * Test doing min_wait for N events, and nothing ever gets posted, and use
230  * a min_wait time that's roughly equal to the total wait. We only expect the
231  * min_wait to elapse.
232  */
test_min_wait_equal(struct io_uring * ring)233 static int test_min_wait_equal(struct io_uring *ring)
234 {
235 	return __test_nop(ring, 0, 45, 55, 50001, __FUNCTION__);
236 }
237 
main(int argc,char * argv[])238 int main(int argc, char *argv[])
239 {
240 	struct io_uring ring1, ring2;
241 	struct io_uring_params p = { };
242 	int ret;
243 
244 	if (argc > 1)
245 		return 0;
246 
247 	ret = t_create_ring_params(8, &ring1, &p);
248 	if (ret == T_SETUP_SKIP)
249 		return T_EXIT_SKIP;
250 	else if (ret != T_SETUP_OK)
251 		return ret;
252 	if (!(p.features & IORING_FEAT_MIN_TIMEOUT))
253 		return T_EXIT_SKIP;
254 
255 	p.flags = IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN;
256 	ret = t_create_ring_params(8, &ring2, &p);
257 	if (ret == T_SETUP_SKIP)
258 		return T_EXIT_SKIP;
259 	else if (ret != T_SETUP_OK)
260 		return ret;
261 
262 	ret = test_already(&ring1);
263 	if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
264 		return ret;
265 
266 	ret = test_already(&ring2);
267 	if (ret == T_EXIT_FAIL)
268 		return T_EXIT_FAIL;
269 
270 	ret = test_some(&ring1);
271 	if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
272 		return ret;
273 
274 	ret = test_some(&ring2);
275 	if (ret == T_EXIT_FAIL)
276 		return T_EXIT_FAIL;
277 
278 	ret = test_late(&ring1);
279 	if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
280 		return ret;
281 
282 	ret = test_late(&ring2);
283 	if (ret == T_EXIT_FAIL)
284 		return T_EXIT_FAIL;
285 
286 	ret = test_post_wait(&ring1);
287 	if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
288 		return ret;
289 
290 	ret = test_post_wait(&ring2);
291 	if (ret == T_EXIT_FAIL)
292 		return T_EXIT_FAIL;
293 
294 	ret = test_some_wait(&ring1);
295 	if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
296 		return ret;
297 
298 	ret = test_some_wait(&ring2);
299 	if (ret == T_EXIT_FAIL)
300 		return T_EXIT_FAIL;
301 
302 	ret = test_nothing(&ring1);
303 	if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
304 		return ret;
305 
306 	ret = test_nothing(&ring2);
307 	if (ret == T_EXIT_FAIL)
308 		return T_EXIT_FAIL;
309 
310 	ret = test_min_wait_biggest(&ring1);
311 	if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
312 		return ret;
313 
314 	ret = test_min_wait_biggest(&ring2);
315 	if (ret == T_EXIT_FAIL)
316 		return T_EXIT_FAIL;
317 
318 	ret = test_min_wait_equal(&ring1);
319 	if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
320 		return ret;
321 
322 	ret = test_min_wait_equal(&ring2);
323 	if (ret == T_EXIT_FAIL)
324 		return T_EXIT_FAIL;
325 
326 	io_uring_queue_exit(&ring1);
327 	io_uring_queue_exit(&ring2);
328 	return T_EXIT_PASS;
329 }
330