All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.