[PATCH 15/20] selftests: ublk: add kublk support for integrity params

Caleb Sander Mateos
2025-12-17T05:35:15Z

Add integrity param command line arguments to kublk. Plumb these to
struct ublk_params for the null and fault_inject targets, as they don't
need to actually read or write the integrity data. Forbid the integrity
params for loop or stripe until the integrity data copy is implemented.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
tools/testing/selftests/ublk/fault_inject.c | 1 +
tools/testing/selftests/ublk/file_backed.c | 4 ++
tools/testing/selftests/ublk/kublk.c | 44 +++++++++++++++++++++
tools/testing/selftests/ublk/kublk.h | 21 ++++++++++
tools/testing/selftests/ublk/null.c | 1 +
tools/testing/selftests/ublk/stripe.c | 4 ++
6 files changed, 75 insertions(+)

diff --git a/tools/testing/selftests/ublk/fault_inject.c b/tools/testing/selftests/ublk/fault_inject.c
index b227bd78b252..3b897f69c014 100644
--- a/tools/testing/selftests/ublk/fault_inject.c
+++ b/tools/testing/selftests/ublk/fault_inject.c
@@ -31,10 +31,11 @@ static int ublk_fault_inject_tgt_init(const struct dev_ctx *ctx,
.io_min_shift = 9,
.max_sectors = info->max_io_buf_bytes >> 9,
.dev_sectors = dev_size >> 9,
},
};
+ ublk_set_integrity_params(ctx, &dev->tgt.params);

dev->private_data = (void *)(unsigned long)(ctx->fault_inject.delay_us * 1000);
return 0;
}

diff --git a/tools/testing/selftests/ublk/file_backed.c b/tools/testing/selftests/ublk/file_backed.c
index 269d5f124e06..c14ce6608696 100644
--- a/tools/testing/selftests/ublk/file_backed.c
+++ b/tools/testing/selftests/ublk/file_backed.c
@@ -156,10 +156,14 @@ static int ublk_loop_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev)

if (ctx->auto_zc_fallback) {
ublk_err("%s: not support auto_zc_fallback\n", __func__);
return -EINVAL;
}
+ if (ctx->metadata_size) {
+ ublk_err("%s: integrity not supported\n", __func__);
+ return -EINVAL;
+ }

ret = backing_file_tgt_init(dev);
if (ret)
return ret;

diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c
index 185ba553686a..cf58d3b60ace 100644
--- a/tools/testing/selftests/ublk/kublk.c
+++ b/tools/testing/selftests/ublk/kublk.c
@@ -1,10 +1,11 @@
/* SPDX-License-Identifier: MIT */
/*
* Description: uring_cmd based ublk
*/

+#include <linux/fs.h>
#include "kublk.h"

#define MAX_NR_TGT_ARG 64

unsigned int ublk_dbg_mask = UBLK_LOG;
@@ -1547,10 +1548,12 @@ static void __cmd_create_help(char *exe, bool recovery)
printf("%s %s -t [null|loop|stripe|fault_inject] [-q nr_queues] [-d depth] [-n dev_id]\n",
exe, recovery ? "recover" : "add");
printf("\t[--foreground] [--quiet] [-z] [--auto_zc] [--auto_zc_fallback] [--debug_mask mask] [-r 0|1] [-g] [-u]\n");
printf("\t[-e 0|1 ] [-i 0|1] [--no_ublk_fixed_fd]\n");
printf("\t[--nthreads threads] [--per_io_tasks]\n");
+ printf("\t[--integrity_capable] [--integrity_reftag] [--metadata_size SIZE] "
+ "[--pi_offset OFFSET] [--csum_type ip|t10dif|nvme] [--tag_size SIZE]\n");
printf("\t[target options] [backfile1] [backfile2] ...\n");
printf("\tdefault: nr_queues=2(max 32), depth=128(max 1024), dev_id=-1(auto allocation)\n");
printf("\tdefault: nthreads=nr_queues");

for (i = 0; i < ARRAY_SIZE(tgt_ops_list); i++) {
@@ -1610,20 +1613,27 @@ int main(int argc, char *argv[])
{ "user_copy", 0, NULL, 'u'},
{ "size", 1, NULL, 's'},
{ "nthreads", 1, NULL, 0 },
{ "per_io_tasks", 0, NULL, 0 },
{ "no_ublk_fixed_fd", 0, NULL, 0 },
+ { "integrity_capable", 0, NULL, 0 },
+ { "integrity_reftag", 0, NULL, 0 },
+ { "metadata_size", 1, NULL, 0 },
+ { "pi_offset", 1, NULL, 0 },
+ { "csum_type", 1, NULL, 0 },
+ { "tag_size", 1, NULL, 0 },
{ 0, 0, 0, 0 }
};
const struct ublk_tgt_ops *ops = NULL;
int option_idx, opt;
const char *cmd = argv[1];
struct dev_ctx ctx = {
.queue_depth = 128,
.nr_hw_queues = 2,
.dev_id = -1,
.tgt_type = "unknown",
+ .csum_type = LBMD_PI_CSUM_NONE,
};
int ret = -EINVAL, i;
int tgt_argc = 1;
char *tgt_argv[MAX_NR_TGT_ARG] = { NULL };
int value;
@@ -1694,10 +1704,32 @@ int main(int argc, char *argv[])
ctx.nthreads = strtol(optarg, NULL, 10);
if (!strcmp(longopts[option_idx].name, "per_io_tasks"))
ctx.per_io_tasks = 1;
if (!strcmp(longopts[option_idx].name, "no_ublk_fixed_fd"))
ctx.no_ublk_fixed_fd = 1;
+ if (!strcmp(longopts[option_idx].name, "integrity_capable"))
+ ctx.integrity_flags |= LBMD_PI_CAP_INTEGRITY;
+ if (!strcmp(longopts[option_idx].name, "integrity_reftag"))
+ ctx.integrity_flags |= LBMD_PI_CAP_REFTAG;
+ if (!strcmp(longopts[option_idx].name, "metadata_size"))
+ ctx.metadata_size = strtoul(optarg, NULL, 0);
+ if (!strcmp(longopts[option_idx].name, "pi_offset"))
+ ctx.pi_offset = strtoul(optarg, NULL, 0);
+ if (!strcmp(longopts[option_idx].name, "csum_type")) {
+ if (!strcmp(optarg, "ip")) {
+ ctx.csum_type = LBMD_PI_CSUM_IP;
+ } else if (!strcmp(optarg, "t10dif")) {
+ ctx.csum_type = LBMD_PI_CSUM_CRC16_T10DIF;
+ } else if (!strcmp(optarg, "nvme")) {
+ ctx.csum_type = LBMD_PI_CSUM_CRC64_NVME;
+ } else {
+ ublk_err("invalid csum_type: %s\n", optarg);
+ return -EINVAL;
+ }
+ }
+ if (!strcmp(longopts[option_idx].name, "tag_size"))
+ ctx.tag_size = strtoul(optarg, NULL, 0);
break;
case '?':
/*
* target requires every option must have argument
*/
@@ -1736,10 +1768,22 @@ int main(int argc, char *argv[])
ctx.auto_zc_fallback > 1) {
fprintf(stderr, "too many data copy modes specified\n");
return -EINVAL;
}

