* [PATCH v2 0/6] io_uring/mock: add basic infra for test mock files
@ 2025-05-26 7:32 Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 1/6] " Pavel Begunkov
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Pavel Begunkov @ 2025-05-26 7:32 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, which was initially suggested by Chase.
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: add trivial poll handler
init/Kconfig | 11 ++
io_uring/Makefile | 1 +
io_uring/mock_file.c | 344 +++++++++++++++++++++++++++++++++++++++++++
io_uring/mock_file.h | 47 ++++++
4 files changed, 403 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] 7+ messages in thread
* [PATCH v2 1/6] io_uring/mock: add basic infra for test mock files
2025-05-26 7:32 [PATCH v2 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
@ 2025-05-26 7:32 ` Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 2/6] io_uring/mock: add cmd using vectored regbufs Pavel Begunkov
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Pavel Begunkov @ 2025-05-26 7:32 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 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..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] 7+ messages in thread
* [PATCH v2 2/6] io_uring/mock: add cmd using vectored regbufs
2025-05-26 7:32 [PATCH v2 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 1/6] " Pavel Begunkov
@ 2025-05-26 7:32 ` Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 3/6] io_uring/mock: add sync read/write Pavel Begunkov
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Pavel Begunkov @ 2025-05-26 7:32 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 | 14 +++++++++
2 files changed, 83 insertions(+), 1 deletion(-)
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index e8ec0aeddbae..6d6100052a26 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_END;
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..0833eb7af1ac 100644
--- a/io_uring/mock_file.h
+++ b/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
\ No newline at end of file
--
2.49.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 3/6] io_uring/mock: add sync read/write
2025-05-26 7:32 [PATCH v2 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 1/6] " Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 2/6] io_uring/mock: add cmd using vectored regbufs Pavel Begunkov
@ 2025-05-26 7:32 ` Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 4/6] io_uring/mock: allow to choose FMODE_NOWAIT Pavel Begunkov
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Pavel Begunkov @ 2025-05-26 7:32 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, chase xd
Add support for synchronous zero read/write for mock files.
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
---
io_uring/mock_file.c | 58 ++++++++++++++++++++++++++++++++++++++++----
io_uring/mock_file.h | 4 ++-
2 files changed, 56 insertions(+), 6 deletions(-)
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index 6d6100052a26..eaecad34ef03 100644
--- a/io_uring/mock_file.c
+++ b/io_uring/mock_file.c
@@ -9,6 +9,10 @@
#include <linux/io_uring_types.h>
#include "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,50 @@ 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 const struct file_operations io_mock_fops = {
.owner = THIS_MODULE,
.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;
uarg = u64_to_user_ptr(READ_ONCE(sqe->addr));
uarg_size = READ_ONCE(sqe->len);
@@ -108,18 +144,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);
@@ -130,7 +176,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;
}
diff --git a/io_uring/mock_file.h b/io_uring/mock_file.h
index 0833eb7af1ac..85be7597c8db 100644
--- a/io_uring/mock_file.h
+++ b/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 {
--
2.49.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 4/6] io_uring/mock: allow to choose FMODE_NOWAIT
2025-05-26 7:32 [PATCH v2 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
` (2 preceding siblings ...)
2025-05-26 7:32 ` [PATCH v2 3/6] io_uring/mock: add sync read/write Pavel Begunkov
@ 2025-05-26 7:32 ` Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 5/6] io_uring/mock: support for async read/write Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 6/6] io_uring: add trivial poll handler Pavel Begunkov
5 siblings, 0 replies; 7+ messages in thread
From: Pavel Begunkov @ 2025-05-26 7:32 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, chase xd
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>
---
io_uring/mock_file.c | 8 +++++++-
io_uring/mock_file.h | 5 +++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index eaecad34ef03..6b9f1222397c 100644
--- a/io_uring/mock_file.c
+++ b/io_uring/mock_file.c
@@ -122,6 +122,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;
@@ -142,7 +144,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;
@@ -165,6 +169,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)) {
diff --git a/io_uring/mock_file.h b/io_uring/mock_file.h
index 85be7597c8db..224226abe23c 100644
--- a/io_uring/mock_file.h
+++ b/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;
--
2.49.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 5/6] io_uring/mock: support for async read/write
2025-05-26 7:32 [PATCH v2 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
` (3 preceding siblings ...)
2025-05-26 7:32 ` [PATCH v2 4/6] io_uring/mock: allow to choose FMODE_NOWAIT Pavel Begunkov
@ 2025-05-26 7:32 ` Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 6/6] io_uring: add trivial poll handler Pavel Begunkov
5 siblings, 0 replies; 7+ messages in thread
From: Pavel Begunkov @ 2025-05-26 7:32 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, chase xd
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>
---
io_uring/mock_file.c | 59 +++++++++++++++++++++++++++++++++++++++++---
io_uring/mock_file.h | 4 ++-
2 files changed, 58 insertions(+), 5 deletions(-)
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index 6b9f1222397c..321d02f923af 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 "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)
@@ -150,6 +197,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;
@@ -159,6 +209,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)) {
diff --git a/io_uring/mock_file.h b/io_uring/mock_file.h
index 224226abe23c..65ed71ca3a8f 100644
--- a/io_uring/mock_file.h
+++ b/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 {
--
2.49.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 6/6] io_uring: add trivial poll handler
2025-05-26 7:32 [PATCH v2 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
` (4 preceding siblings ...)
2025-05-26 7:32 ` [PATCH v2 5/6] io_uring/mock: support for async read/write Pavel Begunkov
@ 2025-05-26 7:32 ` Pavel Begunkov
5 siblings, 0 replies; 7+ messages in thread
From: Pavel Begunkov @ 2025-05-26 7:32 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence, chase xd
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>
---
io_uring/mock_file.c | 36 ++++++++++++++++++++++++++++++++++--
io_uring/mock_file.h | 2 ++
2 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c
index 321d02f923af..79a729953529 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 const struct file_operations io_mock_fops = {
.owner = THIS_MODULE,
.uring_cmd = io_mock_cmd,
@@ -169,10 +184,21 @@ 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,
+ .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;
@@ -208,9 +234,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);
diff --git a/io_uring/mock_file.h b/io_uring/mock_file.h
index 65ed71ca3a8f..7fa71145850a 100644
--- a/io_uring/mock_file.h
+++ b/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 {
--
2.49.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-05-26 7:31 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-26 7:32 [PATCH v2 0/6] io_uring/mock: add basic infra for test mock files Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 1/6] " Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 2/6] io_uring/mock: add cmd using vectored regbufs Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 3/6] io_uring/mock: add sync read/write Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 4/6] io_uring/mock: allow to choose FMODE_NOWAIT Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 5/6] io_uring/mock: support for async read/write Pavel Begunkov
2025-05-26 7:32 ` [PATCH v2 6/6] io_uring: add trivial poll handler 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.