/* SPDX-License-Identifier: MIT */ /* * Description: test many files being polled for * */ #include #include #include #include #include #include #include #include #include #include "liburing.h" #define NFILES 5000 #define BATCH 500 #define NLOOPS 1000 #define RING_SIZE 512 struct p { int fd[2]; int triggered; }; static struct p p[NFILES]; static int arm_poll(struct io_uring *ring, int off) { struct io_uring_sqe *sqe; sqe = io_uring_get_sqe(ring); if (!sqe) { fprintf(stderr, "failed getting sqe\n"); return 1; } io_uring_prep_poll_add(sqe, p[off].fd[0], POLLIN); sqe->user_data = off; return 0; } static int reap_polls(struct io_uring *ring) { struct io_uring_cqe *cqe; int i, ret, off; char c; for (i = 0; i < BATCH; i++) { ret = io_uring_wait_cqe(ring, &cqe); if (ret) { fprintf(stderr, "wait cqe %d\n", ret); return ret; } off = cqe->user_data; p[off].triggered = 0; ret = read(p[off].fd[0], &c, 1); if (ret != 1) { fprintf(stderr, "read got %d/%d\n", ret, errno); break; } if (arm_poll(ring, off)) break; io_uring_cqe_seen(ring, cqe); } if (i != BATCH) { fprintf(stderr, "gave up at %d\n", i); return 1; } ret = io_uring_submit(ring); if (ret != BATCH) { fprintf(stderr, "submitted %d, %d\n", ret, BATCH); return 1; } return 0; } static int trigger_polls(void) { char c = 89; int i, ret; for (i = 0; i < BATCH; i++) { int off; do { off = rand() % NFILES; if (!p[off].triggered) break; } while (1); p[off].triggered = 1; ret = write(p[off].fd[1], &c, 1); if (ret != 1) { fprintf(stderr, "write got %d/%d\n", ret, errno); return 1; } } return 0; } static int arm_polls(struct io_uring *ring) { int ret, to_arm = NFILES, i, off; off = 0; while (to_arm) { int this_arm; this_arm = to_arm; if (this_arm > RING_SIZE) this_arm = RING_SIZE; for (i = 0; i < this_arm; i++) { if (arm_poll(ring, off)) { fprintf(stderr, "arm failed at %d\n", off); return 1; } off++; } ret = io_uring_submit(ring); if (ret != this_arm) { fprintf(stderr, "submitted %d, %d\n", ret, this_arm); return 1; } to_arm -= this_arm; } return 0; } int main(int argc, char *argv[]) { struct io_uring ring; struct io_uring_params params = { }; struct rlimit rlim; int i, ret; if (argc > 1) return 0; if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { perror("getrlimit"); goto err_noring; } if (rlim.rlim_cur < (2 * NFILES + 5)) { rlim.rlim_cur = (2 * NFILES + 5); rlim.rlim_max = rlim.rlim_cur; if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { if (errno == EPERM) goto err_nofail; perror("setrlimit"); goto err_noring; } } for (i = 0; i < NFILES; i++) { if (pipe(p[i].fd) < 0) { perror("pipe"); goto err_noring; } } params.flags = IORING_SETUP_CQSIZE; params.cq_entries = 4096; ret = io_uring_queue_init_params(RING_SIZE, &ring, ¶ms); if (ret) { if (ret == -EINVAL) { fprintf(stdout, "No CQSIZE, trying without\n"); ret = io_uring_queue_init(RING_SIZE, &ring, 0); if (ret) { fprintf(stderr, "ring setup failed: %d\n", ret); return 1; } } } if (arm_polls(&ring)) goto err; for (i = 0; i < NLOOPS; i++) { trigger_polls(); ret = reap_polls(&ring); if (ret) goto err; } io_uring_queue_exit(&ring); return 0; err: io_uring_queue_exit(&ring); err_noring: fprintf(stderr, "poll-many failed\n"); return 1; err_nofail: fprintf(stderr, "poll-many: not enough files available (and not root), " "skipped\n"); return 0; }