]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
io_uring: add support for IORING_OP_PIPE
authorJens Axboe <axboe@kernel.dk>
Fri, 4 Apr 2025 20:50:59 +0000 (14:50 -0600)
committerJens Axboe <axboe@kernel.dk>
Mon, 21 Apr 2025 11:06:58 +0000 (05:06 -0600)
This works just like pipe2(2), except it also supports fixed file
descriptors. Used in a similar fashion as for other fd instantiating
opcodes (like accept, socket, open, etc), where sqe->file_slot is set
appropriately if two direct descriptors are desired rather than a set
of normal file descriptors.

sqe->addr must be set to a pointer to an array of 2 integers, which
is where the fixed/normal file descriptors are copied to.

sqe->pipe_flags contains flags, same as what is allowed for pipe2(2).

Future expansion of per-op private flags can go in sqe->ioprio,
like we do for other opcodes that take both a "syscall" flag set and
an io_uring opcode specific flag set.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
include/uapi/linux/io_uring.h
io_uring/opdef.c
io_uring/openclose.c
io_uring/openclose.h

index 8f1fc12bac4620b9b40728cdd9baa2243e3cafd7..130f3bc71a69117ddd210f7f9864ec3ba8220526 100644 (file)
@@ -73,6 +73,7 @@ struct io_uring_sqe {
                __u32           futex_flags;
                __u32           install_fd_flags;
                __u32           nop_flags;
+               __u32           pipe_flags;
        };
        __u64   user_data;      /* data to be passed back at completion time */
        /* pack this to avoid bogus arm OABI complaints */
@@ -283,6 +284,7 @@ enum io_uring_op {
        IORING_OP_EPOLL_WAIT,
        IORING_OP_READV_FIXED,
        IORING_OP_WRITEV_FIXED,
+       IORING_OP_PIPE,
 
        /* this goes last, obviously */
        IORING_OP_LAST,
index 489384c0438bd8b07be9cb8467fe6e0f82227cd5..db36433c2294ef8a62542e45286723b4de8fb9cd 100644 (file)
@@ -569,6 +569,10 @@ const struct io_issue_def io_issue_defs[] = {
                .prep                   = io_prep_writev_fixed,
                .issue                  = io_write,
        },
+       [IORING_OP_PIPE] = {
+               .prep                   = io_pipe_prep,
+               .issue                  = io_pipe,
+       },
 };
 
 const struct io_cold_def io_cold_defs[] = {
@@ -815,6 +819,9 @@ const struct io_cold_def io_cold_defs[] = {
                .cleanup                = io_readv_writev_cleanup,
                .fail                   = io_rw_fail,
        },
+       [IORING_OP_PIPE] = {
+               .name                   = "PIPE",
+       },
 };
 
 const char *io_uring_get_opcode(u8 opcode)
index e3357dfa14ca42dd5b25e6cf9ce4a4be8b7ee0f4..4dd461163457836a5d64b5dbb3042fc78e5de8e8 100644 (file)
@@ -6,6 +6,8 @@
 #include <linux/fdtable.h>
 #include <linux/fsnotify.h>
 #include <linux/namei.h>
+#include <linux/pipe_fs_i.h>
+#include <linux/watch_queue.h>
 #include <linux/io_uring.h>
 
 #include <uapi/linux/io_uring.h>
@@ -302,3 +304,134 @@ int io_install_fixed_fd(struct io_kiocb *req, unsigned int issue_flags)
        io_req_set_res(req, ret, 0);
        return IOU_OK;
 }
