test/init-mem: test io_uring_queue_init_mem() with various sizes Include a pre and post redzone of what liburing tells us it used, and run 2 * CQ size number of operations on the ring, verifying if the pre and post redzones are intact after each round. Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/test/Makefile b/test/Makefile index 2521e12..b32beea 100644 --- a/test/Makefile +++ b/test/Makefile
@@ -101,6 +101,7 @@ fsync.c \ futex.c \ hardlink.c \ + init-mem.c \ io-cancel.c \ iopoll.c \ iopoll-leak.c \
diff --git a/test/init-mem.c b/test/init-mem.c new file mode 100644 index 0000000..2648c42 --- /dev/null +++ b/test/init-mem.c
@@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Description: Check that io_uring_queue_init_mem() doesn't underestimate + * the memory required for various size rings. + */ +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <sys/mman.h> +#include <linux/mman.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/udp.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <error.h> + +#include "liburing.h" +#include "helpers.h" + +#define PRE_RED 0x5aa55aa55aa55aa5ULL +#define POST_RED 0xa55aa55aa55aa55aULL + +struct ctx { + struct io_uring ring; + void *ring_mem; + void *mem; + unsigned long long *pre; + unsigned long long *post; +}; + +struct q_entries { + unsigned int sqes; + unsigned int cqes; +}; + +static int setup_ctx(struct ctx *ctx, struct q_entries *q) +{ + struct io_uring_params p = { }; + int ret; + + if (posix_memalign(&ctx->mem, 4096, 2*1024*1024)) + return T_EXIT_FAIL; + + ctx->pre = ctx->mem + 4096 - sizeof(unsigned long); + *ctx->pre = PRE_RED; + + ctx->ring_mem = ctx->mem + 4096; + p.flags |= IORING_SETUP_CQSIZE | IORING_SETUP_NO_SQARRAY; + p.sq_entries = q->sqes; + p.cq_entries = q->cqes; + + ret = io_uring_queue_init_mem(q->sqes, &ctx->ring, &p, + ctx->ring_mem, 2*1024*1024); + + if (ret < 0) { + if (ret == -EINVAL) + return T_EXIT_SKIP; + fprintf(stderr, "queue init: %d\n", ret); + return T_EXIT_FAIL; + } + + printf("Used %d bytes\n", ret); + ctx->post = ctx->ring_mem + ret; + *ctx->post = POST_RED; + return 0; +} + +static void clean_ctx(struct ctx *ctx) +{ + io_uring_queue_exit(&ctx->ring); +} + +static int check_red(struct ctx *ctx, unsigned long i) +{ + int fail = 0; + + if (*ctx->pre != PRE_RED) { + printf("pre redzone=%llx at i=%lu\n", *ctx->pre, i); + fail = 1; + } + if (*ctx->post != POST_RED) { + printf("post redzone=%llx at i=%lu\n", *ctx->post, i); + fail = 1; + } + return fail; +} + +static int test(struct q_entries *q) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct ctx ctx = { }; + unsigned long i, ud; + int j, ret, batch; + + ret = setup_ctx(&ctx, q); + if (ret == T_EXIT_SKIP) + return T_EXIT_SKIP; + else if (ret != T_EXIT_PASS) + return ret; + + batch = 64; + if (batch > q->sqes) + batch = q->sqes; + + i = ud = 0; + while (i < q->cqes * 2) { + if (check_red(&ctx, i)) + return T_EXIT_FAIL; + for (j = 0; j < batch; j++) { + sqe = io_uring_get_sqe(&ctx.ring); + io_uring_prep_nop(sqe); + sqe->user_data = j + (unsigned long) i; + } + io_uring_submit(&ctx.ring); + for (j = 0; j < batch; j++) { + ret = io_uring_wait_cqe(&ctx.ring, &cqe); + if (ret) + goto err; + if (cqe->user_data != ud) { + fprintf(stderr, "ud=%lu, wanted %lu\n", (unsigned long) cqe->user_data, ud); + goto err; + } + ud++; + io_uring_cqe_seen(&ctx.ring, cqe); + } + i += batch; + } + + clean_ctx(&ctx); + return T_EXIT_PASS; +err: + clean_ctx(&ctx); + return T_EXIT_FAIL; +} + +int main(int argc, char *argv[]) +{ + struct q_entries q_entries[] = { + { 256, 16384 }, + { 32, 4096 }, + { 128, 8192 }, + { 4096, 32768 }, + { 1, 8 }, + { 2, 1024 }, + }; + int i, ret; + + if (argc > 1) + return T_EXIT_SKIP; + + for (i = 0; i < ARRAY_SIZE(q_entries); i++) { + ret = test(&q_entries[i]); + if (ret == T_EXIT_SKIP) { + return T_EXIT_SKIP; + } else if (ret != T_EXIT_PASS) { + fprintf(stderr, "Failed at %d/%d\n", q_entries[i].sqes, + q_entries[i].cqes); + return T_EXIT_FAIL; + } + } + + return T_EXIT_PASS; +}