Add the header / definitions and tests for the query interface. Signed-off-by: Pavel Begunkov --- src/include/liburing.h | 1 + src/include/liburing/io_uring.h | 3 + src/include/liburing/io_uring/query.h | 41 ++++ test/Makefile | 1 + test/ring-query.c | 322 ++++++++++++++++++++++++++ 5 files changed, 368 insertions(+) create mode 100644 src/include/liburing/io_uring/query.h create mode 100644 test/ring-query.c diff --git a/src/include/liburing.h b/src/include/liburing.h index e3f394ea..46d3cccf 100644 --- a/src/include/liburing.h +++ b/src/include/liburing.h @@ -16,6 +16,7 @@ #include #include "liburing/compat.h" #include "liburing/io_uring.h" +#include "liburing/io_uring/query.h" #include "liburing/io_uring_version.h" #include "liburing/barrier.h" diff --git a/src/include/liburing/io_uring.h b/src/include/liburing/io_uring.h index 212b5874..55b69f9d 100644 --- a/src/include/liburing/io_uring.h +++ b/src/include/liburing/io_uring.h @@ -641,6 +641,9 @@ enum io_uring_register_op { IORING_REGISTER_MEM_REGION = 34, + /* query various aspects of io_uring, see linux/io_uring/query.h */ + IORING_REGISTER_QUERY = 35, + /* this goes last */ IORING_REGISTER_LAST, diff --git a/src/include/liburing/io_uring/query.h b/src/include/liburing/io_uring/query.h new file mode 100644 index 00000000..5d754322 --- /dev/null +++ b/src/include/liburing/io_uring/query.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR MIT */ +/* + * Header file for the io_uring query interface. + */ +#ifndef LINUX_IO_URING_QUERY_H +#define LINUX_IO_URING_QUERY_H + +#include + +struct io_uring_query_hdr { + __u64 next_entry; + __u64 query_data; + __u32 query_op; + __u32 size; + __s32 result; + __u32 __resv[3]; +}; + +enum { + IO_URING_QUERY_OPCODES = 0, + + __IO_URING_QUERY_MAX, +}; + +/* Doesn't require a ring */ +struct io_uring_query_opcode { + /* The number of supported IORING_OP_* opcodes */ + __u32 nr_request_opcodes; + /* The number of supported IORING_[UN]REGISTER_* opcodes */ + __u32 nr_register_opcodes; + /* Bitmask of all supported IORING_FEAT_* flags */ + __u64 feature_flags; + /* Bitmask of all supported IORING_SETUP_* flags */ + __u64 ring_setup_flags; + /* Bitmask of all supported IORING_ENTER_** flags */ + __u64 enter_flags; + /* Bitmask of all supported IOSQE_* flags */ + __u64 sqe_flags; +}; + +#endif diff --git a/test/Makefile b/test/Makefile index edfc0df7..626ae674 100644 --- a/test/Makefile +++ b/test/Makefile @@ -255,6 +255,7 @@ test_srcs := \ zcrx.c \ vec-regbuf.c \ timestamp.c \ + ring-query.c \ # EOL # Please keep this list sorted alphabetically. diff --git a/test/ring-query.c b/test/ring-query.c new file mode 100644 index 00000000..46080c77 --- /dev/null +++ b/test/ring-query.c @@ -0,0 +1,322 @@ +/* SPDX-License-Identifier: MIT */ +#include +#include +#include +#include +#include +#include + +#include "liburing.h" +#include "test.h" +#include "helpers.h" + +struct io_uring_query_opcode_short { + __u32 nr_request_opcodes; + __u32 nr_register_opcodes; +}; + +struct io_uring_query_opcode_large { + __u32 nr_request_opcodes; + __u32 nr_register_opcodes; + __u64 feature_flags; + __u64 ring_setup_flags; + __u64 enter_flags; + __u64 sqe_flags; + __u64 placeholder[8]; +}; + +static struct io_uring_query_opcode sys_ops; + +static int io_uring_query(struct io_uring *ring, struct io_uring_query_hdr *arg) +{ + int fd = ring ? ring->ring_fd : -1; + + return io_uring_register(fd, IORING_REGISTER_QUERY, arg, 0); +} + +static int test_basic_query(void) +{ + struct io_uring_query_opcode op; + struct io_uring_query_hdr hdr = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = uring_ptr_to_u64(&op), + .size = sizeof(op), + }; + int ret; + + ret = io_uring_query(NULL, &hdr); + if (ret == -EINVAL) + return T_EXIT_SKIP; + + if (ret != 0) { + fprintf(stderr, "query failed %d\n", ret); + return T_EXIT_FAIL; + } + + if (hdr.size != sizeof(op)) { + fprintf(stderr, "unexpected size %i vs %i\n", + (int)hdr.size, (int)sizeof(op)); + return T_EXIT_FAIL; + } + + if (hdr.result) { + fprintf(stderr, "unexpected result %i\n", hdr.result); + return T_EXIT_FAIL; + } + + if (op.nr_register_opcodes <= IORING_REGISTER_QUERY) { + fprintf(stderr, "too few opcodes (%i)\n", op.nr_register_opcodes); + return T_EXIT_FAIL; + } + + memcpy(&sys_ops, &op, sizeof(sys_ops)); + return T_EXIT_PASS; +} + +static int test_invalid(void) +{ + int ret; + struct io_uring_query_opcode op; + struct io_uring_query_hdr invalid_hdr = { + .query_op = -1U, + .query_data = uring_ptr_to_u64(&op), + .size = sizeof(struct io_uring_query_opcode), + }; + struct io_uring_query_hdr invalid_next_hdr = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = uring_ptr_to_u64(&op), + .size = sizeof(struct io_uring_query_opcode), + .next_entry = 0xdeadbeefUL, + }; + struct io_uring_query_hdr invalid_data_hdr = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = 0xdeadbeefUL, + .size = sizeof(struct io_uring_query_opcode), + }; + + ret = io_uring_query(NULL, &invalid_hdr); + if (ret || invalid_hdr.result != -EOPNOTSUPP) { + fprintf(stderr, "failed invalid opcode %i (%i)\n", + ret, invalid_hdr.result); + return T_EXIT_FAIL; + } + + ret = io_uring_query(NULL, &invalid_next_hdr); + if (ret != -EFAULT) { + fprintf(stderr, "invalid next %i\n", ret); + return T_EXIT_FAIL; + } + + ret = io_uring_query(NULL, &invalid_data_hdr); + if (ret != -EFAULT) { + fprintf(stderr, "invalid next %i\n", ret); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} + +static int test_chain(void) +{ + int ret; + struct io_uring_query_opcode op1, op2, op3; + struct io_uring_query_hdr hdr3 = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = uring_ptr_to_u64(&op3), + .size = sizeof(struct io_uring_query_opcode), + }; + struct io_uring_query_hdr hdr2 = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = uring_ptr_to_u64(&op2), + .size = sizeof(struct io_uring_query_opcode), + .next_entry = uring_ptr_to_u64(&hdr3), + }; + struct io_uring_query_hdr hdr1 = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = uring_ptr_to_u64(&op1), + .size = sizeof(struct io_uring_query_opcode), + .next_entry = uring_ptr_to_u64(&hdr2), + }; + + ret = io_uring_query(NULL, &hdr1); + if (ret) { + fprintf(stderr, "chain failed %i\n", ret); + return T_EXIT_FAIL; + } + + if (hdr1.result || hdr2.result || hdr3.result) { + fprintf(stderr, "chain invalid result entries %i %i %i\n", + hdr1.result, hdr2.result, hdr3.result); + return T_EXIT_FAIL; + } + + if (op1.nr_register_opcodes != sys_ops.nr_register_opcodes || + op2.nr_register_opcodes != sys_ops.nr_register_opcodes || + op3.nr_register_opcodes != sys_ops.nr_register_opcodes) { + fprintf(stderr, "chain invalid register opcodes\n"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} + +static int test_chain_loop(void) +{ + int ret; + struct io_uring_query_opcode op1, op2; + struct io_uring_query_hdr hdr2 = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = uring_ptr_to_u64(&op2), + .size = sizeof(struct io_uring_query_opcode), + }; + struct io_uring_query_hdr hdr1 = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = uring_ptr_to_u64(&op1), + .size = sizeof(struct io_uring_query_opcode), + }; + struct io_uring_query_hdr hdr_self_circular = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = uring_ptr_to_u64(&op1), + .size = sizeof(struct io_uring_query_opcode), + .next_entry = uring_ptr_to_u64(&hdr_self_circular), + }; + + hdr1.next_entry = uring_ptr_to_u64(&hdr2); + hdr2.next_entry = uring_ptr_to_u64(&hdr1); + ret = io_uring_query(NULL, &hdr1); + if (!ret) { + fprintf(stderr, "chain loop failed %i\n", ret); + return T_EXIT_FAIL; + } + + ret = io_uring_query(NULL, &hdr_self_circular); + if (!ret) { + fprintf(stderr, "chain loop failed %i\n", ret); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} + +static int test_compatibile_shorter(void) +{ + int ret; + struct io_uring_query_opcode_short op; + struct io_uring_query_hdr hdr = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = uring_ptr_to_u64(&op), + .size = sizeof(op), + }; + + ret = io_uring_query(NULL, &hdr); + if (ret || hdr.result) { + fprintf(stderr, "failed invalid short result %i (%i)\n", + ret, hdr.result); + return T_EXIT_FAIL; + } + + if (hdr.size != sizeof(struct io_uring_query_opcode_short)) { + fprintf(stderr, "unexpected short query size %i %i\n", + (int)hdr.size, + (int)sizeof(struct io_uring_query_opcode_short)); + return T_EXIT_FAIL; + } + + if (sys_ops.nr_register_opcodes != op.nr_register_opcodes || + sys_ops.nr_request_opcodes != op.nr_request_opcodes) { + fprintf(stderr, "invalid short data\n"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} + +static int test_compatibile_larger(void) +{ + int ret; + struct io_uring_query_opcode_large op; + struct io_uring_query_hdr hdr = { + .query_op = IO_URING_QUERY_OPCODES, + .query_data = uring_ptr_to_u64(&op), + .size = sizeof(op), + }; + + ret = io_uring_query(NULL, &hdr); + if (ret || hdr.result) { + fprintf(stderr, "failed invalid large result %i (%i)\n", + ret, hdr.result); + return T_EXIT_FAIL; + } + + if (hdr.size < sizeof(struct io_uring_query_opcode)) { + fprintf(stderr, "unexpected large query size %i %i\n", + (int)hdr.size, + (int)sizeof(struct io_uring_query_opcode)); + return T_EXIT_FAIL; + } + + if (sys_ops.nr_register_opcodes != op.nr_register_opcodes || + sys_ops.nr_request_opcodes != op.nr_request_opcodes || + sys_ops.ring_setup_flags != op.ring_setup_flags || + sys_ops.feature_flags != op.feature_flags) { + fprintf(stderr, "invalid large data\n"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + int ret; + + if (argc > 1) + return 0; + + ret = test_basic_query(); + if (ret != T_EXIT_PASS) { + if (ret == T_EXIT_SKIP) + fprintf(stderr, "ring query not supported, skip\n"); + else + fprintf(stderr, "test_basic_query failed\n"); + + return T_EXIT_SKIP; + } + + ret = io_uring_queue_init(8, &ring, 0); + if (ret) { + fprintf(stderr, "init failed\n"); + return T_EXIT_FAIL; + } + + ret = test_invalid(); + if (ret) + return T_EXIT_FAIL; + + ret = test_chain(); + if (ret) { + fprintf(stderr, "test_chain failed\n"); + return T_EXIT_FAIL; + } + + ret = test_chain_loop(); + if (ret) { + fprintf(stderr, "test_chain_loop failed\n"); + return T_EXIT_FAIL; + } + + ret = test_compatibile_shorter(); + if (ret) { + fprintf(stderr, "test_compatibile_shorter failed\n"); + return T_EXIT_FAIL; + } + + ret = test_compatibile_larger(); + if (ret) { + fprintf(stderr, "test_compatibile_larger failed\n"); + return T_EXIT_FAIL; + } + + return 0; +} -- 2.49.0