* [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files
@ 2025-06-30 18:16 Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 1/6] " Pavel Begunkov
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-06-30 18:16 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
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, which was initially suggested by Chase.
v5: moves headers to uapi/
taint the kernel
v4: require CAP_ADMIN and limit to KASAN builds
v3: fix memleak, + release fop callback
v2: add rw support with basic options
implement features not as bitmask but sequence number
Pavel Begunkov (6):
io_uring/mock: add basic infra for test mock files
io_uring/mock: add cmd using vectored regbufs
io_uring/mock: add sync read/write
io_uring/mock: allow to choose FMODE_NOWAIT
io_uring/mock: support for async read/write
io_uring/mock: add trivial poll handler
MAINTAINERS | 1 +
include/uapi/linux/io_uring/mock_file.h | 47 +++
init/Kconfig | 11 +
io_uring/Makefile | 1 +
io_uring/mock_file.c | 363 ++++++++++++++++++++++++
5 files changed, 423 insertions(+)
create mode 100644 include/uapi/linux/io_uring/mock_file.h
create mode 100644 io_uring/mock_file.c
--
2.49.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v5 1/6] io_uring/mock: add basic infra for test mock files
2025-06-30 18:16 [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
@ 2025-06-30 18:16 ` Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 2/6] io_uring/mock: add cmd using vectored regbufs Pavel Begunkov
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-06-30 18:16 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
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>
---
MAINTAINERS | 1 +
include/uapi/linux/io_uring/mock_file.h | 22 ++++
init/Kconfig | 11 ++
io_uring/Makefile | 1 +
io_uring/mock_file.c | 148 ++++++++++++++++++++++++
5 files changed, 183 insertions(+)
create mode 100644 include/uapi/linux/io_uring/mock_file.h
create mode 100644 io_uring/mock_file.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a92290fffa16..088348e3b3ae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12679,6 +12679,7 @@ F: include/linux/io_uring.h
F: include/linux/io_uring_types.h
F: include/trace/events/io_uring.h
F: include/uapi/linux/io_uring.h
+F: include/uapi/linux/io_uring/
F: io_uring/
IPMI SUBSYSTEM
diff --git a/include/uapi/linux/io_uring/mock_file.h b/include/uapi/linux/io_uring/mock_file.h
new file mode 100644
index 000000000000..a44273fd526d
--- /dev/null
+++ b/include/uapi/linux/io_uring/mock_file.h
@@ -0,0 +1,22 @@
+#ifndef LINUX_IO_URING_MOCK_FILE_H
+#define LINUX_IO_URING_MOCK_FILE_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
diff --git a/init/Kconfig b/init/Kconfig
index af4c2f085455..c40a7c65fb4c 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1801,6 +1801,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 d97c6b51d584..b3f1bd492804 100644
--- a/io_uring/Makefile
+++ b/io_uring/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_EPOLL) += epoll.o
obj-$(CONFIG_NET_RX_BUSY_POLL) += napi.o
obj-$(CONFIG_NET) += net.o cmd_net.o
obj-$(CONFIG_PROC_FS) += fdinfo.o
+obj-$(CONFIG_IO_URING_MOCK_FILE) += mock_file.o
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
new file mode 100644
index 000000000000..3681d0b8d8de
--- /dev/null
+++ b/io_uring/mock_file.c
@@ -0,0 +1,148 @@
+#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 <uapi/linux/io_uring/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;
+
+ /*
+ * It's a testing only driver that allows exercising edge cases
+ * that wouldn't be possible to hit otherwise.
+ */
+ add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
+
+ 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)
+{
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ 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");
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 2/6] io_uring/mock: add cmd using vectored regbufs
2025-06-30 18:16 [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 1/6] " Pavel Begunkov
@ 2025-06-30 18:16 ` Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 3/6] io_uring/mock: add sync read/write Pavel Begunkov
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-06-30 18:16 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
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>
---
include/uapi/linux/io_uring/mock_file.h | 14 +++++
io_uring/mock_file.c | 70 ++++++++++++++++++++++++-
2 files changed, 83 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/io_uring/mock_file.h b/include/uapi/linux/io_uring/mock_file.h
index a44273fd526d..73aca477d5c8 100644
--- a/include/uapi/linux/io_uring/mock_file.h
+++ b/include/uapi/linux/io_uring/mock_file.h
@@ -3,6 +3,12 @@
#include <linux/types.h>
+enum {
+ IORING_MOCK_FEAT_CMD_COPY,
+
+ IORING_MOCK_FEAT_END,
+};
+
struct io_uring_mock_probe {
__u64 features;
__u64 __resv[9];
@@ -19,4 +25,12 @@ enum {
IORING_MOCK_MGR_CMD_CREATE,
};
+enum {
+ IORING_MOCK_CMD_COPY_REGBUF,
+};
+
+enum {
+ IORING_MOCK_COPY_FROM = 1,
+};
+
#endif
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index 3681d0b8d8de..8285393f4a5b 100644
--- a/io_uring/mock_file.c
+++ b/io_uring/mock_file.c
@@ -9,8 +9,76 @@
#include <linux/io_uring_types.h>
#include <uapi/linux/io_uring/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;
}
@@ -91,7 +159,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_END;
if (copy_to_user(uarg, &mp, uarg_size))
return -EFAULT;
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 3/6] io_uring/mock: add sync read/write
2025-06-30 18:16 [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 1/6] " Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 2/6] io_uring/mock: add cmd using vectored regbufs Pavel Begunkov
@ 2025-06-30 18:16 ` Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 4/6] io_uring/mock: allow to choose FMODE_NOWAIT Pavel Begunkov
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-06-30 18:16 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Add support for synchronous zero read/write for mock files.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
include/uapi/linux/io_uring/mock_file.h | 4 +-
io_uring/mock_file.c | 67 +++++++++++++++++++++++--
2 files changed, 65 insertions(+), 6 deletions(-)
diff --git a/include/uapi/linux/io_uring/mock_file.h b/include/uapi/linux/io_uring/mock_file.h
index 73aca477d5c8..de27295bb365 100644
--- a/include/uapi/linux/io_uring/mock_file.h
+++ b/include/uapi/linux/io_uring/mock_file.h
@@ -5,6 +5,7 @@
enum {
IORING_MOCK_FEAT_CMD_COPY,
+ IORING_MOCK_FEAT_RW_ZERO,
IORING_MOCK_FEAT_END,
};
@@ -17,7 +18,8 @@ struct io_uring_mock_probe {
struct io_uring_mock_create {
__u32 out_fd;
__u32 flags;
- __u64 __resv[15];
+ __u64 file_size;
+ __u64 __resv[14];
};
enum {
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index 8285393f4a5b..90160ccb50f0 100644
--- a/io_uring/mock_file.c
+++ b/io_uring/mock_file.c
@@ -9,6 +9,10 @@
#include <linux/io_uring_types.h>
#include <uapi/linux/io_uring/mock_file.h>
+struct io_mock_file {
+ size_t size;
+};
+
#define IO_VALID_COPY_CMD_FLAGS IORING_MOCK_COPY_FROM
static int io_copy_regbuf(struct iov_iter *reg_iter, void __user *ubuf)
@@ -82,18 +86,59 @@ static int io_mock_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
return -ENOTSUPP;
}
+static ssize_t io_mock_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+ struct io_mock_file *mf = iocb->ki_filp->private_data;
+ size_t len = iov_iter_count(to);
+
+ if (iocb->ki_pos + len > mf->size)
+ return -EINVAL;
+ return iov_iter_zero(len, to);
+}
+
+static ssize_t io_mock_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ struct io_mock_file *mf = iocb->ki_filp->private_data;
+ size_t len = iov_iter_count(from);
+
+ if (iocb->ki_pos + len > mf->size)
+ return -EINVAL;
+ iov_iter_advance(from, len);
+ return len;
+}
+
+static loff_t io_mock_llseek(struct file *file, loff_t offset, int whence)
+{
+ struct io_mock_file *mf = file->private_data;
+
+ return fixed_size_llseek(file, offset, whence, mf->size);
+}
+
+static int io_mock_release(struct inode *inode, struct file *file)
+{
+ struct io_mock_file *mf = file->private_data;
+
+ kfree(mf);
+ return 0;
+}
+
static const struct file_operations io_mock_fops = {
.owner = THIS_MODULE,
+ .release = io_mock_release,
.uring_cmd = io_mock_cmd,
+ .read_iter = io_mock_read_iter,
+ .write_iter = io_mock_write_iter,
+ .llseek = io_mock_llseek,
};
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 io_mock_file *mf = NULL;
struct file *file = NULL;
size_t uarg_size;
- int fd, ret;
+ int fd = -1, ret;
/*
* It's a testing only driver that allows exercising edge cases
@@ -114,18 +159,28 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag
return -EFAULT;
if (!mem_is_zero(mc.__resv, sizeof(mc.__resv)) || mc.flags)
return -EINVAL;
+ if (mc.file_size > SZ_1G)
+ return -EINVAL;
+ mf = kzalloc(sizeof(*mf), GFP_KERNEL_ACCOUNT);
+ if (!mf)
+ return -ENOMEM;
- fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
+ ret = fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
if (fd < 0)
- return fd;
+ goto fail;
+ mf->size = mc.file_size;
file = anon_inode_create_getfile("[io_uring_mock]", &io_mock_fops,
- NULL, O_RDWR | O_CLOEXEC, NULL);
+ mf, O_RDWR | O_CLOEXEC, NULL);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto fail;
}
+ file->f_mode |= FMODE_READ | FMODE_CAN_READ |
+ FMODE_WRITE | FMODE_CAN_WRITE |
+ FMODE_LSEEK;
+
mc.out_fd = fd;
if (copy_to_user(uarg, &mc, uarg_size)) {
fput(file);
@@ -136,7 +191,9 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag
fd_install(fd, file);
return 0;
fail:
- put_unused_fd(fd);
+ if (fd >= 0)
+ put_unused_fd(fd);
+ kfree(mf);
return ret;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 4/6] io_uring/mock: allow to choose FMODE_NOWAIT
2025-06-30 18:16 [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
` (2 preceding siblings ...)
2025-06-30 18:16 ` [PATCH v5 3/6] io_uring/mock: add sync read/write Pavel Begunkov
@ 2025-06-30 18:16 ` Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 5/6] io_uring/mock: support for async read/write Pavel Begunkov
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-06-30 18:16 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Add an option to choose whether the file supports FMODE_NOWAIT, that
changes the execution path io_uring request takes.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
include/uapi/linux/io_uring/mock_file.h | 5 +++++
io_uring/mock_file.c | 8 +++++++-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/io_uring/mock_file.h b/include/uapi/linux/io_uring/mock_file.h
index de27295bb365..125949d2b5ce 100644
--- a/include/uapi/linux/io_uring/mock_file.h
+++ b/include/uapi/linux/io_uring/mock_file.h
@@ -6,6 +6,7 @@
enum {
IORING_MOCK_FEAT_CMD_COPY,
IORING_MOCK_FEAT_RW_ZERO,
+ IORING_MOCK_FEAT_RW_NOWAIT,
IORING_MOCK_FEAT_END,
};
@@ -15,6 +16,10 @@ struct io_uring_mock_probe {
__u64 __resv[9];
};
+enum {
+ IORING_MOCK_CREATE_F_SUPPORT_NOWAIT = 1,
+};
+
struct io_uring_mock_create {
__u32 out_fd;
__u32 flags;
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index 90160ccb50f0..0eb1d3bd6368 100644
--- a/io_uring/mock_file.c
+++ b/io_uring/mock_file.c
@@ -131,6 +131,8 @@ static const struct file_operations io_mock_fops = {
.llseek = io_mock_llseek,
};
+#define IO_VALID_CREATE_FLAGS (IORING_MOCK_CREATE_F_SUPPORT_NOWAIT)
+
static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flags)
{
const struct io_uring_sqe *sqe = cmd->sqe;
@@ -157,7 +159,9 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag
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)
+ if (!mem_is_zero(mc.__resv, sizeof(mc.__resv)))
+ return -EINVAL;
+ if (mc.flags & ~IO_VALID_CREATE_FLAGS)
return -EINVAL;
if (mc.file_size > SZ_1G)
return -EINVAL;
@@ -180,6 +184,8 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag
file->f_mode |= FMODE_READ | FMODE_CAN_READ |
FMODE_WRITE | FMODE_CAN_WRITE |
FMODE_LSEEK;
+ if (mc.flags & IORING_MOCK_CREATE_F_SUPPORT_NOWAIT)
+ file->f_mode |= FMODE_NOWAIT;
mc.out_fd = fd;
if (copy_to_user(uarg, &mc, uarg_size)) {
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 5/6] io_uring/mock: support for async read/write
2025-06-30 18:16 [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
` (3 preceding siblings ...)
2025-06-30 18:16 ` [PATCH v5 4/6] io_uring/mock: allow to choose FMODE_NOWAIT Pavel Begunkov
@ 2025-06-30 18:16 ` Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 6/6] io_uring/mock: add trivial poll handler Pavel Begunkov
2025-07-02 14:17 ` [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Jens Axboe
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-06-30 18:16 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Let the user to specify a delay to read/write request. io_uring will
start a timer, return -EIOCBQUEUED and complete the request
asynchronously after the delay pass.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
include/uapi/linux/io_uring/mock_file.h | 4 +-
io_uring/mock_file.c | 59 +++++++++++++++++++++++--
2 files changed, 58 insertions(+), 5 deletions(-)
diff --git a/include/uapi/linux/io_uring/mock_file.h b/include/uapi/linux/io_uring/mock_file.h
index 125949d2b5ce..c8fa77e39c68 100644
--- a/include/uapi/linux/io_uring/mock_file.h
+++ b/include/uapi/linux/io_uring/mock_file.h
@@ -7,6 +7,7 @@ enum {
IORING_MOCK_FEAT_CMD_COPY,
IORING_MOCK_FEAT_RW_ZERO,
IORING_MOCK_FEAT_RW_NOWAIT,
+ IORING_MOCK_FEAT_RW_ASYNC,
IORING_MOCK_FEAT_END,
};
@@ -24,7 +25,8 @@ struct io_uring_mock_create {
__u32 out_fd;
__u32 flags;
__u64 file_size;
- __u64 __resv[14];
+ __u64 rw_delay_ns;
+ __u64 __resv[13];
};
enum {
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index 0eb1d3bd6368..ed6a5505763e 100644
--- a/io_uring/mock_file.c
+++ b/io_uring/mock_file.c
@@ -4,13 +4,22 @@
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/anon_inodes.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
#include <linux/io_uring/cmd.h>
#include <linux/io_uring_types.h>
#include <uapi/linux/io_uring/mock_file.h>
+struct io_mock_iocb {
+ struct kiocb *iocb;
+ struct hrtimer timer;
+ int res;
+};
+
struct io_mock_file {
- size_t size;
+ size_t size;
+ u64 rw_delay_ns;
};
#define IO_VALID_COPY_CMD_FLAGS IORING_MOCK_COPY_FROM
@@ -86,14 +95,48 @@ static int io_mock_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
return -ENOTSUPP;
}
+static enum hrtimer_restart io_mock_rw_timer_expired(struct hrtimer *timer)
+{
+ struct io_mock_iocb *mio = container_of(timer, struct io_mock_iocb, timer);
+ struct kiocb *iocb = mio->iocb;
+
+ WRITE_ONCE(iocb->private, NULL);
+ iocb->ki_complete(iocb, mio->res);
+ kfree(mio);
+ return HRTIMER_NORESTART;
+}
+
+static ssize_t io_mock_delay_rw(struct kiocb *iocb, size_t len)
+{
+ struct io_mock_file *mf = iocb->ki_filp->private_data;
+ struct io_mock_iocb *mio;
+
+ mio = kzalloc(sizeof(*mio), GFP_KERNEL);
+ if (!mio)
+ return -ENOMEM;
+
+ mio->iocb = iocb;
+ mio->res = len;
+ hrtimer_setup(&mio->timer, io_mock_rw_timer_expired,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_start(&mio->timer, ns_to_ktime(mf->rw_delay_ns),
+ HRTIMER_MODE_REL);
+ return -EIOCBQUEUED;
+}
+
static ssize_t io_mock_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
struct io_mock_file *mf = iocb->ki_filp->private_data;
size_t len = iov_iter_count(to);
+ size_t nr_zeroed;
if (iocb->ki_pos + len > mf->size)
return -EINVAL;
- return iov_iter_zero(len, to);
+ nr_zeroed = iov_iter_zero(len, to);
+ if (!mf->rw_delay_ns || nr_zeroed != len)
+ return nr_zeroed;
+
+ return io_mock_delay_rw(iocb, len);
}
static ssize_t io_mock_write_iter(struct kiocb *iocb, struct iov_iter *from)
@@ -103,8 +146,12 @@ static ssize_t io_mock_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (iocb->ki_pos + len > mf->size)
return -EINVAL;
- iov_iter_advance(from, len);
- return len;
+ if (!mf->rw_delay_ns) {
+ iov_iter_advance(from, len);
+ return len;
+ }
+
+ return io_mock_delay_rw(iocb, len);
}
static loff_t io_mock_llseek(struct file *file, loff_t offset, int whence)
@@ -165,6 +212,9 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag
return -EINVAL;
if (mc.file_size > SZ_1G)
return -EINVAL;
+ if (mc.rw_delay_ns > NSEC_PER_SEC)
+ return -EINVAL;
+
mf = kzalloc(sizeof(*mf), GFP_KERNEL_ACCOUNT);
if (!mf)
return -ENOMEM;
@@ -174,6 +224,7 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag
goto fail;
mf->size = mc.file_size;
+ mf->rw_delay_ns = mc.rw_delay_ns;
file = anon_inode_create_getfile("[io_uring_mock]", &io_mock_fops,
mf, O_RDWR | O_CLOEXEC, NULL);
if (IS_ERR(file)) {
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 6/6] io_uring/mock: add trivial poll handler
2025-06-30 18:16 [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
` (4 preceding siblings ...)
2025-06-30 18:16 ` [PATCH v5 5/6] io_uring/mock: support for async read/write Pavel Begunkov
@ 2025-06-30 18:16 ` Pavel Begunkov
2025-07-02 14:17 ` [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Jens Axboe
6 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-06-30 18:16 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
Add a flag that enables polling on the mock file. For now it's trivially
says that there is always data available, it'll be extended in the
future.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
include/uapi/linux/io_uring/mock_file.h | 2 ++
io_uring/mock_file.c | 37 +++++++++++++++++++++++--
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/include/uapi/linux/io_uring/mock_file.h b/include/uapi/linux/io_uring/mock_file.h
index c8fa77e39c68..debeee8e4527 100644
--- a/include/uapi/linux/io_uring/mock_file.h
+++ b/include/uapi/linux/io_uring/mock_file.h
@@ -8,6 +8,7 @@ enum {
IORING_MOCK_FEAT_RW_ZERO,
IORING_MOCK_FEAT_RW_NOWAIT,
IORING_MOCK_FEAT_RW_ASYNC,
+ IORING_MOCK_FEAT_POLL,
IORING_MOCK_FEAT_END,
};
@@ -19,6 +20,7 @@ struct io_uring_mock_probe {
enum {
IORING_MOCK_CREATE_F_SUPPORT_NOWAIT = 1,
+ IORING_MOCK_CREATE_F_POLL = 2,
};
struct io_uring_mock_create {
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index ed6a5505763e..45d3735b2708 100644
--- a/io_uring/mock_file.c
+++ b/io_uring/mock_file.c
@@ -6,6 +6,7 @@
#include <linux/anon_inodes.h>
#include <linux/ktime.h>
#include <linux/hrtimer.h>
+#include <linux/poll.h>
#include <linux/io_uring/cmd.h>
#include <linux/io_uring_types.h>
@@ -20,6 +21,8 @@ struct io_mock_iocb {
struct io_mock_file {
size_t size;
u64 rw_delay_ns;
+ bool pollable;
+ struct wait_queue_head poll_wq;
};
#define IO_VALID_COPY_CMD_FLAGS IORING_MOCK_COPY_FROM
@@ -161,6 +164,18 @@ static loff_t io_mock_llseek(struct file *file, loff_t offset, int whence)
return fixed_size_llseek(file, offset, whence, mf->size);
}
+static __poll_t io_mock_poll(struct file *file, struct poll_table_struct *pt)
+{
+ struct io_mock_file *mf = file->private_data;
+ __poll_t mask = 0;
+
+ poll_wait(file, &mf->poll_wq, pt);
+
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
+ return mask;
+}
+
static int io_mock_release(struct inode *inode, struct file *file)
{
struct io_mock_file *mf = file->private_data;
@@ -178,10 +193,22 @@ static const struct file_operations io_mock_fops = {
.llseek = io_mock_llseek,
};
-#define IO_VALID_CREATE_FLAGS (IORING_MOCK_CREATE_F_SUPPORT_NOWAIT)
+static const struct file_operations io_mock_poll_fops = {
+ .owner = THIS_MODULE,
+ .release = io_mock_release,
+ .uring_cmd = io_mock_cmd,
+ .read_iter = io_mock_read_iter,
+ .write_iter = io_mock_write_iter,
+ .llseek = io_mock_llseek,
+ .poll = io_mock_poll,
+};
+
+#define IO_VALID_CREATE_FLAGS (IORING_MOCK_CREATE_F_SUPPORT_NOWAIT | \
+ IORING_MOCK_CREATE_F_POLL)
static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flags)
{
+ const struct file_operations *fops = &io_mock_fops;
const struct io_uring_sqe *sqe = cmd->sqe;
struct io_uring_mock_create mc, __user *uarg;
struct io_mock_file *mf = NULL;
@@ -223,9 +250,15 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag
if (fd < 0)
goto fail;
+ init_waitqueue_head(&mf->poll_wq);
mf->size = mc.file_size;
mf->rw_delay_ns = mc.rw_delay_ns;
- file = anon_inode_create_getfile("[io_uring_mock]", &io_mock_fops,
+ if (mc.flags & IORING_MOCK_CREATE_F_POLL) {
+ fops = &io_mock_poll_fops;
+ mf->pollable = true;
+ }
+
+ file = anon_inode_create_getfile("[io_uring_mock]", fops,
mf, O_RDWR | O_CLOEXEC, NULL);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
--
2.49.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files
2025-06-30 18:16 [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
` (5 preceding siblings ...)
2025-06-30 18:16 ` [PATCH v5 6/6] io_uring/mock: add trivial poll handler Pavel Begunkov
@ 2025-07-02 14:17 ` Jens Axboe
6 siblings, 0 replies; 8+ messages in thread
From: Jens Axboe @ 2025-07-02 14:17 UTC (permalink / raw)
To: io-uring, Pavel Begunkov
On Mon, 30 Jun 2025 19:16:50 +0100, Pavel Begunkov wrote:
> 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, which was initially suggested by Chase.
>
> [...]
Applied, thanks!
[1/6] io_uring/mock: add basic infra for test mock files
(no commit info)
[2/6] io_uring/mock: add cmd using vectored regbufs
(no commit info)
[3/6] io_uring/mock: add sync read/write
(no commit info)
[4/6] io_uring/mock: allow to choose FMODE_NOWAIT
(no commit info)
[5/6] io_uring/mock: support for async read/write
(no commit info)
[6/6] io_uring/mock: add trivial poll handler
(no commit info)
Best regards,
--
Jens Axboe
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-07-02 14:17 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-30 18:16 [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 1/6] " Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 2/6] io_uring/mock: add cmd using vectored regbufs Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 3/6] io_uring/mock: add sync read/write Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 4/6] io_uring/mock: allow to choose FMODE_NOWAIT Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 5/6] io_uring/mock: support for async read/write Pavel Begunkov
2025-06-30 18:16 ` [PATCH v5 6/6] io_uring/mock: add trivial poll handler Pavel Begunkov
2025-07-02 14:17 ` [PATCH v5 0/6] io_uring/mock: add basic infra for test mock files Jens Axboe
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.