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