]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ublk: allow UBLK_IO_(UN)REGISTER_IO_BUF on any task
authorCaleb Sander Mateos <csander@purestorage.com>
Fri, 20 Jun 2025 15:10:03 +0000 (09:10 -0600)
committerJens Axboe <axboe@kernel.dk>
Tue, 1 Jul 2025 02:13:42 +0000 (20:13 -0600)
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>
drivers/block/ublk_drv.c
include/uapi/linux/ublk_cmd.h

index 0c244fe76d277d7f30581dcc38d365d049ec74a1..fa1859c062115c371ef18470f745ecf4c63544ab 100644 (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 |
index c9751bdfd937461d5fda5f66eb6aa8f1e1fd3063..ec77dabba45b23f21d148f7f381100c7b1d0601c 100644 (file)
  */
 #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