Commit 53db8a71 authored by Jens Axboe's avatar Jens Axboe
Browse files

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: default avatarJens Axboe <axboe@kernel.dk>
parent bd32923e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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,
+7 −0
Original line number Diff line number Diff line
@@ -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)
+133 −0
Original line number Diff line number Diff line
@@ -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;
}
+3 −0
Original line number Diff line number Diff line
@@ -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);