xref: /aosp_15_r20/external/liburing/test/timeout-new.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Description: tests for getevents timeout
4  *
5  */
6 #include <stdio.h>
7 #include <sys/time.h>
8 #include <unistd.h>
9 #include <pthread.h>
10 #include "liburing.h"
11 
12 #define TIMEOUT_MSEC	200
13 #define TIMEOUT_SEC	10
14 
15 int thread_ret0, thread_ret1;
16 int cnt = 0;
17 pthread_mutex_t mutex;
18 
msec_to_ts(struct __kernel_timespec * ts,unsigned int msec)19 static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
20 {
21 	ts->tv_sec = msec / 1000;
22 	ts->tv_nsec = (msec % 1000) * 1000000;
23 }
24 
mtime_since(const struct timeval * s,const struct timeval * e)25 static unsigned long long mtime_since(const struct timeval *s,
26 				      const struct timeval *e)
27 {
28 	long long sec, usec;
29 
30 	sec = e->tv_sec - s->tv_sec;
31 	usec = (e->tv_usec - s->tv_usec);
32 	if (sec > 0 && usec < 0) {
33 		sec--;
34 		usec += 1000000;
35 	}
36 
37 	sec *= 1000;
38 	usec /= 1000;
39 	return sec + usec;
40 }
41 
mtime_since_now(struct timeval * tv)42 static unsigned long long mtime_since_now(struct timeval *tv)
43 {
44 	struct timeval end;
45 
46 	gettimeofday(&end, NULL);
47 	return mtime_since(tv, &end);
48 }
49 
50 
test_return_before_timeout(struct io_uring * ring)51 static int test_return_before_timeout(struct io_uring *ring)
52 {
53 	struct io_uring_cqe *cqe;
54 	struct io_uring_sqe *sqe;
55 	int ret;
56 	bool retried = false;
57 	struct __kernel_timespec ts;
58 
59 	msec_to_ts(&ts, TIMEOUT_MSEC);
60 
61 	sqe = io_uring_get_sqe(ring);
62 	io_uring_prep_nop(sqe);
63 
64 	ret = io_uring_submit(ring);
65 	if (ret <= 0) {
66 		fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
67 		return 1;
68 	}
69 
70 again:
71 	ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
72 	if (ret == -ETIME && (ring->flags & IORING_SETUP_SQPOLL) && !retried) {
73 		/*
74 		 * there is a small chance SQPOLL hasn't been waked up yet,
75 		 * give it one more try.
76 		 */
77 		printf("warning: funky SQPOLL timing\n");
78 		sleep(1);
79 		retried = true;
80 		goto again;
81 	} else if (ret < 0) {
82 		fprintf(stderr, "%s: timeout error: %d\n", __FUNCTION__, ret);
83 		return 1;
84 	}
85 	io_uring_cqe_seen(ring, cqe);
86 	return 0;
87 }
88 
test_return_after_timeout(struct io_uring * ring)89 static int test_return_after_timeout(struct io_uring *ring)
90 {
91 	struct io_uring_cqe *cqe;
92 	int ret;
93 	struct __kernel_timespec ts;
94 	struct timeval tv;
95 	unsigned long long exp;
96 
97 	msec_to_ts(&ts, TIMEOUT_MSEC);
98 	gettimeofday(&tv, NULL);
99 	ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
100 	exp = mtime_since_now(&tv);
101 	if (ret != -ETIME) {
102 		fprintf(stderr, "%s: timeout error: %d\n", __FUNCTION__, ret);
103 		return 1;
104 	}
105 
106 	if (exp < TIMEOUT_MSEC / 2 || exp > (TIMEOUT_MSEC  * 3) / 2) {
107 		fprintf(stderr, "%s: Timeout seems wonky (got %llu)\n", __FUNCTION__, exp);
108 		return 1;
109 	}
110 
111 	return 0;
112 }
113 
__reap_thread_fn(void * data)114 int __reap_thread_fn(void *data) {
115 	struct io_uring *ring = (struct io_uring *)data;
116 	struct io_uring_cqe *cqe;
117 	struct __kernel_timespec ts;
118 
119 	msec_to_ts(&ts, TIMEOUT_SEC);
120 	pthread_mutex_lock(&mutex);
121 	cnt++;
122 	pthread_mutex_unlock(&mutex);
123 	return io_uring_wait_cqe_timeout(ring, &cqe, &ts);
124 }
125 
reap_thread_fn0(void * data)126 void *reap_thread_fn0(void *data) {
127 	thread_ret0 = __reap_thread_fn(data);
128 	return NULL;
129 }
130 
reap_thread_fn1(void * data)131 void *reap_thread_fn1(void *data) {
132 	thread_ret1 = __reap_thread_fn(data);
133 	return NULL;
134 }
135 
136 /*
137  * This is to test issuing a sqe in main thread and reaping it in two child-thread
138  * at the same time. To see if timeout feature works or not.
139  */
test_multi_threads_timeout()140 int test_multi_threads_timeout() {
141 	struct io_uring ring;
142 	int ret;
143 	bool both_wait = false;
144 	pthread_t reap_thread0, reap_thread1;
145 	struct io_uring_sqe *sqe;
146 
147 	ret = io_uring_queue_init(8, &ring, 0);
148 	if (ret) {
149 		fprintf(stderr, "%s: ring setup failed: %d\n", __FUNCTION__, ret);
150 		return 1;
151 	}
152 
153 	pthread_create(&reap_thread0, NULL, reap_thread_fn0, &ring);
154 	pthread_create(&reap_thread1, NULL, reap_thread_fn1, &ring);
155 
156 	/*
157 	 * make two threads both enter io_uring_wait_cqe_timeout() before issuing the sqe
158 	 * as possible as we can. So that there are two threads in the ctx->wait queue.
159 	 * In this way, we can test if a cqe wakes up two threads at the same time.
160 	 */
161 	while(!both_wait) {
162 		pthread_mutex_lock(&mutex);
163 		if (cnt == 2)
164 			both_wait = true;
165 		pthread_mutex_unlock(&mutex);
166 		sleep(1);
167 	}
168 
169 	sqe = io_uring_get_sqe(&ring);
170 	if (!sqe) {
171 		fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
172 		goto err;
173 	}
174 
175 	io_uring_prep_nop(sqe);
176 
177 	ret = io_uring_submit(&ring);
178 	if (ret <= 0) {
179 		fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
180 		goto err;
181 	}
182 
183 	pthread_join(reap_thread0, NULL);
184 	pthread_join(reap_thread1, NULL);
185 
186 	if ((thread_ret0 && thread_ret0 != -ETIME) || (thread_ret1 && thread_ret1 != -ETIME)) {
187 		fprintf(stderr, "%s: thread wait cqe timeout failed: %d %d\n",
188 				__FUNCTION__, thread_ret0, thread_ret1);
189 		goto err;
190 	}
191 
192 	return 0;
193 err:
194 	return 1;
195 }
196 
main(int argc,char * argv[])197 int main(int argc, char *argv[])
198 {
199 	struct io_uring ring_normal, ring_sq;
200 	int ret;
201 
202 	if (argc > 1)
203 		return 0;
204 
205 	ret = io_uring_queue_init(8, &ring_normal, 0);
206 	if (ret) {
207 		fprintf(stderr, "ring_normal setup failed: %d\n", ret);
208 		return 1;
209 	}
210 	if (!(ring_normal.features & IORING_FEAT_EXT_ARG)) {
211 		fprintf(stderr, "feature IORING_FEAT_EXT_ARG not supported, skipping.\n");
212 		return 0;
213 	}
214 
215 	ret = test_return_before_timeout(&ring_normal);
216 	if (ret) {
217 		fprintf(stderr, "ring_normal: test_return_before_timeout failed\n");
218 		return ret;
219 	}
220 
221 	ret = test_return_after_timeout(&ring_normal);
222 	if (ret) {
223 		fprintf(stderr, "ring_normal: test_return_after_timeout failed\n");
224 		return ret;
225 	}
226 
227 	ret = io_uring_queue_init(8, &ring_sq, IORING_SETUP_SQPOLL);
228 	if (ret) {
229 		fprintf(stderr, "ring_sq setup failed: %d\n", ret);
230 		return 1;
231 	}
232 
233 	ret = test_return_before_timeout(&ring_sq);
234 	if (ret) {
235 		fprintf(stderr, "ring_sq: test_return_before_timeout failed\n");
236 		return ret;
237 	}
238 
239 	ret = test_return_after_timeout(&ring_sq);
240 	if (ret) {
241 		fprintf(stderr, "ring_sq: test_return_after_timeout failed\n");
242 		return ret;
243 	}
244 
245 	ret = test_multi_threads_timeout();
246 	if (ret) {
247 		fprintf(stderr, "test_multi_threads_timeout failed\n");
248 		return ret;
249 	}
250 
251 	return 0;
252 }
253