• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Description: test many files being polled for and updated
4  *
5  */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <signal.h>
12 #include <poll.h>
13 #include <sys/resource.h>
14 #include <fcntl.h>
15 #include <pthread.h>
16 
17 #include "liburing.h"
18 
19 #define	NFILES	5000
20 #define BATCH	500
21 #define NLOOPS	1000
22 
23 #define RING_SIZE	512
24 
25 struct p {
26 	int fd[2];
27 	int triggered;
28 };
29 
30 static struct p p[NFILES];
31 
has_poll_update(void)32 static int has_poll_update(void)
33 {
34 	struct io_uring ring;
35 	struct io_uring_cqe *cqe;
36 	struct io_uring_sqe *sqe;
37 	bool has_update = false;
38 	int ret;
39 
40 	ret = io_uring_queue_init(8, &ring, 0);
41 	if (ret)
42 		return -1;
43 
44 	sqe = io_uring_get_sqe(&ring);
45 	io_uring_prep_poll_update(sqe, 0, 0, POLLIN, IORING_TIMEOUT_UPDATE);
46 
47 	ret = io_uring_submit(&ring);
48 	if (ret != 1)
49 		return -1;
50 
51 	ret = io_uring_wait_cqe(&ring, &cqe);
52 	if (!ret) {
53 		if (cqe->res == -ENOENT)
54 			has_update = true;
55 		else if (cqe->res != -EINVAL)
56 			return -1;
57 		io_uring_cqe_seen(&ring, cqe);
58 	}
59 	io_uring_queue_exit(&ring);
60 	return has_update;
61 }
62 
arm_poll(struct io_uring * ring,int off)63 static int arm_poll(struct io_uring *ring, int off)
64 {
65 	struct io_uring_sqe *sqe;
66 
67 	sqe = io_uring_get_sqe(ring);
68 	if (!sqe) {
69 		fprintf(stderr, "failed getting sqe\n");
70 		return 1;
71 	}
72 
73 	io_uring_prep_poll_multishot(sqe, p[off].fd[0], POLLIN);
74 	sqe->user_data = off;
75 	return 0;
76 }
77 
submit_arm_poll(struct io_uring * ring,int off)78 static int submit_arm_poll(struct io_uring *ring, int off)
79 {
80 	int ret;
81 
82 	ret = arm_poll(ring, off);
83 	if (ret)
84 		return ret;
85 
86 	ret = io_uring_submit(ring);
87 	if (ret < 0)
88 		return ret;
89 	return ret == 1 ? 0 : -1;
90 }
91 
reap_polls(struct io_uring * ring)92 static int reap_polls(struct io_uring *ring)
93 {
94 	struct io_uring_cqe *cqe;
95 	int i, ret, off;
96 	char c;
97 
98 	for (i = 0; i < BATCH; i++) {
99 		struct io_uring_sqe *sqe;
100 
101 		sqe = io_uring_get_sqe(ring);
102 		/* update event */
103 		io_uring_prep_poll_update(sqe, i, 0, POLLIN,
104 						IORING_POLL_UPDATE_EVENTS);
105 		sqe->user_data = 0x12345678;
106 	}
107 
108 	ret = io_uring_submit(ring);
109 	if (ret != BATCH) {
110 		fprintf(stderr, "submitted %d, %d\n", ret, BATCH);
111 		return 1;
112 	}
113 
114 	for (i = 0; i < 2 * BATCH; i++) {
115 		ret = io_uring_wait_cqe(ring, &cqe);
116 		if (ret) {
117 			fprintf(stderr, "wait cqe %d\n", ret);
118 			return ret;
119 		}
120 		off = cqe->user_data;
121 		if (off == 0x12345678)
122 			goto seen;
123 		if (!(cqe->flags & IORING_CQE_F_MORE)) {
124 			/* need to re-arm poll */
125 			ret = submit_arm_poll(ring, off);
126 			if (ret)
127 				break;
128 			if (cqe->res <= 0) {
129 				/* retry this one */
130 				i--;
131 				goto seen;
132 			}
133 		}
134 
135 		ret = read(p[off].fd[0], &c, 1);
136 		if (ret != 1) {
137 			if (ret == -1 && errno == EAGAIN)
138 				goto seen;
139 			fprintf(stderr, "read got %d/%d\n", ret, errno);
140 			break;
141 		}
142 seen:
143 		io_uring_cqe_seen(ring, cqe);
144 	}
145 
146 	if (i != 2 * BATCH) {
147 		fprintf(stderr, "gave up at %d\n", i);
148 		return 1;
149 	}
150 
151 	return 0;
152 }
153 
trigger_polls(void)154 static int trigger_polls(void)
155 {
156 	char c = 89;
157 	int i, ret;
158 
159 	for (i = 0; i < BATCH; i++) {
160 		int off;
161 
162 		do {
163 			off = rand() % NFILES;
164 			if (!p[off].triggered)
165 				break;
166 		} while (1);
167 
168 		p[off].triggered = 1;
169 		ret = write(p[off].fd[1], &c, 1);
170 		if (ret != 1) {
171 			fprintf(stderr, "write got %d/%d\n", ret, errno);
172 			return 1;
173 		}
174 	}
175 
176 	return 0;
177 }
178 
trigger_polls_fn(void * data)179 static void *trigger_polls_fn(void *data)
180 {
181 	trigger_polls();
182 	return NULL;
183 }
184 
arm_polls(struct io_uring * ring)185 static int arm_polls(struct io_uring *ring)
186 {
187 	int ret, to_arm = NFILES, i, off;
188 
189 	off = 0;
190 	while (to_arm) {
191 		int this_arm;
192 
193 		this_arm = to_arm;
194 		if (this_arm > RING_SIZE)
195 			this_arm = RING_SIZE;
196 
197 		for (i = 0; i < this_arm; i++) {
198 			if (arm_poll(ring, off)) {
199 				fprintf(stderr, "arm failed at %d\n", off);
200 				return 1;
201 			}
202 			off++;
203 		}
204 
205 		ret = io_uring_submit(ring);
206 		if (ret != this_arm) {
207 			fprintf(stderr, "submitted %d, %d\n", ret, this_arm);
208 			return 1;
209 		}
210 		to_arm -= this_arm;
211 	}
212 
213 	return 0;
214 }
215 
run(int cqe)216 static int run(int cqe)
217 {
218 	struct io_uring ring;
219 	struct io_uring_params params = { };
220 	pthread_t thread;
221 	int i, j, ret;
222 
223 	for (i = 0; i < NFILES; i++) {
224 		if (pipe(p[i].fd) < 0) {
225 			perror("pipe");
226 			return 1;
227 		}
228 		fcntl(p[i].fd[0], F_SETFL, O_NONBLOCK);
229 	}
230 
231 	params.flags = IORING_SETUP_CQSIZE;
232 	params.cq_entries = cqe;
233 	ret = io_uring_queue_init_params(RING_SIZE, &ring, &params);
234 	if (ret) {
235 		if (ret == -EINVAL) {
236 			fprintf(stdout, "No CQSIZE, trying without\n");
237 			ret = io_uring_queue_init(RING_SIZE, &ring, 0);
238 			if (ret) {
239 				fprintf(stderr, "ring setup failed: %d\n", ret);
240 				return 1;
241 			}
242 		}
243 	}
244 
245 	if (arm_polls(&ring))
246 		goto err;
247 
248 	for (i = 0; i < NLOOPS; i++) {
249 		pthread_create(&thread, NULL, trigger_polls_fn, NULL);
250 		ret = reap_polls(&ring);
251 		if (ret)
252 			goto err;
253 		pthread_join(thread, NULL);
254 
255 		for (j = 0; j < NFILES; j++)
256 			p[j].triggered = 0;
257 	}
258 
259 	io_uring_queue_exit(&ring);
260 	for (i = 0; i < NFILES; i++) {
261 		close(p[i].fd[0]);
262 		close(p[i].fd[1]);
263 	}
264 	return 0;
265 err:
266 	io_uring_queue_exit(&ring);
267 	return 1;
268 }
269 
main(int argc,char * argv[])270 int main(int argc, char *argv[])
271 {
272 	struct rlimit rlim;
273 	int ret;
274 
275 	if (argc > 1)
276 		return 0;
277 
278 	ret = has_poll_update();
279 	if (ret < 0) {
280 		fprintf(stderr, "poll update check failed %i\n", ret);
281 		return -1;
282 	} else if (!ret) {
283 		fprintf(stderr, "no poll update, skip\n");
284 		return 0;
285 	}
286 
287 	if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
288 		perror("getrlimit");
289 		goto err;
290 	}
291 
292 	if (rlim.rlim_cur < (2 * NFILES + 5)) {
293 		rlim.rlim_cur = (2 * NFILES + 5);
294 		rlim.rlim_max = rlim.rlim_cur;
295 		if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
296 			if (errno == EPERM)
297 				goto err_nofail;
298 			perror("setrlimit");
299 			goto err;
300 		}
301 	}
302 
303 	ret = run(1024);
304 	if (ret) {
305 		fprintf(stderr, "run(1024) failed\n");
306 		goto err;
307 	}
308 
309 	ret = run(8192);
310 	if (ret) {
311 		fprintf(stderr, "run(8192) failed\n");
312 		goto err;
313 	}
314 
315 	return 0;
316 err:
317 	fprintf(stderr, "poll-many failed\n");
318 	return 1;
319 err_nofail:
320 	fprintf(stderr, "poll-many: not enough files available (and not root), "
321 			"skipped\n");
322 	return 0;
323 }
324