io_uring: add support for IORING_OP_PIPE

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>
This commit is contained in:
Jens Axboe
2025-04-04 14:50:59 -06:00
parent bd32923e5f
commit 53db8a71ec
4 changed files with 145 additions and 0 deletions

View 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,

View 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)

View 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;
}

View 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);