* [RFC 0/2] mock files for advanced io_uring testing
@ 2025-05-22 15:10 Pavel Begunkov
2025-05-22 15:10 ` [PATCH 1/2] io_uring/mock: add basic infra for test mock files Pavel Begunkov
2025-05-22 15:10 ` [PATCH 2/2] io_uring/mock: add cmd using vectored regbufs Pavel Begunkov
0 siblings, 2 replies; 3+ messages in thread
From: Pavel Begunkov @ 2025-05-22 15:10 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, chase xd
There are core io_uring features that are hard to test without having
some specific file types. That includes various read/write and polling
edge cases, but the problem got even more distinct with the introduction
of commands and io_uring adding new features for them.
For improving testing we need a finer control over file behaviour. Add
special / mock files we can create and configure. First, the file is
implementing a set of io_uring commands that uses various cmd io_uring
features.
Chase also suggestedd adding read/write/poll file operations and varying
their characteristics: pollability, nowait support, whether the IO is
async, etc., which will be added in the future.
Liburing tests:
https://github.com/isilence/liburing/tree/mock-file-tests
git https://github.com/isilence/liburing.git mock-file-tests
Pavel Begunkov (2):
io_uring/mock: add basic infra for test mock files
io_uring/mock: add cmd using vectored regbufs
init/Kconfig | 11 +++
io_uring/Makefile | 1 +
io_uring/mock_file.c | 207 +++++++++++++++++++++++++++++++++++++++++++
io_uring/mock_file.h | 34 +++++++
4 files changed, 253 insertions(+)
create mode 100644 io_uring/mock_file.c
create mode 100644 io_uring/mock_file.h
--
2.49.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] io_uring/mock: add basic infra for test mock files
2025-05-22 15:10 [RFC 0/2] mock files for advanced io_uring testing Pavel Begunkov
@ 2025-05-22 15:10 ` Pavel Begunkov
2025-05-22 15:10 ` [PATCH 2/2] io_uring/mock: add cmd using vectored regbufs Pavel Begunkov
1 sibling, 0 replies; 3+ messages in thread
From: Pavel Begunkov @ 2025-05-22 15:10 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, chase xd
io_uring commands provide an ioctl style interface for files to
implement file specific operations. io_uring provides many features and
advanced api to commands, and it's getting hard to test as it requires
specific files/devices.
Add basic infrastucture for creating special mock files that will be
implementing the cmd api and using various io_uring features we want to
test. It'll also be useful to test some more obscure read/write/polling
edge cases in the future.
Suggested-by: chase xd <sl1589472800@gmail.com>
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
init/Kconfig | 11 ++++
io_uring/Makefile | 1 +
io_uring/mock_file.c | 139 +++++++++++++++++++++++++++++++++++++++++++
io_uring/mock_file.h | 22 +++++++
4 files changed, 173 insertions(+)
create mode 100644 io_uring/mock_file.c
create mode 100644 io_uring/mock_file.h
diff --git a/init/Kconfig b/init/Kconfig
index 63f5974b9fa6..856b37c2de8d 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1774,6 +1774,17 @@ config GCOV_PROFILE_URING
the io_uring subsystem, hence this should only be enabled for
specific test purposes.
+config IO_URING_MOCK_FILE
+ tristate "Enable io_uring mock files (Experimental)" if EXPERT
+ default n
+ depends on IO_URING
+ help
+ Enable mock files for io_uring subststem testing. The ABI might
+ still change, so it's still experimental and should only be enabled
+ for specific test purposes.
+
+ If unsure, say N.
+
config ADVISE_SYSCALLS
bool "Enable madvise/fadvise syscalls" if EXPERT
default y
diff --git a/io_uring/Makefile b/io_uring/Makefile
index 11a739927a62..a630f369828d 100644
--- a/io_uring/Makefile
+++ b/io_uring/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_IO_URING) += io_uring.o opdef.o kbuf.o rsrc.o notif.o \
statx.o timeout.o fdinfo.o cancel.o \
waitid.o register.o truncate.o \
memmap.o alloc_cache.o
+obj-$(CONFIG_IO_URING_MOCK_FILE) += mock_file.o
obj-$(CONFIG_IO_URING_ZCRX) += zcrx.o
obj-$(CONFIG_IO_WQ) += io-wq.o
obj-$(CONFIG_FUTEX) += futex.o
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
new file mode 100644
index 000000000000..e8ec0aeddbae
--- /dev/null
+++ b/io_uring/mock_file.c
@@ -0,0 +1,139 @@
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/anon_inodes.h>
+
+#include <linux/io_uring/cmd.h>
+#include <linux/io_uring_types.h>
+#include "mock_file.h"
+
+static int io_mock_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
+{
+ return -ENOTSUPP;
+}
+
+static const struct file_operations io_mock_fops = {
+ .owner = THIS_MODULE,
+ .uring_cmd = io_mock_cmd,
+};
+
+static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flags)
+{
+ const struct io_uring_sqe *sqe = cmd->sqe;
+ struct io_uring_mock_create mc, __user *uarg;
+ struct file *file = NULL;
+ size_t uarg_size;
+ int fd, ret;
+
+ uarg = u64_to_user_ptr(READ_ONCE(sqe->addr));
+ uarg_size = READ_ONCE(sqe->len);
+
+ if (sqe->ioprio || sqe->__pad1 || sqe->addr3 || sqe->file_index)
+ return -EINVAL;
+ if (uarg_size != sizeof(mc))
+ return -EINVAL;
+
+ memset(&mc, 0, sizeof(mc));
+ if (copy_from_user(&mc, uarg, uarg_size))
+ return -EFAULT;
+ if (!mem_is_zero(mc.__resv, sizeof(mc.__resv)) || mc.flags)
+ return -EINVAL;
+
+ fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ file = anon_inode_create_getfile("[io_uring_mock]", &io_mock_fops,
+ NULL, O_RDWR | O_CLOEXEC, NULL);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto fail;
+ }
+
+ mc.out_fd = fd;
+ if (copy_to_user(uarg, &mc, uarg_size)) {
+ fput(file);
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ fd_install(fd, file);
+ return 0;
+fail:
+ put_unused_fd(fd);
+ return ret;
+}
+
+static int io_probe_mock(struct io_uring_cmd *cmd)
+{
+ const struct io_uring_sqe *sqe = cmd->sqe;
+ struct io_uring_mock_probe mp, __user *uarg;
+ size_t uarg_size;
+
+ uarg = u64_to_user_ptr(READ_ONCE(sqe->addr));
+ uarg_size = READ_ONCE(sqe->len);
+
+ if (sqe->ioprio || sqe->__pad1 || sqe->addr3 || sqe->file_index ||
+ uarg_size != sizeof(mp))
+ return -EINVAL;
+
+ memset(&mp, 0, sizeof(mp));
+ if (copy_from_user(&mp, uarg, uarg_size))
+ return -EFAULT;
+ if (!mem_is_zero(&mp, sizeof(mp)))
+ return -EINVAL;
+
+ mp.features = 0;
+
+ if (copy_to_user(uarg, &mp, uarg_size))
+ return -EFAULT;
+ return 0;
+}
+
+static int iou_mock_mgr_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
+{
+ switch (cmd->cmd_op) {
+ case IORING_MOCK_MGR_CMD_PROBE:
+ return io_probe_mock(cmd);
+ case IORING_MOCK_MGR_CMD_CREATE:
+ return io_create_mock_file(cmd, issue_flags);
+ }
+ return -EOPNOTSUPP;
+}
+
+static const struct file_operations iou_mock_dev_fops = {
+ .owner = THIS_MODULE,
+ .uring_cmd = iou_mock_mgr_cmd,
+};
+
+static struct miscdevice iou_mock_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "io_uring_mock",
+ .fops = &iou_mock_dev_fops,
+};
+
+static int __init io_mock_init(void)
+{
+ int ret;
+
+ ret = misc_register(&iou_mock_miscdev);
+ if (ret < 0) {
+ pr_err("Could not initialize io_uring mock device\n");
+ return ret;
+ }
+ return 0;
+}
+
+static void __exit io_mock_exit(void)
+{
+ misc_deregister(&iou_mock_miscdev);
+}
+
+module_init(io_mock_init)
+module_exit(io_mock_exit)
+
+MODULE_AUTHOR("Pavel Begunkov <asml.silence@gmail.com>");
+MODULE_DESCRIPTION("io_uring mock file");
+MODULE_LICENSE("GPL");
diff --git a/io_uring/mock_file.h b/io_uring/mock_file.h
new file mode 100644
index 000000000000..de7318a5b1f1
--- /dev/null
+++ b/io_uring/mock_file.h
@@ -0,0 +1,22 @@
+#ifndef IOU_MOCK_H
+#define IOU_MOCK_H
+
+#include <linux/types.h>
+
+struct io_uring_mock_probe {
+ __u64 features;
+ __u64 __resv[9];
+};
+
+struct io_uring_mock_create {
+ __u32 out_fd;
+ __u32 flags;
+ __u64 __resv[15];
+};
+
+enum {
+ IORING_MOCK_MGR_CMD_PROBE,
+ IORING_MOCK_MGR_CMD_CREATE,
+};
+
+#endif
\ No newline at end of file
--
2.49.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/2] io_uring/mock: add cmd using vectored regbufs
2025-05-22 15:10 [RFC 0/2] mock files for advanced io_uring testing Pavel Begunkov
2025-05-22 15:10 ` [PATCH 1/2] io_uring/mock: add basic infra for test mock files Pavel Begunkov
@ 2025-05-22 15:10 ` Pavel Begunkov
1 sibling, 0 replies; 3+ messages in thread
From: Pavel Begunkov @ 2025-05-22 15:10 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, chase xd
There is a command api allowing to import vectored registered buffers,
add a new mock command that uses the feature and simply copies the
specified registered buffer into user space or vice versa.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
io_uring/mock_file.c | 70 +++++++++++++++++++++++++++++++++++++++++++-
io_uring/mock_file.h | 12 ++++++++
2 files changed, 81 insertions(+), 1 deletion(-)
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index e8ec0aeddbae..45fc5c9f939e 100644
--- a/io_uring/mock_file.c
+++ b/io_uring/mock_file.c
@@ -9,8 +9,76 @@
#include <linux/io_uring_types.h>
#include "mock_file.h"
+#define IO_VALID_COPY_CMD_FLAGS IORING_MOCK_COPY_FROM
+
+static int io_copy_regbuf(struct iov_iter *reg_iter, void __user *ubuf)
+{
+ size_t ret, copied = 0;
+ size_t buflen = PAGE_SIZE;
+ void *tmp_buf;
+
+ tmp_buf = kzalloc(buflen, GFP_KERNEL);
+ if (!tmp_buf)
+ return -ENOMEM;
+
+ while (iov_iter_count(reg_iter)) {
+ size_t len = min(iov_iter_count(reg_iter), buflen);
+
+ if (iov_iter_rw(reg_iter) == ITER_SOURCE) {
+ ret = copy_from_iter(tmp_buf, len, reg_iter);
+ if (ret <= 0)
+ break;
+ if (copy_to_user(ubuf, tmp_buf, ret))
+ break;
+ } else {
+ if (copy_from_user(tmp_buf, ubuf, len))
+ break;
+ ret = copy_to_iter(tmp_buf, len, reg_iter);
+ if (ret <= 0)
+ break;
+ }
+ ubuf += ret;
+ copied += ret;
+ }
+
+ kfree(tmp_buf);
+ return copied;
+}
+
+static int io_cmd_copy_regbuf(struct io_uring_cmd *cmd, unsigned int issue_flags)
+{
+ const struct io_uring_sqe *sqe = cmd->sqe;
+ const struct iovec __user *iovec;
+ unsigned flags, iovec_len;
+ struct iov_iter iter;
+ void __user *ubuf;
+ int dir, ret;
+
+ ubuf = u64_to_user_ptr(READ_ONCE(sqe->addr3));
+ iovec = u64_to_user_ptr(READ_ONCE(sqe->addr));
+ iovec_len = READ_ONCE(sqe->len);
+ flags = READ_ONCE(sqe->file_index);
+
+ if (unlikely(sqe->ioprio || sqe->__pad1))
+ return -EINVAL;
+ if (flags & ~IO_VALID_COPY_CMD_FLAGS)
+ return -EINVAL;
+
+ dir = (flags & IORING_MOCK_COPY_FROM) ? ITER_SOURCE : ITER_DEST;
+ ret = io_uring_cmd_import_fixed_vec(cmd, iovec, iovec_len, dir, &iter,
+ issue_flags);
+ if (ret)
+ return ret;
+ ret = io_copy_regbuf(&iter, ubuf);
+ return ret ? ret : -EFAULT;
+}
+
static int io_mock_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
{
+ switch (cmd->cmd_op) {
+ case IORING_MOCK_CMD_COPY_REGBUF:
+ return io_cmd_copy_regbuf(cmd, issue_flags);
+ }
return -ENOTSUPP;
}
@@ -85,7 +153,7 @@ static int io_probe_mock(struct io_uring_cmd *cmd)
if (!mem_is_zero(&mp, sizeof(mp)))
return -EINVAL;
- mp.features = 0;
+ mp.features = IORING_MOCK_FEAT_COPY_CMD;
if (copy_to_user(uarg, &mp, uarg_size))
return -EFAULT;
diff --git a/io_uring/mock_file.h b/io_uring/mock_file.h
index de7318a5b1f1..9a6f47bab01e 100644
--- a/io_uring/mock_file.h
+++ b/io_uring/mock_file.h
@@ -3,6 +3,10 @@
#include <linux/types.h>
+enum {
+ IORING_MOCK_FEAT_COPY_CMD = 1,
+};
+
struct io_uring_mock_probe {
__u64 features;
__u64 __resv[9];
@@ -19,4 +23,12 @@ enum {
IORING_MOCK_MGR_CMD_CREATE,
};
+enum {
+ IORING_MOCK_CMD_COPY_REGBUF,
+};
+
+enum {
+ IORING_MOCK_COPY_FROM = 1,
+};
+
#endif
\ No newline at end of file
--
2.49.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2025-05-22 15:09 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-22 15:10 [RFC 0/2] mock files for advanced io_uring testing Pavel Begunkov
2025-05-22 15:10 ` [PATCH 1/2] io_uring/mock: add basic infra for test mock files Pavel Begunkov
2025-05-22 15:10 ` [PATCH 2/2] io_uring/mock: add cmd using vectored regbufs 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.