Add nop testing for IORING_SETUP_CQE_MIXED Add NOP testing for mixed sized CQEs. Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/src/include/liburing/io_uring.h b/src/include/liburing/io_uring.h index 4c8c9da..643514e 100644 --- a/src/include/liburing/io_uring.h +++ b/src/include/liburing/io_uring.h
@@ -436,6 +436,7 @@ * IORING_NOP_INJECT_RESULT Inject result from sqe->result */ #define IORING_NOP_INJECT_RESULT (1U << 0) +#define IORING_NOP_CQE32 (1U << 5) /* * IO completion data structure (Completion Queue Entry)
diff --git a/test/Makefile b/test/Makefile index edfc0df..54251bf 100644 --- a/test/Makefile +++ b/test/Makefile
@@ -148,6 +148,8 @@ no-mmap-inval.c \ nop-all-sizes.c \ nop.c \ + nop32.c \ + nop32-overflow.c \ ooo-file-unreg.c \ openat2.c \ open-close.c \
diff --git a/test/nop.c b/test/nop.c index d3ab766..b02dcb8 100644 --- a/test/nop.c +++ b/test/nop.c
@@ -83,7 +83,7 @@ goto err; } if (!cqe->user_data) { - fprintf(stderr, "Unexpected 0 user_data\n"); + fprintf(stderr, "Unexpected 0 user_data: %ld\n", (long) cqe->user_data); goto err; } if (cqe32) { @@ -140,7 +140,7 @@ goto err; } if (!cqe->user_data) { - fprintf(stderr, "Unexpected 0 user_data\n"); + fprintf(stderr, "Unexpected 0 user_data: %ld\n", (long) cqe->user_data); goto err; } if (cqe32) {
diff --git a/test/nop32-overflow.c b/test/nop32-overflow.c new file mode 100644 index 0000000..72beac2 --- /dev/null +++ b/test/nop32-overflow.c
@@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Description: run various nop tests + * + */ +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> + +#include "liburing.h" +#include "helpers.h" +#include "test.h" + +static int seq; + +static int fail_cqe(struct io_uring_cqe *cqe, int off, const char *msg) +{ + fprintf(stderr, "Bad cqe at off %d: %s\n", off, msg); + fprintf(stderr, "CQE ud=%lu, res=%u, flags=%x\n", (long) cqe->user_data, cqe->res, cqe->flags); + return T_EXIT_FAIL; +} + +static int test_ring(unsigned flags) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + struct io_uring_params p = { }; + unsigned head; + int ret, i, cqe_nr; + + p.flags = flags | IORING_SETUP_CQSIZE; + p.cq_entries = 8; + ret = io_uring_queue_init_params(8, &ring, &p); + if (ret) { + if (ret == -EINVAL) + return T_EXIT_SKIP; + fprintf(stderr, "ring setup failed: %d\n", ret); + return T_EXIT_FAIL; + } + + /* + * Prep 8 NOP requests, with the first being a 16b one to + * unalign the ring. The 4 latter ones will hit overflow, + * and after the first 4 we should see a SKIP cqe. + */ + for (i = 0; i < 8; i++) { + sqe = io_uring_get_sqe(&ring); + io_uring_prep_nop(sqe); + if (i) + sqe->nop_flags = IORING_NOP_CQE32; + sqe->user_data = ++seq; + } + + io_uring_submit(&ring); + + /* + * Should find the following CQEs: + * + * 1: 16b with ud 1 + * 2: 32b with ud 2 + * 3: 32b with ud 3 + * 4: 32b with ud 4 + * 5: 16b skip CQE + * + * And then we should have overflow pending to flush the rest + */ + i = cqe_nr = 0; + io_uring_for_each_cqe(&ring, head, cqe) { + switch (i) { + case 0: + if (cqe->user_data != 1) + return fail_cqe(cqe, i, "Non-overflow UD"); + if (cqe->flags & IORING_CQE_F_32) + return fail_cqe(cqe, i, "Non-overflow flags"); + break; + case 1: + case 2: + case 3: + if (cqe->user_data != i + 1) + return fail_cqe(cqe, i, "Non-overflow 32b UD"); + if (!(cqe->flags & IORING_CQE_F_32)) + return fail_cqe(cqe, i, "Non-overflow 32b flags"); + break; + case 4: + if (!(cqe->flags & IORING_CQE_F_SKIP)) + return fail_cqe(cqe, i, "Non-overflow skip"); + break; + default: + return fail_cqe(cqe, i, "Bogus CQE"); + } + i++; + cqe_nr += io_uring_cqe_nr(cqe); + } + io_uring_cq_advance(&ring, cqe_nr); + + /* + * This should flush overflow, and we should see: + * + * 1: 32b with ud 5 + * 2: 32b with ud 6 + * 3: 32b with ud 7 + * 4: 32b with ud 8 + */ + for (i = 0; i < 4; i++) { + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait ret %d\n", ret); + return T_EXIT_FAIL; + } + switch (i) { + case 0: + case 1: + case 2: + case 3: + if (cqe->user_data != i + 5) + return fail_cqe(cqe, i + 5, "Overflow UD"); + if (!(cqe->flags & IORING_CQE_F_32)) + return fail_cqe(cqe, i + 5, "Overflow flags"); + break; + default: + return fail_cqe(cqe, i + 5, "Bogus CQE"); + } + io_uring_cqe_seen(&ring, cqe); + } + + io_uring_queue_exit(&ring); + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = test_ring(IORING_SETUP_CQE_MIXED); + if (ret == T_EXIT_SKIP) { + return T_EXIT_SKIP; + } else if (ret != T_EXIT_PASS) { + fprintf(stderr, "Mixed ring test failed\n"); + return ret; + } + + return T_EXIT_PASS; +}
diff --git a/test/nop32.c b/test/nop32.c new file mode 100644 index 0000000..7eee506 --- /dev/null +++ b/test/nop32.c
@@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Description: run various nop tests + * + */ +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> + +#include "liburing.h" +#include "helpers.h" +#include "test.h" + +static int seq; +static int extra1 = 100, extra2 = 200; +static int want_extra1 = 100, want_extra2 = 200; + +static int test_single_nop(struct io_uring *ring, unsigned req_flags, bool cqe32) +{ + struct io_uring_cqe *cqe; + struct io_uring_sqe *sqe; + int ret; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + fprintf(stderr, "get sqe failed\n"); + goto err; + } + + io_uring_prep_nop(sqe); + sqe->user_data = ++seq; + sqe->flags |= req_flags; + if (cqe32) { + sqe->nop_flags = IORING_NOP_CQE32; + sqe->off = extra1++; + sqe->addr = extra2++; + } + + ret = io_uring_submit(ring); + if (ret <= 0) { + fprintf(stderr, "sqe submit failed: %d\n", ret); + goto err; + } + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret < 0) { + fprintf(stderr, "wait completion %d\n", ret); + goto err; + } + if (!cqe->user_data) { + fprintf(stderr, "Unexpected 0 user_data: %ld\n", (long) cqe->user_data); + goto err; + } + if (cqe32) { + if (!(cqe->flags & IORING_CQE_F_32)) { + fprintf(stderr, "CQE_F_32 not set\n"); + goto err; + } + if (cqe->big_cqe[0] != want_extra1) { + fprintf(stderr, "Unexpected extra1: %ld, want %d\n", (long) cqe->big_cqe[0], want_extra1); + goto err; + + } + want_extra1++; + if (cqe->big_cqe[1] != want_extra2) { + fprintf(stderr, "Unexpected extra2: %ld, want %d\n", (long) cqe->big_cqe[1], want_extra2); + goto err; + } + want_extra2++; + } + io_uring_cqe_seen(ring, cqe); + return T_EXIT_PASS; +err: + return T_EXIT_FAIL; +} + +static int test_ring(unsigned flags) +{ + struct io_uring ring; + struct io_uring_params p = { }; + int ret, i; + + p.flags = flags; + ret = io_uring_queue_init_params(8, &ring, &p); + if (ret) { + if (ret == -EINVAL) + return T_EXIT_SKIP; + fprintf(stderr, "ring setup failed: %d\n", ret); + return T_EXIT_FAIL; + } + + test_single_nop(&ring, 0, 0); + + for (i = 0; i < 16; i++) { + ret = test_single_nop(&ring, 0, 1); + if (ret) { + printf("fail off %d\n", i); + fprintf(stderr, "test_single_nop failed\n"); + goto err; + } + } +err: + io_uring_queue_exit(&ring); + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = test_ring(IORING_SETUP_CQE_MIXED); + if (ret == T_EXIT_SKIP) { + return T_EXIT_SKIP; + } else if (ret != T_EXIT_PASS) { + fprintf(stderr, "Mixed ring test failed\n"); + return ret; + } + + return T_EXIT_PASS; +}