Commit 763ff02c authored by Caleb Sander Mateos's avatar Caleb Sander Mateos Committed by Jens Axboe
Browse files

ublk: allow UBLK_IO_(UN)REGISTER_IO_BUF on any task



Currently, UBLK_IO_REGISTER_IO_BUF and UBLK_IO_UNREGISTER_IO_BUF are
only permitted on the ublk_io's daemon task. But this restriction is
unnecessary. ublk_register_io_buf() calls __ublk_check_and_get_req() to
look up the request from the tagset and atomically take a reference on
the request without accessing the ublk_io. ublk_unregister_io_buf()
doesn't use the q_id or tag at all.

So allow these opcodes even on tasks other than io->task.

Handle UBLK_IO_UNREGISTER_IO_BUF before obtaining the ubq and io since
the buffer index being unregistered is not necessarily related to the
specified q_id and tag.

Add a feature flag UBLK_F_BUF_REG_OFF_DAEMON that userspace can use to
determine whether the kernel supports off-daemon buffer registration.

Suggested-by: default avatarMing Lei <ming.lei@redhat.com>
Signed-off-by: default avatarCaleb Sander Mateos <csander@purestorage.com>
Reviewed-by: default avatarMing Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20250620151008.3976463-10-csander@purestorage.com


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 2da1e7bb
Loading
Loading
Loading
Loading
+22 −5
Original line number Diff line number Diff line
@@ -70,7 +70,8 @@
		| UBLK_F_UPDATE_SIZE \
		| UBLK_F_AUTO_BUF_REG \
		| UBLK_F_QUIESCE \
		| UBLK_F_PER_IO_DAEMON)
		| UBLK_F_PER_IO_DAEMON \
		| UBLK_F_BUF_REG_OFF_DAEMON)

#define UBLK_F_ALL_RECOVERY_FLAGS (UBLK_F_USER_RECOVERY \
		| UBLK_F_USER_RECOVERY_REISSUE \
@@ -2204,6 +2205,14 @@ static int __ublk_ch_uring_cmd(struct io_uring_cmd *cmd,
	if (ret)
		goto out;

	/*
	 * io_buffer_unregister_bvec() doesn't access the ubq or io,
	 * so no need to validate the q_id, tag, or task
	 */
	if (_IOC_NR(cmd_op) == UBLK_IO_UNREGISTER_IO_BUF)
		return ublk_unregister_io_buf(cmd, ub, ub_cmd->addr,
					      issue_flags);

	ret = -EINVAL;
	if (ub_cmd->q_id >= ub->dev_info.nr_hw_queues)
		goto out;
@@ -2224,8 +2233,17 @@ static int __ublk_ch_uring_cmd(struct io_uring_cmd *cmd,
		return -EIOCBQUEUED;
	}

	if (READ_ONCE(io->task) != current)
	if (READ_ONCE(io->task) != current) {
		/*
		 * ublk_register_io_buf() accesses only the io's refcount,
		 * so can be handled on any task
		 */
		if (_IOC_NR(cmd_op) == UBLK_IO_REGISTER_IO_BUF)
			return ublk_register_io_buf(cmd, ubq, io, ub_cmd->addr,
						    issue_flags);

		goto out;
	}

	/* there is pending io cmd, something must be wrong */
	if (!(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV)) {
@@ -2244,8 +2262,6 @@ static int __ublk_ch_uring_cmd(struct io_uring_cmd *cmd,
	switch (_IOC_NR(cmd_op)) {
	case UBLK_IO_REGISTER_IO_BUF:
		return ublk_register_io_buf(cmd, ubq, io, ub_cmd->addr, issue_flags);
	case UBLK_IO_UNREGISTER_IO_BUF:
		return ublk_unregister_io_buf(cmd, ub, ub_cmd->addr, issue_flags);
	case UBLK_IO_COMMIT_AND_FETCH_REQ:
		ret = ublk_commit_and_fetch(ubq, io, cmd, ub_cmd, issue_flags);
		if (ret)
@@ -2961,7 +2977,8 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)

	ub->dev_info.flags |= UBLK_F_CMD_IOCTL_ENCODE |
		UBLK_F_URING_CMD_COMP_IN_TASK |
		UBLK_F_PER_IO_DAEMON;
		UBLK_F_PER_IO_DAEMON |
		UBLK_F_BUF_REG_OFF_DAEMON;

	/* GET_DATA isn't needed any more with USER_COPY or ZERO COPY */
	if (ub->dev_info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
+10 −0
Original line number Diff line number Diff line
@@ -301,6 +301,16 @@
 */
#define UBLK_F_PER_IO_DAEMON (1ULL << 13)

/*
 * If this feature is set, UBLK_U_IO_REGISTER_IO_BUF/UBLK_U_IO_UNREGISTER_IO_BUF
 * can be issued for an I/O on any task. q_id and tag are also ignored in
 * UBLK_U_IO_UNREGISTER_IO_BUF's ublksrv_io_cmd.
 * If it is unset, zero-copy buffers can only be registered and unregistered by
 * the I/O's daemon task. The q_id and tag of the registered buffer are required
 * in UBLK_U_IO_UNREGISTER_IO_BUF's ublksrv_io_cmd.
 */
#define UBLK_F_BUF_REG_OFF_DAEMON (1ULL << 14)

/* device state */
#define UBLK_S_DEV_DEAD	0
#define UBLK_S_DEV_LIVE	1