]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selftests: ublk: implement integrity user copy in kublk
authorCaleb Sander Mateos <csander@purestorage.com>
Thu, 8 Jan 2026 09:19:43 +0000 (02:19 -0700)
committerJens Axboe <axboe@kernel.dk>
Mon, 12 Jan 2026 16:16:38 +0000 (09:16 -0700)
If integrity data is enabled for kublk, allocate an integrity buffer for
each I/O. Extend ublk_user_copy() to copy the integrity data between the
ublk request and the integrity buffer if the ublksrv_io_desc indicates
that the request has integrity data.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
tools/testing/selftests/ublk/kublk.c
tools/testing/selftests/ublk/kublk.h

index 48e1865b4875b670b94b7a6ee101e03b5975c75b..d95937dd616737f07351c8b5db820c6dc294aa46 100644 (file)
@@ -416,8 +416,10 @@ static void ublk_queue_deinit(struct ublk_queue *q)
        if (q->io_cmd_buf)
                munmap(q->io_cmd_buf, ublk_queue_cmd_buf_sz(q));
 
-       for (i = 0; i < nr_ios; i++)
+       for (i = 0; i < nr_ios; i++) {
                free(q->ios[i].buf_addr);
+               free(q->ios[i].integrity_buf);
+       }
 }
 
 static void ublk_thread_deinit(struct ublk_thread *t)
@@ -433,12 +435,13 @@ static void ublk_thread_deinit(struct ublk_thread *t)
        }
 }
 
-static int ublk_queue_init(struct ublk_queue *q, unsigned long long extra_flags)
+static int ublk_queue_init(struct ublk_queue *q, unsigned long long extra_flags,
+                          __u8 metadata_size)
 {
        struct ublk_dev *dev = q->dev;
        int depth = dev->dev_info.queue_depth;
        int i;
-       int cmd_buf_size, io_buf_size;
+       int cmd_buf_size, io_buf_size, integrity_size;
        unsigned long off;
 
        q->tgt_ops = dev->tgt.ops;
@@ -446,6 +449,7 @@ static int ublk_queue_init(struct ublk_queue *q, unsigned long long extra_flags)
        q->q_depth = depth;
        q->flags = dev->dev_info.flags;
        q->flags |= extra_flags;
+       q->metadata_size = metadata_size;
 
        /* Cache fd in queue for fast path access */
        q->ublk_fd = dev->fds[0];
@@ -461,11 +465,23 @@ static int ublk_queue_init(struct ublk_queue *q, unsigned long long extra_flags)
        }
 
        io_buf_size = dev->dev_info.max_io_buf_bytes;
+       integrity_size = ublk_integrity_len(q, io_buf_size);
        for (i = 0; i < q->q_depth; i++) {
                q->ios[i].buf_addr = NULL;
                q->ios[i].flags = UBLKS_IO_NEED_FETCH_RQ | UBLKS_IO_FREE;
                q->ios[i].tag = i;
 
+               if (integrity_size) {
+                       q->ios[i].integrity_buf = malloc(integrity_size);
+                       if (!q->ios[i].integrity_buf) {
+                               ublk_err("ublk dev %d queue %d io %d malloc(%d) failed: %m\n",
+                                        dev->dev_info.dev_id, q->q_id, i,
+                                        integrity_size);
+                               goto fail;
+                       }
+               }
+
+
                if (ublk_queue_no_buf(q))
                        continue;
 
@@ -608,13 +624,13 @@ static void ublk_user_copy(const struct ublk_io *io, __u8 match_ublk_op)
        __u8 ublk_op = ublksrv_get_op(iod);
        __u32 len = iod->nr_sectors << 9;
        void *addr = io->buf_addr;
+       ssize_t copied;
 
        if (ublk_op != match_ublk_op)
                return;
 
        while (len) {
                __u32 copy_len = min(len, UBLK_USER_COPY_LEN);
-               ssize_t copied;
 
                if (ublk_op == UBLK_IO_OP_WRITE)
                        copied = pread(q->ublk_fd, addr, copy_len, off);
@@ -627,6 +643,20 @@ static void ublk_user_copy(const struct ublk_io *io, __u8 match_ublk_op)
                off += copy_len;
                len -= copy_len;
        }
+
+       if (!(iod->op_flags & UBLK_IO_F_INTEGRITY))
+               return;
+
+       len = ublk_integrity_len(q, iod->nr_sectors << 9);
+       off = ublk_user_copy_offset(q->q_id, io->tag);
+       off |= UBLKSRV_IO_INTEGRITY_FLAG;
+       if (ublk_op == UBLK_IO_OP_WRITE)
+               copied = pread(q->ublk_fd, io->integrity_buf, len, off);
+       else if (ublk_op == UBLK_IO_OP_READ)
+               copied = pwrite(q->ublk_fd, io->integrity_buf, len, off);
+       else
+               assert(0);
+       assert(copied == (ssize_t)len);
 }
 
 int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io)
@@ -1013,7 +1043,8 @@ static int ublk_start_daemon(const struct dev_ctx *ctx, struct ublk_dev *dev)
                dev->q[i].dev = dev;
                dev->q[i].q_id = i;
 
-               ret = ublk_queue_init(&dev->q[i], extra_flags);
+               ret = ublk_queue_init(&dev->q[i], extra_flags,
+                                     ctx->metadata_size);
                if (ret) {
                        ublk_err("ublk dev %d queue %d init queue failed\n",
                                 dinfo->dev_id, i);
index d00f2b465cdfb2d0b2b664b7117b65838022d497..830b49a7716ac6acd1a470d6525516f73954e2f5 100644 (file)
@@ -112,6 +112,7 @@ struct ublk_ctrl_cmd_data {
 
 struct ublk_io {
        char *buf_addr;
+       void *integrity_buf;
 
 #define UBLKS_IO_NEED_FETCH_RQ         (1UL << 0)
 #define UBLKS_IO_NEED_COMMIT_RQ_COMP   (1UL << 1)
@@ -175,6 +176,7 @@ struct ublk_queue {
 #define UBLKS_Q_NO_UBLK_FIXED_FD       (1ULL << 62)
        __u64 flags;
        int ublk_fd;    /* cached ublk char device fd */
+       __u8 metadata_size;
        struct ublk_io ios[UBLK_QUEUE_DEPTH];
 };
 
@@ -224,6 +226,18 @@ static inline void ublk_set_integrity_params(const struct dev_ctx *ctx,
        };
 }
 
+static inline size_t ublk_integrity_len(const struct ublk_queue *q, size_t len)
+{
+       /* All targets currently use interval_exp = logical_bs_shift = 9 */
+       return (len >> 9) * q->metadata_size;
+}
+
+static inline size_t
+ublk_integrity_data_len(const struct ublk_queue *q, size_t integrity_len)
+{
+       return (integrity_len / q->metadata_size) << 9;
+}
+
 static inline int ublk_io_auto_zc_fallback(const struct ublksrv_io_desc *iod)
 {
        return !!(iod->op_flags & UBLK_IO_F_NEED_REG_BUF);