1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: run various file registration tests
4 *
5 */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <fcntl.h>
12 #include <limits.h>
13 #include <sys/resource.h>
14
15 #include "helpers.h"
16 #include "liburing.h"
17
18 static int no_update = 0;
19
close_files(int * files,int nr_files,int add)20 static void close_files(int *files, int nr_files, int add)
21 {
22 char fname[32];
23 int i;
24
25 for (i = 0; i < nr_files; i++) {
26 if (files)
27 close(files[i]);
28 if (!add)
29 sprintf(fname, ".reg.%d", i);
30 else
31 sprintf(fname, ".add.%d", i + add);
32 unlink(fname);
33 }
34 if (files)
35 free(files);
36 }
37
open_files(int nr_files,int extra,int add)38 static int *open_files(int nr_files, int extra, int add)
39 {
40 char fname[32];
41 int *files;
42 int i;
43
44 files = t_calloc(nr_files + extra, sizeof(int));
45
46 for (i = 0; i < nr_files; i++) {
47 if (!add)
48 sprintf(fname, ".reg.%d", i);
49 else
50 sprintf(fname, ".add.%d", i + add);
51 files[i] = open(fname, O_RDWR | O_CREAT, 0644);
52 if (files[i] < 0) {
53 perror("open");
54 free(files);
55 files = NULL;
56 break;
57 }
58 }
59 if (extra) {
60 for (i = nr_files; i < nr_files + extra; i++)
61 files[i] = -1;
62 }
63
64 return files;
65 }
66
test_shrink(struct io_uring * ring)67 static int test_shrink(struct io_uring *ring)
68 {
69 int ret, off, fd;
70 int *files;
71
72 files = open_files(50, 0, 0);
73 ret = io_uring_register_files(ring, files, 50);
74 if (ret) {
75 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
76 goto err;
77 }
78
79 off = 0;
80 do {
81 fd = -1;
82 ret = io_uring_register_files_update(ring, off, &fd, 1);
83 if (ret != 1) {
84 if (off == 50 && ret == -EINVAL)
85 break;
86 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
87 break;
88 }
89 off++;
90 } while (1);
91
92 ret = io_uring_unregister_files(ring);
93 if (ret) {
94 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
95 goto err;
96 }
97
98 close_files(files, 50, 0);
99 return 0;
100 err:
101 close_files(files, 50, 0);
102 return 1;
103 }
104
105
test_grow(struct io_uring * ring)106 static int test_grow(struct io_uring *ring)
107 {
108 int ret, off;
109 int *files, *fds = NULL;
110
111 files = open_files(50, 250, 0);
112 ret = io_uring_register_files(ring, files, 300);
113 if (ret) {
114 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
115 goto err;
116 }
117
118 off = 50;
119 do {
120 fds = open_files(1, 0, off);
121 ret = io_uring_register_files_update(ring, off, fds, 1);
122 if (ret != 1) {
123 if (off == 300 && ret == -EINVAL)
124 break;
125 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
126 break;
127 }
128 if (off >= 300) {
129 fprintf(stderr, "%s: Succeeded beyond end-of-list?\n", __FUNCTION__);
130 goto err;
131 }
132 off++;
133 } while (1);
134
135 ret = io_uring_unregister_files(ring);
136 if (ret) {
137 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
138 goto err;
139 }
140
141 close_files(files, 100, 0);
142 close_files(NULL, 251, 50);
143 return 0;
144 err:
145 close_files(files, 100, 0);
146 close_files(NULL, 251, 50);
147 return 1;
148 }
149
test_replace_all(struct io_uring * ring)150 static int test_replace_all(struct io_uring *ring)
151 {
152 int *files, *fds = NULL;
153 int ret, i;
154
155 files = open_files(100, 0, 0);
156 ret = io_uring_register_files(ring, files, 100);
157 if (ret) {
158 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
159 goto err;
160 }
161
162 fds = t_malloc(100 * sizeof(int));
163 for (i = 0; i < 100; i++)
164 fds[i] = -1;
165
166 ret = io_uring_register_files_update(ring, 0, fds, 100);
167 if (ret != 100) {
168 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
169 goto err;
170 }
171
172 ret = io_uring_unregister_files(ring);
173 if (ret) {
174 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
175 goto err;
176 }
177
178 close_files(files, 100, 0);
179 if (fds)
180 free(fds);
181 return 0;
182 err:
183 close_files(files, 100, 0);
184 if (fds)
185 free(fds);
186 return 1;
187 }
188
test_replace(struct io_uring * ring)189 static int test_replace(struct io_uring *ring)
190 {
191 int *files, *fds = NULL;
192 int ret;
193
194 files = open_files(100, 0, 0);
195 ret = io_uring_register_files(ring, files, 100);
196 if (ret) {
197 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
198 goto err;
199 }
200
201 fds = open_files(10, 0, 1);
202 ret = io_uring_register_files_update(ring, 90, fds, 10);
203 if (ret != 10) {
204 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
205 goto err;
206 }
207
208 ret = io_uring_unregister_files(ring);
209 if (ret) {
210 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
211 goto err;
212 }
213
214 close_files(files, 100, 0);
215 if (fds)
216 close_files(fds, 10, 1);
217 return 0;
218 err:
219 close_files(files, 100, 0);
220 if (fds)
221 close_files(fds, 10, 1);
222 return 1;
223 }
224
test_removals(struct io_uring * ring)225 static int test_removals(struct io_uring *ring)
226 {
227 int *files, *fds = NULL;
228 int ret, i;
229
230 files = open_files(100, 0, 0);
231 ret = io_uring_register_files(ring, files, 100);
232 if (ret) {
233 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
234 goto err;
235 }
236
237 fds = t_calloc(10, sizeof(int));
238 for (i = 0; i < 10; i++)
239 fds[i] = -1;
240
241 ret = io_uring_register_files_update(ring, 50, fds, 10);
242 if (ret != 10) {
243 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
244 goto err;
245 }
246
247 ret = io_uring_unregister_files(ring);
248 if (ret) {
249 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
250 goto err;
251 }
252
253 close_files(files, 100, 0);
254 if (fds)
255 free(fds);
256 return 0;
257 err:
258 close_files(files, 100, 0);
259 if (fds)
260 free(fds);
261 return 1;
262 }
263
test_additions(struct io_uring * ring)264 static int test_additions(struct io_uring *ring)
265 {
266 int *files, *fds = NULL;
267 int ret;
268
269 files = open_files(100, 100, 0);
270 ret = io_uring_register_files(ring, files, 200);
271 if (ret) {
272 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
273 goto err;
274 }
275
276 fds = open_files(2, 0, 1);
277 ret = io_uring_register_files_update(ring, 100, fds, 2);
278 if (ret != 2) {
279 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
280 goto err;
281 }
282
283 ret = io_uring_unregister_files(ring);
284 if (ret) {
285 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
286 goto err;
287 }
288
289 close_files(files, 100, 0);
290 if (fds)
291 close_files(fds, 2, 1);
292 return 0;
293 err:
294 close_files(files, 100, 0);
295 if (fds)
296 close_files(fds, 2, 1);
297 return 1;
298 }
299
test_sparse(struct io_uring * ring)300 static int test_sparse(struct io_uring *ring)
301 {
302 int *files;
303 int ret;
304
305 files = open_files(100, 100, 0);
306 ret = io_uring_register_files(ring, files, 200);
307 if (ret) {
308 if (ret == -EBADF || ret == -EINVAL) {
309 fprintf(stdout, "Sparse files not supported, skipping\n");
310 no_update = 1;
311 goto done;
312 }
313 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
314 goto err;
315 }
316 ret = io_uring_unregister_files(ring);
317 if (ret) {
318 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
319 goto err;
320 }
321 done:
322 close_files(files, 100, 0);
323 return 0;
324 err:
325 close_files(files, 100, 0);
326 return 1;
327 }
328
test_basic_many(struct io_uring * ring)329 static int test_basic_many(struct io_uring *ring)
330 {
331 int *files;
332 int ret;
333
334 files = open_files(768, 0, 0);
335 ret = io_uring_register_files(ring, files, 768);
336 if (ret) {
337 fprintf(stderr, "%s: register %d\n", __FUNCTION__, ret);
338 goto err;
339 }
340 ret = io_uring_unregister_files(ring);
341 if (ret) {
342 fprintf(stderr, "%s: unregister %d\n", __FUNCTION__, ret);
343 goto err;
344 }
345 close_files(files, 768, 0);
346 return 0;
347 err:
348 close_files(files, 768, 0);
349 return 1;
350 }
351
test_basic(struct io_uring * ring,int fail)352 static int test_basic(struct io_uring *ring, int fail)
353 {
354 int *files;
355 int ret, i;
356 int nr_files = fail ? 10 : 100;
357
358 files = open_files(nr_files, fail ? 90 : 0, 0);
359 if (fail) {
360 for (i = nr_files; i < nr_files + 90; i++)
361 files[i] = -2;
362 }
363 ret = io_uring_register_files(ring, files, 100);
364 if (ret) {
365 if (fail) {
366 if (ret == -EBADF || ret == -EFAULT)
367 return 0;
368 }
369 fprintf(stderr, "%s: register %d\n", __FUNCTION__, ret);
370 goto err;
371 }
372 if (fail) {
373 fprintf(stderr, "Registration succeeded, but expected fail\n");
374 goto err;
375 }
376 ret = io_uring_unregister_files(ring);
377 if (ret) {
378 fprintf(stderr, "%s: unregister %d\n", __FUNCTION__, ret);
379 goto err;
380 }
381 close_files(files, nr_files, 0);
382 return 0;
383 err:
384 close_files(files, nr_files, 0);
385 return 1;
386 }
387
388 /*
389 * Register 0 files, but reserve space for 10. Then add one file.
390 */
test_zero(struct io_uring * ring)391 static int test_zero(struct io_uring *ring)
392 {
393 int *files, *fds = NULL;
394 int ret;
395
396 files = open_files(0, 10, 0);
397 ret = io_uring_register_files(ring, files, 10);
398 if (ret) {
399 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
400 goto err;
401 }
402
403 fds = open_files(1, 0, 1);
404 ret = io_uring_register_files_update(ring, 0, fds, 1);
405 if (ret != 1) {
406 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
407 goto err;
408 }
409
410 ret = io_uring_unregister_files(ring);
411 if (ret) {
412 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
413 goto err;
414 }
415
416 if (fds)
417 close_files(fds, 1, 1);
418 free(files);
419 return 0;
420 err:
421 if (fds)
422 close_files(fds, 1, 1);
423 free(files);
424 return 1;
425 }
426
test_fixed_read_write(struct io_uring * ring,int index)427 static int test_fixed_read_write(struct io_uring *ring, int index)
428 {
429 struct io_uring_sqe *sqe;
430 struct io_uring_cqe *cqe;
431 struct iovec iov[2];
432 int ret;
433
434 iov[0].iov_base = t_malloc(4096);
435 iov[0].iov_len = 4096;
436 memset(iov[0].iov_base, 0x5a, 4096);
437
438 iov[1].iov_base = t_malloc(4096);
439 iov[1].iov_len = 4096;
440
441 sqe = io_uring_get_sqe(ring);
442 if (!sqe) {
443 fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
444 return 1;
445 }
446 io_uring_prep_writev(sqe, index, &iov[0], 1, 0);
447 sqe->flags |= IOSQE_FIXED_FILE;
448 sqe->user_data = 1;
449
450 ret = io_uring_submit(ring);
451 if (ret != 1) {
452 fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
453 return 1;
454 }
455
456 ret = io_uring_wait_cqe(ring, &cqe);
457 if (ret < 0) {
458 fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
459 return 1;
460 }
461 if (cqe->res != 4096) {
462 fprintf(stderr, "%s: write cqe->res=%d\n", __FUNCTION__, cqe->res);
463 return 1;
464 }
465 io_uring_cqe_seen(ring, cqe);
466
467 sqe = io_uring_get_sqe(ring);
468 if (!sqe) {
469 fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
470 return 1;
471 }
472 io_uring_prep_readv(sqe, index, &iov[1], 1, 0);
473 sqe->flags |= IOSQE_FIXED_FILE;
474 sqe->user_data = 2;
475
476 ret = io_uring_submit(ring);
477 if (ret != 1) {
478 fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
479 return 1;
480 }
481
482 ret = io_uring_wait_cqe(ring, &cqe);
483 if (ret < 0) {
484 fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
485 return 1;
486 }
487 if (cqe->res != 4096) {
488 fprintf(stderr, "%s: read cqe->res=%d\n", __FUNCTION__, cqe->res);
489 return 1;
490 }
491 io_uring_cqe_seen(ring, cqe);
492
493 if (memcmp(iov[1].iov_base, iov[0].iov_base, 4096)) {
494 fprintf(stderr, "%s: data mismatch\n", __FUNCTION__);
495 return 1;
496 }
497
498 free(iov[0].iov_base);
499 free(iov[1].iov_base);
500 return 0;
501 }
502
adjust_nfiles(int want_files)503 static void adjust_nfiles(int want_files)
504 {
505 struct rlimit rlim;
506
507 if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
508 return;
509 if (rlim.rlim_cur >= want_files)
510 return;
511 rlim.rlim_cur = want_files;
512 setrlimit(RLIMIT_NOFILE, &rlim);
513 }
514
515 /*
516 * Register 8K of sparse files, update one at a random spot, then do some
517 * file IO to verify it works.
518 */
test_huge(struct io_uring * ring)519 static int test_huge(struct io_uring *ring)
520 {
521 int *files;
522 int ret;
523
524 adjust_nfiles(16384);
525
526 files = open_files(0, 8192, 0);
527 ret = io_uring_register_files(ring, files, 8192);
528 if (ret) {
529 /* huge sets not supported */
530 if (ret == -EMFILE) {
531 fprintf(stdout, "%s: No huge file set support, skipping\n", __FUNCTION__);
532 goto out;
533 }
534 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
535 goto err;
536 }
537
538 files[7193] = open(".reg.7193", O_RDWR | O_CREAT, 0644);
539 if (files[7193] < 0) {
540 fprintf(stderr, "%s: open=%d\n", __FUNCTION__, errno);
541 goto err;
542 }
543
544 ret = io_uring_register_files_update(ring, 7193, &files[7193], 1);
545 if (ret != 1) {
546 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
547 goto err;
548 }
549
550 if (test_fixed_read_write(ring, 7193))
551 goto err;
552
553 ret = io_uring_unregister_files(ring);
554 if (ret) {
555 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
556 goto err;
557 }
558
559 if (files[7193] != -1) {
560 close(files[7193]);
561 unlink(".reg.7193");
562 }
563 out:
564 free(files);
565 return 0;
566 err:
567 if (files[7193] != -1) {
568 close(files[7193]);
569 unlink(".reg.7193");
570 }
571 free(files);
572 return 1;
573 }
574
test_skip(struct io_uring * ring)575 static int test_skip(struct io_uring *ring)
576 {
577 int *files;
578 int ret;
579
580 files = open_files(100, 0, 0);
581 ret = io_uring_register_files(ring, files, 100);
582 if (ret) {
583 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
584 goto err;
585 }
586
587 files[90] = IORING_REGISTER_FILES_SKIP;
588 ret = io_uring_register_files_update(ring, 90, &files[90], 1);
589 if (ret != 1) {
590 if (ret == -EBADF) {
591 fprintf(stdout, "Skipping files not supported\n");
592 goto done;
593 }
594 fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
595 goto err;
596 }
597
598 /* verify can still use file index 90 */
599 if (test_fixed_read_write(ring, 90))
600 goto err;
601
602 ret = io_uring_unregister_files(ring);
603 if (ret) {
604 fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
605 goto err;
606 }
607
608 done:
609 close_files(files, 100, 0);
610 return 0;
611 err:
612 close_files(files, 100, 0);
613 return 1;
614 }
615
test_sparse_updates(void)616 static int test_sparse_updates(void)
617 {
618 struct io_uring ring;
619 int ret, i, *fds, newfd;
620
621 ret = io_uring_queue_init(8, &ring, 0);
622 if (ret) {
623 fprintf(stderr, "queue_init: %d\n", ret);
624 return ret;
625 }
626
627 fds = t_malloc(256 * sizeof(int));
628 for (i = 0; i < 256; i++)
629 fds[i] = -1;
630
631 ret = io_uring_register_files(&ring, fds, 256);
632 if (ret) {
633 fprintf(stderr, "file_register: %d\n", ret);
634 return ret;
635 }
636
637 newfd = 1;
638 for (i = 0; i < 256; i++) {
639 ret = io_uring_register_files_update(&ring, i, &newfd, 1);
640 if (ret != 1) {
641 fprintf(stderr, "file_update: %d\n", ret);
642 return ret;
643 }
644 }
645 io_uring_unregister_files(&ring);
646
647 for (i = 0; i < 256; i++)
648 fds[i] = 1;
649
650 ret = io_uring_register_files(&ring, fds, 256);
651 if (ret) {
652 fprintf(stderr, "file_register: %d\n", ret);
653 return ret;
654 }
655
656 newfd = -1;
657 for (i = 0; i < 256; i++) {
658 ret = io_uring_register_files_update(&ring, i, &newfd, 1);
659 if (ret != 1) {
660 fprintf(stderr, "file_update: %d\n", ret);
661 return ret;
662 }
663 }
664 io_uring_unregister_files(&ring);
665
666 io_uring_queue_exit(&ring);
667 return 0;
668 }
669
test_fixed_removal_ordering(void)670 static int test_fixed_removal_ordering(void)
671 {
672 char buffer[128];
673 struct io_uring ring;
674 struct io_uring_sqe *sqe;
675 struct io_uring_cqe *cqe;
676 struct __kernel_timespec ts;
677 int ret, fd, i, fds[2];
678
679 ret = io_uring_queue_init(8, &ring, 0);
680 if (ret < 0) {
681 fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
682 return ret;
683 }
684 if (pipe(fds)) {
685 perror("pipe");
686 return -1;
687 }
688 ret = io_uring_register_files(&ring, fds, 2);
689 if (ret) {
690 fprintf(stderr, "file_register: %d\n", ret);
691 return ret;
692 }
693 /* ring should have fds referenced, can close them */
694 close(fds[0]);
695 close(fds[1]);
696
697 sqe = io_uring_get_sqe(&ring);
698 if (!sqe) {
699 fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
700 return 1;
701 }
702 /* outwait file recycling delay */
703 ts.tv_sec = 3;
704 ts.tv_nsec = 0;
705 io_uring_prep_timeout(sqe, &ts, 0, 0);
706 sqe->flags |= IOSQE_IO_LINK | IOSQE_IO_HARDLINK;
707 sqe->user_data = 1;
708
709 sqe = io_uring_get_sqe(&ring);
710 if (!sqe) {
711 printf("get sqe failed\n");
712 return -1;
713 }
714 io_uring_prep_write(sqe, 1, buffer, sizeof(buffer), 0);
715 sqe->flags |= IOSQE_FIXED_FILE;
716 sqe->user_data = 2;
717
718 ret = io_uring_submit(&ring);
719 if (ret != 2) {
720 fprintf(stderr, "%s: got %d, wanted 2\n", __FUNCTION__, ret);
721 return -1;
722 }
723
724 /* remove unused pipe end */
725 fd = -1;
726 ret = io_uring_register_files_update(&ring, 0, &fd, 1);
727 if (ret != 1) {
728 fprintf(stderr, "update off=0 failed\n");
729 return -1;
730 }
731
732 /* remove used pipe end */
733 fd = -1;
734 ret = io_uring_register_files_update(&ring, 1, &fd, 1);
735 if (ret != 1) {
736 fprintf(stderr, "update off=1 failed\n");
737 return -1;
738 }
739
740 for (i = 0; i < 2; ++i) {
741 ret = io_uring_wait_cqe(&ring, &cqe);
742 if (ret < 0) {
743 fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
744 return 1;
745 }
746 io_uring_cqe_seen(&ring, cqe);
747 }
748
749 io_uring_queue_exit(&ring);
750 return 0;
751 }
752
753 /* mix files requiring SCM-accounting and not in a single register */
test_mixed_af_unix(void)754 static int test_mixed_af_unix(void)
755 {
756 struct io_uring ring;
757 int i, ret, fds[2];
758 int reg_fds[32];
759 int sp[2];
760
761 ret = io_uring_queue_init(8, &ring, 0);
762 if (ret < 0) {
763 fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
764 return ret;
765 }
766 if (pipe(fds)) {
767 perror("pipe");
768 return -1;
769 }
770 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp) != 0) {
771 perror("Failed to create Unix-domain socket pair\n");
772 return 1;
773 }
774
775 for (i = 0; i < 16; i++) {
776 reg_fds[i * 2] = fds[0];
777 reg_fds[i * 2 + 1] = sp[0];
778 }
779
780 ret = io_uring_register_files(&ring, reg_fds, 32);
781 if (ret) {
782 fprintf(stderr, "file_register: %d\n", ret);
783 return ret;
784 }
785
786 close(fds[0]);
787 close(fds[1]);
788 close(sp[0]);
789 close(sp[1]);
790 io_uring_queue_exit(&ring);
791 return 0;
792 }
793
test_partial_register_fail(void)794 static int test_partial_register_fail(void)
795 {
796 char buffer[128];
797 struct io_uring ring;
798 int ret, fds[2];
799 int reg_fds[5];
800
801 ret = io_uring_queue_init(8, &ring, 0);
802 if (ret < 0) {
803 fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
804 return ret;
805 }
806 if (pipe(fds)) {
807 perror("pipe");
808 return -1;
809 }
810
811 /*
812 * Expect register to fail as it doesn't support io_uring fds, shouldn't
813 * leave any fds referenced afterwards.
814 */
815 reg_fds[0] = fds[0];
816 reg_fds[1] = fds[1];
817 reg_fds[2] = -1;
818 reg_fds[3] = ring.ring_fd;
819 reg_fds[4] = -1;
820 ret = io_uring_register_files(&ring, reg_fds, 5);
821 if (!ret) {
822 fprintf(stderr, "file_register unexpectedly succeeded\n");
823 return 1;
824 }
825
826 /* ring should have fds referenced, can close them */
827 close(fds[1]);
828
829 /* confirm that fds[1] is actually close and to ref'ed by io_uring */
830 ret = read(fds[0], buffer, 10);
831 if (ret < 0)
832 perror("read");
833 close(fds[0]);
834 io_uring_queue_exit(&ring);
835 return 0;
836 }
837
file_update_alloc(struct io_uring * ring,int * fd)838 static int file_update_alloc(struct io_uring *ring, int *fd)
839 {
840 struct io_uring_sqe *sqe;
841 struct io_uring_cqe *cqe;
842 int ret;
843
844 sqe = io_uring_get_sqe(ring);
845 io_uring_prep_files_update(sqe, fd, 1, IORING_FILE_INDEX_ALLOC);
846
847 ret = io_uring_submit(ring);
848 if (ret != 1) {
849 fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
850 return -1;
851 }
852
853 ret = io_uring_wait_cqe(ring, &cqe);
854 if (ret < 0) {
855 fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
856 return -1;
857 }
858 ret = cqe->res;
859 io_uring_cqe_seen(ring, cqe);
860 return ret;
861 }
862
test_out_of_range_file_ranges(struct io_uring * ring)863 static int test_out_of_range_file_ranges(struct io_uring *ring)
864 {
865 int ret;
866
867 ret = io_uring_register_file_alloc_range(ring, 8, 3);
868 if (ret != -EINVAL) {
869 fprintf(stderr, "overlapping range %i\n", ret);
870 return 1;
871 }
872
873 ret = io_uring_register_file_alloc_range(ring, 10, 1);
874 if (ret != -EINVAL) {
875 fprintf(stderr, "out of range index %i\n", ret);
876 return 1;
877 }
878
879 ret = io_uring_register_file_alloc_range(ring, 7, ~1U);
880 if (ret != -EOVERFLOW) {
881 fprintf(stderr, "overflow %i\n", ret);
882 return 1;
883 }
884
885 return 0;
886 }
887
test_overallocating_file_range(struct io_uring * ring,int fds[2])888 static int test_overallocating_file_range(struct io_uring *ring, int fds[2])
889 {
890 int roff = 7, rlen = 2;
891 int ret, i, fd;
892
893 ret = io_uring_register_file_alloc_range(ring, roff, rlen);
894 if (ret) {
895 fprintf(stderr, "io_uring_register_file_alloc_range %i\n", ret);
896 return 1;
897 }
898
899 for (i = 0; i < rlen; i++) {
900 fd = fds[0];
901 ret = file_update_alloc(ring, &fd);
902 if (ret != 1) {
903 fprintf(stderr, "file_update_alloc\n");
904 return 1;
905 }
906
907 if (fd < roff || fd >= roff + rlen) {
908 fprintf(stderr, "invalid off result %i\n", fd);
909 return 1;
910 }
911 }
912
913 fd = fds[0];
914 ret = file_update_alloc(ring, &fd);
915 if (ret != -ENFILE) {
916 fprintf(stderr, "overallocated %i, off %i\n", ret, fd);
917 return 1;
918 }
919
920 return 0;
921 }
922
test_zero_range_alloc(struct io_uring * ring,int fds[2])923 static int test_zero_range_alloc(struct io_uring *ring, int fds[2])
924 {
925 int ret, fd;
926
927 ret = io_uring_register_file_alloc_range(ring, 7, 0);
928 if (ret) {
929 fprintf(stderr, "io_uring_register_file_alloc_range failed %i\n", ret);
930 return 1;
931 }
932
933 fd = fds[0];
934 ret = file_update_alloc(ring, &fd);
935 if (ret != -ENFILE) {
936 fprintf(stderr, "zero alloc %i\n", ret);
937 return 1;
938 }
939 return 0;
940 }
941
test_defer_taskrun(void)942 static int test_defer_taskrun(void)
943 {
944 struct io_uring_sqe *sqe;
945 struct io_uring ring;
946 int ret, fds[2];
947 char buff = 'x';
948
949 ret = io_uring_queue_init(8, &ring,
950 IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER);
951 if (ret) {
952 fprintf(stderr, "ring init\n");
953 return 1;
954 }
955
956 ret = pipe(fds);
957 if (ret) {
958 fprintf(stderr, "bad pipes\n");
959 return 1;
960 }
961
962 ret = io_uring_register_files(&ring, &fds[0], 2);
963 if (ret) {
964 fprintf(stderr, "bad register %d\n", ret);
965 return 1;
966 }
967
968 sqe = io_uring_get_sqe(&ring);
969 io_uring_prep_read(sqe, 0, &buff, 1, 0);
970 sqe->flags |= IOSQE_FIXED_FILE;
971 ret = io_uring_submit(&ring);
972 if (ret != 1) {
973 fprintf(stderr, "bad submit\n");
974 return 1;
975 }
976
977 ret = write(fds[1], &buff, 1);
978 if (ret != 1) {
979 fprintf(stderr, "bad pipe write\n");
980 return 1;
981 }
982
983 ret = io_uring_unregister_files(&ring);
984 if (ret) {
985 fprintf(stderr, "bad unregister %d\n", ret);
986 return 1;
987 }
988
989 close(fds[0]);
990 close(fds[1]);
991 io_uring_queue_exit(&ring);
992 return 0;
993 }
994
test_file_alloc_ranges(void)995 static int test_file_alloc_ranges(void)
996 {
997 struct io_uring ring;
998 int ret, pipe_fds[2];
999
1000 if (pipe(pipe_fds)) {
1001 fprintf(stderr, "pipes\n");
1002 return 1;
1003 }
1004 ret = io_uring_queue_init(8, &ring, 0);
1005 if (ret) {
1006 fprintf(stderr, "queue_init: %d\n", ret);
1007 return 1;
1008 }
1009
1010 ret = io_uring_register_files_sparse(&ring, 10);
1011 if (ret == -EINVAL) {
1012 not_supported:
1013 close(pipe_fds[0]);
1014 close(pipe_fds[1]);
1015 io_uring_queue_exit(&ring);
1016 printf("file alloc ranges are not supported, skip\n");
1017 return 0;
1018 } else if (ret) {
1019 fprintf(stderr, "io_uring_register_files_sparse %i\n", ret);
1020 return ret;
1021 }
1022
1023 ret = io_uring_register_file_alloc_range(&ring, 0, 1);
1024 if (ret) {
1025 if (ret == -EINVAL)
1026 goto not_supported;
1027 fprintf(stderr, "io_uring_register_file_alloc_range %i\n", ret);
1028 return 1;
1029 }
1030
1031 ret = test_overallocating_file_range(&ring, pipe_fds);
1032 if (ret) {
1033 fprintf(stderr, "test_overallocating_file_range() failed\n");
1034 return 1;
1035 }
1036
1037 ret = test_out_of_range_file_ranges(&ring);
1038 if (ret) {
1039 fprintf(stderr, "test_out_of_range_file_ranges() failed\n");
1040 return 1;
1041 }
1042
1043 ret = test_zero_range_alloc(&ring, pipe_fds);
1044 if (ret) {
1045 fprintf(stderr, "test_zero_range_alloc() failed\n");
1046 return 1;
1047 }
1048
1049 close(pipe_fds[0]);
1050 close(pipe_fds[1]);
1051 io_uring_queue_exit(&ring);
1052 return 0;
1053 }
1054
main(int argc,char * argv[])1055 int main(int argc, char *argv[])
1056 {
1057 struct io_uring ring;
1058 int ret;
1059
1060 if (argc > 1)
1061 return T_EXIT_SKIP;
1062
1063 ret = io_uring_queue_init(8, &ring, 0);
1064 if (ret) {
1065 fprintf(stderr, "ring setup failed\n");
1066 return T_EXIT_FAIL;
1067 }
1068
1069 ret = test_basic(&ring, 0);
1070 if (ret) {
1071 fprintf(stderr, "test_basic failed\n");
1072 return T_EXIT_FAIL;
1073 }
1074
1075 ret = test_basic(&ring, 1);
1076 if (ret) {
1077 fprintf(stderr, "test_basic failed\n");
1078 return T_EXIT_FAIL;
1079 }
1080
1081 ret = test_basic_many(&ring);
1082 if (ret) {
1083 fprintf(stderr, "test_basic_many failed\n");
1084 return T_EXIT_FAIL;
1085 }
1086
1087 ret = test_sparse(&ring);
1088 if (ret) {
1089 fprintf(stderr, "test_sparse failed\n");
1090 return T_EXIT_FAIL;
1091 }
1092
1093 if (no_update)
1094 return T_EXIT_SKIP;
1095
1096 ret = test_additions(&ring);
1097 if (ret) {
1098 fprintf(stderr, "test_additions failed\n");
1099 return T_EXIT_FAIL;
1100 }
1101
1102 ret = test_removals(&ring);
1103 if (ret) {
1104 fprintf(stderr, "test_removals failed\n");
1105 return T_EXIT_FAIL;
1106 }
1107
1108 ret = test_replace(&ring);
1109 if (ret) {
1110 fprintf(stderr, "test_replace failed\n");
1111 return T_EXIT_FAIL;
1112 }
1113
1114 ret = test_replace_all(&ring);
1115 if (ret) {
1116 fprintf(stderr, "test_replace_all failed\n");
1117 return T_EXIT_FAIL;
1118 }
1119
1120 ret = test_grow(&ring);
1121 if (ret) {
1122 fprintf(stderr, "test_grow failed\n");
1123 return T_EXIT_FAIL;
1124 }
1125
1126 ret = test_shrink(&ring);
1127 if (ret) {
1128 fprintf(stderr, "test_shrink failed\n");
1129 return T_EXIT_FAIL;
1130 }
1131
1132 ret = test_zero(&ring);
1133 if (ret) {
1134 fprintf(stderr, "test_zero failed\n");
1135 return T_EXIT_FAIL;
1136 }
1137
1138 ret = test_huge(&ring);
1139 if (ret) {
1140 fprintf(stderr, "test_huge failed\n");
1141 return T_EXIT_FAIL;
1142 }
1143
1144 ret = test_skip(&ring);
1145 if (ret) {
1146 fprintf(stderr, "test_skip failed\n");
1147 return T_EXIT_FAIL;
1148 }
1149
1150 ret = test_sparse_updates();
1151 if (ret) {
1152 fprintf(stderr, "test_sparse_updates failed\n");
1153 return T_EXIT_FAIL;
1154 }
1155
1156 ret = test_fixed_removal_ordering();
1157 if (ret) {
1158 fprintf(stderr, "test_fixed_removal_ordering failed\n");
1159 return T_EXIT_FAIL;
1160 }
1161
1162 ret = test_mixed_af_unix();
1163 if (ret) {
1164 fprintf(stderr, "test_mixed_af_unix failed\n");
1165 return T_EXIT_FAIL;
1166 }
1167
1168 ret = test_partial_register_fail();
1169 if (ret) {
1170 fprintf(stderr, "test_partial_register_fail failed\n");
1171 return T_EXIT_FAIL;
1172 }
1173
1174 ret = test_file_alloc_ranges();
1175 if (ret) {
1176 fprintf(stderr, "test_partial_register_fail failed\n");
1177 return T_EXIT_FAIL;
1178 }
1179
1180 if (t_probe_defer_taskrun()) {
1181 ret = test_defer_taskrun();
1182 if (ret) {
1183 fprintf(stderr, "test_defer_taskrun failed\n");
1184 return T_EXIT_FAIL;
1185 }
1186 }
1187
1188 return T_EXIT_PASS;
1189 }
1190