• 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 
reap_polls(struct io_uring * ring)78 static int reap_polls(struct io_uring *ring)
79 {
80 	struct io_uring_cqe *cqe;
81 	int i, ret, off;
82 	char c;
83 
84 	for (i = 0; i < BATCH; i++) {
85 		struct io_uring_sqe *sqe;
86 
87 		sqe = io_uring_get_sqe(ring);
88 		/* update event */
89 		io_uring_prep_poll_update(sqe, i, 0, POLLIN,
90 						IORING_POLL_UPDATE_EVENTS);
91 		sqe->user_data = 0x12345678;
92 	}
93 
94 	ret = io_uring_submit(ring);
95 	if (ret != BATCH) {
96 		fprintf(stderr, "submitted %d, %d\n", ret, BATCH);
97 		return 1;
98 	}
99 
100 	for (i = 0; i < 2 * BATCH; i++) {
101 		ret = io_uring_wait_cqe(ring, &cqe);
102 		if (ret) {
103 			fprintf(stderr, "wait cqe %d\n", ret);
104 			return ret;
105 		}
106 		off = cqe->user_data;
107 		if (off == 0x12345678)
108 			goto seen;
109 		ret = read(p[off].fd[0], &c, 1);
110 		if (ret != 1) {
111 			if (ret == -1 && errno == EAGAIN)
112 				goto seen;
113 			fprintf(stderr, "read got %d/%d\n", ret, errno);
114 			break;
115 		}
116 seen:
117 		io_uring_cqe_seen(ring, cqe);
118 	}
119 
120 	if (i != 2 * BATCH) {
121 		fprintf(stderr, "gave up at %d\n", i);
122 		return 1;
123 	}
124 
125 	return 0;
126 }
127 
trigger_polls(void)128 static int trigger_polls(void)
129 {
130 	char c = 89;
131 	int i, ret;
132 
133 	for (i = 0; i < BATCH; i++) {
134 		int off;
135 
136 		do {
137 			off = rand() % NFILES;
138 			if (!p[off].triggered)
139 				break;
140 		} while (1);
141 
142 		p[off].triggered = 1;
143 		ret = write(p[off].fd[1], &c, 1);
144 		if (ret != 1) {
145 			fprintf(stderr, "write got %d/%d\n", ret, errno);
146 			return 1;
147 		}
148 	}
149 
150 	return 0;
151 }
152 
trigger_polls_fn(void * data)153 static void *trigger_polls_fn(void *data)
154 {
155 	trigger_polls();
156 	return NULL;
157 }
158 
arm_polls(struct io_uring * ring)159 static int arm_polls(struct io_uring *ring)
160 {
161 	int ret, to_arm = NFILES, i, off;
162 
163 	off = 0;
164 	while (to_arm) {
165 		int this_arm;
166 
167 		this_arm = to_arm;
168 		if (this_arm > RING_SIZE)
169 			this_arm = RING_SIZE;
170 
171 		for (i = 0; i < this_arm; i++) {
172 			if (arm_poll(ring, off)) {
173 				fprintf(stderr, "arm failed at %d\n", off);
174 				return 1;
175 			}
176 			off++;
177 		}
178 
179 		ret = io_uring_submit(ring);
180 		if (ret != this_arm) {
181 			fprintf(stderr, "submitted %d, %d\n", ret, this_arm);
182 			return 1;
183 		}
184 		to_arm -= this_arm;
185 	}
186 
187 	return 0;
188 }
189 
main(int argc,char * argv[])190 int main(int argc, char *argv[])
191 {
192 	struct io_uring ring;
193 	struct io_uring_params params = { };
194 	struct rlimit rlim;
195 	pthread_t thread;
196 	int i, j, ret;
197 
198 	if (argc > 1)
199 		return 0;
200 
201 	ret = has_poll_update();
202 	if (ret < 0) {
203 		fprintf(stderr, "poll update check failed %i\n", ret);
204 		return -1;
205 	} else if (!ret) {
206 		fprintf(stderr, "no poll update, skip\n");
207 		return 0;
208 	}
209 
210 	if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
211 		perror("getrlimit");
212 		goto err_noring;
213 	}
214 
215 	if (rlim.rlim_cur < (2 * NFILES + 5)) {
216 		rlim.rlim_cur = (2 * NFILES + 5);
217 		rlim.rlim_max = rlim.rlim_cur;
218 		if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
219 			if (errno == EPERM)
220 				goto err_nofail;
221 			perror("setrlimit");
222 			goto err_noring;
223 		}
224 	}
225 
226 	for (i = 0; i < NFILES; i++) {
227 		if (pipe(p[i].fd) < 0) {
228 			perror("pipe");
229 			goto err_noring;
230 		}
231 		fcntl(p[i].fd[0], F_SETFL, O_NONBLOCK);
232 	}
233 
234 	params.flags = IORING_SETUP_CQSIZE;
235 	params.cq_entries = 4096;
236 	ret = io_uring_queue_init_params(RING_SIZE, &ring, &params);
237 	if (ret) {
238 		if (ret == -EINVAL) {
239 			fprintf(stdout, "No CQSIZE, trying without\n");
240 			ret = io_uring_queue_init(RING_SIZE, &ring, 0);
241 			if (ret) {
242 				fprintf(stderr, "ring setup failed: %d\n", ret);
243 				return 1;
244 			}
245 		}
246 	}
247 
248 	if (arm_polls(&ring))
249 		goto err;
250 
251 	for (i = 0; i < NLOOPS; i++) {
252 		pthread_create(&thread, NULL, trigger_polls_fn, NULL);
253 		ret = reap_polls(&ring);
254 		if (ret)
255 			goto err;
256 		pthread_join(thread, NULL);
257 
258 		for (j = 0; j < NFILES; j++)
259 			p[j].triggered = 0;
260 	}
261 
262 	io_uring_queue_exit(&ring);
263 	return 0;
264 err:
265 	io_uring_queue_exit(&ring);
266 err_noring:
267 	fprintf(stderr, "poll-many failed\n");
268 	return 1;
269 err_nofail:
270 	fprintf(stderr, "poll-many: not enough files available (and not root), "
271 			"skipped\n");
272 	return 0;
273 }
274