• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params);
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, &params);
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, &params);
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