| /* SPDX-License-Identifier: MIT */ |
| /* |
| * Description: run various CQ ring overflow tests |
| * |
| */ |
| #include <errno.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <fcntl.h> |
| |
| #include "helpers.h" |
| #include "liburing.h" |
| |
| #define FILE_SIZE (256 * 1024) |
| #define BS 4096 |
| #define BUFFERS (FILE_SIZE / BS) |
| |
| static struct iovec *vecs; |
| |
| #define ENTRIES 8 |
| |
| static int test_io(const char *file, unsigned long usecs, unsigned *drops, int fault) |
| { |
| struct io_uring_sqe *sqe; |
| struct io_uring_cqe *cqe; |
| struct io_uring_params p; |
| unsigned reaped, total; |
| struct io_uring ring; |
| int nodrop, i, fd, ret; |
| |
| fd = open(file, O_RDONLY | O_DIRECT); |
| if (fd < 0) { |
| perror("file open"); |
| goto err; |
| } |
| |
| memset(&p, 0, sizeof(p)); |
| ret = io_uring_queue_init_params(ENTRIES, &ring, &p); |
| if (ret) { |
| fprintf(stderr, "ring create failed: %d\n", ret); |
| goto err; |
| } |
| nodrop = 0; |
| if (p.features & IORING_FEAT_NODROP) |
| nodrop = 1; |
| |
| total = 0; |
| for (i = 0; i < BUFFERS / 2; i++) { |
| off_t offset; |
| |
| sqe = io_uring_get_sqe(&ring); |
| if (!sqe) { |
| fprintf(stderr, "sqe get failed\n"); |
| goto err; |
| } |
| offset = BS * (rand() % BUFFERS); |
| if (fault && i == ENTRIES + 4) |
| vecs[i].iov_base = NULL; |
| io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset); |
| |
| ret = io_uring_submit(&ring); |
| if (nodrop && ret == -EBUSY) { |
| *drops = 1; |
| total = i; |
| break; |
| } else if (ret != 1) { |
| fprintf(stderr, "submit got %d, wanted %d\n", ret, 1); |
| total = i; |
| break; |
| } |
| total++; |
| } |
| |
| if (*drops) |
| goto reap_it; |
| |
| usleep(usecs); |
| |
| for (i = total; i < BUFFERS; i++) { |
| off_t offset; |
| |
| sqe = io_uring_get_sqe(&ring); |
| if (!sqe) { |
| fprintf(stderr, "sqe get failed\n"); |
| goto err; |
| } |
| offset = BS * (rand() % BUFFERS); |
| io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset); |
| |
| ret = io_uring_submit(&ring); |
| if (nodrop && ret == -EBUSY) { |
| *drops = 1; |
| break; |
| } else if (ret != 1) { |
| fprintf(stderr, "submit got %d, wanted %d\n", ret, 1); |
| break; |
| } |
| total++; |
| } |
| |
| reap_it: |
| reaped = 0; |
| do { |
| if (nodrop) { |
| /* nodrop should never lose events */ |
| if (reaped == total) |
| break; |
| } else { |
| if (reaped + *ring.cq.koverflow == total) |
| break; |
| } |
| ret = io_uring_wait_cqe(&ring, &cqe); |
| if (ret) { |
| fprintf(stderr, "wait_cqe=%d\n", ret); |
| goto err; |
| } |
| if (cqe->res != BS) { |
| if (!(fault && cqe->res == -EFAULT)) { |
| fprintf(stderr, "cqe res %d, wanted %d\n", |
| cqe->res, BS); |
| goto err; |
| } |
| } |
| io_uring_cqe_seen(&ring, cqe); |
| reaped++; |
| } while (1); |
| |
| if (!io_uring_peek_cqe(&ring, &cqe)) { |
| fprintf(stderr, "found unexpected completion\n"); |
| goto err; |
| } |
| |
| if (!nodrop) { |
| *drops = *ring.cq.koverflow; |
| } else if (*ring.cq.koverflow) { |
| fprintf(stderr, "Found %u overflows\n", *ring.cq.koverflow); |
| goto err; |
| } |
| |
| io_uring_queue_exit(&ring); |
| close(fd); |
| return 0; |
| err: |
| if (fd != -1) |
| close(fd); |
| io_uring_queue_exit(&ring); |
| return 1; |
| } |
| |
| static int reap_events(struct io_uring *ring, unsigned nr_events, int do_wait) |
| { |
| struct io_uring_cqe *cqe; |
| int i, ret = 0, seq = 0; |
| |
| for (i = 0; i < nr_events; i++) { |
| if (do_wait) |
| ret = io_uring_wait_cqe(ring, &cqe); |
| else |
| ret = io_uring_peek_cqe(ring, &cqe); |
| if (ret) { |
| if (ret != -EAGAIN) |
| fprintf(stderr, "cqe peek failed: %d\n", ret); |
| break; |
| } |
| if (cqe->user_data != seq) { |
| fprintf(stderr, "cqe sequence out-of-order\n"); |
| fprintf(stderr, "got %d, wanted %d\n", (int) cqe->user_data, |
| seq); |
| return -EINVAL; |
| } |
| seq++; |
| io_uring_cqe_seen(ring, cqe); |
| } |
| |
| return i ? i : ret; |
| } |
| |
| /* |
| * Submit some NOPs and watch if the overflow is correct |
| */ |
| static int test_overflow(void) |
| { |
| struct io_uring ring; |
| struct io_uring_params p; |
| struct io_uring_sqe *sqe; |
| unsigned pending; |
| int ret, i, j; |
| |
| memset(&p, 0, sizeof(p)); |
| ret = io_uring_queue_init_params(4, &ring, &p); |
| if (ret) { |
| fprintf(stderr, "io_uring_queue_init failed %d\n", ret); |
| return 1; |
| } |
| |
| /* submit 4x4 SQEs, should overflow the ring by 8 */ |
| pending = 0; |
| for (i = 0; i < 4; i++) { |
| for (j = 0; j < 4; j++) { |
| sqe = io_uring_get_sqe(&ring); |
| if (!sqe) { |
| fprintf(stderr, "get sqe failed\n"); |
| goto err; |
| } |
| |
| io_uring_prep_nop(sqe); |
| sqe->user_data = (i * 4) + j; |
| } |
| |
| ret = io_uring_submit(&ring); |
| if (ret == 4) { |
| pending += 4; |
| continue; |
| } |
| if (p.features & IORING_FEAT_NODROP) { |
| if (ret == -EBUSY) |
| break; |
| } |
| fprintf(stderr, "sqe submit failed: %d\n", ret); |
| goto err; |
| } |
| |
| /* we should now have 8 completions ready */ |
| ret = reap_events(&ring, pending, 0); |
| if (ret < 0) |
| goto err; |
| |
| if (!(p.features & IORING_FEAT_NODROP)) { |
| if (*ring.cq.koverflow != 8) { |
| fprintf(stderr, "cq ring overflow %d, expected 8\n", |
| *ring.cq.koverflow); |
| goto err; |
| } |
| } |
| io_uring_queue_exit(&ring); |
| return 0; |
| err: |
| io_uring_queue_exit(&ring); |
| return 1; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| unsigned iters, drops; |
| unsigned long usecs; |
| int ret; |
| |
| if (argc > 1) |
| return 0; |
| |
| ret = test_overflow(); |
| if (ret) { |
| printf("test_overflow failed\n"); |
| return ret; |
| } |
| |
| t_create_file(".basic-rw", FILE_SIZE); |
| |
| vecs = t_create_buffers(BUFFERS, BS); |
| |
| iters = 0; |
| usecs = 1000; |
| do { |
| drops = 0; |
| |
| if (test_io(".basic-rw", usecs, &drops, 0)) { |
| fprintf(stderr, "test_io nofault failed\n"); |
| goto err; |
| } |
| if (drops) |
| break; |
| usecs = (usecs * 12) / 10; |
| iters++; |
| } while (iters < 40); |
| |
| if (test_io(".basic-rw", usecs, &drops, 0)) { |
| fprintf(stderr, "test_io nofault failed\n"); |
| goto err; |
| } |
| |
| if (test_io(".basic-rw", usecs, &drops, 1)) { |
| fprintf(stderr, "test_io fault failed\n"); |
| goto err; |
| } |
| |
| unlink(".basic-rw"); |
| return 0; |
| err: |
| unlink(".basic-rw"); |
| return 1; |
| } |