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