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 struct __kernel_timespec ts;
57
58 sqe = io_uring_get_sqe(ring);
59 if (!sqe) {
60 fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
61 return 1;
62 }
63
64 io_uring_prep_nop(sqe);
65
66 ret = io_uring_submit(ring);
67 if (ret <= 0) {
68 fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
69 return 1;
70 }
71
72 msec_to_ts(&ts, TIMEOUT_MSEC);
73 ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
74 if (ret < 0) {
75 fprintf(stderr, "%s: timeout error: %d\n", __FUNCTION__, ret);
76 return 1;
77 }
78
79 io_uring_cqe_seen(ring, cqe);
80 return 0;
81 }
82
test_return_after_timeout(struct io_uring * ring)83 static int test_return_after_timeout(struct io_uring *ring)
84 {
85 struct io_uring_cqe *cqe;
86 int ret;
87 struct __kernel_timespec ts;
88 struct timeval tv;
89 unsigned long long exp;
90
91 msec_to_ts(&ts, TIMEOUT_MSEC);
92 gettimeofday(&tv, NULL);
93 ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
94 exp = mtime_since_now(&tv);
95 if (ret != -ETIME) {
96 fprintf(stderr, "%s: timeout error: %d\n", __FUNCTION__, ret);
97 return 1;
98 }
99
100 if (exp < TIMEOUT_MSEC / 2 || exp > (TIMEOUT_MSEC * 3) / 2) {
101 fprintf(stderr, "%s: Timeout seems wonky (got %llu)\n", __FUNCTION__, exp);
102 return 1;
103 }
104
105 return 0;
106 }
107
__reap_thread_fn(void * data)108 int __reap_thread_fn(void *data) {
109 struct io_uring *ring = (struct io_uring *)data;
110 struct io_uring_cqe *cqe;
111 struct __kernel_timespec ts;
112
113 msec_to_ts(&ts, TIMEOUT_SEC);
114 pthread_mutex_lock(&mutex);
115 cnt++;
116 pthread_mutex_unlock(&mutex);
117 return io_uring_wait_cqe_timeout(ring, &cqe, &ts);
118 }
119
reap_thread_fn0(void * data)120 void *reap_thread_fn0(void *data) {
121 thread_ret0 = __reap_thread_fn(data);
122 return NULL;
123 }
124
reap_thread_fn1(void * data)125 void *reap_thread_fn1(void *data) {
126 thread_ret1 = __reap_thread_fn(data);
127 return NULL;
128 }
129
130 /*
131 * This is to test issuing a sqe in main thread and reaping it in two child-thread
132 * at the same time. To see if timeout feature works or not.
133 */
test_multi_threads_timeout()134 int test_multi_threads_timeout() {
135 struct io_uring ring;
136 int ret;
137 bool both_wait = false;
138 pthread_t reap_thread0, reap_thread1;
139 struct io_uring_sqe *sqe;
140
141 ret = io_uring_queue_init(8, &ring, 0);
142 if (ret) {
143 fprintf(stderr, "%s: ring setup failed: %d\n", __FUNCTION__, ret);
144 return 1;
145 }
146
147 pthread_create(&reap_thread0, NULL, reap_thread_fn0, &ring);
148 pthread_create(&reap_thread1, NULL, reap_thread_fn1, &ring);
149
150 /*
151 * make two threads both enter io_uring_wait_cqe_timeout() before issuing the sqe
152 * as possible as we can. So that there are two threads in the ctx->wait queue.
153 * In this way, we can test if a cqe wakes up two threads at the same time.
154 */
155 while(!both_wait) {
156 pthread_mutex_lock(&mutex);
157 if (cnt == 2)
158 both_wait = true;
159 pthread_mutex_unlock(&mutex);
160 sleep(1);
161 }
162
163 sqe = io_uring_get_sqe(&ring);
164 if (!sqe) {
165 fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
166 goto err;
167 }
168
169 io_uring_prep_nop(sqe);
170
171 ret = io_uring_submit(&ring);
172 if (ret <= 0) {
173 fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
174 goto err;
175 }
176
177 pthread_join(reap_thread0, NULL);
178 pthread_join(reap_thread1, NULL);
179
180 if ((thread_ret0 && thread_ret0 != -ETIME) || (thread_ret1 && thread_ret1 != -ETIME)) {
181 fprintf(stderr, "%s: thread wait cqe timeout failed: %d %d\n",
182 __FUNCTION__, thread_ret0, thread_ret1);
183 goto err;
184 }
185
186 return 0;
187 err:
188 return 1;
189 }
190
main(int argc,char * argv[])191 int main(int argc, char *argv[])
192 {
193 struct io_uring ring_normal, ring_sq;
194 int ret;
195
196 if (argc > 1)
197 return 0;
198
199 ret = io_uring_queue_init(8, &ring_normal, 0);
200 if (ret) {
201 fprintf(stderr, "ring_normal setup failed: %d\n", ret);
202 return 1;
203 }
204 if (!(ring_normal.features & IORING_FEAT_EXT_ARG)) {
205 fprintf(stderr, "feature IORING_FEAT_EXT_ARG not supported.\n");
206 return 0;
207 }
208
209 ret = test_return_before_timeout(&ring_normal);
210 if (ret) {
211 fprintf(stderr, "ring_normal: test_return_before_timeout failed\n");
212 return ret;
213 }
214
215 ret = test_return_after_timeout(&ring_normal);
216 if (ret) {
217 fprintf(stderr, "ring_normal: test_return_after_timeout failed\n");
218 return ret;
219 }
220
221 ret = io_uring_queue_init(8, &ring_sq, IORING_SETUP_SQPOLL);
222 if (ret) {
223 fprintf(stderr, "ring_sq setup failed: %d\n", ret);
224 return 1;
225 }
226
227 ret = test_return_before_timeout(&ring_sq);
228 if (ret) {
229 fprintf(stderr, "ring_sq: test_return_before_timeout failed\n");
230 return ret;
231 }
232
233 ret = test_return_after_timeout(&ring_sq);
234 if (ret) {
235 fprintf(stderr, "ring_sq: test_return_after_timeout failed\n");
236 return ret;
237 }
238
239 ret = test_multi_threads_timeout();
240 if (ret) {
241 fprintf(stderr, "test_multi_threads_timeout failed\n");
242 return ret;
243 }
244
245 return 0;
246 }
247