+ if (!ctx.metadata_size && (ctx.integrity_flags ||
+ ctx.pi_offset ||
+ ctx.csum_type != LBMD_PI_CSUM_NONE ||
+ ctx.tag_size)) {
+ ublk_err("integrity parameters require metadata_size\n");
+ return -EINVAL;
+ }
+ if (ctx.metadata_size && !(ctx.flags & UBLK_F_USER_COPY)) {
+ ublk_err("integrity requires user_copy\n");
+ return -EINVAL;
+ }
+
i = optind;
while (i < argc && ctx.nr_files < MAX_BACK_FILES) {
ctx.files[ctx.nr_files++] = argv[i++];
}

diff --git a/tools/testing/selftests/ublk/kublk.h b/tools/testing/selftests/ublk/kublk.h
index 8a83b90ec603..d00f2b465cdf 100644
--- a/tools/testing/selftests/ublk/kublk.h
+++ b/tools/testing/selftests/ublk/kublk.h
@@ -76,10 +76,15 @@ struct dev_ctx {
unsigned int fg:1;
unsigned int recovery:1;
unsigned int auto_zc_fallback:1;
unsigned int per_io_tasks:1;
unsigned int no_ublk_fixed_fd:1;
+ __u32 integrity_flags;
+ __u8 metadata_size;
+ __u8 pi_offset;
+ __u8 csum_type;
+ __u8 tag_size;

int _evtfd;
int _shmid;

/* built from shmem, only for ublk_dump_dev() */
@@ -200,10 +205,26 @@ struct ublk_dev {
void *private_data;
};

extern int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io);

+static inline void ublk_set_integrity_params(const struct dev_ctx *ctx,
+ struct ublk_params *params)
+{
+ if (!ctx->metadata_size)
+ return;
+
+ params->types |= UBLK_PARAM_TYPE_INTEGRITY;
+ params->integrity = (struct ublk_param_integrity) {
+ .flags = ctx->integrity_flags,
+ .interval_exp = params->basic.logical_bs_shift,
+ .metadata_size = ctx->metadata_size,
+ .pi_offset = ctx->pi_offset,
+ .csum_type = ctx->csum_type,
+ .tag_size = ctx->tag_size,
+ };
+}

static inline int ublk_io_auto_zc_fallback(const struct ublksrv_io_desc *iod)
{
return !!(iod->op_flags & UBLK_IO_F_NEED_REG_BUF);
}
diff --git a/tools/testing/selftests/ublk/null.c b/tools/testing/selftests/ublk/null.c
index 280043f6b689..3aa162f08476 100644
--- a/tools/testing/selftests/ublk/null.c
+++ b/tools/testing/selftests/ublk/null.c
@@ -34,10 +34,11 @@ static int ublk_null_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev)
.seg_boundary_mask = 4095,
.max_segment_size = 32 << 10,
.max_segments = 32,
},
};
+ ublk_set_integrity_params(ctx, &dev->tgt.params);

if (info->flags & UBLK_F_SUPPORT_ZERO_COPY)
dev->tgt.sq_depth = dev->tgt.cq_depth = 2 * info->queue_depth;
return 0;
}
diff --git a/tools/testing/selftests/ublk/stripe.c b/tools/testing/selftests/ublk/stripe.c
index fd412e1f01c0..d4aaf3351d71 100644
--- a/tools/testing/selftests/ublk/stripe.c
+++ b/tools/testing/selftests/ublk/stripe.c
@@ -296,10 +296,14 @@ static int ublk_stripe_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev)

if (ctx->auto_zc_fallback) {
ublk_err("%s: not support auto_zc_fallback\n", __func__);
return -EINVAL;
}
+ if (ctx->metadata_size) {
+ ublk_err("%s: integrity not supported\n", __func__);
+ return -EINVAL;
+ }

if ((chunk_size & (chunk_size - 1)) || !chunk_size) {
ublk_err("invalid chunk size %u\n", chunk_size);
return -EINVAL;
}
--
2.45.2