io-uring.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH liburing 0/2] mock file tests
@ 2025-05-26  7:36 Pavel Begunkov
  2025-05-26  7:36 ` [PATCH liburing 1/2] tests: test cmd regvec support with mock file Pavel Begunkov
  2025-05-26  7:36 ` [PATCH liburing 2/2] io_uring/tests: add read/write mock file tests Pavel Begunkov
  0 siblings, 2 replies; 3+ messages in thread
From: Pavel Begunkov @ 2025-05-26  7:36 UTC (permalink / raw)
  To: io-uring; +Cc: asml.silence

Add tests using mock files. Patch 1 adds basic infra and tests
vectored cmd with registered buffers. Patch 2 add a trivial
read/write test varying mock file options.

Pavel Begunkov (2):
  tests: test cmd regvec support with mock file
  io_uring/tests: add read/write mock file tests

 test/Makefile     |   1 +
 test/mock.h       |  47 ++++++
 test/mock_tests.c | 373 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 421 insertions(+)
 create mode 100644 test/mock.h
 create mode 100644 test/mock_tests.c

-- 
2.49.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH liburing 1/2] tests: test cmd regvec support with mock file
  2025-05-26  7:36 [PATCH liburing 0/2] mock file tests Pavel Begunkov
@ 2025-05-26  7:36 ` Pavel Begunkov
  2025-05-26  7:36 ` [PATCH liburing 2/2] io_uring/tests: add read/write mock file tests Pavel Begunkov
  1 sibling, 0 replies; 3+ messages in thread
From: Pavel Begunkov @ 2025-05-26  7:36 UTC (permalink / raw)
  To: io-uring; +Cc: asml.silence

Tests io_uring cmds with vectored registered buffers, which relies on
io_uring mock files.t

Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
 test/Makefile     |   1 +
 test/mock.h       |  47 +++++++++
 test/mock_tests.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 308 insertions(+)
 create mode 100644 test/mock.h
 create mode 100644 test/mock_tests.c

diff --git a/test/Makefile b/test/Makefile
index a0542dcb..e4309edc 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -245,6 +245,7 @@ test_srcs := \
 	xattr.c \
 	zcrx.c \
 	vec-regbuf.c \
+	mock_tests.c \
 	# EOL
 
 # Please keep this list sorted alphabetically.
diff --git a/test/mock.h b/test/mock.h
new file mode 100644
index 00000000..7fa71145
--- /dev/null
+++ b/test/mock.h
@@ -0,0 +1,47 @@
+#ifndef IOU_MOCK_H
+#define IOU_MOCK_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
\ No newline at end of file
diff --git a/test/mock_tests.c b/test/mock_tests.c
new file mode 100644
index 00000000..5f3063ff
--- /dev/null
+++ b/test/mock_tests.c
@@ -0,0 +1,260 @@
+#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.h"
+
+static struct io_uring mgr_ring;
+static __u64 mock_features;
+static int mgr_fd;
+
+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 (IORING_MOCK_FEAT_CMD_COPY < mock_features) {
+		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;
+}
+
+int main(int argc, char *argv[])
+{
+	int ret;
+
+	ret = setup_mgr();
+	if (ret)
+		return ret;
+
+	ret = test_cmds();
+	if (ret)
+		return T_EXIT_FAIL;
+
+	io_uring_queue_exit(&mgr_ring);
+	close(mgr_fd);
+	return 0;
+}
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH liburing 2/2] io_uring/tests: add read/write mock file tests
  2025-05-26  7:36 [PATCH liburing 0/2] mock file tests Pavel Begunkov
  2025-05-26  7:36 ` [PATCH liburing 1/2] tests: test cmd regvec support with mock file Pavel Begunkov
@ 2025-05-26  7:36 ` Pavel Begunkov
  1 sibling, 0 replies; 3+ messages in thread
From: Pavel Begunkov @ 2025-05-26  7:36 UTC (permalink / raw)
  To: io-uring; +Cc: asml.silence

Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
 test/mock_tests.c | 115 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 114 insertions(+), 1 deletion(-)

diff --git a/test/mock_tests.c b/test/mock_tests.c
index 5f3063ff..d1253ebb 100644
--- a/test/mock_tests.c
+++ b/test/mock_tests.c
@@ -17,6 +17,11 @@ 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;
@@ -228,7 +233,7 @@ static int test_cmds(void)
 		return 1;
 	}
 
-	if (IORING_MOCK_FEAT_CMD_COPY < mock_features) {
+	if (has_feature(IORING_MOCK_FEAT_CMD_COPY)) {
 		ret = test_regvec_cmd(&ring, mock_fd);
 		if (ret) {
 			fprintf(stderr, "test_regvec_cmd() failed\n");
@@ -242,6 +247,108 @@ static int test_cmds(void)
 	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;
@@ -254,6 +361,12 @@ int main(int argc, char *argv[])
 	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;
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-05-26  7:35 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-26  7:36 [PATCH liburing 0/2] mock file tests Pavel Begunkov
2025-05-26  7:36 ` [PATCH liburing 1/2] tests: test cmd regvec support with mock file Pavel Begunkov
2025-05-26  7:36 ` [PATCH liburing 2/2] io_uring/tests: add read/write mock file tests Pavel Begunkov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).