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, ¶ms);
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