mirror of
https://github.com/torvalds/linux.git
synced 2025-11-01 00:58:39 +02:00
io_uring: uring_cmd: add multishot support
Add UAPI flag IORING_URING_CMD_MULTISHOT for supporting multishot uring_cmd operations with provided buffer. This enables drivers to post multiple completion events from a single uring_cmd submission, which is useful for: - Notifying userspace of device events (e.g., interrupt handling) - Supporting devices with multiple event sources (e.g., multi-queue devices) - Avoiding the need for device poll() support when events originate from multiple sources device-wide The implementation adds two new APIs: - io_uring_cmd_select_buffer(): selects a buffer from the provided buffer group for multishot uring_cmd - io_uring_mshot_cmd_post_cqe(): posts a CQE after event data is pushed to the provided buffer Multishot uring_cmd must be used with buffer select (IOSQE_BUFFER_SELECT) and is mutually exclusive with IORING_URING_CMD_FIXED for now. The ublk driver will be the first user of this functionality: https://github.com/ming1/linux/commits/ublk-devel/ Signed-off-by: Ming Lei <ming.lei@redhat.com> Link: https://lore.kernel.org/r/20250821040210.1152145-3-ming.lei@redhat.com [axboe: fold in fix for !CONFIG_IO_URING] Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
d589bcddaa
commit
620a50c927
4 changed files with 102 additions and 2 deletions
|
|
@ -70,6 +70,21 @@ void io_uring_cmd_mark_cancelable(struct io_uring_cmd *cmd,
|
||||||
/* Execute the request from a blocking context */
|
/* Execute the request from a blocking context */
|
||||||
void io_uring_cmd_issue_blocking(struct io_uring_cmd *ioucmd);
|
void io_uring_cmd_issue_blocking(struct io_uring_cmd *ioucmd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Select a buffer from the provided buffer group for multishot uring_cmd.
|
||||||
|
* Returns the selected buffer address and size.
|
||||||
|
*/
|
||||||
|
struct io_br_sel io_uring_cmd_buffer_select(struct io_uring_cmd *ioucmd,
|
||||||
|
unsigned buf_group, size_t *len,
|
||||||
|
unsigned int issue_flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Complete a multishot uring_cmd event. This will post a CQE to the completion
|
||||||
|
* queue and update the provided buffer.
|
||||||
|
*/
|
||||||
|
bool io_uring_mshot_cmd_post_cqe(struct io_uring_cmd *ioucmd,
|
||||||
|
struct io_br_sel *sel, unsigned int issue_flags);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static inline int
|
static inline int
|
||||||
io_uring_cmd_import_fixed(u64 ubuf, unsigned long len, int rw,
|
io_uring_cmd_import_fixed(u64 ubuf, unsigned long len, int rw,
|
||||||
|
|
@ -102,6 +117,17 @@ static inline void io_uring_cmd_mark_cancelable(struct io_uring_cmd *cmd,
|
||||||
static inline void io_uring_cmd_issue_blocking(struct io_uring_cmd *ioucmd)
|
static inline void io_uring_cmd_issue_blocking(struct io_uring_cmd *ioucmd)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
static inline struct io_br_sel
|
||||||
|
io_uring_cmd_buffer_select(struct io_uring_cmd *ioucmd, unsigned buf_group,
|
||||||
|
size_t *len, unsigned int issue_flags)
|
||||||
|
{
|
||||||
|
return (struct io_br_sel) { .val = -EOPNOTSUPP };
|
||||||
|
}
|
||||||
|
static inline bool io_uring_mshot_cmd_post_cqe(struct io_uring_cmd *ioucmd,
|
||||||
|
ssize_t ret, unsigned int issue_flags)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -298,9 +298,13 @@ enum io_uring_op {
|
||||||
* sqe->uring_cmd_flags top 8bits aren't available for userspace
|
* sqe->uring_cmd_flags top 8bits aren't available for userspace
|
||||||
* IORING_URING_CMD_FIXED use registered buffer; pass this flag
|
* IORING_URING_CMD_FIXED use registered buffer; pass this flag
|
||||||
* along with setting sqe->buf_index.
|
* along with setting sqe->buf_index.
|
||||||
|
* IORING_URING_CMD_MULTISHOT must be used with buffer select, like other
|
||||||
|
* multishot commands. Not compatible with
|
||||||
|
* IORING_URING_CMD_FIXED, for now.
|
||||||
*/
|
*/
|
||||||
#define IORING_URING_CMD_FIXED (1U << 0)
|
#define IORING_URING_CMD_FIXED (1U << 0)
|
||||||
#define IORING_URING_CMD_MASK IORING_URING_CMD_FIXED
|
#define IORING_URING_CMD_MULTISHOT (1U << 1)
|
||||||
|
#define IORING_URING_CMD_MASK (IORING_URING_CMD_FIXED | IORING_URING_CMD_MULTISHOT)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -413,6 +413,7 @@ const struct io_issue_def io_issue_defs[] = {
|
||||||
#endif
|
#endif
|
||||||
},
|
},
|
||||||
[IORING_OP_URING_CMD] = {
|
[IORING_OP_URING_CMD] = {
|
||||||
|
.buffer_select = 1,
|
||||||
.needs_file = 1,
|
.needs_file = 1,
|
||||||
.plug = 1,
|
.plug = 1,
|
||||||
.iopoll = 1,
|
.iopoll = 1,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include "io_uring.h"
|
#include "io_uring.h"
|
||||||
#include "alloc_cache.h"
|
#include "alloc_cache.h"
|
||||||
#include "rsrc.h"
|
#include "rsrc.h"
|
||||||
|
#include "kbuf.h"
|
||||||
#include "uring_cmd.h"
|
#include "uring_cmd.h"
|
||||||
#include "poll.h"
|
#include "poll.h"
|
||||||
|
|
||||||
|
|
@ -194,8 +195,21 @@ int io_uring_cmd_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
|
||||||
if (ioucmd->flags & ~IORING_URING_CMD_MASK)
|
if (ioucmd->flags & ~IORING_URING_CMD_MASK)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (ioucmd->flags & IORING_URING_CMD_FIXED)
|
if (ioucmd->flags & IORING_URING_CMD_FIXED) {
|
||||||
|
if (ioucmd->flags & IORING_URING_CMD_MULTISHOT)
|
||||||
|
return -EINVAL;
|
||||||
req->buf_index = READ_ONCE(sqe->buf_index);
|
req->buf_index = READ_ONCE(sqe->buf_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioucmd->flags & IORING_URING_CMD_MULTISHOT) {
|
||||||
|
if (ioucmd->flags & IORING_URING_CMD_FIXED)
|
||||||
|
return -EINVAL;
|
||||||
|
if (!(req->flags & REQ_F_BUFFER_SELECT))
|
||||||
|
return -EINVAL;
|
||||||
|
} else {
|
||||||
|
if (req->flags & REQ_F_BUFFER_SELECT)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
ioucmd->cmd_op = READ_ONCE(sqe->cmd_op);
|
ioucmd->cmd_op = READ_ONCE(sqe->cmd_op);
|
||||||
|
|
||||||
|
|
@ -251,6 +265,10 @@ int io_uring_cmd(struct io_kiocb *req, unsigned int issue_flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = file->f_op->uring_cmd(ioucmd, issue_flags);
|
ret = file->f_op->uring_cmd(ioucmd, issue_flags);
|
||||||
|
if (ioucmd->flags & IORING_URING_CMD_MULTISHOT) {
|
||||||
|
if (ret >= 0)
|
||||||
|
return IOU_ISSUE_SKIP_COMPLETE;
|
||||||
|
}
|
||||||
if (ret == -EAGAIN) {
|
if (ret == -EAGAIN) {
|
||||||
ioucmd->flags |= IORING_URING_CMD_REISSUE;
|
ioucmd->flags |= IORING_URING_CMD_REISSUE;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -333,3 +351,54 @@ bool io_uring_cmd_post_mshot_cqe32(struct io_uring_cmd *cmd,
|
||||||
return false;
|
return false;
|
||||||
return io_req_post_cqe32(req, cqe);
|
return io_req_post_cqe32(req, cqe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Work with io_uring_mshot_cmd_post_cqe() together for committing the
|
||||||
|
* provided buffer upfront
|
||||||
|
*/
|
||||||
|
struct io_br_sel io_uring_cmd_buffer_select(struct io_uring_cmd *ioucmd,
|
||||||
|
unsigned buf_group, size_t *len,
|
||||||
|
unsigned int issue_flags)
|
||||||
|
{
|
||||||
|
struct io_kiocb *req = cmd_to_io_kiocb(ioucmd);
|
||||||
|
|
||||||
|
if (!(ioucmd->flags & IORING_URING_CMD_MULTISHOT))
|
||||||
|
return (struct io_br_sel) { .val = -EINVAL };
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(!io_do_buffer_select(req)))
|
||||||
|
return (struct io_br_sel) { .val = -EINVAL };
|
||||||
|
|
||||||
|
return io_buffer_select(req, len, buf_group, issue_flags);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(io_uring_cmd_buffer_select);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return true if this multishot uring_cmd needs to be completed, otherwise
|
||||||
|
* the event CQE is posted successfully.
|
||||||
|
*
|
||||||
|
* This function must use `struct io_br_sel` returned from
|
||||||
|
* io_uring_cmd_buffer_select() for committing the buffer in the same
|
||||||
|
* uring_cmd submission context.
|
||||||
|
*/
|
||||||
|
bool io_uring_mshot_cmd_post_cqe(struct io_uring_cmd *ioucmd,
|
||||||
|
struct io_br_sel *sel, unsigned int issue_flags)
|
||||||
|
{
|
||||||
|
struct io_kiocb *req = cmd_to_io_kiocb(ioucmd);
|
||||||
|
unsigned int cflags = 0;
|
||||||
|
|
||||||
|
if (!(ioucmd->flags & IORING_URING_CMD_MULTISHOT))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (sel->val > 0) {
|
||||||
|
cflags = io_put_kbuf(req, sel->val, sel->buf_list);
|
||||||
|
if (io_req_post_cqe(req, sel->val, cflags | IORING_CQE_F_MORE))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
io_kbuf_recycle(req, sel->buf_list, issue_flags);
|
||||||
|
if (sel->val < 0)
|
||||||
|
req_set_fail(req);
|
||||||
|
io_req_set_res(req, sel->val, cflags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(io_uring_mshot_cmd_post_cqe);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue