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: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20250620151008.3976463-10-csander@purestorage.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Caleb Sander Mateos
2025-06-20 09:10:03 -06:00
committed by Jens Axboe
parent 2da1e7bb3f
commit 763ff02ce2
2 changed files with 32 additions and 5 deletions

View File

@@ -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 |

View File

@@ -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