162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/kernel.h>
362306a36Sopenharmony_ci#include <linux/errno.h>
462306a36Sopenharmony_ci#include <linux/file.h>
562306a36Sopenharmony_ci#include <linux/io_uring.h>
662306a36Sopenharmony_ci#include <linux/security.h>
762306a36Sopenharmony_ci#include <linux/nospec.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <uapi/linux/io_uring.h>
1062306a36Sopenharmony_ci#include <asm/ioctls.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "io_uring.h"
1362306a36Sopenharmony_ci#include "rsrc.h"
1462306a36Sopenharmony_ci#include "uring_cmd.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic void io_uring_cmd_work(struct io_kiocb *req, struct io_tw_state *ts)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd);
1962306a36Sopenharmony_ci	unsigned issue_flags = ts->locked ? 0 : IO_URING_F_UNLOCKED;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	ioucmd->task_work_cb(ioucmd, issue_flags);
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_civoid __io_uring_cmd_do_in_task(struct io_uring_cmd *ioucmd,
2562306a36Sopenharmony_ci			void (*task_work_cb)(struct io_uring_cmd *, unsigned),
2662306a36Sopenharmony_ci			unsigned flags)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct io_kiocb *req = cmd_to_io_kiocb(ioucmd);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	ioucmd->task_work_cb = task_work_cb;
3162306a36Sopenharmony_ci	req->io_task_work.func = io_uring_cmd_work;
3262306a36Sopenharmony_ci	__io_req_task_work_add(req, flags);
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__io_uring_cmd_do_in_task);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_civoid io_uring_cmd_do_in_task_lazy(struct io_uring_cmd *ioucmd,
3762306a36Sopenharmony_ci			void (*task_work_cb)(struct io_uring_cmd *, unsigned))
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	__io_uring_cmd_do_in_task(ioucmd, task_work_cb, IOU_F_TWQ_LAZY_WAKE);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(io_uring_cmd_do_in_task_lazy);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic inline void io_req_set_cqe32_extra(struct io_kiocb *req,
4462306a36Sopenharmony_ci					  u64 extra1, u64 extra2)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	req->big_cqe.extra1 = extra1;
4762306a36Sopenharmony_ci	req->big_cqe.extra2 = extra2;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * Called by consumers of io_uring_cmd, if they originally returned
5262306a36Sopenharmony_ci * -EIOCBQUEUED upon receiving the command.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_civoid io_uring_cmd_done(struct io_uring_cmd *ioucmd, ssize_t ret, ssize_t res2,
5562306a36Sopenharmony_ci		       unsigned issue_flags)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct io_kiocb *req = cmd_to_io_kiocb(ioucmd);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (ret < 0)
6062306a36Sopenharmony_ci		req_set_fail(req);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	io_req_set_res(req, ret, 0);
6362306a36Sopenharmony_ci	if (req->ctx->flags & IORING_SETUP_CQE32)
6462306a36Sopenharmony_ci		io_req_set_cqe32_extra(req, res2, 0);
6562306a36Sopenharmony_ci	if (req->ctx->flags & IORING_SETUP_IOPOLL) {
6662306a36Sopenharmony_ci		/* order with io_iopoll_req_issued() checking ->iopoll_complete */
6762306a36Sopenharmony_ci		smp_store_release(&req->iopoll_completed, 1);
6862306a36Sopenharmony_ci	} else {
6962306a36Sopenharmony_ci		struct io_tw_state ts = {
7062306a36Sopenharmony_ci			.locked = !(issue_flags & IO_URING_F_UNLOCKED),
7162306a36Sopenharmony_ci		};
7262306a36Sopenharmony_ci		io_req_task_complete(req, &ts);
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(io_uring_cmd_done);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciint io_uring_cmd_prep_async(struct io_kiocb *req)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	memcpy(req->async_data, ioucmd->sqe, uring_sqe_size(req->ctx));
8262306a36Sopenharmony_ci	ioucmd->sqe = req->async_data;
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint io_uring_cmd_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (sqe->__pad1)
9162306a36Sopenharmony_ci		return -EINVAL;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	ioucmd->flags = READ_ONCE(sqe->uring_cmd_flags);
9462306a36Sopenharmony_ci	if (ioucmd->flags & ~IORING_URING_CMD_FIXED)
9562306a36Sopenharmony_ci		return -EINVAL;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (ioucmd->flags & IORING_URING_CMD_FIXED) {
9862306a36Sopenharmony_ci		struct io_ring_ctx *ctx = req->ctx;
9962306a36Sopenharmony_ci		u16 index;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		req->buf_index = READ_ONCE(sqe->buf_index);
10262306a36Sopenharmony_ci		if (unlikely(req->buf_index >= ctx->nr_user_bufs))
10362306a36Sopenharmony_ci			return -EFAULT;
10462306a36Sopenharmony_ci		index = array_index_nospec(req->buf_index, ctx->nr_user_bufs);
10562306a36Sopenharmony_ci		req->imu = ctx->user_bufs[index];
10662306a36Sopenharmony_ci		io_req_set_rsrc_node(req, ctx, 0);
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci	ioucmd->sqe = sqe;
10962306a36Sopenharmony_ci	ioucmd->cmd_op = READ_ONCE(sqe->cmd_op);
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciint io_uring_cmd(struct io_kiocb *req, unsigned int issue_flags)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd);
11662306a36Sopenharmony_ci	struct io_ring_ctx *ctx = req->ctx;
11762306a36Sopenharmony_ci	struct file *file = req->file;
11862306a36Sopenharmony_ci	int ret;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (!file->f_op->uring_cmd)
12162306a36Sopenharmony_ci		return -EOPNOTSUPP;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	ret = security_uring_cmd(ioucmd);
12462306a36Sopenharmony_ci	if (ret)
12562306a36Sopenharmony_ci		return ret;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (ctx->flags & IORING_SETUP_SQE128)
12862306a36Sopenharmony_ci		issue_flags |= IO_URING_F_SQE128;
12962306a36Sopenharmony_ci	if (ctx->flags & IORING_SETUP_CQE32)
13062306a36Sopenharmony_ci		issue_flags |= IO_URING_F_CQE32;
13162306a36Sopenharmony_ci	if (ctx->flags & IORING_SETUP_IOPOLL) {
13262306a36Sopenharmony_ci		if (!file->f_op->uring_cmd_iopoll)
13362306a36Sopenharmony_ci			return -EOPNOTSUPP;
13462306a36Sopenharmony_ci		issue_flags |= IO_URING_F_IOPOLL;
13562306a36Sopenharmony_ci		req->iopoll_completed = 0;
13662306a36Sopenharmony_ci		WRITE_ONCE(ioucmd->cookie, NULL);
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	ret = file->f_op->uring_cmd(ioucmd, issue_flags);
14062306a36Sopenharmony_ci	if (ret == -EAGAIN) {
14162306a36Sopenharmony_ci		if (!req_has_async_data(req)) {
14262306a36Sopenharmony_ci			if (io_alloc_async_data(req))
14362306a36Sopenharmony_ci				return -ENOMEM;
14462306a36Sopenharmony_ci			io_uring_cmd_prep_async(req);
14562306a36Sopenharmony_ci		}
14662306a36Sopenharmony_ci		return -EAGAIN;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (ret != -EIOCBQUEUED) {
15062306a36Sopenharmony_ci		if (ret < 0)
15162306a36Sopenharmony_ci			req_set_fail(req);
15262306a36Sopenharmony_ci		io_req_set_res(req, ret, 0);
15362306a36Sopenharmony_ci		return ret;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return IOU_ISSUE_SKIP_COMPLETE;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ciint io_uring_cmd_import_fixed(u64 ubuf, unsigned long len, int rw,
16062306a36Sopenharmony_ci			      struct iov_iter *iter, void *ioucmd)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct io_kiocb *req = cmd_to_io_kiocb(ioucmd);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return io_import_fixed(rw, iter, req->imu, ubuf, len);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(io_uring_cmd_import_fixed);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciint io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct socket *sock = cmd->file->private_data;
17162306a36Sopenharmony_ci	struct sock *sk = sock->sk;
17262306a36Sopenharmony_ci	struct proto *prot = READ_ONCE(sk->sk_prot);
17362306a36Sopenharmony_ci	int ret, arg = 0;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (!prot || !prot->ioctl)
17662306a36Sopenharmony_ci		return -EOPNOTSUPP;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	switch (cmd->sqe->cmd_op) {
17962306a36Sopenharmony_ci	case SOCKET_URING_OP_SIOCINQ:
18062306a36Sopenharmony_ci		ret = prot->ioctl(sk, SIOCINQ, &arg);
18162306a36Sopenharmony_ci		if (ret)
18262306a36Sopenharmony_ci			return ret;
18362306a36Sopenharmony_ci		return arg;
18462306a36Sopenharmony_ci	case SOCKET_URING_OP_SIOCOUTQ:
18562306a36Sopenharmony_ci		ret = prot->ioctl(sk, SIOCOUTQ, &arg);
18662306a36Sopenharmony_ci		if (ret)
18762306a36Sopenharmony_ci			return ret;
18862306a36Sopenharmony_ci		return arg;
18962306a36Sopenharmony_ci	default:
19062306a36Sopenharmony_ci		return -EOPNOTSUPP;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(io_uring_cmd_sock);
194