1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: test many files being polled for
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
16 #include "liburing.h"
17 #include "helpers.h"
18
19 #define NFILES 5000
20 #define BATCH 500
21 #define NLOOPS 1000
22
23 #define RING_SIZE 512
24
25 static int nfiles = NFILES;
26
27 struct p {
28 int fd[2];
29 int triggered;
30 };
31
32 static struct p p[NFILES];
33
arm_poll(struct io_uring * ring,int off)34 static int arm_poll(struct io_uring *ring, int off)
35 {
36 struct io_uring_sqe *sqe;
37
38 sqe = io_uring_get_sqe(ring);
39 if (!sqe) {
40 fprintf(stderr, "failed getting sqe\n");
41 return 1;
42 }
43
44 io_uring_prep_poll_add(sqe, p[off].fd[0], POLLIN);
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 ret = io_uring_wait_cqe(ring, &cqe);
57 if (ret) {
58 fprintf(stderr, "wait cqe %d\n", ret);
59 return ret;
60 }
61 off = cqe->user_data;
62 p[off].triggered = 0;
63 ret = read(p[off].fd[0], &c, 1);
64 if (ret != 1) {
65 fprintf(stderr, "read got %d/%d\n", ret, errno);
66 break;
67 }
68 if (arm_poll(ring, off))
69 break;
70 io_uring_cqe_seen(ring, cqe);
71 }
72
73 if (i != BATCH) {
74 fprintf(stderr, "gave up at %d\n", i);
75 return 1;
76 }
77
78 ret = io_uring_submit(ring);
79 if (ret != BATCH) {
80 fprintf(stderr, "submitted %d, %d\n", ret, BATCH);
81 return 1;
82 }
83
84 return 0;
85 }
86
trigger_polls(void)87 static int trigger_polls(void)
88 {
89 char c = 89;
90 int i, ret;
91
92 for (i = 0; i < BATCH; i++) {
93 int off;
94
95 do {
96 off = rand() % nfiles;
97 if (!p[off].triggered)
98 break;
99 } while (1);
100
101 p[off].triggered = 1;
102 ret = write(p[off].fd[1], &c, 1);
103 if (ret != 1) {
104 fprintf(stderr, "write got %d/%d\n", ret, errno);
105 return 1;
106 }
107 }
108
109 return 0;
110 }
111
arm_polls(struct io_uring * ring)112 static int arm_polls(struct io_uring *ring)
113 {
114 int ret, to_arm = nfiles, i, off;
115
116 off = 0;
117 while (to_arm) {
118 int this_arm;
119
120 this_arm = to_arm;
121 if (this_arm > RING_SIZE)
122 this_arm = RING_SIZE;
123
124 for (i = 0; i < this_arm; i++) {
125 if (arm_poll(ring, off)) {
126 fprintf(stderr, "arm failed at %d\n", off);
127 return 1;
128 }
129 off++;
130 }
131
132 ret = io_uring_submit(ring);
133 if (ret != this_arm) {
134 fprintf(stderr, "submitted %d, %d\n", ret, this_arm);
135 return 1;
136 }
137 to_arm -= this_arm;
138 }
139
140 return 0;
141 }
142
do_test(struct io_uring * ring)143 static int do_test(struct io_uring *ring)
144 {
145 int i;
146
147 if (arm_polls(ring))
148 return 1;
149
150 for (i = 0; i < NLOOPS; i++) {
151 trigger_polls();
152 if (reap_polls(ring))
153 return 1;
154 }
155 return 0;
156 }
157
main(int argc,char * argv[])158 int main(int argc, char *argv[])
159 {
160 struct io_uring ring;
161 struct io_uring_params params = { };
162 struct rlimit rlim;
163 int i, ret;
164
165 if (argc > 1)
166 return T_EXIT_SKIP;
167
168 if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
169 perror("getrlimit");
170 return T_EXIT_FAIL;
171 }
172
173 if (rlim.rlim_cur < (2 * NFILES + 5)) {
174 rlim.rlim_cur = rlim.rlim_max;
175 nfiles = (rlim.rlim_cur / 2) - 5;
176 if (nfiles > NFILES)
177 nfiles = NFILES;
178 if (nfiles <= 0)
179 goto err_nofail;
180 if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
181 if (errno == EPERM)
182 goto err_nofail;
183 perror("setrlimit");
184 return T_EXIT_FAIL;
185 }
186 }
187
188 for (i = 0; i < nfiles; i++) {
189 if (pipe(p[i].fd) < 0) {
190 perror("pipe");
191 return T_EXIT_FAIL;
192 }
193 }
194
195 params.flags = IORING_SETUP_CQSIZE;
196 params.cq_entries = 4096;
197 ret = io_uring_queue_init_params(RING_SIZE, &ring, ¶ms);
198 if (ret) {
199 if (ret == -EINVAL) {
200 fprintf(stdout, "No CQSIZE, trying without\n");
201
202 params.flags &= ~IORING_SETUP_CQSIZE;
203 params.cq_entries = 0;
204 ret = io_uring_queue_init_params(RING_SIZE, &ring, ¶ms);
205 if (ret) {
206 fprintf(stderr, "ring setup failed: %d\n", ret);
207 return T_EXIT_FAIL;
208 }
209 }
210 }
211
212 if (do_test(&ring)) {
213 fprintf(stderr, "test (normal) failed\n");
214 return T_EXIT_FAIL;
215 }
216 io_uring_queue_exit(&ring);
217
218 if (t_probe_defer_taskrun()) {
219 params.flags |= IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN;
220 ret = io_uring_queue_init_params(RING_SIZE, &ring, ¶ms);
221 if (ret) {
222 fprintf(stderr, "ring DEFER setup failed: %d\n", ret);
223 return T_EXIT_FAIL;
224 }
225 if (do_test(&ring)) {
226 fprintf(stderr, "test (DEFER) failed\n");
227 return T_EXIT_FAIL;
228 }
229 io_uring_queue_exit(&ring);
230 }
231 return 0;
232 err_nofail:
233 fprintf(stderr, "poll-many: not enough files available (and not root), "
234 "skipped\n");
235 return T_EXIT_SKIP;
236 }
237