public inbox for io-uring@vger.kernel.org
 help / color / mirror / Atom feed
* [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)&mp;
+	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