From: Yingjie Wang uring_cmd requests already allocate enough asynchronous state to hold a copied 128-byte SQE. Once a driver has consumed every SQE field and committed to asynchronous completion, that storage is no longer needed. Add a helper that transfers the storage to the driver until command completion without growing the request or adding an allocation. Invalidate the SQE pointer on transfer and fail a subsequent -EAGAIN return with -EIO so a command cannot be reissued after overwriting its SQE copy. Signed-off-by: Yingjie Wang --- include/linux/io_uring/cmd.h | 24 ++++++++++++++++++++++++ io_uring/uring_cmd.c | 22 ++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/include/linux/io_uring/cmd.h b/include/linux/io_uring/cmd.h index e18d4af62e1a..6a9c06bcccc6 100644 --- a/include/linux/io_uring/cmd.h +++ b/include/linux/io_uring/cmd.h @@ -8,6 +8,7 @@ #include /* only top 8 bits of sqe->uring_cmd_flags for kernel internal use */ +#define IORING_URING_CMD_SQE_CONSUMED BIT(29) #define IORING_URING_CMD_CANCELABLE (1U << 30) /* io_uring_cmd is being issued again */ #define IORING_URING_CMD_REISSUE (1U << 31) @@ -82,6 +83,22 @@ int io_uring_cmd_import_fixed_vec(struct io_uring_cmd *ioucmd, */ void __io_uring_cmd_done(struct io_uring_cmd *, unsigned issue_flags); +/** + * io_uring_cmd_get_completion_data - reuse the command's SQE copy storage + * @ioucmd: command that has consumed all of its SQE fields + * @len: amount of completion storage required + * + * Transfer the per-request storage reserved for an SQE copy to the command. + * After a successful call, @ioucmd->sqe is invalid and the command must not be + * reissued. The returned storage remains valid until io_uring_cmd_done*(). + * Multishot commands cannot use this helper. + * + * Return: completion storage on success, or NULL if it is unavailable or too + * large. + */ +void *io_uring_cmd_get_completion_data(struct io_uring_cmd *ioucmd, + size_t len); + void __io_uring_cmd_do_in_task(struct io_uring_cmd *ioucmd, io_req_tw_func_t task_work_cb, unsigned flags); @@ -140,6 +157,13 @@ static inline void __io_uring_cmd_done(struct io_uring_cmd *cmd, unsigned issue_flags) { } + +static inline void * +io_uring_cmd_get_completion_data(struct io_uring_cmd *ioucmd, size_t len) +{ + return NULL; +} + static inline void __io_uring_cmd_do_in_task(struct io_uring_cmd *ioucmd, io_req_tw_func_t task_work_cb, unsigned flags) { diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c index b5486b56ee0c..87b1b300ae20 100644 --- a/io_uring/uring_cmd.c +++ b/io_uring/uring_cmd.c @@ -166,6 +166,23 @@ void __io_uring_cmd_done(struct io_uring_cmd *ioucmd, unsigned issue_flags) } EXPORT_SYMBOL_GPL(__io_uring_cmd_done); +void *io_uring_cmd_get_completion_data(struct io_uring_cmd *ioucmd, size_t len) +{ + struct io_kiocb *req = cmd_to_io_kiocb(ioucmd); + struct io_async_cmd *ac = req->async_data; + + if (!ac || !ioucmd->sqe || + ioucmd->flags & (IORING_URING_CMD_MULTISHOT | + IORING_URING_CMD_SQE_CONSUMED) || + len > sizeof(ac->sqes)) + return NULL; + + ioucmd->flags |= IORING_URING_CMD_SQE_CONSUMED; + ioucmd->sqe = NULL; + return ac->sqes; +} +EXPORT_SYMBOL_GPL(io_uring_cmd_get_completion_data); + int io_uring_cmd_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); @@ -257,11 +274,16 @@ int io_uring_cmd(struct io_kiocb *req, unsigned int issue_flags) ret = file->f_op->uring_cmd(ioucmd, issue_flags); if (ret == -EAGAIN) { + if (ioucmd->flags & IORING_URING_CMD_SQE_CONSUMED) { + ret = -EIO; + goto done; + } ioucmd->flags |= IORING_URING_CMD_REISSUE; return ret; } if (ret == -EIOCBQUEUED) return ret; +done: if (ret < 0) req_set_fail(req); io_req_uring_cleanup(req, issue_flags); -- 2.43.0