* [PATCH liburing 0/6] Add query and mock tests
@ 2025-09-11 11:26 Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 1/6] tests: test the query interface Pavel Begunkov
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-09-11 11:26 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Also introduces a bunch of test/ helper functions.
Pavel Begunkov (6):
tests: test the query interface
tests: add t_submit_and_wait_single helper
tests: introduce t_iovec_data_length helper
tests: add t_sqe_prep_cmd helper
tests: add helper for iov data verification
tests: add mock file based tests
src/include/liburing.h | 1 +
src/include/liburing/io_uring.h | 3 +
src/include/liburing/io_uring/query.h | 41 +++
test/Makefile | 2 +
test/helpers.c | 71 +++++
test/helpers.h | 14 +
test/mock_file.c | 373 ++++++++++++++++++++++++++
test/mock_file.h | 47 ++++
test/ring-query.c | 322 ++++++++++++++++++++++
test/vec-regbuf.c | 6 +-
10 files changed, 876 insertions(+), 4 deletions(-)
create mode 100644 src/include/liburing/io_uring/query.h
create mode 100644 test/mock_file.c
create mode 100644 test/mock_file.h
create mode 100644 test/ring-query.c
--
2.49.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH liburing 1/6] tests: test the query interface
2025-09-11 11:26 [PATCH liburing 0/6] Add query and mock tests Pavel Begunkov
@ 2025-09-11 11:26 ` Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 2/6] tests: add t_submit_and_wait_single helper Pavel Begunkov
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-09-11 11:26 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Add the header / definitions and tests for the query interface.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
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 <sys/wait.h>
#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 <linux/types.h>
+
+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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#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
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH liburing 2/6] tests: add t_submit_and_wait_single helper
2025-09-11 11:26 [PATCH liburing 0/6] Add query and mock tests Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 1/6] tests: test the query interface Pavel Begunkov
@ 2025-09-11 11:26 ` Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 3/6] tests: introduce t_iovec_data_length helper Pavel Begunkov
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-09-11 11:26 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
test/helpers.c | 17 +++++++++++++++++
test/helpers.h | 2 ++
2 files changed, 19 insertions(+)
diff --git a/test/helpers.c b/test/helpers.c
index 05895486..18af2be8 100644
--- a/test/helpers.c
+++ b/test/helpers.c
@@ -509,3 +509,20 @@ void t_clear_nonblock(int fd)
{
__t_toggle_nonblock(fd, 0);
}
+
+int t_submit_and_wait_single(struct io_uring *ring, struct io_uring_cqe **cqe)
+{
+ int ret;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return -1;
+ }
+ ret = io_uring_wait_cqe(ring, cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
diff --git a/test/helpers.h b/test/helpers.h
index 3f0c11ab..b7465890 100644
--- a/test/helpers.h
+++ b/test/helpers.h
@@ -122,6 +122,8 @@ unsigned long long mtime_since_now(struct timeval *tv);
unsigned long long utime_since(const struct timeval *s, const struct timeval *e);
unsigned long long utime_since_now(struct timeval *tv);
+int t_submit_and_wait_single(struct io_uring *ring, struct io_uring_cqe **cqe);
+
#ifdef __cplusplus
}
#endif
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH liburing 3/6] tests: introduce t_iovec_data_length helper
2025-09-11 11:26 [PATCH liburing 0/6] Add query and mock tests Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 1/6] tests: test the query interface Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 2/6] tests: add t_submit_and_wait_single helper Pavel Begunkov
@ 2025-09-11 11:26 ` Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 4/6] tests: add t_sqe_prep_cmd helper Pavel Begunkov
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-09-11 11:26 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
test/helpers.c | 10 ++++++++++
test/helpers.h | 2 ++
test/vec-regbuf.c | 6 ++----
3 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/test/helpers.c b/test/helpers.c
index 18af2be8..15ceb17a 100644
--- a/test/helpers.c
+++ b/test/helpers.c
@@ -526,3 +526,13 @@ int t_submit_and_wait_single(struct io_uring *ring, struct io_uring_cqe **cqe)
}
return 0;
}
+
+size_t t_iovec_data_length(struct iovec *iov, unsigned iov_len)
+{
+ size_t sz = 0;
+ int i;
+
+ for (i = 0; i < iov_len; i++)
+ sz += iov[i].iov_len;
+ return sz;
+}
diff --git a/test/helpers.h b/test/helpers.h
index b7465890..a45b8683 100644
--- a/test/helpers.h
+++ b/test/helpers.h
@@ -124,6 +124,8 @@ unsigned long long utime_since_now(struct timeval *tv);
int t_submit_and_wait_single(struct io_uring *ring, struct io_uring_cqe **cqe);
+size_t t_iovec_data_length(struct iovec *iov, unsigned iov_len);
+
#ifdef __cplusplus
}
#endif
diff --git a/test/vec-regbuf.c b/test/vec-regbuf.c
index 286b78c6..0cbe2c74 100644
--- a/test/vec-regbuf.c
+++ b/test/vec-regbuf.c
@@ -269,7 +269,7 @@ static int test_vec(struct buf_desc *bd, struct iovec *vecs, int nr_vec,
struct sockaddr_storage addr;
int sock_server, sock_client;
struct verify_data vd;
- size_t total_len = 0;
+ size_t total_len;
int i, ret;
void *verify_res;
pthread_t th;
@@ -284,9 +284,7 @@ static int test_vec(struct buf_desc *bd, struct iovec *vecs, int nr_vec,
for (i = 0; i < bd->size; i++)
bd->buf_wr[i] = i;
memset(bd->buf_rd, 0, bd->size);
-
- for (i = 0; i < nr_vec; i++)
- total_len += vecs[i].iov_len;
+ total_len = t_iovec_data_length(vecs, nr_vec);
vd.bd = bd;
vd.vecs = vecs;
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH liburing 4/6] tests: add t_sqe_prep_cmd helper
2025-09-11 11:26 [PATCH liburing 0/6] Add query and mock tests Pavel Begunkov
` (2 preceding siblings ...)
2025-09-11 11:26 ` [PATCH liburing 3/6] tests: introduce t_iovec_data_length helper Pavel Begunkov
@ 2025-09-11 11:26 ` Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 5/6] tests: add helper for iov data verification Pavel Begunkov
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-09-11 11:26 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
test/helpers.h | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/test/helpers.h b/test/helpers.h
index a45b8683..7dafeb17 100644
--- a/test/helpers.h
+++ b/test/helpers.h
@@ -126,6 +126,13 @@ int t_submit_and_wait_single(struct io_uring *ring, struct io_uring_cqe **cqe);
size_t t_iovec_data_length(struct iovec *iov, unsigned iov_len);
+static inline void t_sqe_prep_cmd(struct io_uring_sqe *sqe,
+ int fd, unsigned cmd_op)
+{
+ io_uring_prep_rw(IORING_OP_URING_CMD, sqe, fd, NULL, 0, 0);
+ sqe->cmd_op = cmd_op;
+}
+
#ifdef __cplusplus
}
#endif
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH liburing 5/6] tests: add helper for iov data verification
2025-09-11 11:26 [PATCH liburing 0/6] Add query and mock tests Pavel Begunkov
` (3 preceding siblings ...)
2025-09-11 11:26 ` [PATCH liburing 4/6] tests: add t_sqe_prep_cmd helper Pavel Begunkov
@ 2025-09-11 11:26 ` Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 6/6] tests: add mock file based tests Pavel Begunkov
2025-09-19 13:15 ` [PATCH liburing 0/6] Add query and mock tests Jens Axboe
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-09-11 11:26 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
test/helpers.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
test/helpers.h | 3 +++
2 files changed, 47 insertions(+)
diff --git a/test/helpers.c b/test/helpers.c
index 15ceb17a..83a128e3 100644
--- a/test/helpers.c
+++ b/test/helpers.c
@@ -536,3 +536,47 @@ size_t t_iovec_data_length(struct iovec *iov, unsigned iov_len)
sz += iov[i].iov_len;
return sz;
}
+
+#define t_min(a, b) ((a) < (b) ? (a) : (b))
+
+unsigned long t_compare_data_iovec(struct iovec *iov_src, unsigned nr_src,
+ struct iovec *iov_dst, unsigned nr_dst)
+{
+ size_t src_len = t_iovec_data_length(iov_src, nr_src);
+ size_t dst_len = t_iovec_data_length(iov_dst, nr_dst);
+ size_t len_left = t_min(src_len, dst_len);
+ unsigned long src_off = 0, dst_off = 0;
+ unsigned long offset = 0;
+
+ while (offset != len_left) {
+ size_t len = len_left - offset;
+ unsigned long i;
+
+ len = t_min(len, iov_src->iov_len - src_off);
+ len = t_min(len, iov_dst->iov_len - dst_off);
+
+ for (i = 0; i < len; i++) {
+ char csrc = ((char *)iov_src->iov_base)[src_off + i];
+ char cdst = ((char *)iov_dst->iov_base)[dst_off + i];
+
+ if (csrc != cdst) {
+ fprintf(stderr, "data mismatch, %i vs %i\n",
+ csrc, cdst);
+ return -EINVAL;
+ }
+ }
+
+ src_off += len;
+ dst_off += len;
+ if (src_off == iov_src->iov_len) {
+ src_off = 0;
+ iov_src++;
+ }
+ if (dst_off == iov_dst->iov_len) {
+ dst_off = 0;
+ iov_dst++;
+ }
+ offset += len;
+ }
+ return 0;
+}
diff --git a/test/helpers.h b/test/helpers.h
index 7dafeb17..cfada945 100644
--- a/test/helpers.h
+++ b/test/helpers.h
@@ -126,6 +126,9 @@ int t_submit_and_wait_single(struct io_uring *ring, struct io_uring_cqe **cqe);
size_t t_iovec_data_length(struct iovec *iov, unsigned iov_len);
+unsigned long t_compare_data_iovec(struct iovec *iov_src, unsigned nr_src,
+ struct iovec *iov_dst, unsigned nr_dst);
+
static inline void t_sqe_prep_cmd(struct io_uring_sqe *sqe,
int fd, unsigned cmd_op)
{
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH liburing 6/6] tests: add mock file based tests
2025-09-11 11:26 [PATCH liburing 0/6] Add query and mock tests Pavel Begunkov
` (4 preceding siblings ...)
2025-09-11 11:26 ` [PATCH liburing 5/6] tests: add helper for iov data verification Pavel Begunkov
@ 2025-09-11 11:26 ` Pavel Begunkov
2025-09-19 13:15 ` [PATCH liburing 0/6] Add query and mock tests Jens Axboe
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-09-11 11:26 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Tests io_uring cmds with vectored registered buffers, which relies on
io_uring mock files. Also test read/write.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
test/Makefile | 1 +
test/mock_file.c | 373 +++++++++++++++++++++++++++++++++++++++++++++++
test/mock_file.h | 47 ++++++
3 files changed, 421 insertions(+)
create mode 100644 test/mock_file.c
create mode 100644 test/mock_file.h
diff --git a/test/Makefile b/test/Makefile
index 626ae674..c1afda5c 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -256,6 +256,7 @@ test_srcs := \
vec-regbuf.c \
timestamp.c \
ring-query.c \
+ mock_file.c \
# EOL
# Please keep this list sorted alphabetically.
diff --git a/test/mock_file.c b/test/mock_file.c
new file mode 100644
index 00000000..e7c3c669
--- /dev/null
+++ b/test/mock_file.c
@@ -0,0 +1,373 @@
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <assert.h>
+
+#include "liburing.h"
+#include "test.h"
+#include "helpers.h"
+
+#include "mock_file.h"
+
+static struct io_uring mgr_ring;
+static __u64 mock_features;
+static int mgr_fd;
+
+static bool has_feature(int feature)
+{
+ return mock_features >= feature;
+}
+
+static int setup_mgr(void)
+{
+ struct io_uring_mock_probe mp;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret;
+
+ ret = mgr_fd = open("/dev/io_uring_mock", O_RDWR);
+ if (mgr_fd < 0) {
+ printf("no io_uring mock files, skip\n");
+ return T_EXIT_SKIP;
+ }
+
+ ret = io_uring_queue_init(8, &mgr_ring, 0);
+ if (ret) {
+ fprintf(stderr, "mgr ring setup failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ memset(&mp, 0, sizeof(mp));
+ sqe = io_uring_get_sqe(&mgr_ring);
+ t_sqe_prep_cmd(sqe, mgr_fd, IORING_MOCK_MGR_CMD_PROBE);
+ sqe->addr = (__u64)(unsigned long)∓
+ sqe->len = sizeof(mp);
+
+ ret = t_submit_and_wait_single(&mgr_ring, &cqe);
+ if (ret || cqe->res) {
+ fprintf(stderr, "probe cmd failed %i %i\n", ret, cqe->res);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(&mgr_ring, cqe);
+ mock_features = mp.features;
+ return 0;
+}
+
+static int create_mock_file(struct io_uring_mock_create *mc)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(&mgr_ring);
+ t_sqe_prep_cmd(sqe, mgr_fd, IORING_MOCK_MGR_CMD_CREATE);
+ sqe->addr = (__u64)(unsigned long)mc;
+ sqe->len = sizeof(*mc);
+
+ ret = t_submit_and_wait_single(&mgr_ring, &cqe);
+ if (ret || cqe->res) {
+ fprintf(stderr, "file create cmd failed %i %i\n", ret, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&mgr_ring, cqe);
+ return 0;
+}
+
+static int t_copy_regvec(struct io_uring *ring, int mock_fd,
+ struct iovec *iov, unsigned iov_len, char *buf,
+ bool from_iov)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ t_sqe_prep_cmd(sqe, mock_fd, IORING_MOCK_CMD_COPY_REGBUF);
+ sqe->addr3 = (__u64)(unsigned long)buf;
+ sqe->addr = (__u64)(unsigned long)iov;
+ sqe->len = iov_len;
+ if (from_iov)
+ sqe->file_index = IORING_MOCK_COPY_FROM;
+ sqe->buf_index = from_iov ? 0 : 1;
+ sqe->user_data = 43;
+ sqe->uring_cmd_flags |= IORING_URING_CMD_FIXED;
+
+ ret = t_submit_and_wait_single(ring, &cqe);
+ if (ret)
+ t_error(1, ret, "submit/wait failed");
+
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+static int t_copy_verify_regvec(struct io_uring *ring, int mock_fd,
+ struct iovec *iov, unsigned iov_len, char *buf,
+ bool from_iov)
+{
+ struct iovec iov2;
+ int ret;
+
+ ret = t_copy_regvec(ring, mock_fd, iov, iov_len, buf, from_iov);
+ if (ret < 0 || ret != t_iovec_data_length(iov, iov_len))
+ return ret < 0 ? ret : -1;
+
+ iov2.iov_base = buf;
+ iov2.iov_len = -1U;
+
+ ret = t_compare_data_iovec(iov, iov_len, &iov2, 1);
+ if (ret) {
+ fprintf(stderr, "iovec1 data mismatch %i\n", ret);
+ return -1;
+ }
+ return 0;
+}
+
+static int test_regvec_cmd(struct io_uring *ring, int mock_fd)
+{
+ struct iovec buf_iovec[2];
+ struct iovec iov[8];
+ size_t size = 4096 * 32;
+ char *buf_src, *buf_dst;
+ int i, ret;
+
+ buf_src = aligned_alloc(4096, size);
+ buf_dst = aligned_alloc(4096, size);
+ if (!buf_src || !buf_dst)
+ t_error(0, -ENOMEM, "can't allocate buffers");
+
+ for (i = 0; i < size; i++)
+ buf_src[i] = 'a' + (i % 26);
+
+ buf_iovec[0].iov_base = buf_src;
+ buf_iovec[0].iov_len = size;
+ buf_iovec[1].iov_base = buf_dst;
+ buf_iovec[1].iov_len = size;
+ ret = t_register_buffers(ring, buf_iovec, 2);
+ if (ret) {
+ free(buf_src);
+ free(buf_dst);
+ return ret == T_SETUP_SKIP ? 0 : T_EXIT_FAIL;
+ }
+
+ memset(buf_dst, 0, size);
+ iov[0].iov_len = size;
+ iov[0].iov_base = buf_src;
+ ret = t_copy_verify_regvec(ring, mock_fd, iov, 1, buf_dst, true);
+ if (ret < 0) {
+ fprintf(stderr, "t_copy_verify_regvec iovec1 failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ memset(buf_dst, 0, size);
+ iov[0].iov_len = size;
+ iov[0].iov_base = buf_dst;
+ ret = t_copy_verify_regvec(ring, mock_fd, iov, 1, buf_src, false);
+ if (ret < 0) {
+ fprintf(stderr, "t_copy_verify_regvec iovec1 reverse failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ memset(buf_dst, 0, size);
+ iov[0].iov_base = buf_src;
+ iov[0].iov_len = 5;
+ iov[1].iov_base = buf_src + 5;
+ iov[1].iov_len = 11;
+ iov[2].iov_base = buf_src + (4096 - 127);
+ iov[2].iov_len = 127;
+ iov[3].iov_base = buf_src + (4096 - 127);
+ iov[3].iov_len = 127 + 4096 + 13;
+ iov[4].iov_base = buf_src + 4 * 4096;
+ iov[4].iov_len = 4096 + 73;
+ iov[5].iov_base = buf_src + 7 * 4096 + 127;
+ iov[5].iov_len = 4096 * 11 + 132;
+ assert(t_iovec_data_length(iov, 6) <= size);
+ ret = t_copy_verify_regvec(ring, mock_fd, iov, 6, buf_dst, true);
+ if (ret < 0) {
+ fprintf(stderr, "t_copy_verify_regvec iovec6 failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ memset(buf_dst, 0, size);
+ iov[0].iov_base = buf_dst;
+ iov[0].iov_len = 5;
+ iov[1].iov_base = buf_dst + 5;
+ iov[1].iov_len = 11;
+ iov[2].iov_base = buf_dst + (4096 - 127);
+ iov[2].iov_len = 127;
+ iov[3].iov_base = buf_dst + 4 * 4096;
+ iov[3].iov_len = 4096 + 73;
+ iov[4].iov_base = buf_dst + 7 * 4096 + 127;
+ iov[4].iov_len = 4096 * 11 + 132;
+ assert(t_iovec_data_length(iov, 5) <= size);
+ ret = t_copy_verify_regvec(ring, mock_fd, iov, 5, buf_src, false);
+ if (ret < 0) {
+ fprintf(stderr, "t_copy_verify_regvec iovec6 reverse failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ free(buf_src);
+ free(buf_dst);
+ return 0;
+}
+
+static int test_cmds(void)
+{
+ struct io_uring_mock_create mc;
+ struct io_uring ring;
+ int ret, mock_fd;
+
+ memset(&mc, 0, sizeof(mc));
+ if (create_mock_file(&mc))
+ return T_EXIT_FAIL;
+ mock_fd = mc.out_fd;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ if (has_feature(IORING_MOCK_FEAT_CMD_COPY)) {
+ ret = test_regvec_cmd(&ring, mock_fd);
+ if (ret) {
+ fprintf(stderr, "test_regvec_cmd() failed\n");
+ return T_EXIT_FAIL;
+ }
+ } else {
+ printf("skip test_regvec_cmd()\n");
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_reads(struct io_uring *ring, int mock_fd, void *buffer)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int io_len = 4096;
+ int nr_reqs = 16;
+ int i, ret;
+
+ for (i = 0; i < nr_reqs; i++) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_read(sqe, mock_fd, buffer, io_len, 0);
+ sqe->user_data = i;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != nr_reqs) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, nr_reqs);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < nr_reqs; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->res != io_len) {
+ fprintf(stderr, "unexpected cqe res %i, data %i\n",
+ cqe->res, (int)cqe->user_data);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+ return 0;
+}
+
+static int test_rw(void)
+{
+ void *buffer;
+ struct io_uring ring;
+ int ret, i;
+
+ if (!has_feature(IORING_MOCK_FEAT_RW_ZERO)) {
+ printf("no mock read-write support, skip\n");
+ return T_EXIT_SKIP;
+ }
+
+ buffer = malloc(4096);
+ if (!buffer) {
+ fprintf(stderr, "can't allocate buffers\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < 8; i++) {
+ struct io_uring_mock_create mc;
+ bool nowait = i & 1;
+ bool async = i & 2;
+ bool poll = i & 4;
+ int mock_fd;
+
+ memset(&mc, 0, sizeof(mc));
+ if (poll) {
+ if (!has_feature(IORING_MOCK_FEAT_POLL))
+ continue;
+ mc.flags |= IORING_MOCK_CREATE_F_POLL;
+ }
+ if (nowait) {
+ if (!has_feature(IORING_MOCK_FEAT_RW_NOWAIT))
+ continue;
+ mc.flags |= IORING_MOCK_CREATE_F_SUPPORT_NOWAIT;
+ }
+ if (async) {
+ if (!has_feature(IORING_MOCK_FEAT_RW_ASYNC))
+ continue;
+ mc.rw_delay_ns = 1000 * 1000 * 50;
+ }
+ mc.file_size = 10 * (1UL << 20);
+ if (create_mock_file(&mc))
+ return T_EXIT_FAIL;
+ mock_fd = mc.out_fd;
+
+ ret = test_reads(&ring, mock_fd, buffer);
+ if (ret) {
+ fprintf(stderr, "rw failed %i/%i/%i\n",
+ nowait, async, poll);
+ return T_EXIT_FAIL;
+ }
+
+ close(mock_fd);
+ }
+
+ free(buffer);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ ret = setup_mgr();
+ if (ret)
+ return ret;
+
+ ret = test_cmds();
+ if (ret)
+ return T_EXIT_FAIL;
+
+ ret = test_rw();
+ if (ret) {
+ fprintf(stderr, "test_rw failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&mgr_ring);
+ close(mgr_fd);
+ return 0;
+}
diff --git a/test/mock_file.h b/test/mock_file.h
new file mode 100644
index 00000000..debeee8e
--- /dev/null
+++ b/test/mock_file.h
@@ -0,0 +1,47 @@
+#ifndef LINUX_IO_URING_MOCK_FILE_H
+#define LINUX_IO_URING_MOCK_FILE_H
+
+#include <linux/types.h>
+
+enum {
+ IORING_MOCK_FEAT_CMD_COPY,
+ IORING_MOCK_FEAT_RW_ZERO,
+ IORING_MOCK_FEAT_RW_NOWAIT,
+ IORING_MOCK_FEAT_RW_ASYNC,
+ IORING_MOCK_FEAT_POLL,
+
+ IORING_MOCK_FEAT_END,
+};
+
+struct io_uring_mock_probe {
+ __u64 features;
+ __u64 __resv[9];
+};
+
+enum {
+ IORING_MOCK_CREATE_F_SUPPORT_NOWAIT = 1,
+ IORING_MOCK_CREATE_F_POLL = 2,
+};
+
+struct io_uring_mock_create {
+ __u32 out_fd;
+ __u32 flags;
+ __u64 file_size;
+ __u64 rw_delay_ns;
+ __u64 __resv[13];
+};
+
+enum {
+ IORING_MOCK_MGR_CMD_PROBE,
+ IORING_MOCK_MGR_CMD_CREATE,
+};
+
+enum {
+ IORING_MOCK_CMD_COPY_REGBUF,
+};
+
+enum {
+ IORING_MOCK_COPY_FROM = 1,
+};
+
+#endif
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH liburing 0/6] Add query and mock tests
2025-09-11 11:26 [PATCH liburing 0/6] Add query and mock tests Pavel Begunkov
` (5 preceding siblings ...)
2025-09-11 11:26 ` [PATCH liburing 6/6] tests: add mock file based tests Pavel Begunkov
@ 2025-09-19 13:15 ` Jens Axboe
6 siblings, 0 replies; 8+ messages in thread
From: Jens Axboe @ 2025-09-19 13:15 UTC (permalink / raw)
To: io-uring, Pavel Begunkov
On Thu, 11 Sep 2025 12:26:25 +0100, Pavel Begunkov wrote:
> Also introduces a bunch of test/ helper functions.
>
> Pavel Begunkov (6):
> tests: test the query interface
> tests: add t_submit_and_wait_single helper
> tests: introduce t_iovec_data_length helper
> tests: add t_sqe_prep_cmd helper
> tests: add helper for iov data verification
> tests: add mock file based tests
>
> [...]
Applied, thanks!
[1/6] tests: test the query interface
commit: 7e565c0116ba6e0cd1bce3a42409b31fd4dd47d3
[2/6] tests: add t_submit_and_wait_single helper
commit: f1fc45cbcdcd35064b2fbe3eab6a2b89fb335ec6
[3/6] tests: introduce t_iovec_data_length helper
commit: 7a936a80be37f50a1851379aa0592eeb3b42a9a1
[4/6] tests: add t_sqe_prep_cmd helper
commit: 7d3773fd9e5352b113b7d425aa5708acdd48d3c0
[5/6] tests: add helper for iov data verification
commit: 9e69daf86de39c9b4e70c2dd23e4046293585f34
[6/6] tests: add mock file based tests
commit: d5673a9b4ad074745e28bf7ddad3692115da01fd
Best regards,
--
Jens Axboe
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-09-19 13:15 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-11 11:26 [PATCH liburing 0/6] Add query and mock tests Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 1/6] tests: test the query interface Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 2/6] tests: add t_submit_and_wait_single helper Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 3/6] tests: introduce t_iovec_data_length helper Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 4/6] tests: add t_sqe_prep_cmd helper Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 5/6] tests: add helper for iov data verification Pavel Begunkov
2025-09-11 11:26 ` [PATCH liburing 6/6] tests: add mock file based tests Pavel Begunkov
2025-09-19 13:15 ` [PATCH liburing 0/6] Add query and mock tests Jens Axboe
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox