mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +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
	
	 Ming Lei
						Ming Lei