• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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