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