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 <sys/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 static int no_update;
32
arm_poll(struct io_uring * ring,int off)33 static int arm_poll(struct io_uring *ring, int off)
34 {
35 struct io_uring_sqe *sqe;
36
37 sqe = io_uring_get_sqe(ring);
38 if (!sqe) {
39 fprintf(stderr, "failed getting sqe\n");
40 return 1;
41 }
42
43 io_uring_prep_poll_add(sqe, p[off].fd[0], POLLIN);
44 sqe->len = 1;
45 sqe->user_data = off;
46 return 0;
47 }
48
reap_polls(struct io_uring * ring)49 static int reap_polls(struct io_uring *ring)
50 {
51 struct io_uring_cqe *cqe;
52 int i, ret, off;
53 char c;
54
55 for (i = 0; i < BATCH; i++) {
56 struct io_uring_sqe *sqe;
57
58 sqe = io_uring_get_sqe(ring);
59 /* update event */
60 io_uring_prep_poll_update(sqe, (void *)(unsigned long)i, NULL,
61 POLLIN, 2);
62 sqe->user_data = 0x12345678;
63 }
64
65 ret = io_uring_submit(ring);
66 if (ret != BATCH) {
67 fprintf(stderr, "submitted %d, %d\n", ret, BATCH);
68 return 1;
69 }
70
71 for (i = 0; i < 2 * BATCH; i++) {
72 ret = io_uring_wait_cqe(ring, &cqe);
73 if (ret) {
74 fprintf(stderr, "wait cqe %d\n", ret);
75 return ret;
76 }
77 off = cqe->user_data;
78 if (off == 0x12345678)
79 goto seen;
80 p[off].triggered = 0;
81 ret = read(p[off].fd[0], &c, 1);
82 if (ret != 1) {
83 if (ret == -1 && errno == EAGAIN)
84 goto seen;
85 fprintf(stderr, "read got %d/%d\n", ret, errno);
86 break;
87 }
88 seen:
89 io_uring_cqe_seen(ring, cqe);
90 }
91
92 if (i != 2 * BATCH) {
93 fprintf(stderr, "gave up at %d\n", i);
94 return 1;
95 }
96
97 return 0;
98 }
99
trigger_polls(void)100 static int trigger_polls(void)
101 {
102 char c = 89;
103 int i, ret;
104
105 for (i = 0; i < BATCH; i++) {
106 int off;
107
108 do {
109 off = rand() % NFILES;
110 if (!p[off].triggered)
111 break;
112 } while (1);
113
114 p[off].triggered = 1;
115 ret = write(p[off].fd[1], &c, 1);
116 if (ret != 1) {
117 fprintf(stderr, "write got %d/%d\n", ret, errno);
118 return 1;
119 }
120 }
121
122 return 0;
123 }
124
trigger_polls_fn(void * data)125 static void *trigger_polls_fn(void *data)
126 {
127 trigger_polls();
128 return NULL;
129 }
130
check_no_update(struct io_uring * ring)131 static int check_no_update(struct io_uring *ring)
132 {
133 struct io_uring_cqe *cqe;
134 int ret;
135
136 ret = io_uring_wait_cqe(ring, &cqe);
137 if (ret)
138 return 0;
139 ret = cqe->res;
140 io_uring_cqe_seen(ring, cqe);
141 return ret == -EINVAL;
142 }
143
arm_polls(struct io_uring * ring)144 static int arm_polls(struct io_uring *ring)
145 {
146 int ret, to_arm = NFILES, i, off;
147
148 off = 0;
149 while (to_arm) {
150 int this_arm;
151
152 this_arm = to_arm;
153 if (this_arm > RING_SIZE)
154 this_arm = RING_SIZE;
155
156 for (i = 0; i < this_arm; i++) {
157 if (arm_poll(ring, off)) {
158 fprintf(stderr, "arm failed at %d\n", off);
159 return 1;
160 }
161 off++;
162 }
163
164 ret = io_uring_submit(ring);
165 if (ret != this_arm) {
166 if (ret > 0 && check_no_update(ring)) {
167 no_update = 1;
168 return 0;
169 }
170 fprintf(stderr, "submitted %d, %d\n", ret, this_arm);
171 return 1;
172 }
173 to_arm -= this_arm;
174 }
175
176 return 0;
177 }
178
main(int argc,char * argv[])179 int main(int argc, char *argv[])
180 {
181 struct io_uring ring;
182 struct io_uring_params params = { };
183 struct rlimit rlim;
184 pthread_t thread;
185 int i, ret;
186
187 if (argc > 1)
188 return 0;
189
190 if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
191 perror("getrlimit");
192 goto err_noring;
193 }
194
195 if (rlim.rlim_cur < (2 * NFILES + 5)) {
196 rlim.rlim_cur = (2 * NFILES + 5);
197 rlim.rlim_max = rlim.rlim_cur;
198 if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
199 if (errno == EPERM)
200 goto err_nofail;
201 perror("setrlimit");
202 goto err_noring;
203 }
204 }
205
206 for (i = 0; i < NFILES; i++) {
207 if (pipe(p[i].fd) < 0) {
208 perror("pipe");
209 goto err_noring;
210 }
211 fcntl(p[i].fd[0], F_SETFL, O_NONBLOCK);
212 }
213
214 params.flags = IORING_SETUP_CQSIZE;
215 params.cq_entries = 4096;
216 ret = io_uring_queue_init_params(RING_SIZE, &ring, ¶ms);
217 if (ret) {
218 if (ret == -EINVAL) {
219 fprintf(stdout, "No CQSIZE, trying without\n");
220 ret = io_uring_queue_init(RING_SIZE, &ring, 0);
221 if (ret) {
222 fprintf(stderr, "ring setup failed: %d\n", ret);
223 return 1;
224 }
225 }
226 }
227
228 if (arm_polls(&ring))
229 goto err;
230 if (no_update) {
231 printf("No poll update support, skipping\n");
232 goto done;
233 }
234
235 for (i = 0; i < NLOOPS; i++) {
236 pthread_create(&thread, NULL, trigger_polls_fn, NULL);
237 ret = reap_polls(&ring);
238 if (ret)
239 goto err;
240 pthread_join(thread, NULL);
241 }
242
243 done:
244 io_uring_queue_exit(&ring);
245 return 0;
246 err:
247 io_uring_queue_exit(&ring);
248 err_noring:
249 fprintf(stderr, "poll-many failed\n");
250 return 1;
251 err_nofail:
252 fprintf(stderr, "poll-many: not enough files available (and not root), "
253 "skipped\n");
254 return 0;
255 }
256