+
+struct io_pipe {
+       struct file *file;
+       int __user *fds;
+       int flags;
+       int file_slot;
+       unsigned long nofile;
+};
+
+int io_pipe_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+{
+       struct io_pipe *p = io_kiocb_to_cmd(req, struct io_pipe);
+
+       if (sqe->fd || sqe->off || sqe->addr3)
+               return -EINVAL;
+
+       p->fds = u64_to_user_ptr(READ_ONCE(sqe->addr));
+       p->flags = READ_ONCE(sqe->pipe_flags);
+       if (p->flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT | O_NOTIFICATION_PIPE))
+               return -EINVAL;
+
+       p->file_slot = READ_ONCE(sqe->file_index);
+       p->nofile = rlimit(RLIMIT_NOFILE);
+       return 0;
+}
+
+static int io_pipe_fixed(struct io_kiocb *req, struct file **files,
+                        unsigned int issue_flags)
+{
+       struct io_pipe *p = io_kiocb_to_cmd(req, struct io_pipe);
+       struct io_ring_ctx *ctx = req->ctx;
+       int ret, fds[2] = { -1, -1 };
+       int slot = p->file_slot;
+
+       if (p->flags & O_CLOEXEC)
+               return -EINVAL;
+
+       io_ring_submit_lock(ctx, issue_flags);
+
+       ret = __io_fixed_fd_install(ctx, files[0], slot);
+       if (ret < 0)
+               goto err;
+       fds[0] = ret;
+       files[0] = NULL;
+
+       /*
+        * If a specific slot is given, next one will be used for
+        * the write side.
+        */
+       if (slot != IORING_FILE_INDEX_ALLOC)
+               slot++;
+
+       ret = __io_fixed_fd_install(ctx, files[1], slot);
+       if (ret < 0)
+               goto err;
+       fds[1] = ret;
+       files[1] = NULL;
+
+       io_ring_submit_unlock(ctx, issue_flags);
+
+       if (!copy_to_user(p->fds, fds, sizeof(fds)))
+               return 0;
+
+       ret = -EFAULT;
+       io_ring_submit_lock(ctx, issue_flags);
+err:
+       if (fds[0] != -1)
+               io_fixed_fd_remove(ctx, fds[0]);
+       if (fds[1] != -1)
+               io_fixed_fd_remove(ctx, fds[1]);
+       io_ring_submit_unlock(ctx, issue_flags);
+       return ret;
+}
+
+static int io_pipe_fd(struct io_kiocb *req, struct file **files)
+{
+       struct io_pipe *p = io_kiocb_to_cmd(req, struct io_pipe);
+       int ret, fds[2] = { -1, -1 };
+
+       ret = __get_unused_fd_flags(p->flags, p->nofile);
+       if (ret < 0)
+               goto err;
+       fds[0] = ret;
+
+       ret = __get_unused_fd_flags(p->flags, p->nofile);
+       if (ret < 0)
+               goto err;
+       fds[1] = ret;
+
+       if (!copy_to_user(p->fds, fds, sizeof(fds))) {
+               fd_install(fds[0], files[0]);
+               fd_install(fds[1], files[1]);
+               return 0;
+       }
+       ret = -EFAULT;
+err:
+       if (fds[0] != -1)
+               put_unused_fd(fds[0]);
+       if (fds[1] != -1)
+               put_unused_fd(fds[1]);
+       return ret;
+}
+
+int io_pipe(struct io_kiocb *req, unsigned int issue_flags)
+{
+       struct io_pipe *p = io_kiocb_to_cmd(req, struct io_pipe);
+       struct file *files[2];
+       int ret;
+
+       ret = create_pipe_files(files, p->flags);
+       if (ret)
+               return ret;
+       files[0]->f_mode |= FMODE_NOWAIT;
+       files[1]->f_mode |= FMODE_NOWAIT;
+
+       if (!!p->file_slot)
+               ret = io_pipe_fixed(req, files, issue_flags);
+       else
+               ret = io_pipe_fd(req, files);
+
+       io_req_set_res(req, ret, 0);
+       if (!ret)
+               return IOU_OK;
+
+       req_set_fail(req);
+       if (files[0])
+               fput(files[0]);
+       if (files[1])
+               fput(files[1]);
+       return ret;
+}
index 8a93c98ad0adc667844a92576d1022edd6e5fa00..4ca2a9935abc9c1e7da39f94b107839d4c80aefc 100644 (file)
@@ -13,5 +13,8 @@ int io_openat2(struct io_kiocb *req, unsigned int issue_flags);
 int io_close_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
 int io_close(struct io_kiocb *req, unsigned int issue_flags);
 
+int io_pipe_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
+int io_pipe(struct io_kiocb *req, unsigned int issue_flags);
+
 int io_install_fixed_fd_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
 int io_install_fixed_fd(struct io_kiocb *req, unsigned int issue_flags);