]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
io_uring/mock: add basic infra for test mock files
authorPavel Begunkov <asml.silence@gmail.com>
Mon, 30 Jun 2025 18:16:51 +0000 (19:16 +0100)
committerJens Axboe <axboe@kernel.dk>
Wed, 2 Jul 2025 14:10:26 +0000 (08:10 -0600)
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>
Link: https://lore.kernel.org/r/93f21b0af58c1367a2b22635d5a7d694ad0272fc.1750599274.git.asml.silence@gmail.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
MAINTAINERS
include/uapi/linux/io_uring/mock_file.h [new file with mode: 0644]
init/Kconfig
io_uring/Makefile
io_uring/mock_file.c [new file with mode: 0644]

index c3f7fbd0d67afe8376ea84bc17d70e9fa4b89bf6..24e11687f8b60de30d3a1d6ba65bdf0078a9eeb4 100644 (file)
@@ -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 (file)
index 0000000..a44273f
--- /dev/null
@@ -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
index af4c2f0854554bbcdf193852cf5c1d2c2accc64f..c40a7c65fb4c27d6bdbc60f3db4e06457cbf1ced 100644 (file)
@@ -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
index d97c6b51d58491c5a75e01608b2827c6308c46cc..b3f1bd492804b01dee2b1b075a5f9a4653e2d595 100644 (file)
@@ -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 (file)
index 0000000..3681d0b
--- /dev/null
@@ -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");