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