xref: /aosp_15_r20/external/liburing/test/timeout-overflow.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Description: run timeout overflow test
4  *
5  */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <limits.h>
9 #include <string.h>
10 #include <sys/time.h>
11 
12 #include "liburing.h"
13 
14 #define TIMEOUT_MSEC	200
15 static int not_supported;
16 
msec_to_ts(struct __kernel_timespec * ts,unsigned int msec)17 static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
18 {
19 	ts->tv_sec = msec / 1000;
20 	ts->tv_nsec = (msec % 1000) * 1000000;
21 }
22 
check_timeout_support(void)23 static int check_timeout_support(void)
24 {
25 	struct io_uring_sqe *sqe;
26 	struct io_uring_cqe *cqe;
27 	struct __kernel_timespec ts;
28 	struct io_uring_params p;
29 	struct io_uring ring;
30 	int ret;
31 
32 	memset(&p, 0, sizeof(p));
33 	ret = io_uring_queue_init_params(1, &ring, &p);
34 	if (ret) {
35 		fprintf(stderr, "ring setup failed: %d\n", ret);
36 		return 1;
37 	}
38 
39 	/* not really a match, but same kernel added batched completions */
40 	if (p.features & IORING_FEAT_POLL_32BITS) {
41 		fprintf(stdout, "Skipping\n");
42 		not_supported = 1;
43 		return 0;
44 	}
45 
46 	sqe = io_uring_get_sqe(&ring);
47 	msec_to_ts(&ts, TIMEOUT_MSEC);
48 	io_uring_prep_timeout(sqe, &ts, 1, 0);
49 
50 	ret = io_uring_submit(&ring);
51 	if (ret < 0) {
52 		fprintf(stderr, "sqe submit failed: %d\n", ret);
53 		goto err;
54 	}
55 
56 	ret = io_uring_wait_cqe(&ring, &cqe);
57 	if (ret < 0) {
58 		fprintf(stderr, "wait completion %d\n", ret);
59 		goto err;
60 	}
61 
62 	if (cqe->res == -EINVAL) {
63 		not_supported = 1;
64 		fprintf(stdout, "Timeout not supported, ignored\n");
65 		return 0;
66 	}
67 
68 	io_uring_cqe_seen(&ring, cqe);
69 	io_uring_queue_exit(&ring);
70 	return 0;
71 err:
72 	io_uring_queue_exit(&ring);
73 	return 1;
74 }
75 
76 /*
77  * We first setup 4 timeout requests, which require a count value of 1, 1, 2,
78  * UINT_MAX, so the sequence is 1, 2, 4, 2. Before really timeout, this 4
79  * requests will not lead the change of cq_cached_tail, so as sq_dropped.
80  *
81  * And before this patch. The order of this four requests will be req1->req2->
82  * req4->req3. Actually, it should be req1->req2->req3->req4.
83  *
84  * Then, if there is 2 nop req. All timeout requests expect req4 will completed
85  * successful after the patch. And req1/req2 will completed successful with
86  * req3/req4 return -ETIME without this patch!
87  */
test_timeout_overflow(void)88 static int test_timeout_overflow(void)
89 {
90 	struct io_uring_sqe *sqe;
91 	struct io_uring_cqe *cqe;
92 	struct __kernel_timespec ts;
93 	struct io_uring ring;
94 	int i, ret;
95 
96 	ret = io_uring_queue_init(16, &ring, 0);
97 	if (ret) {
98 		fprintf(stderr, "ring setup failed: %d\n", ret);
99 		return 1;
100 	}
101 
102 	msec_to_ts(&ts, TIMEOUT_MSEC);
103 	for (i = 0; i < 4; i++) {
104 		unsigned num = 0;
105 		sqe = io_uring_get_sqe(&ring);
106 		switch (i) {
107 		case 0:
108 		case 1:
109 			num = 1;
110 			break;
111 		case 2:
112 			num = 2;
113 			break;
114 		case 3:
115 			num = UINT_MAX;
116 			break;
117 		}
118 		io_uring_prep_timeout(sqe, &ts, num, 0);
119 	}
120 
121 	for (i = 0; i < 2; i++) {
122 		sqe = io_uring_get_sqe(&ring);
123 		io_uring_prep_nop(sqe);
124 		io_uring_sqe_set_data(sqe, (void *) 1);
125 	}
126 	ret = io_uring_submit(&ring);
127 	if (ret < 0) {
128 		fprintf(stderr, "sqe submit failed: %d\n", ret);
129 		goto err;
130 	}
131 
132 	i = 0;
133 	while (i < 6) {
134 		ret = io_uring_wait_cqe(&ring, &cqe);
135 		if (ret < 0) {
136 			fprintf(stderr, "wait completion %d\n", ret);
137 			goto err;
138 		}
139 
140 		/*
141 		 * cqe1: first nop req
142 		 * cqe2: first timeout req, because of cqe1
143 		 * cqe3: second timeout req because of cqe1 + cqe2
144 		 * cqe4: second nop req
145 		 * cqe5~cqe6: the left three timeout req
146 		 */
147 		switch (i) {
148 		case 0:
149 		case 3:
150 			if (io_uring_cqe_get_data(cqe) != (void *) 1) {
151 				fprintf(stderr, "nop not seen as 1 or 2\n");
152 				goto err;
153 			}
154 			break;
155 		case 1:
156 		case 2:
157 		case 4:
158 			if (cqe->res == -ETIME) {
159 				fprintf(stderr, "expected not return -ETIME "
160 					"for the #%d timeout req\n", i - 1);
161 				goto err;
162 			}
163 			break;
164 		case 5:
165 			if (cqe->res != -ETIME) {
166 				fprintf(stderr, "expected return -ETIME for "
167 					"the #%d timeout req\n", i - 1);
168 				goto err;
169 			}
170 			break;
171 		}
172 		io_uring_cqe_seen(&ring, cqe);
173 		i++;
174 	}
175 
176 	return 0;
177 err:
178 	return 1;
179 }
180 
main(int argc,char * argv[])181 int main(int argc, char *argv[])
182 {
183 	int ret;
184 
185 	if (argc > 1)
186 		return 0;
187 
188 	ret = check_timeout_support();
189 	if (ret) {
190 		fprintf(stderr, "check_timeout_support failed: %d\n", ret);
191 		return 1;
192 	}
193 
194 	if (not_supported)
195 		return 0;
196 
197 	ret = test_timeout_overflow();
198 	if (ret) {
199 		fprintf(stderr, "test_timeout_overflow failed\n");
200 		return 1;
201 	}
202 
203 	return 0;
204 }
205