• 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 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, &params);
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, &params);
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, &params);
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