linux-block.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/19] ublk: add support for integrity data
@ 2026-01-06  0:57 Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 01/19] blk-integrity: take const pointer in blk_integrity_rq() Caleb Sander Mateos
                   ` (18 more replies)
  0 siblings, 19 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

Much work has recently gone into supporting block device integrity data
(sometimes called "metadata") in Linux. Many NVMe devices these days
support metadata transfers and/or automatic protection information
generation and verification. However, ublk devices can't yet advertise
integrity data capabilities. This patch series wires up support for
integrity data in ublk. The ublk feature is referred to as "integrity"
rather than "metadata" to match the block layer's name for it and to
avoid confusion with the existing and unrelated UBLK_IO_F_META.

To advertise support for integrity data, a ublk server fills out the
struct ublk_params's integrity field and sets UBLK_PARAM_TYPE_INTEGRITY.
The struct ublk_param_integrity flags and csum_type fields use the
existing LBMD_PI_* constants from the linux/fs.h UAPI header. The ublk
driver fills out a corresponding struct blk_integrity.

When a request with integrity data is issued to the ublk device, the
ublk driver sets UBLK_IO_F_INTEGRITY in struct ublksrv_io_desc's
op_flags field. This is necessary for a ublk server for which
bi_offload_capable() returns true to distinguish requests with integrity
data from those without.

Integrity data transfers can currently only be performed via the ublk
user copy mechanism. The overhead of zero-copy buffer registration makes
it less appealing for the small transfers typical of integrity data.
Additionally, neither io_uring NVMe passthru nor IORING_RW_ATTR_FLAG_PI
currently allow an io_uring registered buffer for the integrity data.
The ki_pos field of the struct kiocb passed to the user copy
->{read,write}_iter() callback gains a bit UBLKSRV_IO_INTEGRITY_FLAG for
a ublk server to indicate whether to access the request's data or
integrity data.

Not yet supported is an analogue for the IO_INTEGRITY_CHK_*/BIP_CHECK_*
flags to ask the ublk server to verify the guard, reftag, and/or apptag
of a request's protection information. The user copy mechanism currently
forbids a ublk server from reading the data/integrity buffer of a
read-direction request. We could potentially relax this restriction for
integrity data on reads. Alternatively, the ublk driver could verify the
requested fields as part of the user copy operation.

v3:
- Drop support for communicating BIP_CHECK_* for now until the interface
 is decided
- Add Reviewed-by tags

v2:
- Communicate BIP_CHECK_* flags and expected reftag seed and app tag to
  ublk server
- Add UBLK_F_INTEGRITY feature flag (Ming)
- Don't change the definition of UBLKSRV_IO_BUF_TOTAL_BITS (Ming)
- Drop patches already applied
- Add Reviewed-by tags

Caleb Sander Mateos (16):
  blk-integrity: take const pointer in blk_integrity_rq()
  ublk: move ublk flag check functions earlier
  ublk: set UBLK_IO_F_INTEGRITY in ublksrv_io_desc
  ublk: add ublk_copy_user_bvec() helper
  ublk: split out ublk_user_copy() helper
  ublk: inline ublk_check_and_get_req() into ublk_user_copy()
  ublk: move offset check out of __ublk_check_and_get_req()
  ublk: optimize ublk_user_copy() on daemon task
  selftests: ublk: display UBLK_F_INTEGRITY support
  selftests: ublk: add utility to get block device metadata size
  selftests: ublk: add kublk support for integrity params
  selftests: ublk: implement integrity user copy in kublk
  selftests: ublk: support non-O_DIRECT backing files
  selftests: ublk: add integrity data support to loop target
  selftests: ublk: add integrity params test
  selftests: ublk: add end-to-end integrity test

Stanley Zhang (3):
  ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation
  ublk: implement integrity user copy
  ublk: support UBLK_F_INTEGRITY

 drivers/block/ublk_drv.c                     | 350 +++++++++++++------
 include/linux/blk-integrity.h                |   6 +-
 include/uapi/linux/ublk_cmd.h                |  24 ++
 tools/testing/selftests/ublk/Makefile        |   6 +-
 tools/testing/selftests/ublk/common.c        |   4 +-
 tools/testing/selftests/ublk/fault_inject.c  |   1 +
 tools/testing/selftests/ublk/file_backed.c   |  61 +++-
 tools/testing/selftests/ublk/kublk.c         |  90 ++++-
 tools/testing/selftests/ublk/kublk.h         |  37 +-
 tools/testing/selftests/ublk/metadata_size.c |  36 ++
 tools/testing/selftests/ublk/null.c          |   1 +
 tools/testing/selftests/ublk/stripe.c        |   6 +-
 tools/testing/selftests/ublk/test_common.sh  |  10 +
 tools/testing/selftests/ublk/test_loop_08.sh | 111 ++++++
 tools/testing/selftests/ublk/test_null_04.sh | 166 +++++++++
 15 files changed, 777 insertions(+), 132 deletions(-)
 create mode 100644 tools/testing/selftests/ublk/metadata_size.c
 create mode 100755 tools/testing/selftests/ublk/test_loop_08.sh
 create mode 100755 tools/testing/selftests/ublk/test_null_04.sh

-- 
2.45.2


^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH v3 01/19] blk-integrity: take const pointer in blk_integrity_rq()
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 02/19] ublk: move ublk flag check functions earlier Caleb Sander Mateos
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

blk_integrity_rq() doesn't modify the struct request passed in, so allow
a const pointer to be passed. Use a matching signature for the
!CONFIG_BLK_DEV_INTEGRITY version.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
---
 include/linux/blk-integrity.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/include/linux/blk-integrity.h b/include/linux/blk-integrity.h
index a6b84206eb94..c15b1ac62765 100644
--- a/include/linux/blk-integrity.h
+++ b/include/linux/blk-integrity.h
@@ -89,11 +89,11 @@ static inline unsigned int bio_integrity_bytes(struct blk_integrity *bi,
 					       unsigned int sectors)
 {
 	return bio_integrity_intervals(bi, sectors) * bi->metadata_size;
 }
 
-static inline bool blk_integrity_rq(struct request *rq)
+static inline bool blk_integrity_rq(const struct request *rq)
 {
 	return rq->cmd_flags & REQ_INTEGRITY;
 }
 
 /*
@@ -166,13 +166,13 @@ static inline unsigned int bio_integrity_intervals(struct blk_integrity *bi,
 static inline unsigned int bio_integrity_bytes(struct blk_integrity *bi,
 					       unsigned int sectors)
 {
 	return 0;
 }
-static inline int blk_integrity_rq(struct request *rq)
+static inline bool blk_integrity_rq(const struct request *rq)
 {
-	return 0;
+	return false;
 }
 
 static inline struct bio_vec rq_integrity_vec(struct request *rq)
 {
 	/* the optimizer will remove all calls to this function */
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 02/19] ublk: move ublk flag check functions earlier
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 01/19] blk-integrity: take const pointer in blk_integrity_rq() Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 03/19] ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation Caleb Sander Mateos
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

ublk_dev_support_user_copy() will be used in ublk_validate_params().
Move these functions next to ublk_{dev,queue}_is_zoned() to avoid
needing to forward-declare them.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/block/ublk_drv.c | 60 ++++++++++++++++++++--------------------
 1 file changed, 30 insertions(+), 30 deletions(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 837fedb02e0d..8e3da9b2b93a 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -273,10 +273,40 @@ static inline struct ublksrv_io_desc *
 ublk_get_iod(const struct ublk_queue *ubq, unsigned tag)
 {
 	return &ubq->io_cmd_buf[tag];
 }
 
+static inline bool ublk_support_zero_copy(const struct ublk_queue *ubq)
+{
+	return ubq->flags & UBLK_F_SUPPORT_ZERO_COPY;
+}
+
+static inline bool ublk_dev_support_zero_copy(const struct ublk_device *ub)
+{
+	return ub->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY;
+}
+
+static inline bool ublk_support_auto_buf_reg(const struct ublk_queue *ubq)
+{
+	return ubq->flags & UBLK_F_AUTO_BUF_REG;
+}
+
+static inline bool ublk_dev_support_auto_buf_reg(const struct ublk_device *ub)
+{
+	return ub->dev_info.flags & UBLK_F_AUTO_BUF_REG;
+}
+
+static inline bool ublk_support_user_copy(const struct ublk_queue *ubq)
+{
+	return ubq->flags & UBLK_F_USER_COPY;
+}
+
+static inline bool ublk_dev_support_user_copy(const struct ublk_device *ub)
+{
+	return ub->dev_info.flags & UBLK_F_USER_COPY;
+}
+
 static inline bool ublk_dev_is_zoned(const struct ublk_device *ub)
 {
 	return ub->dev_info.flags & UBLK_F_ZONED;
 }
 
@@ -671,40 +701,10 @@ static void ublk_apply_params(struct ublk_device *ub)
 
 	if (ub->params.types & UBLK_PARAM_TYPE_ZONED)
 		ublk_dev_param_zoned_apply(ub);
 }
 
-static inline bool ublk_support_zero_copy(const struct ublk_queue *ubq)
-{
-	return ubq->flags & UBLK_F_SUPPORT_ZERO_COPY;
-}
-
-static inline bool ublk_dev_support_zero_copy(const struct ublk_device *ub)
-{
-	return ub->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY;
-}
-
-static inline bool ublk_support_auto_buf_reg(const struct ublk_queue *ubq)
-{
-	return ubq->flags & UBLK_F_AUTO_BUF_REG;
-}
-
-static inline bool ublk_dev_support_auto_buf_reg(const struct ublk_device *ub)
-{
-	return ub->dev_info.flags & UBLK_F_AUTO_BUF_REG;
-}
-
-static inline bool ublk_support_user_copy(const struct ublk_queue *ubq)
-{
-	return ubq->flags & UBLK_F_USER_COPY;
-}
-
-static inline bool ublk_dev_support_user_copy(const struct ublk_device *ub)
-{
-	return ub->dev_info.flags & UBLK_F_USER_COPY;
-}
-
 static inline bool ublk_need_map_io(const struct ublk_queue *ubq)
 {
 	return !ublk_support_user_copy(ubq) && !ublk_support_zero_copy(ubq) &&
 		!ublk_support_auto_buf_reg(ubq);
 }
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 03/19] ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 01/19] blk-integrity: take const pointer in blk_integrity_rq() Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 02/19] ublk: move ublk flag check functions earlier Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06 13:09   ` Ming Lei
  2026-01-06  0:57 ` [PATCH v3 04/19] ublk: set UBLK_IO_F_INTEGRITY in ublksrv_io_desc Caleb Sander Mateos
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

From: Stanley Zhang <stazhang@purestorage.com>

Add a feature flag UBLK_F_INTEGRITY for a ublk server to request
integrity/metadata support when creating a ublk device. The ublk server
can also check for the feature flag on the created device or the result
of UBLK_U_CMD_GET_FEATURES to tell if the ublk driver supports it.
UBLK_F_INTEGRITY requires UBLK_F_USER_COPY, as user copy is the only
data copy mode initially supported for integrity data.
Add UBLK_PARAM_TYPE_INTEGRITY and struct ublk_param_integrity to struct
ublk_params to specify the integrity params of a ublk device.
UBLK_PARAM_TYPE_INTEGRITY requires UBLK_F_INTEGRITY and a nonzero
metadata_size. The LBMD_PI_CAP_* and LBMD_PI_CSUM_* values from the
linux/fs.h UAPI header are used for the flags and csum_type fields.
If the UBLK_PARAM_TYPE_INTEGRITY flag is set, validate the integrity
parameters and apply them to the blk_integrity limits.
The struct ublk_param_integrity validations are based on the checks in
blk_validate_integrity_limits(). Any invalid parameters should be
rejected before being applied to struct blk_integrity.

Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
[csander: drop redundant pi_tuple_size field, use block metadata UAPI
 constants, add param validation]
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 drivers/block/ublk_drv.c      | 94 ++++++++++++++++++++++++++++++++++-
 include/uapi/linux/ublk_cmd.h | 18 +++++++
 2 files changed, 111 insertions(+), 1 deletion(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 8e3da9b2b93a..066c6ae062a0 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -42,10 +42,12 @@
 #include <linux/mm.h>
 #include <asm/page.h>
 #include <linux/task_work.h>
 #include <linux/namei.h>
 #include <linux/kref.h>
+#include <linux/blk-integrity.h>
+#include <uapi/linux/fs.h>
 #include <uapi/linux/ublk_cmd.h>
 
 #define UBLK_MINORS		(1U << MINORBITS)
 
 #define UBLK_INVALID_BUF_IDX 	((u16)-1)
@@ -81,11 +83,12 @@
 
 /* All UBLK_PARAM_TYPE_* should be included here */
 #define UBLK_PARAM_TYPE_ALL                                \
 	(UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD | \
 	 UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED |    \
-	 UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT)
+	 UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT | \
+	 UBLK_PARAM_TYPE_INTEGRITY)
 
 struct ublk_uring_cmd_pdu {
 	/*
 	 * Store requests in same batch temporarily for queuing them to
 	 * daemon context.
@@ -628,10 +631,57 @@ static void ublk_dev_param_basic_apply(struct ublk_device *ub)
 		set_disk_ro(ub->ub_disk, true);
 
 	set_capacity(ub->ub_disk, p->dev_sectors);
 }
 
+static int ublk_integrity_flags(u32 flags)
+{
+	int ret_flags = 0;
+
+	if (flags & LBMD_PI_CAP_INTEGRITY) {
+		flags &= ~LBMD_PI_CAP_INTEGRITY;
+		ret_flags |= BLK_INTEGRITY_DEVICE_CAPABLE;
+	}
+	if (flags & LBMD_PI_CAP_REFTAG) {
+		flags &= ~LBMD_PI_CAP_REFTAG;
+		ret_flags |= BLK_INTEGRITY_REF_TAG;
+	}
+	return flags ? -EINVAL : ret_flags;
+}
+
+static int ublk_integrity_pi_tuple_size(u8 csum_type)
+{
+	switch (csum_type) {
+	case LBMD_PI_CSUM_NONE:
+		return 0;
+	case LBMD_PI_CSUM_IP:
+	case LBMD_PI_CSUM_CRC16_T10DIF:
+		return 8;
+	case LBMD_PI_CSUM_CRC64_NVME:
+		return 16;
+	default:
+		return -EINVAL;
+	}
+}
+
+static enum blk_integrity_checksum ublk_integrity_csum_type(u8 csum_type)
+{
+	switch (csum_type) {
+	case LBMD_PI_CSUM_NONE:
+		return BLK_INTEGRITY_CSUM_NONE;
+	case LBMD_PI_CSUM_IP:
+		return BLK_INTEGRITY_CSUM_IP;
+	case LBMD_PI_CSUM_CRC16_T10DIF:
+		return BLK_INTEGRITY_CSUM_CRC;
+	case LBMD_PI_CSUM_CRC64_NVME:
+		return BLK_INTEGRITY_CSUM_CRC64;
+	default:
+		WARN_ON_ONCE(1);
+		return BLK_INTEGRITY_CSUM_NONE;
+	}
+}
+
 static int ublk_validate_params(const struct ublk_device *ub)
 {
 	/* basic param is the only one which must be set */
 	if (ub->params.types & UBLK_PARAM_TYPE_BASIC) {
 		const struct ublk_param_basic *p = &ub->params.basic;
@@ -690,10 +740,33 @@ static int ublk_validate_params(const struct ublk_device *ub)
 			return -EINVAL;
 		if (p->max_segment_size < UBLK_MIN_SEGMENT_SIZE)
 			return -EINVAL;
 	}
 
+	if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
+		const struct ublk_param_integrity *p = &ub->params.integrity;
+		int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
+		int flags = ublk_integrity_flags(p->flags);
+
+		if (!(ub->dev_info.flags & UBLK_F_INTEGRITY))
+			return -EINVAL;
+		if (flags < 0)
+			return flags;
+		if (pi_tuple_size < 0)
+			return pi_tuple_size;
+		if (!p->metadata_size)
+			return -EINVAL;
+		if (p->csum_type == LBMD_PI_CSUM_NONE &&
+		    p->flags & LBMD_PI_CAP_REFTAG)
+			return -EINVAL;
+		if (p->pi_offset + pi_tuple_size > p->metadata_size)
+			return -EINVAL;
+		if (p->interval_exp < SECTOR_SHIFT ||
+		    p->interval_exp > ub->params.basic.logical_bs_shift)
+			return -EINVAL;
+	}
+
 	return 0;
 }
 
 static void ublk_apply_params(struct ublk_device *ub)
 {
@@ -2941,10 +3014,25 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub,
 		lim.seg_boundary_mask = ub->params.seg.seg_boundary_mask;
 		lim.max_segment_size = ub->params.seg.max_segment_size;
 		lim.max_segments = ub->params.seg.max_segments;
 	}
 
+	if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
+		const struct ublk_param_integrity *p = &ub->params.integrity;
+		int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
+
+		lim.integrity = (struct blk_integrity) {
+			.flags = ublk_integrity_flags(p->flags),
+			.csum_type = ublk_integrity_csum_type(p->csum_type),
+			.metadata_size = p->metadata_size,
+			.pi_offset = p->pi_offset,
+			.interval_exp = p->interval_exp,
+			.tag_size = p->tag_size,
+			.pi_tuple_size = pi_tuple_size,
+		};
+	}
+
 	if (wait_for_completion_interruptible(&ub->completion) != 0)
 		return -EINTR;
 
 	if (ub->ublksrv_tgid != ublksrv_pid)
 		return -EINVAL;
@@ -3131,10 +3219,14 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
 		if (info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
 					UBLK_F_AUTO_BUF_REG))
 			return -EINVAL;
 	}
 
+	/* User copy is required to access integrity buffer */
+	if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY))
+		return -EINVAL;
+
 	/* the created device is always owned by current user */
 	ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
 
 	if (header->dev_id != info.dev_id) {
 		pr_warn("%s: dev id not match %u %u\n",
diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
index ec77dabba45b..a54c47832fa2 100644
--- a/include/uapi/linux/ublk_cmd.h
+++ b/include/uapi/linux/ublk_cmd.h
@@ -309,10 +309,16 @@
  * 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)
 
+/*
+ * ublk device supports requests with integrity/metadata buffer.
+ * Requires UBLK_F_USER_COPY.
+ */
+#define UBLK_F_INTEGRITY (1ULL << 16)
+
 /* device state */
 #define UBLK_S_DEV_DEAD	0
 #define UBLK_S_DEV_LIVE	1
 #define UBLK_S_DEV_QUIESCED	2
 #define UBLK_S_DEV_FAIL_IO 	3
@@ -598,10 +604,20 @@ struct ublk_param_segment {
 	__u32 	max_segment_size;
 	__u16 	max_segments;
 	__u8	pad[2];
 };
 
+struct ublk_param_integrity {
+	__u32	flags; /* LBMD_PI_CAP_* from linux/fs.h */
+	__u8	interval_exp;
+	__u8	metadata_size; /* UBLK_PARAM_TYPE_INTEGRITY requires nonzero */
+	__u8	pi_offset;
+	__u8	csum_type; /* LBMD_PI_CSUM_* from linux/fs.h */
+	__u8	tag_size;
+	__u8	pad[7];
+};
+
 struct ublk_params {
 	/*
 	 * Total length of parameters, userspace has to set 'len' for both
 	 * SET_PARAMS and GET_PARAMS command, and driver may update len
 	 * if two sides use different version of 'ublk_params', same with
@@ -612,16 +628,18 @@ struct ublk_params {
 #define UBLK_PARAM_TYPE_DISCARD         (1 << 1)
 #define UBLK_PARAM_TYPE_DEVT            (1 << 2)
 #define UBLK_PARAM_TYPE_ZONED           (1 << 3)
 #define UBLK_PARAM_TYPE_DMA_ALIGN       (1 << 4)
 #define UBLK_PARAM_TYPE_SEGMENT         (1 << 5)
+#define UBLK_PARAM_TYPE_INTEGRITY       (1 << 6) /* requires UBLK_F_INTEGRITY */
 	__u32	types;			/* types of parameter included */
 
 	struct ublk_param_basic		basic;
 	struct ublk_param_discard	discard;
 	struct ublk_param_devt		devt;
 	struct ublk_param_zoned	zoned;
 	struct ublk_param_dma_align	dma;
 	struct ublk_param_segment	seg;
+	struct ublk_param_integrity	integrity;
 };
 
 #endif
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 04/19] ublk: set UBLK_IO_F_INTEGRITY in ublksrv_io_desc
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (2 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 03/19] ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 05/19] ublk: add ublk_copy_user_bvec() helper Caleb Sander Mateos
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

Indicate to the ublk server when an incoming request has integrity data
by setting UBLK_IO_F_INTEGRITY in the ublksrv_io_desc's op_flags field.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/block/ublk_drv.c      | 3 +++
 include/uapi/linux/ublk_cmd.h | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 066c6ae062a0..2b0a9720921d 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -1120,10 +1120,13 @@ static inline unsigned int ublk_req_build_flags(struct request *req)
 		flags |= UBLK_IO_F_NOUNMAP;
 
 	if (req->cmd_flags & REQ_SWAP)
 		flags |= UBLK_IO_F_SWAP;
 
+	if (blk_integrity_rq(req))
+		flags |= UBLK_IO_F_INTEGRITY;
+
 	return flags;
 }
 
 static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req)
 {
diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
index a54c47832fa2..c1103ad5925b 100644
--- a/include/uapi/linux/ublk_cmd.h
+++ b/include/uapi/linux/ublk_cmd.h
@@ -412,10 +412,12 @@ struct ublksrv_ctrl_dev_info {
  *
  * ublk server has to check this flag if UBLK_AUTO_BUF_REG_FALLBACK is
  * passed in.
  */
 #define		UBLK_IO_F_NEED_REG_BUF		(1U << 17)
+/* Request has an integrity data buffer */
+#define		UBLK_IO_F_INTEGRITY		(1UL << 18)
 
 /*
  * io cmd is described by this structure, and stored in share memory, indexed
  * by request tag.
  *
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 05/19] ublk: add ublk_copy_user_bvec() helper
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (3 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 04/19] ublk: set UBLK_IO_F_INTEGRITY in ublksrv_io_desc Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06 13:14   ` Ming Lei
  2026-01-06  0:57 ` [PATCH v3 06/19] ublk: split out ublk_user_copy() helper Caleb Sander Mateos
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

Factor a helper function ublk_copy_user_bvec() out of
ublk_copy_user_pages(). It will be used for copying integrity data too.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 drivers/block/ublk_drv.c | 52 +++++++++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 22 deletions(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 2b0a9720921d..2ce9afdecc15 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -1000,10 +1000,39 @@ static const struct block_device_operations ub_fops = {
 	.open =		ublk_open,
 	.free_disk =	ublk_free_disk,
 	.report_zones =	ublk_report_zones,
 };
 
+static bool ublk_copy_user_bvec(const struct bio_vec *bv, unsigned *offset,
+				struct iov_iter *uiter, int dir, size_t *done)
+{
+	unsigned len;
+	void *bv_buf;
+	size_t copied;
+
+	if (*offset >= bv->bv_len) {
+		*offset -= bv->bv_len;
+		return true;
+	}
+
+	len = bv->bv_len - *offset;
+	bv_buf = kmap_local_page(bv->bv_page) + bv->bv_offset + *offset;
+	if (dir == ITER_DEST)
+		copied = copy_to_iter(bv_buf, len, uiter);
+	else
+		copied = copy_from_iter(bv_buf, len, uiter);
+
+	kunmap_local(bv_buf);
+
+	*done += copied;
+	if (copied < len)
+		return false;
+
+	*offset = 0;
+	return true;
+}
+
 /*
  * Copy data between request pages and io_iter, and 'offset'
  * is the start point of linear offset of request.
  */
 static size_t ublk_copy_user_pages(const struct request *req,
@@ -1012,33 +1041,12 @@ static size_t ublk_copy_user_pages(const struct request *req,
 	struct req_iterator iter;
 	struct bio_vec bv;
 	size_t done = 0;
 
 	rq_for_each_segment(bv, req, iter) {
-		unsigned len;
-		void *bv_buf;
-		size_t copied;
-
-		if (offset >= bv.bv_len) {
-			offset -= bv.bv_len;
-			continue;
-		}
-
-		len = bv.bv_len - offset;
-		bv_buf = kmap_local_page(bv.bv_page) + bv.bv_offset + offset;
-		if (dir == ITER_DEST)
-			copied = copy_to_iter(bv_buf, len, uiter);
-		else
-			copied = copy_from_iter(bv_buf, len, uiter);
-
-		kunmap_local(bv_buf);
-
-		done += copied;
-		if (copied < len)
+		if (!ublk_copy_user_bvec(&bv, &offset, uiter, dir, &done))
 			break;
-
-		offset = 0;
 	}
 	return done;
 }
 
 static inline bool ublk_need_map_req(const struct request *req)
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 06/19] ublk: split out ublk_user_copy() helper
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (4 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 05/19] ublk: add ublk_copy_user_bvec() helper Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 07/19] ublk: inline ublk_check_and_get_req() into ublk_user_copy() Caleb Sander Mateos
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

ublk_ch_read_iter() and ublk_ch_write_iter() are nearly identical except
for the iter direction. Split out a helper function ublk_user_copy() to
reduce the code duplication as these functions are about to get larger.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/block/ublk_drv.c | 26 ++++++++++----------------
 1 file changed, 10 insertions(+), 16 deletions(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 2ce9afdecc15..c3832ed8cec1 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -2695,42 +2695,36 @@ static struct request *ublk_check_and_get_req(struct kiocb *iocb,
 fail:
 	ublk_put_req_ref(*io, req);
 	return ERR_PTR(-EACCES);
 }
 
-static ssize_t ublk_ch_read_iter(struct kiocb *iocb, struct iov_iter *to)
+static ssize_t
+ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
 {
 	struct request *req;
 	struct ublk_io *io;
 	size_t buf_off;
 	size_t ret;
 
-	req = ublk_check_and_get_req(iocb, to, &buf_off, ITER_DEST, &io);
+	req = ublk_check_and_get_req(iocb, iter, &buf_off, dir, &io);
 	if (IS_ERR(req))
 		return PTR_ERR(req);
 
-	ret = ublk_copy_user_pages(req, buf_off, to, ITER_DEST);
+	ret = ublk_copy_user_pages(req, buf_off, iter, dir);
 	ublk_put_req_ref(io, req);
 
 	return ret;
 }
 
-static ssize_t ublk_ch_write_iter(struct kiocb *iocb, struct iov_iter *from)
+static ssize_t ublk_ch_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
-	struct request *req;
-	struct ublk_io *io;
-	size_t buf_off;
-	size_t ret;
-
-	req = ublk_check_and_get_req(iocb, from, &buf_off, ITER_SOURCE, &io);
-	if (IS_ERR(req))
-		return PTR_ERR(req);
-
-	ret = ublk_copy_user_pages(req, buf_off, from, ITER_SOURCE);
-	ublk_put_req_ref(io, req);
+	return ublk_user_copy(iocb, to, ITER_DEST);
+}
 
-	return ret;
+static ssize_t ublk_ch_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+	return ublk_user_copy(iocb, from, ITER_SOURCE);
 }
 
 static const struct file_operations ublk_ch_fops = {
 	.owner = THIS_MODULE,
 	.open = ublk_ch_open,
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 07/19] ublk: inline ublk_check_and_get_req() into ublk_user_copy()
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (5 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 06/19] ublk: split out ublk_user_copy() helper Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 08/19] ublk: move offset check out of __ublk_check_and_get_req() Caleb Sander Mateos
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

ublk_check_and_get_req() has a single callsite in ublk_user_copy(). It
takes a ton of arguments in order to pass local variables from
ublk_user_copy() to ublk_check_and_get_req() and vice versa. And more
are about to be added. Combine the functions to reduce the argument
passing noise.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/block/ublk_drv.c | 51 ++++++++++++++--------------------------
 1 file changed, 18 insertions(+), 33 deletions(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index c3832ed8cec1..abb668b460a8 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -2650,70 +2650,55 @@ static inline bool ublk_check_ubuf_dir(const struct request *req,
 		return true;
 
 	return false;
 }
 
-static struct request *ublk_check_and_get_req(struct kiocb *iocb,
-		struct iov_iter *iter, size_t *off, int dir,
-		struct ublk_io **io)
+static ssize_t
+ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
 {
 	struct ublk_device *ub = iocb->ki_filp->private_data;
 	struct ublk_queue *ubq;
 	struct request *req;
+	struct ublk_io *io;
 	size_t buf_off;
 	u16 tag, q_id;
+	ssize_t ret;
 
 	if (!user_backed_iter(iter))
-		return ERR_PTR(-EACCES);
+		return -EACCES;
 
 	if (ub->dev_info.state == UBLK_S_DEV_DEAD)
-		return ERR_PTR(-EACCES);
+		return -EACCES;
 
 	tag = ublk_pos_to_tag(iocb->ki_pos);
 	q_id = ublk_pos_to_hwq(iocb->ki_pos);
 	buf_off = ublk_pos_to_buf_off(iocb->ki_pos);
 
 	if (q_id >= ub->dev_info.nr_hw_queues)
-		return ERR_PTR(-EINVAL);
+		return -EINVAL;
 
 	ubq = ublk_get_queue(ub, q_id);
 	if (!ublk_dev_support_user_copy(ub))
-		return ERR_PTR(-EACCES);
+		return -EACCES;
 
 	if (tag >= ub->dev_info.queue_depth)
-		return ERR_PTR(-EINVAL);
+		return -EINVAL;
 
-	*io = &ubq->ios[tag];
-	req = __ublk_check_and_get_req(ub, q_id, tag, *io, buf_off);
+	io = &ubq->ios[tag];
+	req = __ublk_check_and_get_req(ub, q_id, tag, io, buf_off);
 	if (!req)
-		return ERR_PTR(-EINVAL);
-
-	if (!ublk_check_ubuf_dir(req, dir))
-		goto fail;
-
-	*off = buf_off;
-	return req;
-fail:
-	ublk_put_req_ref(*io, req);
-	return ERR_PTR(-EACCES);
-}
-
-static ssize_t
-ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
-{
-	struct request *req;
-	struct ublk_io *io;
-	size_t buf_off;
-	size_t ret;
+		return -EINVAL;
 
-	req = ublk_check_and_get_req(iocb, iter, &buf_off, dir, &io);
-	if (IS_ERR(req))
-		return PTR_ERR(req);
+	if (!ublk_check_ubuf_dir(req, dir)) {
+		ret = -EACCES;
+		goto out;
+	}
 
 	ret = ublk_copy_user_pages(req, buf_off, iter, dir);
-	ublk_put_req_ref(io, req);
 
+out:
+	ublk_put_req_ref(io, req);
 	return ret;
 }
 
 static ssize_t ublk_ch_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 08/19] ublk: move offset check out of __ublk_check_and_get_req()
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (6 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 07/19] ublk: inline ublk_check_and_get_req() into ublk_user_copy() Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 09/19] ublk: implement integrity user copy Caleb Sander Mateos
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

__ublk_check_and_get_req() checks that the passed in offset is within
the data length of the specified ublk request. However, only user copy
(ublk_check_and_get_req()) supports accessing ublk request data at a
nonzero offset. Zero-copy buffer registration (ublk_register_io_buf())
always passes 0 for the offset, so the check is unnecessary. Move the
check from __ublk_check_and_get_req() to ublk_check_and_get_req().

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/block/ublk_drv.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index abb668b460a8..e44ab9981ef4 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -253,11 +253,11 @@ struct ublk_params_header {
 
 static void ublk_io_release(void *priv);
 static void ublk_stop_dev_unlocked(struct ublk_device *ub);
 static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq);
 static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub,
-		u16 q_id, u16 tag, struct ublk_io *io, size_t offset);
+		u16 q_id, u16 tag, struct ublk_io *io);
 static inline unsigned int ublk_req_build_flags(struct request *req);
 
 static void ublk_partition_scan_work(struct work_struct *work)
 {
 	struct ublk_device *ub =
@@ -2283,11 +2283,11 @@ static int ublk_register_io_buf(struct io_uring_cmd *cmd,
 	int ret;
 
 	if (!ublk_dev_support_zero_copy(ub))
 		return -EINVAL;
 
-	req = __ublk_check_and_get_req(ub, q_id, tag, io, 0);
+	req = __ublk_check_and_get_req(ub, q_id, tag, io);
 	if (!req)
 		return -EINVAL;
 
 	ret = io_buffer_register_bvec(cmd, req, ublk_io_release, index,
 				      issue_flags);
@@ -2577,11 +2577,11 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
 			__func__, cmd_op, tag, ret, io ? io->flags : 0);
 	return ret;
 }
 
 static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub,
-		u16 q_id, u16 tag, struct ublk_io *io, size_t offset)
+		u16 q_id, u16 tag, struct ublk_io *io)
 {
 	struct request *req;
 
 	/*
 	 * can't use io->req in case of concurrent UBLK_IO_COMMIT_AND_FETCH_REQ,
@@ -2598,13 +2598,10 @@ static inline struct request *__ublk_check_and_get_req(struct ublk_device *ub,
 		goto fail_put;
 
 	if (!ublk_rq_has_data(req))
 		goto fail_put;
 
-	if (offset > blk_rq_bytes(req))
-		goto fail_put;
-
 	return req;
 fail_put:
 	ublk_put_req_ref(io, req);
 	return NULL;
 }
@@ -2682,14 +2679,19 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
 
 	if (tag >= ub->dev_info.queue_depth)
 		return -EINVAL;
 
 	io = &ubq->ios[tag];
-	req = __ublk_check_and_get_req(ub, q_id, tag, io, buf_off);
+	req = __ublk_check_and_get_req(ub, q_id, tag, io);
 	if (!req)
 		return -EINVAL;
 
+	if (buf_off > blk_rq_bytes(req)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
 	if (!ublk_check_ubuf_dir(req, dir)) {
 		ret = -EACCES;
 		goto out;
 	}
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 09/19] ublk: implement integrity user copy
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (7 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 08/19] ublk: move offset check out of __ublk_check_and_get_req() Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06 13:34   ` Ming Lei
  2026-01-06 13:46   ` Ming Lei
  2026-01-06  0:57 ` [PATCH v3 10/19] ublk: support UBLK_F_INTEGRITY Caleb Sander Mateos
                   ` (9 subsequent siblings)
  18 siblings, 2 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

From: Stanley Zhang <stazhang@purestorage.com>

Add a function ublk_copy_user_integrity() to copy integrity information
between a request and a user iov_iter. This mirrors the existing
ublk_copy_user_pages() but operates on request integrity data instead of
regular data. Check UBLKSRV_IO_INTEGRITY_FLAG in iocb->ki_pos in
ublk_user_copy() to choose between copying data or integrity data.

Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
[csander: change offset units from data bytes to integrity data bytes,
 test UBLKSRV_IO_INTEGRITY_FLAG after subtracting UBLKSRV_IO_BUF_OFFSET,
 fix CONFIG_BLK_DEV_INTEGRITY=n build,
 rebase on ublk user copy refactor]
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 drivers/block/ublk_drv.c      | 52 +++++++++++++++++++++++++++++++++--
 include/uapi/linux/ublk_cmd.h |  4 +++
 2 files changed, 53 insertions(+), 3 deletions(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index e44ab9981ef4..9694a4c1caa7 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -621,10 +621,15 @@ static inline unsigned ublk_pos_to_tag(loff_t pos)
 {
 	return ((pos - UBLKSRV_IO_BUF_OFFSET) >> UBLK_TAG_OFF) &
 		UBLK_TAG_BITS_MASK;
 }
 
+static inline bool ublk_pos_is_integrity(loff_t pos)
+{
+	return !!((pos - UBLKSRV_IO_BUF_OFFSET) & UBLKSRV_IO_INTEGRITY_FLAG);
+}
+
 static void ublk_dev_param_basic_apply(struct ublk_device *ub)
 {
 	const struct ublk_param_basic *p = &ub->params.basic;
 
 	if (p->attrs & UBLK_ATTR_READ_ONLY)
@@ -1047,10 +1052,37 @@ static size_t ublk_copy_user_pages(const struct request *req,
 			break;
 	}
 	return done;
 }
 
+#ifdef CONFIG_BLK_DEV_INTEGRITY
+static size_t ublk_copy_user_integrity(const struct request *req,
+		unsigned offset, struct iov_iter *uiter, int dir)
+{
+	size_t done = 0;
+	struct bio *bio = req->bio;
+	struct bvec_iter iter;
+	struct bio_vec iv;
+
+	if (!blk_integrity_rq(req))
+		return 0;
+
+	bio_for_each_integrity_vec(iv, bio, iter) {
+		if (!ublk_copy_user_bvec(&iv, &offset, uiter, dir, &done))
+			break;
+	}
+
+	return done;
+}
+#else /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
+static size_t ublk_copy_user_integrity(const struct request *req,
+		unsigned offset, struct iov_iter *uiter, int dir)
+{
+	return 0;
+}
+#endif /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
+
 static inline bool ublk_need_map_req(const struct request *req)
 {
 	return ublk_rq_has_data(req) && req_op(req) == REQ_OP_WRITE;
 }
 
@@ -2654,10 +2686,12 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
 {
 	struct ublk_device *ub = iocb->ki_filp->private_data;
 	struct ublk_queue *ubq;
 	struct request *req;
 	struct ublk_io *io;
+	unsigned data_len;
+	bool is_integrity;
 	size_t buf_off;
 	u16 tag, q_id;
 	ssize_t ret;
 
 	if (!user_backed_iter(iter))
@@ -2667,10 +2701,11 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
 		return -EACCES;
 
 	tag = ublk_pos_to_tag(iocb->ki_pos);
 	q_id = ublk_pos_to_hwq(iocb->ki_pos);
 	buf_off = ublk_pos_to_buf_off(iocb->ki_pos);
+	is_integrity = ublk_pos_is_integrity(iocb->ki_pos);
 
 	if (q_id >= ub->dev_info.nr_hw_queues)
 		return -EINVAL;
 
 	ubq = ublk_get_queue(ub, q_id);
@@ -2683,21 +2718,31 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
 	io = &ubq->ios[tag];
 	req = __ublk_check_and_get_req(ub, q_id, tag, io);
 	if (!req)
 		return -EINVAL;
 
-	if (buf_off > blk_rq_bytes(req)) {
+	if (is_integrity) {
+		struct blk_integrity *bi = &req->q->limits.integrity;
+
+		data_len = bio_integrity_bytes(bi, blk_rq_sectors(req));
+	} else {
+		data_len = blk_rq_bytes(req);
+	}
+	if (buf_off > data_len) {
 		ret = -EINVAL;
 		goto out;
 	}
 
 	if (!ublk_check_ubuf_dir(req, dir)) {
 		ret = -EACCES;
 		goto out;
 	}
 
-	ret = ublk_copy_user_pages(req, buf_off, iter, dir);
+	if (is_integrity)
+		ret = ublk_copy_user_integrity(req, buf_off, iter, dir);
+	else
+		ret = ublk_copy_user_pages(req, buf_off, iter, dir);
 
 out:
 	ublk_put_req_ref(io, req);
 	return ret;
 }
@@ -3931,11 +3976,12 @@ static struct miscdevice ublk_misc = {
 static int __init ublk_init(void)
 {
 	int ret;
 
 	BUILD_BUG_ON((u64)UBLKSRV_IO_BUF_OFFSET +
-			UBLKSRV_IO_BUF_TOTAL_SIZE < UBLKSRV_IO_BUF_OFFSET);
+			UBLKSRV_IO_BUF_TOTAL_SIZE +
+			UBLKSRV_IO_INTEGRITY_FLAG < UBLKSRV_IO_BUF_OFFSET);
 	BUILD_BUG_ON(sizeof(struct ublk_auto_buf_reg) != 8);
 
 	init_waitqueue_head(&ublk_idr_wq);
 
 	ret = misc_register(&ublk_misc);
diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
index c1103ad5925b..3af7e3684834 100644
--- a/include/uapi/linux/ublk_cmd.h
+++ b/include/uapi/linux/ublk_cmd.h
@@ -132,10 +132,14 @@
 #define UBLK_MAX_NR_QUEUES	(1U << UBLK_QID_BITS)
 
 #define UBLKSRV_IO_BUF_TOTAL_BITS	(UBLK_QID_OFF + UBLK_QID_BITS)
 #define UBLKSRV_IO_BUF_TOTAL_SIZE	(1ULL << UBLKSRV_IO_BUF_TOTAL_BITS)
 
+/* Copy to/from request integrity buffer instead of data buffer */
+#define UBLK_INTEGRITY_FLAG_OFF UBLKSRV_IO_BUF_TOTAL_BITS
+#define UBLKSRV_IO_INTEGRITY_FLAG (1ULL << UBLK_INTEGRITY_FLAG_OFF)
+
 /*
  * ublk server can register data buffers for incoming I/O requests with a sparse
  * io_uring buffer table. The request buffer can then be used as the data buffer
  * for io_uring operations via the fixed buffer index.
  * Note that the ublk server can never directly access the request data memory.
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 10/19] ublk: support UBLK_F_INTEGRITY
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (8 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 09/19] ublk: implement integrity user copy Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06 13:36   ` Ming Lei
  2026-01-06  0:57 ` [PATCH v3 11/19] ublk: optimize ublk_user_copy() on daemon task Caleb Sander Mateos
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

From: Stanley Zhang <stazhang@purestorage.com>

Now that all the components of the ublk integrity feature have been
implemented, add UBLK_F_INTEGRITY to UBLK_F_ALL, conditional on block
layer integrity support (CONFIG_BLK_DEV_INTEGRITY). This allows ublk
servers to create ublk devices with UBLK_F_INTEGRITY set and
UBLK_U_CMD_GET_FEATURES to report the feature as supported.

Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
[csander: make feature conditional on CONFIG_BLK_DEV_INTEGRITY]
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 drivers/block/ublk_drv.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 9694a4c1caa7..4ffafbfcde3c 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -73,11 +73,12 @@
 		| UBLK_F_USER_RECOVERY_FAIL_IO \
 		| UBLK_F_UPDATE_SIZE \
 		| UBLK_F_AUTO_BUF_REG \
 		| UBLK_F_QUIESCE \
 		| UBLK_F_PER_IO_DAEMON \
-		| UBLK_F_BUF_REG_OFF_DAEMON)
+		| UBLK_F_BUF_REG_OFF_DAEMON \
+		| (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) ? UBLK_F_INTEGRITY : 0))
 
 #define UBLK_F_ALL_RECOVERY_FLAGS (UBLK_F_USER_RECOVERY \
 		| UBLK_F_USER_RECOVERY_REISSUE \
 		| UBLK_F_USER_RECOVERY_FAIL_IO)
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 11/19] ublk: optimize ublk_user_copy() on daemon task
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (9 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 10/19] ublk: support UBLK_F_INTEGRITY Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 12/19] selftests: ublk: display UBLK_F_INTEGRITY support Caleb Sander Mateos
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

ublk user copy syscalls may be issued from any task, so they take a
reference count on the struct ublk_io to check whether it is owned by
the ublk server and prevent a concurrent UBLK_IO_COMMIT_AND_FETCH_REQ
from completing the request. However, if the user copy syscall is issued
on the io's daemon task, a concurrent UBLK_IO_COMMIT_AND_FETCH_REQ isn't
possible, so the atomic reference count dance is unnecessary. Check for
UBLK_IO_FLAG_OWNED_BY_SRV to ensure the request is dispatched to the
sever and obtain the request from ublk_io's req field instead of looking
it up on the tagset. Skip the reference count increment and decrement.
Commit 8a8fe42d765b ("ublk: optimize UBLK_IO_REGISTER_IO_BUF on daemon
task") made an analogous optimization for ublk zero copy buffer
registration.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/block/ublk_drv.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 4ffafbfcde3c..af9ae828fd82 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -181,11 +181,11 @@ struct ublk_io {
 	/*
 	 * The number of uses of this I/O by the ublk server
 	 * if user copy or zero copy are enabled:
 	 * - UBLK_REFCOUNT_INIT from dispatch to the server
 	 *   until UBLK_IO_COMMIT_AND_FETCH_REQ
-	 * - 1 for each inflight ublk_ch_{read,write}_iter() call
+	 * - 1 for each inflight ublk_ch_{read,write}_iter() call not on task
 	 * - 1 for each io_uring registered buffer not registered on task
 	 * The I/O can only be completed once all references are dropped.
 	 * User copy and buffer registration operations are only permitted
 	 * if the reference count is nonzero.
 	 */
@@ -2689,10 +2689,11 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
 	struct ublk_queue *ubq;
 	struct request *req;
 	struct ublk_io *io;
 	unsigned data_len;
 	bool is_integrity;
+	bool on_daemon;
 	size_t buf_off;
 	u16 tag, q_id;
 	ssize_t ret;
 
 	if (!user_backed_iter(iter))
@@ -2715,13 +2716,24 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
 
 	if (tag >= ub->dev_info.queue_depth)
 		return -EINVAL;
 
 	io = &ubq->ios[tag];
-	req = __ublk_check_and_get_req(ub, q_id, tag, io);
-	if (!req)
-		return -EINVAL;
+	on_daemon = current == READ_ONCE(io->task);
+	if (on_daemon) {
+		/* On daemon, io can't be completed concurrently, so skip ref */
+		if (!(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV))
+			return -EINVAL;
+
+		req = io->req;
+		if (!ublk_rq_has_data(req))
+			return -EINVAL;
+	} else {
+		req = __ublk_check_and_get_req(ub, q_id, tag, io);
+		if (!req)
+			return -EINVAL;
+	}
 
 	if (is_integrity) {
 		struct blk_integrity *bi = &req->q->limits.integrity;
 
 		data_len = bio_integrity_bytes(bi, blk_rq_sectors(req));
@@ -2742,11 +2754,12 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
 		ret = ublk_copy_user_integrity(req, buf_off, iter, dir);
 	else
 		ret = ublk_copy_user_pages(req, buf_off, iter, dir);
 
 out:
-	ublk_put_req_ref(io, req);
+	if (!on_daemon)
+		ublk_put_req_ref(io, req);
 	return ret;
 }
 
 static ssize_t ublk_ch_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 12/19] selftests: ublk: display UBLK_F_INTEGRITY support
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (10 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 11/19] ublk: optimize ublk_user_copy() on daemon task Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06 13:38   ` Ming Lei
  2026-01-06  0:57 ` [PATCH v3 13/19] selftests: ublk: add utility to get block device metadata size Caleb Sander Mateos
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

Add support for printing the UBLK_F_INTEGRITY feature flag in the
human-readable kublk features output.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 tools/testing/selftests/ublk/kublk.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c
index 185ba553686a..261095f19c93 100644
--- a/tools/testing/selftests/ublk/kublk.c
+++ b/tools/testing/selftests/ublk/kublk.c
@@ -1452,10 +1452,11 @@ static int cmd_dev_get_features(void)
 		FEAT_NAME(UBLK_F_UPDATE_SIZE),
 		FEAT_NAME(UBLK_F_AUTO_BUF_REG),
 		FEAT_NAME(UBLK_F_QUIESCE),
 		FEAT_NAME(UBLK_F_PER_IO_DAEMON),
 		FEAT_NAME(UBLK_F_BUF_REG_OFF_DAEMON),
+		FEAT_NAME(UBLK_F_INTEGRITY),
 	};
 	struct ublk_dev *dev;
 	__u64 features = 0;
 	int ret;
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 13/19] selftests: ublk: add utility to get block device metadata size
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (11 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 12/19] selftests: ublk: display UBLK_F_INTEGRITY support Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06 13:50   ` Ming Lei
  2026-01-06  0:57 ` [PATCH v3 14/19] selftests: ublk: add kublk support for integrity params Caleb Sander Mateos
                   ` (5 subsequent siblings)
  18 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

Some block device integrity parameters are available in sysfs, but
others are only accessible using the FS_IOC_GETLBMD_CAP ioctl. Add a
metadata_size utility program to print out the logical block metadata
size, PI offset, and PI size within the metadata. Example output:
$ metadata_size /dev/ublkb0
metadata_size: 64
pi_offset: 56
pi_tuple_size: 8

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 tools/testing/selftests/ublk/Makefile        |  4 +--
 tools/testing/selftests/ublk/metadata_size.c | 36 ++++++++++++++++++++
 2 files changed, 38 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/ublk/metadata_size.c

diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
index 06ba6fde098d..41f776bb86a6 100644
--- a/tools/testing/selftests/ublk/Makefile
+++ b/tools/testing/selftests/ublk/Makefile
@@ -47,14 +47,14 @@ TEST_PROGS += test_stress_03.sh
 TEST_PROGS += test_stress_04.sh
 TEST_PROGS += test_stress_05.sh
 TEST_PROGS += test_stress_06.sh
 TEST_PROGS += test_stress_07.sh
 
-TEST_GEN_PROGS_EXTENDED = kublk
+TEST_GEN_PROGS_EXTENDED = kublk metadata_size
 
 LOCAL_HDRS += $(wildcard *.h)
 include ../lib.mk
 
-$(TEST_GEN_PROGS_EXTENDED): $(wildcard *.c)
+$(OUTPUT)/kublk: common.c fault_inject.c file_backed.c kublk.c null.c stripe.c
 
 check:
 	shellcheck -x -f gcc *.sh
diff --git a/tools/testing/selftests/ublk/metadata_size.c b/tools/testing/selftests/ublk/metadata_size.c
new file mode 100644
index 000000000000..76ecddf04d25
--- /dev/null
+++ b/tools/testing/selftests/ublk/metadata_size.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+
+int main(int argc, char **argv)
+{
+	struct logical_block_metadata_cap cap = {};
+	const char *filename;
+	int fd;
+	int result;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s BLOCK_DEVICE\n", argv[0]);
+		return 1;
+	}
+
+	filename = argv[1];
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		perror(filename);
+		return 1;
+	}
+
+	result = ioctl(fd, FS_IOC_GETLBMD_CAP, &cap);
+	if (result < 0) {
+		perror("ioctl");
+		return 1;
+	}
+
+	printf("metadata_size: %u\n", cap.lbmd_size);
+	printf("pi_offset: %u\n", cap.lbmd_pi_offset);
+	printf("pi_tuple_size: %u\n", cap.lbmd_pi_size);
+	return 0;
+}
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 14/19] selftests: ublk: add kublk support for integrity params
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (12 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 13/19] selftests: ublk: add utility to get block device metadata size Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 15/19] selftests: ublk: implement integrity user copy in kublk Caleb Sander Mateos
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

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        | 48 +++++++++++++++++++++
 tools/testing/selftests/ublk/kublk.h        | 21 +++++++++
 tools/testing/selftests/ublk/null.c         |  1 +
 tools/testing/selftests/ublk/stripe.c       |  4 ++
 6 files changed, 79 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 261095f19c93..6ff110d0dcae 100644
--- a/tools/testing/selftests/ublk/kublk.c
+++ b/tools/testing/selftests/ublk/kublk.c
@@ -1,10 +1,12 @@
 /* SPDX-License-Identifier: MIT */
 /*
  * Description: uring_cmd based ublk
  */
 
+#include "linux/ublk_cmd.h"
+#include <linux/fs.h>
 #include "kublk.h"
 
 #define MAX_NR_TGT_ARG 	64
 
 unsigned int ublk_dbg_mask = UBLK_LOG;
@@ -1548,10 +1550,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++) {
@@ -1611,20 +1615,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;
@@ -1695,10 +1706,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
 			 */
@@ -1737,10 +1770,25 @@ 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) {
+		if (!(ctx.flags & UBLK_F_USER_COPY)) {
+			ublk_err("integrity requires user_copy\n");
+			return -EINVAL;
+		}
+
+		ctx.flags |= UBLK_F_INTEGRITY;
+	} else if (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;
+	}
+
 	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


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 15/19] selftests: ublk: implement integrity user copy in kublk
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (13 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 14/19] selftests: ublk: add kublk support for integrity params Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 16/19] selftests: ublk: support non-O_DIRECT backing files Caleb Sander Mateos
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

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>
---
 tools/testing/selftests/ublk/kublk.c | 41 ++++++++++++++++++++++++----
 tools/testing/selftests/ublk/kublk.h | 14 ++++++++++
 2 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c
index 6ff110d0dcae..dfba808657b1 100644
--- a/tools/testing/selftests/ublk/kublk.c
+++ b/tools/testing/selftests/ublk/kublk.c
@@ -415,12 +415,14 @@ static void ublk_queue_deinit(struct ublk_queue *q)
 	int nr_ios = q->q_depth;
 
 	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)
 {
 	io_uring_unregister_buffers(&t->ring);
@@ -432,23 +434,25 @@ static void ublk_thread_deinit(struct ublk_thread *t)
 		close(t->ring.ring_fd);
 		t->ring.ring_fd = -1;
 	}
 }
 
-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;
 	q->flags = 0;
 	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];
 
 	cmd_buf_size = ublk_queue_cmd_buf_sz(q);
@@ -460,15 +464,27 @@ static int ublk_queue_init(struct ublk_queue *q, unsigned long long extra_flags)
 				q->dev->dev_info.dev_id, q->q_id);
 		goto fail;
 	}
 
 	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;
 
 		if (posix_memalign((void **)&q->ios[i].buf_addr,
 					getpagesize(), io_buf_size)) {
@@ -607,17 +623,17 @@ static void ublk_user_copy(const struct ublk_io *io, __u8 match_ublk_op)
 	const struct ublksrv_io_desc *iod = ublk_get_iod(q, io->tag);
 	__u64 off = ublk_user_copy_offset(q->q_id, io->tag);
 	__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);
 		else if (ublk_op == UBLK_IO_OP_READ)
 			copied = pwrite(q->ublk_fd, addr, copy_len, off);
@@ -626,10 +642,24 @@ static void ublk_user_copy(const struct ublk_io *io, __u8 match_ublk_op)
 		assert(copied == (ssize_t)copy_len);
 		addr += copy_len;
 		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)
 {
 	struct ublk_queue *q = ublk_io_to_queue(io);
@@ -1012,11 +1042,12 @@ static int ublk_start_daemon(const struct dev_ctx *ctx, struct ublk_dev *dev)
 
 	for (i = 0; i < dinfo->nr_hw_queues; i++) {
 		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);
 			goto fail;
 		}
diff --git a/tools/testing/selftests/ublk/kublk.h b/tools/testing/selftests/ublk/kublk.h
index d00f2b465cdf..830b49a7716a 100644
--- a/tools/testing/selftests/ublk/kublk.h
+++ b/tools/testing/selftests/ublk/kublk.h
@@ -110,10 +110,11 @@ struct ublk_ctrl_cmd_data {
 	__u32 len;
 };
 
 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)
 #define UBLKS_IO_FREE			(1UL << 2)
 #define UBLKS_IO_NEED_GET_DATA           (1UL << 3)
@@ -173,10 +174,11 @@ struct ublk_queue {
 /* borrow one bit of ublk uapi flags, which may never be used */
 #define UBLKS_Q_AUTO_BUF_REG_FALLBACK	(1ULL << 63)
 #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];
 };
 
 struct ublk_thread {
 	struct ublk_dev *dev;
@@ -222,10 +224,22 @@ static inline void ublk_set_integrity_params(const struct dev_ctx *ctx,
 		.csum_type = ctx->csum_type,
 		.tag_size = ctx->tag_size,
 	};
 }
 
+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);
 }
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 16/19] selftests: ublk: support non-O_DIRECT backing files
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (14 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 15/19] selftests: ublk: implement integrity user copy in kublk Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 17/19] selftests: ublk: add integrity data support to loop target Caleb Sander Mateos
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

A subsequent commit will add support for using a backing file to store
integrity data. Since integrity data is accessed in intervals of
metadata_size, which may be much smaller than a logical block on the
backing device, direct I/O cannot be used. Add an argument to
backing_file_tgt_init() to specify the number of files to open for
direct I/O. The remaining files will use buffered I/O. For now, continue
to request direct I/O for all the files.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 tools/testing/selftests/ublk/common.c      | 4 ++--
 tools/testing/selftests/ublk/file_backed.c | 2 +-
 tools/testing/selftests/ublk/kublk.h       | 2 +-
 tools/testing/selftests/ublk/stripe.c      | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/tools/testing/selftests/ublk/common.c b/tools/testing/selftests/ublk/common.c
index 01580a6f8519..d9873d4d50d0 100644
--- a/tools/testing/selftests/ublk/common.c
+++ b/tools/testing/selftests/ublk/common.c
@@ -10,11 +10,11 @@ void backing_file_tgt_deinit(struct ublk_dev *dev)
 		fsync(dev->fds[i]);
 		close(dev->fds[i]);
 	}
 }
 
-int backing_file_tgt_init(struct ublk_dev *dev)
+int backing_file_tgt_init(struct ublk_dev *dev, unsigned int nr_direct)
 {
 	int fd, i;
 
 	assert(dev->nr_fds == 1);
 
@@ -23,11 +23,11 @@ int backing_file_tgt_init(struct ublk_dev *dev)
 		unsigned long bytes;
 		struct stat st;
 
 		ublk_dbg(UBLK_DBG_DEV, "%s: file %d: %s\n", __func__, i, file);
 
-		fd = open(file, O_RDWR | O_DIRECT);
+		fd = open(file, O_RDWR | (i < nr_direct ? O_DIRECT : 0));
 		if (fd < 0) {
 			ublk_err("%s: backing file %s can't be opened: %s\n",
 					__func__, file, strerror(errno));
 			return -EBADF;
 		}
diff --git a/tools/testing/selftests/ublk/file_backed.c b/tools/testing/selftests/ublk/file_backed.c
index c14ce6608696..db4c176a4f28 100644
--- a/tools/testing/selftests/ublk/file_backed.c
+++ b/tools/testing/selftests/ublk/file_backed.c
@@ -161,11 +161,11 @@ static int ublk_loop_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev)
 	if (ctx->metadata_size) {
 		ublk_err("%s: integrity not supported\n", __func__);
 		return -EINVAL;
 	}
 
-	ret = backing_file_tgt_init(dev);
+	ret = backing_file_tgt_init(dev, 1);
 	if (ret)
 		return ret;
 
 	if (dev->tgt.nr_backing_files != 1)
 		return -EINVAL;
diff --git a/tools/testing/selftests/ublk/kublk.h b/tools/testing/selftests/ublk/kublk.h
index 830b49a7716a..96c66b337bc0 100644
--- a/tools/testing/selftests/ublk/kublk.h
+++ b/tools/testing/selftests/ublk/kublk.h
@@ -460,8 +460,8 @@ extern const struct ublk_tgt_ops null_tgt_ops;
 extern const struct ublk_tgt_ops loop_tgt_ops;
 extern const struct ublk_tgt_ops stripe_tgt_ops;
 extern const struct ublk_tgt_ops fault_inject_tgt_ops;
 
 void backing_file_tgt_deinit(struct ublk_dev *dev);
-int backing_file_tgt_init(struct ublk_dev *dev);
+int backing_file_tgt_init(struct ublk_dev *dev, unsigned int nr_direct);
 
 #endif
diff --git a/tools/testing/selftests/ublk/stripe.c b/tools/testing/selftests/ublk/stripe.c
index d4aaf3351d71..2be1c36438e7 100644
--- a/tools/testing/selftests/ublk/stripe.c
+++ b/tools/testing/selftests/ublk/stripe.c
@@ -313,11 +313,11 @@ static int ublk_stripe_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev)
 		return -EINVAL;
 	}
 
 	chunk_shift = ilog2(chunk_size);
 
-	ret = backing_file_tgt_init(dev);
+	ret = backing_file_tgt_init(dev, dev->tgt.nr_backing_files);
 	if (ret)
 		return ret;
 
 	if (!dev->tgt.nr_backing_files || dev->tgt.nr_backing_files > NR_STRIPE)
 		return -EINVAL;
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 17/19] selftests: ublk: add integrity data support to loop target
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (15 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 16/19] selftests: ublk: support non-O_DIRECT backing files Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 18/19] selftests: ublk: add integrity params test Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 19/19] selftests: ublk: add end-to-end integrity test Caleb Sander Mateos
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

To perform and end-to-end test of integrity information through a ublk
device, we need to actually store it somewhere and retrieve it. Add this
support to kublk's loop target. It uses a second backing file for the
integrity data corresponding to the data stored in the first file.
The integrity file is opened without O_DIRECT since it will be accessed
at sub-block granularity. Each incoming read/write results in a pair of
reads/writes, one to the data file, and one to the integrity file. If
either backing I/O fails, the error is propagated to the ublk request.
If both backing I/Os read/write some bytes, the ublk request is
completed with the smaller of the number of blocks accessed by each I/O.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 tools/testing/selftests/ublk/file_backed.c | 63 +++++++++++++++-------
 1 file changed, 45 insertions(+), 18 deletions(-)

diff --git a/tools/testing/selftests/ublk/file_backed.c b/tools/testing/selftests/ublk/file_backed.c
index db4c176a4f28..b8aacaa928a4 100644
--- a/tools/testing/selftests/ublk/file_backed.c
+++ b/tools/testing/selftests/ublk/file_backed.c
@@ -33,48 +33,62 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
 	unsigned ublk_op = ublksrv_get_op(iod);
 	unsigned zc = ublk_queue_use_zc(q);
 	unsigned auto_zc = ublk_queue_use_auto_zc(q);
 	enum io_uring_op op = ublk_to_uring_op(iod, zc | auto_zc);
 	struct ublk_io *io = ublk_get_io(q, tag);
+	__u64 offset = iod->start_sector << 9;
+	__u32 len = iod->nr_sectors << 9;
 	struct io_uring_sqe *sqe[3];
 	void *addr = io->buf_addr;
 
+	if (iod->op_flags & UBLK_IO_F_INTEGRITY) {
+		ublk_io_alloc_sqes(t, sqe, 1);
+		/* Use second backing file for integrity data */
+		io_uring_prep_rw(op, sqe[0], ublk_get_registered_fd(q, 2),
+				 io->integrity_buf,
+				 ublk_integrity_len(q, len),
+				 ublk_integrity_len(q, offset));
+		sqe[0]->flags = IOSQE_FIXED_FILE;
+		/* tgt_data = 1 indicates integrity I/O */
+		sqe[0]->user_data = build_user_data(tag, ublk_op, 1, q->q_id, 1);
+	}
+
 	if (!zc || auto_zc) {
 		ublk_io_alloc_sqes(t, sqe, 1);
 		if (!sqe[0])
 			return -ENOMEM;
 
 		io_uring_prep_rw(op, sqe[0], ublk_get_registered_fd(q, 1) /*fds[1]*/,
 				addr,
-				iod->nr_sectors << 9,
-				iod->start_sector << 9);
+				len,
+				offset);
 		if (auto_zc)
 			sqe[0]->buf_index = tag;
 		io_uring_sqe_set_flags(sqe[0], IOSQE_FIXED_FILE);
 		/* bit63 marks us as tgt io */
 		sqe[0]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1);
-		return 1;
+		return !!(iod->op_flags & UBLK_IO_F_INTEGRITY) + 1;
 	}
 
 	ublk_io_alloc_sqes(t, sqe, 3);
 
 	io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, io->buf_index);
 	sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK;
 	sqe[0]->user_data = build_user_data(tag,
 			ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1);
 
 	io_uring_prep_rw(op, sqe[1], ublk_get_registered_fd(q, 1) /*fds[1]*/, 0,
-		iod->nr_sectors << 9,
-		iod->start_sector << 9);
+			len,
+			offset);
 	sqe[1]->buf_index = tag;
 	sqe[1]->flags |= IOSQE_FIXED_FILE | IOSQE_IO_HARDLINK;
 	sqe[1]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1);
 
 	io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, io->buf_index);
 	sqe[2]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op), 0, q->q_id, 1);
 
-	return 2;
+	return !!(iod->op_flags & UBLK_IO_F_INTEGRITY) + 2;
 }
 
 static int loop_queue_tgt_io(struct ublk_thread *t, struct ublk_queue *q, int tag)
 {
 	const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag);
@@ -117,16 +131,21 @@ static void ublk_loop_io_done(struct ublk_thread *t, struct ublk_queue *q,
 {
 	unsigned tag = user_data_to_tag(cqe->user_data);
 	unsigned op = user_data_to_op(cqe->user_data);
 	struct ublk_io *io = ublk_get_io(q, tag);
 
-	if (cqe->res < 0 || op != ublk_cmd_op_nr(UBLK_U_IO_UNREGISTER_IO_BUF)) {
-		if (!io->result)
-			io->result = cqe->res;
-		if (cqe->res < 0)
-			ublk_err("%s: io failed op %x user_data %lx\n",
-					__func__, op, cqe->user_data);
+	if (cqe->res < 0) {
+		io->result = cqe->res;
+		ublk_err("%s: io failed op %x user_data %lx\n",
+				__func__, op, cqe->user_data);
+	} else if (op != ublk_cmd_op_nr(UBLK_U_IO_UNREGISTER_IO_BUF)) {
+		__s32 data_len = user_data_to_tgt_data(cqe->user_data)
+			? ublk_integrity_data_len(q, cqe->res)
+			: cqe->res;
+
+		if (!io->result || data_len < io->result)
+			io->result = data_len;
 	}
 
 	/* buffer register op is IOSQE_CQE_SKIP_SUCCESS */
 	if (op == ublk_cmd_op_nr(UBLK_U_IO_REGISTER_IO_BUF))
 		io->tgt_ios += 1;
@@ -136,10 +155,11 @@ static void ublk_loop_io_done(struct ublk_thread *t, struct ublk_queue *q,
 }
 
 static int ublk_loop_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev)
 {
 	unsigned long long bytes;
+	unsigned long blocks;
 	int ret;
 	struct ublk_params p = {
 		.types = UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DMA_ALIGN,
 		.basic = {
 			.attrs = UBLK_ATTR_VOLATILE_CACHE,
@@ -152,27 +172,34 @@ static int ublk_loop_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev)
 		.dma = {
 			.alignment = 511,
 		},
 	};
 
+	ublk_set_integrity_params(ctx, &p);
 	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;
-	}
 
+	/* Use O_DIRECT only for data file */
 	ret = backing_file_tgt_init(dev, 1);
 	if (ret)
 		return ret;
 
-	if (dev->tgt.nr_backing_files != 1)
+	/* Expect a second file for integrity data */
+	if (dev->tgt.nr_backing_files != 1 + !!ctx->metadata_size)
 		return -EINVAL;
 
-	bytes = dev->tgt.backing_file_size[0];
+	blocks = dev->tgt.backing_file_size[0] >> p.basic.logical_bs_shift;
+	if (ctx->metadata_size) {
+		unsigned long metadata_blocks =
+			dev->tgt.backing_file_size[1] / ctx->metadata_size;
+
+		/* Ensure both data and integrity data fit in backing files */
+		blocks = min(blocks, metadata_blocks);
+	}
+	bytes = blocks << p.basic.logical_bs_shift;
 	dev->tgt.dev_size = bytes;
 	p.basic.dev_sectors = bytes >> 9;
 	dev->tgt.params = p;
 
 	return 0;
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 18/19] selftests: ublk: add integrity params test
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (16 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 17/19] selftests: ublk: add integrity data support to loop target Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06  0:57 ` [PATCH v3 19/19] selftests: ublk: add end-to-end integrity test Caleb Sander Mateos
  18 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

Add test case null_04 to exercise all the different integrity params. It
creates 4 different ublk devices with different combinations of
integrity arguments and verifies their integrity limits via sysfs and
the metadata_size utility.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 tools/testing/selftests/ublk/Makefile        |   1 +
 tools/testing/selftests/ublk/test_common.sh  |  10 ++
 tools/testing/selftests/ublk/test_null_04.sh | 166 +++++++++++++++++++
 3 files changed, 177 insertions(+)
 create mode 100755 tools/testing/selftests/ublk/test_null_04.sh

diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
index 41f776bb86a6..bfd68ae64142 100644
--- a/tools/testing/selftests/ublk/Makefile
+++ b/tools/testing/selftests/ublk/Makefile
@@ -25,10 +25,11 @@ TEST_PROGS += test_generic_14.sh
 TEST_PROGS += test_generic_15.sh
 
 TEST_PROGS += test_null_01.sh
 TEST_PROGS += test_null_02.sh
 TEST_PROGS += test_null_03.sh
+TEST_PROGS += test_null_04.sh
 TEST_PROGS += test_loop_01.sh
 TEST_PROGS += test_loop_02.sh
 TEST_PROGS += test_loop_03.sh
 TEST_PROGS += test_loop_04.sh
 TEST_PROGS += test_loop_05.sh
diff --git a/tools/testing/selftests/ublk/test_common.sh b/tools/testing/selftests/ublk/test_common.sh
index ea9a5f3eb70a..7ff6ce79d62c 100755
--- a/tools/testing/selftests/ublk/test_common.sh
+++ b/tools/testing/selftests/ublk/test_common.sh
@@ -382,10 +382,20 @@ run_io_and_recover()
 _ublk_test_top_dir()
 {
 	cd "$(dirname "$0")" && pwd
 }
 
+METADATA_SIZE_PROG="$(_ublk_test_top_dir)/metadata_size"
+
+_get_metadata_size()
+{
+	local dev_id=$1
+	local field=$2
+
+	"$METADATA_SIZE_PROG" "/dev/ublkb$dev_id" | grep "$field" | grep -o "[0-9]*"
+}
+
 UBLK_PROG=$(_ublk_test_top_dir)/kublk
 UBLK_TEST_QUIET=1
 UBLK_TEST_SHOW_RESULT=1
 UBLK_BACKFILES=()
 export UBLK_PROG
diff --git a/tools/testing/selftests/ublk/test_null_04.sh b/tools/testing/selftests/ublk/test_null_04.sh
new file mode 100755
index 000000000000..0b0719ea33a3
--- /dev/null
+++ b/tools/testing/selftests/ublk/test_null_04.sh
@@ -0,0 +1,166 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
+
+TID=null_04
+
+_prep_test "null" "integrity params"
+
+dev_id=$(_add_ublk_dev -t null -u --metadata_size 8)
+_check_add_dev $TID $?
+metadata_size=$(_get_metadata_size "$dev_id" metadata_size)
+if [ "$metadata_size" != 8 ]; then
+	echo "metadata_size $metadata_size != 8"
+	_show_result $TID 255
+fi
+pi_offset=$(_get_metadata_size "$dev_id" pi_offset)
+if [ "$pi_offset" != 0 ]; then
+	echo "pi_offset $pi_offset != 0"
+	_show_result $TID 255
+fi
+pi_tuple_size=$(_get_metadata_size "$dev_id" pi_tuple_size)
+if [ "$pi_tuple_size" != 0 ]; then
+	echo "pi_tuple_size $pi_tuple_size != 0"
+	_show_result $TID 255
+fi
+capable=$(cat "/sys/block/ublkb$dev_id/integrity/device_is_integrity_capable")
+if [ "$capable" != 0 ]; then
+	echo "device_is_integrity_capable $capable != 0"
+	_show_result $TID 255
+fi
+format=$(cat "/sys/block/ublkb$dev_id/integrity/format")
+if [ "$format" != nop ]; then
+	echo "format $format != nop"
+	_show_result $TID 255
+fi
+protection_interval_bytes=$(cat "/sys/block/ublkb$dev_id/integrity/protection_interval_bytes")
+if [ "$protection_interval_bytes" != 512 ]; then
+	echo "protection_interval_bytes $protection_interval_bytes != 512"
+	_show_result $TID 255
+fi
+tag_size=$(cat "/sys/block/ublkb$dev_id/integrity/tag_size")
+if [ "$tag_size" != 0 ]; then
+	echo "tag_size $tag_size != 0"
+	_show_result $TID 255
+fi
+_cleanup_test
+
+dev_id=$(_add_ublk_dev -t null -u --integrity_capable --metadata_size 64 --pi_offset 56 --csum_type ip)
+_check_add_dev $TID $?
+metadata_size=$(_get_metadata_size "$dev_id" metadata_size)
+if [ "$metadata_size" != 64 ]; then
+	echo "metadata_size $metadata_size != 64"
+	_show_result $TID 255
+fi
+pi_offset=$(_get_metadata_size "$dev_id" pi_offset)
+if [ "$pi_offset" != 56 ]; then
+	echo "pi_offset $pi_offset != 56"
+	_show_result $TID 255
+fi
+pi_tuple_size=$(_get_metadata_size "$dev_id" pi_tuple_size)
+if [ "$pi_tuple_size" != 8 ]; then
+	echo "pi_tuple_size $pi_tuple_size != 8"
+	_show_result $TID 255
+fi
+capable=$(cat "/sys/block/ublkb$dev_id/integrity/device_is_integrity_capable")
+if [ "$capable" != 1 ]; then
+	echo "device_is_integrity_capable $capable != 1"
+	_show_result $TID 255
+fi
+format=$(cat "/sys/block/ublkb$dev_id/integrity/format")
+if [ "$format" != T10-DIF-TYPE3-IP ]; then
+	echo "format $format != T10-DIF-TYPE3-IP"
+	_show_result $TID 255
+fi
+protection_interval_bytes=$(cat "/sys/block/ublkb$dev_id/integrity/protection_interval_bytes")
+if [ "$protection_interval_bytes" != 512 ]; then
+	echo "protection_interval_bytes $protection_interval_bytes != 512"
+	_show_result $TID 255
+fi
+tag_size=$(cat "/sys/block/ublkb$dev_id/integrity/tag_size")
+if [ "$tag_size" != 0 ]; then
+	echo "tag_size $tag_size != 0"
+	_show_result $TID 255
+fi
+_cleanup_test
+
+dev_id=$(_add_ublk_dev -t null -u --integrity_reftag --metadata_size 8 --csum_type t10dif)
+_check_add_dev $TID $?
+metadata_size=$(_get_metadata_size "$dev_id" metadata_size)
+if [ "$metadata_size" != 8 ]; then
+	echo "metadata_size $metadata_size != 8"
+	_show_result $TID 255
+fi
+pi_offset=$(_get_metadata_size "$dev_id" pi_offset)
+if [ "$pi_offset" != 0 ]; then
+	echo "pi_offset $pi_offset != 0"
+	_show_result $TID 255
+fi
+pi_tuple_size=$(_get_metadata_size "$dev_id" pi_tuple_size)
+if [ "$pi_tuple_size" != 8 ]; then
+	echo "pi_tuple_size $pi_tuple_size != 8"
+	_show_result $TID 255
+fi
+capable=$(cat "/sys/block/ublkb$dev_id/integrity/device_is_integrity_capable")
+if [ "$capable" != 0 ]; then
+	echo "device_is_integrity_capable $capable != 0"
+	_show_result $TID 255
+fi
+format=$(cat "/sys/block/ublkb$dev_id/integrity/format")
+if [ "$format" != T10-DIF-TYPE1-CRC ]; then
+	echo "format $format != T10-DIF-TYPE1-CRC"
+	_show_result $TID 255
+fi
+protection_interval_bytes=$(cat "/sys/block/ublkb$dev_id/integrity/protection_interval_bytes")
+if [ "$protection_interval_bytes" != 512 ]; then
+	echo "protection_interval_bytes $protection_interval_bytes != 512"
+	_show_result $TID 255
+fi
+tag_size=$(cat "/sys/block/ublkb$dev_id/integrity/tag_size")
+if [ "$tag_size" != 0 ]; then
+	echo "tag_size $tag_size != 0"
+	_show_result $TID 255
+fi
+_cleanup_test
+
+dev_id=$(_add_ublk_dev -t null -u --metadata_size 16 --csum_type nvme --tag_size 8)
+_check_add_dev $TID $?
+metadata_size=$(_get_metadata_size "$dev_id" metadata_size)
+if [ "$metadata_size" != 16 ]; then
+	echo "metadata_size $metadata_size != 16"
+	_show_result $TID 255
+fi
+pi_offset=$(_get_metadata_size "$dev_id" pi_offset)
+if [ "$pi_offset" != 0 ]; then
+	echo "pi_offset $pi_offset != 0"
+	_show_result $TID 255
+fi
+pi_tuple_size=$(_get_metadata_size "$dev_id" pi_tuple_size)
+if [ "$pi_tuple_size" != 16 ]; then
+	echo "pi_tuple_size $pi_tuple_size != 16"
+	_show_result $TID 255
+fi
+capable=$(cat "/sys/block/ublkb$dev_id/integrity/device_is_integrity_capable")
+if [ "$capable" != 0 ]; then
+	echo "device_is_integrity_capable $capable != 0"
+	_show_result $TID 255
+fi
+format=$(cat "/sys/block/ublkb$dev_id/integrity/format")
+if [ "$format" != EXT-DIF-TYPE3-CRC64 ]; then
+	echo "format $format != EXT-DIF-TYPE3-CRC64"
+	_show_result $TID 255
+fi
+protection_interval_bytes=$(cat "/sys/block/ublkb$dev_id/integrity/protection_interval_bytes")
+if [ "$protection_interval_bytes" != 512 ]; then
+	echo "protection_interval_bytes $protection_interval_bytes != 512"
+	_show_result $TID 255
+fi
+tag_size=$(cat "/sys/block/ublkb$dev_id/integrity/tag_size")
+if [ "$tag_size" != 8 ]; then
+	echo "tag_size $tag_size != 8"
+	_show_result $TID 255
+fi
+_cleanup_test
+
+_show_result $TID 0
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 19/19] selftests: ublk: add end-to-end integrity test
  2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
                   ` (17 preceding siblings ...)
  2026-01-06  0:57 ` [PATCH v3 18/19] selftests: ublk: add integrity params test Caleb Sander Mateos
@ 2026-01-06  0:57 ` Caleb Sander Mateos
  2026-01-06 14:10   ` Ming Lei
  18 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06  0:57 UTC (permalink / raw)
  To: Ming Lei, Jens Axboe, Shuah Khan
  Cc: linux-block, linux-kselftest, linux-kernel, Stanley Zhang,
	Uday Shankar, Martin K . Petersen, Caleb Sander Mateos

Add test case loop_08 to verify the ublk integrity data flow. It uses
the kublk loop target to create a ublk device with integrity on top of
backing data and integrity files. It then writes to the whole device
with fio configured to generate integrity data. Then it reads back the
whole device with fio configured to verify the integrity data.
It also verifies that injected guard, reftag, and apptag corruptions are
correctly detected.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 tools/testing/selftests/ublk/Makefile        |   1 +
 tools/testing/selftests/ublk/test_loop_08.sh | 111 +++++++++++++++++++
 2 files changed, 112 insertions(+)
 create mode 100755 tools/testing/selftests/ublk/test_loop_08.sh

diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
index bfd68ae64142..ab745443fd58 100644
--- a/tools/testing/selftests/ublk/Makefile
+++ b/tools/testing/selftests/ublk/Makefile
@@ -33,10 +33,11 @@ TEST_PROGS += test_loop_02.sh
 TEST_PROGS += test_loop_03.sh
 TEST_PROGS += test_loop_04.sh
 TEST_PROGS += test_loop_05.sh
 TEST_PROGS += test_loop_06.sh
 TEST_PROGS += test_loop_07.sh
+TEST_PROGS += test_loop_08.sh
 TEST_PROGS += test_stripe_01.sh
 TEST_PROGS += test_stripe_02.sh
 TEST_PROGS += test_stripe_03.sh
 TEST_PROGS += test_stripe_04.sh
 TEST_PROGS += test_stripe_05.sh
diff --git a/tools/testing/selftests/ublk/test_loop_08.sh b/tools/testing/selftests/ublk/test_loop_08.sh
new file mode 100755
index 000000000000..ca289cfb2ad4
--- /dev/null
+++ b/tools/testing/selftests/ublk/test_loop_08.sh
@@ -0,0 +1,111 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
+
+if ! _have_program fio; then
+	exit $UBLK_SKIP_CODE
+fi
+
+fio_version=$(fio --version)
+if [[ "$fio_version" =~ fio-[0-9]+\.[0-9]+$ ]]; then
+	echo "Requires development fio version with https://github.com/axboe/fio/pull/1992"
+	exit $UBLK_SKIP_CODE
+fi
+
+TID=loop_08
+
+_prep_test "loop" "end-to-end integrity"
+
+_create_backfile 0 256M
+_create_backfile 1 32M # 256M * (64 integrity bytes / 512 data bytes)
+integrity_params="--integrity_capable --integrity_reftag
+                  --metadata_size 64 --pi_offset 56 --csum_type t10dif"
+dev_id=$(_add_ublk_dev -t loop -u $integrity_params "${UBLK_BACKFILES[@]}")
+_check_add_dev $TID $?
+
+# 1M * (64 integrity bytes / 512 data bytes) = 128K
+fio_args="--ioengine io_uring --direct 1 --bsrange 512-1M --iodepth 32
+          --md_per_io_size 128K --pi_act 0 --pi_chk GUARD,REFTAG,APPTAG
+          --filename /dev/ublkb$dev_id"
+fio --name fill --rw randwrite $fio_args > /dev/null
+err=$?
+if [ $err != 0 ]; then
+	echo "fio fill failed"
+	_show_result $TID $err
+fi
+
+fio --name verify --rw randread $fio_args > /dev/null
+err=$?
+if [ $err != 0 ]; then
+	echo "fio verify failed"
+	_show_result $TID $err
+fi
+
+fio_err=$(mktemp fio_err_XXXXX)
+
+# Overwrite 4-byte reftag at offset 56 + 4 = 60
+dd_reftag_args="bs=1 seek=60 count=4 oflag=dsync conv=notrunc status=none"
+dd if=/dev/urandom "of=${UBLK_BACKFILES[1]}" $dd_reftag_args
+err=$?
+if [ $err != 0 ]; then
+	echo "dd corrupted_reftag failed"
+	rm -f "$fio_err"
+	_show_result $TID $err
+fi
+if fio --name corrupted_reftag --rw randread $fio_args > /dev/null 2> "$fio_err"; then
+	echo "fio corrupted_reftag unexpectedly succeeded"
+	rm -f "$fio_err"
+	_show_result $TID 255
+fi
+expected_err="REFTAG compare error: LBA: 0 Expected=0, Actual="
+if ! grep -q "$expected_err" "$fio_err"; then
+	echo "fio corrupted_reftag message not found: $expected_err"
+	rm -f "$fio_err"
+	_show_result $TID 255
+fi
+# Reset to 0
+dd if=/dev/zero "of=${UBLK_BACKFILES[1]}" $dd_reftag_args
+err=$?
+if [ $err != 0 ]; then
+	echo "dd restore corrupted_reftag failed"
+	rm -f "$fio_err"
+	_show_result $TID $err
+fi
+
+dd_data_args="bs=512 count=1 oflag=direct,dsync conv=notrunc status=none"
+dd if=/dev/zero "of=${UBLK_BACKFILES[0]}" $dd_data_args
+err=$?
+if [ $err != 0 ]; then
+	echo "dd corrupted_data failed"
+	rm -f "$fio_err"
+	_show_result $TID $err
+fi
+if fio --name corrupted_data --rw randread $fio_args > /dev/null 2> "$fio_err"; then
+	echo "fio corrupted_data unexpectedly succeeded"
+	rm -f "$fio_err"
+	_show_result $TID 255
+fi
+expected_err="Guard compare error: LBA: 0 Expected=0, Actual="
+if ! grep -q "$expected_err" "$fio_err"; then
+	echo "fio corrupted_data message not found: $expected_err"
+	rm -f "$fio_err"
+	_show_result $TID 255
+fi
+
+if fio --name bad_apptag --rw randread $fio_args --apptag 0x4321 > /dev/null 2> "$fio_err"; then
+	echo "fio bad_apptag unexpectedly succeeded"
+	rm -f "$fio_err"
+	_show_result $TID 255
+fi
+expected_err="APPTAG compare error: LBA: [0-9]* Expected=4321, Actual=1234"
+if ! grep -q "$expected_err" "$fio_err"; then
+	echo "fio bad_apptag message not found: $expected_err"
+	rm -f "$fio_err"
+	_show_result $TID 255
+fi
+
+rm -f "$fio_err"
+
+_cleanup_test
+_show_result $TID 0
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 03/19] ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation
  2026-01-06  0:57 ` [PATCH v3 03/19] ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation Caleb Sander Mateos
@ 2026-01-06 13:09   ` Ming Lei
  2026-01-06 16:32     ` Caleb Sander Mateos
  0 siblings, 1 reply; 40+ messages in thread
From: Ming Lei @ 2026-01-06 13:09 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Mon, Jan 05, 2026 at 05:57:35PM -0700, Caleb Sander Mateos wrote:
> From: Stanley Zhang <stazhang@purestorage.com>
> 
> Add a feature flag UBLK_F_INTEGRITY for a ublk server to request
> integrity/metadata support when creating a ublk device. The ublk server
> can also check for the feature flag on the created device or the result
> of UBLK_U_CMD_GET_FEATURES to tell if the ublk driver supports it.
> UBLK_F_INTEGRITY requires UBLK_F_USER_COPY, as user copy is the only
> data copy mode initially supported for integrity data.
> Add UBLK_PARAM_TYPE_INTEGRITY and struct ublk_param_integrity to struct
> ublk_params to specify the integrity params of a ublk device.
> UBLK_PARAM_TYPE_INTEGRITY requires UBLK_F_INTEGRITY and a nonzero
> metadata_size. The LBMD_PI_CAP_* and LBMD_PI_CSUM_* values from the
> linux/fs.h UAPI header are used for the flags and csum_type fields.
> If the UBLK_PARAM_TYPE_INTEGRITY flag is set, validate the integrity
> parameters and apply them to the blk_integrity limits.
> The struct ublk_param_integrity validations are based on the checks in
> blk_validate_integrity_limits(). Any invalid parameters should be
> rejected before being applied to struct blk_integrity.
> 
> Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> [csander: drop redundant pi_tuple_size field, use block metadata UAPI
>  constants, add param validation]
> Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> ---
>  drivers/block/ublk_drv.c      | 94 ++++++++++++++++++++++++++++++++++-
>  include/uapi/linux/ublk_cmd.h | 18 +++++++
>  2 files changed, 111 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> index 8e3da9b2b93a..066c6ae062a0 100644
> --- a/drivers/block/ublk_drv.c
> +++ b/drivers/block/ublk_drv.c
> @@ -42,10 +42,12 @@
>  #include <linux/mm.h>
>  #include <asm/page.h>
>  #include <linux/task_work.h>
>  #include <linux/namei.h>
>  #include <linux/kref.h>
> +#include <linux/blk-integrity.h>
> +#include <uapi/linux/fs.h>
>  #include <uapi/linux/ublk_cmd.h>
>  
>  #define UBLK_MINORS		(1U << MINORBITS)
>  
>  #define UBLK_INVALID_BUF_IDX 	((u16)-1)
> @@ -81,11 +83,12 @@
>  
>  /* All UBLK_PARAM_TYPE_* should be included here */
>  #define UBLK_PARAM_TYPE_ALL                                \
>  	(UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD | \
>  	 UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED |    \
> -	 UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT)
> +	 UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT | \
> +	 UBLK_PARAM_TYPE_INTEGRITY)
>  
>  struct ublk_uring_cmd_pdu {
>  	/*
>  	 * Store requests in same batch temporarily for queuing them to
>  	 * daemon context.
> @@ -628,10 +631,57 @@ static void ublk_dev_param_basic_apply(struct ublk_device *ub)
>  		set_disk_ro(ub->ub_disk, true);
>  
>  	set_capacity(ub->ub_disk, p->dev_sectors);
>  }
>  
> +static int ublk_integrity_flags(u32 flags)
> +{
> +	int ret_flags = 0;
> +
> +	if (flags & LBMD_PI_CAP_INTEGRITY) {
> +		flags &= ~LBMD_PI_CAP_INTEGRITY;
> +		ret_flags |= BLK_INTEGRITY_DEVICE_CAPABLE;
> +	}
> +	if (flags & LBMD_PI_CAP_REFTAG) {
> +		flags &= ~LBMD_PI_CAP_REFTAG;
> +		ret_flags |= BLK_INTEGRITY_REF_TAG;
> +	}
> +	return flags ? -EINVAL : ret_flags;
> +}
> +
> +static int ublk_integrity_pi_tuple_size(u8 csum_type)
> +{
> +	switch (csum_type) {
> +	case LBMD_PI_CSUM_NONE:
> +		return 0;
> +	case LBMD_PI_CSUM_IP:
> +	case LBMD_PI_CSUM_CRC16_T10DIF:
> +		return 8;
> +	case LBMD_PI_CSUM_CRC64_NVME:
> +		return 16;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static enum blk_integrity_checksum ublk_integrity_csum_type(u8 csum_type)
> +{
> +	switch (csum_type) {
> +	case LBMD_PI_CSUM_NONE:
> +		return BLK_INTEGRITY_CSUM_NONE;
> +	case LBMD_PI_CSUM_IP:
> +		return BLK_INTEGRITY_CSUM_IP;
> +	case LBMD_PI_CSUM_CRC16_T10DIF:
> +		return BLK_INTEGRITY_CSUM_CRC;
> +	case LBMD_PI_CSUM_CRC64_NVME:
> +		return BLK_INTEGRITY_CSUM_CRC64;
> +	default:
> +		WARN_ON_ONCE(1);
> +		return BLK_INTEGRITY_CSUM_NONE;
> +	}
> +}
> +
>  static int ublk_validate_params(const struct ublk_device *ub)
>  {
>  	/* basic param is the only one which must be set */
>  	if (ub->params.types & UBLK_PARAM_TYPE_BASIC) {
>  		const struct ublk_param_basic *p = &ub->params.basic;
> @@ -690,10 +740,33 @@ static int ublk_validate_params(const struct ublk_device *ub)
>  			return -EINVAL;
>  		if (p->max_segment_size < UBLK_MIN_SEGMENT_SIZE)
>  			return -EINVAL;
>  	}
>  
> +	if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
> +		const struct ublk_param_integrity *p = &ub->params.integrity;
> +		int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
> +		int flags = ublk_integrity_flags(p->flags);
> +
> +		if (!(ub->dev_info.flags & UBLK_F_INTEGRITY))
> +			return -EINVAL;
> +		if (flags < 0)
> +			return flags;
> +		if (pi_tuple_size < 0)
> +			return pi_tuple_size;
> +		if (!p->metadata_size)
> +			return -EINVAL;
> +		if (p->csum_type == LBMD_PI_CSUM_NONE &&
> +		    p->flags & LBMD_PI_CAP_REFTAG)
> +			return -EINVAL;
> +		if (p->pi_offset + pi_tuple_size > p->metadata_size)
> +			return -EINVAL;
> +		if (p->interval_exp < SECTOR_SHIFT ||
> +		    p->interval_exp > ub->params.basic.logical_bs_shift)
> +			return -EINVAL;
> +	}
> +
>  	return 0;
>  }
>  
>  static void ublk_apply_params(struct ublk_device *ub)
>  {
> @@ -2941,10 +3014,25 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub,
>  		lim.seg_boundary_mask = ub->params.seg.seg_boundary_mask;
>  		lim.max_segment_size = ub->params.seg.max_segment_size;
>  		lim.max_segments = ub->params.seg.max_segments;
>  	}
>  
> +	if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
> +		const struct ublk_param_integrity *p = &ub->params.integrity;
> +		int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
> +
> +		lim.integrity = (struct blk_integrity) {
> +			.flags = ublk_integrity_flags(p->flags),
> +			.csum_type = ublk_integrity_csum_type(p->csum_type),
> +			.metadata_size = p->metadata_size,
> +			.pi_offset = p->pi_offset,
> +			.interval_exp = p->interval_exp,
> +			.tag_size = p->tag_size,
> +			.pi_tuple_size = pi_tuple_size,
> +		};
> +	}
> +
>  	if (wait_for_completion_interruptible(&ub->completion) != 0)
>  		return -EINTR;
>  
>  	if (ub->ublksrv_tgid != ublksrv_pid)
>  		return -EINVAL;
> @@ -3131,10 +3219,14 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
>  		if (info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
>  					UBLK_F_AUTO_BUF_REG))
>  			return -EINVAL;
>  	}
>  
> +	/* User copy is required to access integrity buffer */
> +	if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY))
> +		return -EINVAL;
> +
>  	/* the created device is always owned by current user */
>  	ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
>  
>  	if (header->dev_id != info.dev_id) {
>  		pr_warn("%s: dev id not match %u %u\n",
> diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
> index ec77dabba45b..a54c47832fa2 100644
> --- a/include/uapi/linux/ublk_cmd.h
> +++ b/include/uapi/linux/ublk_cmd.h
> @@ -309,10 +309,16 @@
>   * 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)
>  
> +/*
> + * ublk device supports requests with integrity/metadata buffer.
> + * Requires UBLK_F_USER_COPY.
> + */
> +#define UBLK_F_INTEGRITY (1ULL << 16)
> +
>  /* device state */
>  #define UBLK_S_DEV_DEAD	0
>  #define UBLK_S_DEV_LIVE	1
>  #define UBLK_S_DEV_QUIESCED	2
>  #define UBLK_S_DEV_FAIL_IO 	3
> @@ -598,10 +604,20 @@ struct ublk_param_segment {
>  	__u32 	max_segment_size;
>  	__u16 	max_segments;
>  	__u8	pad[2];
>  };
>  
> +struct ublk_param_integrity {
> +	__u32	flags; /* LBMD_PI_CAP_* from linux/fs.h */
> +	__u8	interval_exp;
> +	__u8	metadata_size; /* UBLK_PARAM_TYPE_INTEGRITY requires nonzero */
> +	__u8	pi_offset;
> +	__u8	csum_type; /* LBMD_PI_CSUM_* from linux/fs.h */
> +	__u8	tag_size;
> +	__u8	pad[7];
> +};

Looks max_integrity_segments is missed, otherwise this patch is fine for me.


Thanks,
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 05/19] ublk: add ublk_copy_user_bvec() helper
  2026-01-06  0:57 ` [PATCH v3 05/19] ublk: add ublk_copy_user_bvec() helper Caleb Sander Mateos
@ 2026-01-06 13:14   ` Ming Lei
  0 siblings, 0 replies; 40+ messages in thread
From: Ming Lei @ 2026-01-06 13:14 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Mon, Jan 05, 2026 at 05:57:37PM -0700, Caleb Sander Mateos wrote:
> Factor a helper function ublk_copy_user_bvec() out of
> ublk_copy_user_pages(). It will be used for copying integrity data too.
> 
> Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> ---
>  drivers/block/ublk_drv.c | 52 +++++++++++++++++++++++-----------------
>  1 file changed, 30 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> index 2b0a9720921d..2ce9afdecc15 100644
> --- a/drivers/block/ublk_drv.c
> +++ b/drivers/block/ublk_drv.c
> @@ -1000,10 +1000,39 @@ static const struct block_device_operations ub_fops = {
>  	.open =		ublk_open,
>  	.free_disk =	ublk_free_disk,
>  	.report_zones =	ublk_report_zones,
>  };
>  
> +static bool ublk_copy_user_bvec(const struct bio_vec *bv, unsigned *offset,
> +				struct iov_iter *uiter, int dir, size_t *done)
> +{
> +	unsigned len;
> +	void *bv_buf;
> +	size_t copied;
> +
> +	if (*offset >= bv->bv_len) {
> +		*offset -= bv->bv_len;
> +		return true;
> +	}
> +
> +	len = bv->bv_len - *offset;
> +	bv_buf = kmap_local_page(bv->bv_page) + bv->bv_offset + *offset;
> +	if (dir == ITER_DEST)
> +		copied = copy_to_iter(bv_buf, len, uiter);
> +	else
> +		copied = copy_from_iter(bv_buf, len, uiter);
> +
> +	kunmap_local(bv_buf);
> +
> +	*done += copied;
> +	if (copied < len)
> +		return false;
> +
> +	*offset = 0;
> +	return true;
> +}
> +
>  /*
>   * Copy data between request pages and io_iter, and 'offset'
>   * is the start point of linear offset of request.
>   */
>  static size_t ublk_copy_user_pages(const struct request *req,
> @@ -1012,33 +1041,12 @@ static size_t ublk_copy_user_pages(const struct request *req,
>  	struct req_iterator iter;
>  	struct bio_vec bv;
>  	size_t done = 0;
>  
>  	rq_for_each_segment(bv, req, iter) {
> -		unsigned len;
> -		void *bv_buf;
> -		size_t copied;
> -
> -		if (offset >= bv.bv_len) {
> -			offset -= bv.bv_len;
> -			continue;
> -		}
> -
> -		len = bv.bv_len - offset;
> -		bv_buf = kmap_local_page(bv.bv_page) + bv.bv_offset + offset;
> -		if (dir == ITER_DEST)
> -			copied = copy_to_iter(bv_buf, len, uiter);
> -		else
> -			copied = copy_from_iter(bv_buf, len, uiter);
> -
> -		kunmap_local(bv_buf);
> -
> -		done += copied;
> -		if (copied < len)
> +		if (!ublk_copy_user_bvec(&bv, &offset, uiter, dir, &done))
>  			break;
> -
> -		offset = 0;
>  	}
>  	return done;
>  }

Reviewed-by: Ming Lei <ming.lei@redhat.com>


Thanks, 
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 09/19] ublk: implement integrity user copy
  2026-01-06  0:57 ` [PATCH v3 09/19] ublk: implement integrity user copy Caleb Sander Mateos
@ 2026-01-06 13:34   ` Ming Lei
  2026-01-06 18:20     ` Caleb Sander Mateos
  2026-01-06 13:46   ` Ming Lei
  1 sibling, 1 reply; 40+ messages in thread
From: Ming Lei @ 2026-01-06 13:34 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Mon, Jan 05, 2026 at 05:57:41PM -0700, Caleb Sander Mateos wrote:
> From: Stanley Zhang <stazhang@purestorage.com>
> 
> Add a function ublk_copy_user_integrity() to copy integrity information
> between a request and a user iov_iter. This mirrors the existing
> ublk_copy_user_pages() but operates on request integrity data instead of
> regular data. Check UBLKSRV_IO_INTEGRITY_FLAG in iocb->ki_pos in
> ublk_user_copy() to choose between copying data or integrity data.
> 
> Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> [csander: change offset units from data bytes to integrity data bytes,
>  test UBLKSRV_IO_INTEGRITY_FLAG after subtracting UBLKSRV_IO_BUF_OFFSET,
>  fix CONFIG_BLK_DEV_INTEGRITY=n build,
>  rebase on ublk user copy refactor]
> Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> ---
>  drivers/block/ublk_drv.c      | 52 +++++++++++++++++++++++++++++++++--
>  include/uapi/linux/ublk_cmd.h |  4 +++
>  2 files changed, 53 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> index e44ab9981ef4..9694a4c1caa7 100644
> --- a/drivers/block/ublk_drv.c
> +++ b/drivers/block/ublk_drv.c
> @@ -621,10 +621,15 @@ static inline unsigned ublk_pos_to_tag(loff_t pos)
>  {
>  	return ((pos - UBLKSRV_IO_BUF_OFFSET) >> UBLK_TAG_OFF) &
>  		UBLK_TAG_BITS_MASK;
>  }
>  
> +static inline bool ublk_pos_is_integrity(loff_t pos)
> +{
> +	return !!((pos - UBLKSRV_IO_BUF_OFFSET) & UBLKSRV_IO_INTEGRITY_FLAG);
> +}
> +

It could be more readable to check UBLKSRV_IO_INTEGRITY_FLAG only.

>  static void ublk_dev_param_basic_apply(struct ublk_device *ub)
>  {
>  	const struct ublk_param_basic *p = &ub->params.basic;
>  
>  	if (p->attrs & UBLK_ATTR_READ_ONLY)
> @@ -1047,10 +1052,37 @@ static size_t ublk_copy_user_pages(const struct request *req,
>  			break;
>  	}
>  	return done;
>  }
>  
> +#ifdef CONFIG_BLK_DEV_INTEGRITY
> +static size_t ublk_copy_user_integrity(const struct request *req,
> +		unsigned offset, struct iov_iter *uiter, int dir)
> +{
> +	size_t done = 0;
> +	struct bio *bio = req->bio;
> +	struct bvec_iter iter;
> +	struct bio_vec iv;
> +
> +	if (!blk_integrity_rq(req))
> +		return 0;
> +
> +	bio_for_each_integrity_vec(iv, bio, iter) {
> +		if (!ublk_copy_user_bvec(&iv, &offset, uiter, dir, &done))
> +			break;
> +	}
> +
> +	return done;
> +}
> +#else /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
> +static size_t ublk_copy_user_integrity(const struct request *req,
> +		unsigned offset, struct iov_iter *uiter, int dir)
> +{
> +	return 0;
> +}
> +#endif /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
> +
>  static inline bool ublk_need_map_req(const struct request *req)
>  {
>  	return ublk_rq_has_data(req) && req_op(req) == REQ_OP_WRITE;
>  }
>  
> @@ -2654,10 +2686,12 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
>  {
>  	struct ublk_device *ub = iocb->ki_filp->private_data;
>  	struct ublk_queue *ubq;
>  	struct request *req;
>  	struct ublk_io *io;
> +	unsigned data_len;
> +	bool is_integrity;
>  	size_t buf_off;
>  	u16 tag, q_id;
>  	ssize_t ret;
>  
>  	if (!user_backed_iter(iter))
> @@ -2667,10 +2701,11 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
>  		return -EACCES;
>  
>  	tag = ublk_pos_to_tag(iocb->ki_pos);
>  	q_id = ublk_pos_to_hwq(iocb->ki_pos);
>  	buf_off = ublk_pos_to_buf_off(iocb->ki_pos);
> +	is_integrity = ublk_pos_is_integrity(iocb->ki_pos);

UBLKSRV_IO_INTEGRITY_FLAG can be set for device without UBLK_F_INTEGRITY,
so UBLK_F_INTEGRITY need to be checked in case of `is_integrity`.

>  
>  	if (q_id >= ub->dev_info.nr_hw_queues)
>  		return -EINVAL;
>  
>  	ubq = ublk_get_queue(ub, q_id);
> @@ -2683,21 +2718,31 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
>  	io = &ubq->ios[tag];
>  	req = __ublk_check_and_get_req(ub, q_id, tag, io);
>  	if (!req)
>  		return -EINVAL;
>  
> -	if (buf_off > blk_rq_bytes(req)) {
> +	if (is_integrity) {
> +		struct blk_integrity *bi = &req->q->limits.integrity;
> +
> +		data_len = bio_integrity_bytes(bi, blk_rq_sectors(req));
> +	} else {
> +		data_len = blk_rq_bytes(req);
> +	}
> +	if (buf_off > data_len) {
>  		ret = -EINVAL;
>  		goto out;
>  	}
>  
>  	if (!ublk_check_ubuf_dir(req, dir)) {
>  		ret = -EACCES;
>  		goto out;
>  	}
>  
> -	ret = ublk_copy_user_pages(req, buf_off, iter, dir);
> +	if (is_integrity)
> +		ret = ublk_copy_user_integrity(req, buf_off, iter, dir);
> +	else
> +		ret = ublk_copy_user_pages(req, buf_off, iter, dir);
>  
>  out:
>  	ublk_put_req_ref(io, req);
>  	return ret;
>  }
> @@ -3931,11 +3976,12 @@ static struct miscdevice ublk_misc = {
>  static int __init ublk_init(void)
>  {
>  	int ret;
>  
>  	BUILD_BUG_ON((u64)UBLKSRV_IO_BUF_OFFSET +
> -			UBLKSRV_IO_BUF_TOTAL_SIZE < UBLKSRV_IO_BUF_OFFSET);
> +			UBLKSRV_IO_BUF_TOTAL_SIZE +
> +			UBLKSRV_IO_INTEGRITY_FLAG < UBLKSRV_IO_BUF_OFFSET);

Maybe it can be simplified as:

BUILD_BUG_ON(UBLK_INTEGRITY_FLAG_OFF >= 63);  /* Must fit in loff_t */

>  	BUILD_BUG_ON(sizeof(struct ublk_auto_buf_reg) != 8);
>  
>  	init_waitqueue_head(&ublk_idr_wq);
>  
>  	ret = misc_register(&ublk_misc);
> diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
> index c1103ad5925b..3af7e3684834 100644
> --- a/include/uapi/linux/ublk_cmd.h
> +++ b/include/uapi/linux/ublk_cmd.h
> @@ -132,10 +132,14 @@
>  #define UBLK_MAX_NR_QUEUES	(1U << UBLK_QID_BITS)
>  
>  #define UBLKSRV_IO_BUF_TOTAL_BITS	(UBLK_QID_OFF + UBLK_QID_BITS)
>  #define UBLKSRV_IO_BUF_TOTAL_SIZE	(1ULL << UBLKSRV_IO_BUF_TOTAL_BITS)
>  
> +/* Copy to/from request integrity buffer instead of data buffer */
> +#define UBLK_INTEGRITY_FLAG_OFF UBLKSRV_IO_BUF_TOTAL_BITS
> +#define UBLKSRV_IO_INTEGRITY_FLAG (1ULL << UBLK_INTEGRITY_FLAG_OFF)
> +
>  /*
>   * ublk server can register data buffers for incoming I/O requests with a sparse
>   * io_uring buffer table. The request buffer can then be used as the data buffer
>   * for io_uring operations via the fixed buffer index.
>   * Note that the ublk server can never directly access the request data memory.
> -- 
> 2.45.2
> 

Thanks,
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 10/19] ublk: support UBLK_F_INTEGRITY
  2026-01-06  0:57 ` [PATCH v3 10/19] ublk: support UBLK_F_INTEGRITY Caleb Sander Mateos
@ 2026-01-06 13:36   ` Ming Lei
  0 siblings, 0 replies; 40+ messages in thread
From: Ming Lei @ 2026-01-06 13:36 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Mon, Jan 05, 2026 at 05:57:42PM -0700, Caleb Sander Mateos wrote:
> From: Stanley Zhang <stazhang@purestorage.com>
> 
> Now that all the components of the ublk integrity feature have been
> implemented, add UBLK_F_INTEGRITY to UBLK_F_ALL, conditional on block
> layer integrity support (CONFIG_BLK_DEV_INTEGRITY). This allows ublk
> servers to create ublk devices with UBLK_F_INTEGRITY set and
> UBLK_U_CMD_GET_FEATURES to report the feature as supported.
> 
> Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> [csander: make feature conditional on CONFIG_BLK_DEV_INTEGRITY]
> Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> ---
>  drivers/block/ublk_drv.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> index 9694a4c1caa7..4ffafbfcde3c 100644
> --- a/drivers/block/ublk_drv.c
> +++ b/drivers/block/ublk_drv.c
> @@ -73,11 +73,12 @@
>  		| UBLK_F_USER_RECOVERY_FAIL_IO \
>  		| UBLK_F_UPDATE_SIZE \
>  		| UBLK_F_AUTO_BUF_REG \
>  		| UBLK_F_QUIESCE \
>  		| UBLK_F_PER_IO_DAEMON \
> -		| UBLK_F_BUF_REG_OFF_DAEMON)
> +		| UBLK_F_BUF_REG_OFF_DAEMON \
> +		| (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) ? UBLK_F_INTEGRITY : 0))

Reviewed-by: Ming Lei <ming.lei@redhat.com>

Thanks,
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 12/19] selftests: ublk: display UBLK_F_INTEGRITY support
  2026-01-06  0:57 ` [PATCH v3 12/19] selftests: ublk: display UBLK_F_INTEGRITY support Caleb Sander Mateos
@ 2026-01-06 13:38   ` Ming Lei
  0 siblings, 0 replies; 40+ messages in thread
From: Ming Lei @ 2026-01-06 13:38 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Mon, Jan 05, 2026 at 05:57:44PM -0700, Caleb Sander Mateos wrote:
> Add support for printing the UBLK_F_INTEGRITY feature flag in the
> human-readable kublk features output.
> 
> Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> ---
>  tools/testing/selftests/ublk/kublk.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c
> index 185ba553686a..261095f19c93 100644
> --- a/tools/testing/selftests/ublk/kublk.c
> +++ b/tools/testing/selftests/ublk/kublk.c
> @@ -1452,10 +1452,11 @@ static int cmd_dev_get_features(void)
>  		FEAT_NAME(UBLK_F_UPDATE_SIZE),
>  		FEAT_NAME(UBLK_F_AUTO_BUF_REG),
>  		FEAT_NAME(UBLK_F_QUIESCE),
>  		FEAT_NAME(UBLK_F_PER_IO_DAEMON),
>  		FEAT_NAME(UBLK_F_BUF_REG_OFF_DAEMON),
> +		FEAT_NAME(UBLK_F_INTEGRITY),

Reviewed-by: Ming Lei <ming.lei@redhat.com>

Thanks,
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 09/19] ublk: implement integrity user copy
  2026-01-06  0:57 ` [PATCH v3 09/19] ublk: implement integrity user copy Caleb Sander Mateos
  2026-01-06 13:34   ` Ming Lei
@ 2026-01-06 13:46   ` Ming Lei
  1 sibling, 0 replies; 40+ messages in thread
From: Ming Lei @ 2026-01-06 13:46 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Mon, Jan 05, 2026 at 05:57:41PM -0700, Caleb Sander Mateos wrote:
> From: Stanley Zhang <stazhang@purestorage.com>
> 
> Add a function ublk_copy_user_integrity() to copy integrity information
> between a request and a user iov_iter. This mirrors the existing
> ublk_copy_user_pages() but operates on request integrity data instead of
> regular data. Check UBLKSRV_IO_INTEGRITY_FLAG in iocb->ki_pos in
> ublk_user_copy() to choose between copying data or integrity data.
> 
> Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> [csander: change offset units from data bytes to integrity data bytes,
>  test UBLKSRV_IO_INTEGRITY_FLAG after subtracting UBLKSRV_IO_BUF_OFFSET,
>  fix CONFIG_BLK_DEV_INTEGRITY=n build,
>  rebase on ublk user copy refactor]
> Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> ---
>  drivers/block/ublk_drv.c      | 52 +++++++++++++++++++++++++++++++++--
>  include/uapi/linux/ublk_cmd.h |  4 +++
>  2 files changed, 53 insertions(+), 3 deletions(-)
> 

...

> diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
> index c1103ad5925b..3af7e3684834 100644
> --- a/include/uapi/linux/ublk_cmd.h
> +++ b/include/uapi/linux/ublk_cmd.h
> @@ -132,10 +132,14 @@
>  #define UBLK_MAX_NR_QUEUES	(1U << UBLK_QID_BITS)
>  
>  #define UBLKSRV_IO_BUF_TOTAL_BITS	(UBLK_QID_OFF + UBLK_QID_BITS)
>  #define UBLKSRV_IO_BUF_TOTAL_SIZE	(1ULL << UBLKSRV_IO_BUF_TOTAL_BITS)
>  
> +/* Copy to/from request integrity buffer instead of data buffer */
> +#define UBLK_INTEGRITY_FLAG_OFF UBLKSRV_IO_BUF_TOTAL_BITS
> +#define UBLKSRV_IO_INTEGRITY_FLAG (1ULL << UBLK_INTEGRITY_FLAG_OFF)

UBLKSRV_IO_INTEGRITY_FLAG is actually one flag, not same with other encoded
fields, maybe it is better to define it from top bit(62) and not mix with
others? Then it can be helpful to extend in future.


Thanks,
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 13/19] selftests: ublk: add utility to get block device metadata size
  2026-01-06  0:57 ` [PATCH v3 13/19] selftests: ublk: add utility to get block device metadata size Caleb Sander Mateos
@ 2026-01-06 13:50   ` Ming Lei
  2026-01-06 17:18     ` Caleb Sander Mateos
  0 siblings, 1 reply; 40+ messages in thread
From: Ming Lei @ 2026-01-06 13:50 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Mon, Jan 05, 2026 at 05:57:45PM -0700, Caleb Sander Mateos wrote:
> Some block device integrity parameters are available in sysfs, but
> others are only accessible using the FS_IOC_GETLBMD_CAP ioctl. Add a
> metadata_size utility program to print out the logical block metadata
> size, PI offset, and PI size within the metadata. Example output:
> $ metadata_size /dev/ublkb0
> metadata_size: 64
> pi_offset: 56
> pi_tuple_size: 8
> 
> Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> ---
>  tools/testing/selftests/ublk/Makefile        |  4 +--
>  tools/testing/selftests/ublk/metadata_size.c | 36 ++++++++++++++++++++
>  2 files changed, 38 insertions(+), 2 deletions(-)
>  create mode 100644 tools/testing/selftests/ublk/metadata_size.c
> 
> diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
> index 06ba6fde098d..41f776bb86a6 100644
> --- a/tools/testing/selftests/ublk/Makefile
> +++ b/tools/testing/selftests/ublk/Makefile
> @@ -47,14 +47,14 @@ TEST_PROGS += test_stress_03.sh
>  TEST_PROGS += test_stress_04.sh
>  TEST_PROGS += test_stress_05.sh
>  TEST_PROGS += test_stress_06.sh
>  TEST_PROGS += test_stress_07.sh
>  
> -TEST_GEN_PROGS_EXTENDED = kublk
> +TEST_GEN_PROGS_EXTENDED = kublk metadata_size
>  
>  LOCAL_HDRS += $(wildcard *.h)
>  include ../lib.mk
>  
> -$(TEST_GEN_PROGS_EXTENDED): $(wildcard *.c)
> +$(OUTPUT)/kublk: common.c fault_inject.c file_backed.c kublk.c null.c stripe.c

I feel wildcard is pretty handy, can we avoid to kill it? Such as:

STANDALONE_UTILS := metadata_size.c
KUBLK_SRCS := $(filter-out $(STANDALONE_UTILS),$(wildcard *.c))


Thanks, 
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 19/19] selftests: ublk: add end-to-end integrity test
  2026-01-06  0:57 ` [PATCH v3 19/19] selftests: ublk: add end-to-end integrity test Caleb Sander Mateos
@ 2026-01-06 14:10   ` Ming Lei
  2026-01-06 17:15     ` Caleb Sander Mateos
  0 siblings, 1 reply; 40+ messages in thread
From: Ming Lei @ 2026-01-06 14:10 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Mon, Jan 05, 2026 at 05:57:51PM -0700, Caleb Sander Mateos wrote:
> Add test case loop_08 to verify the ublk integrity data flow. It uses
> the kublk loop target to create a ublk device with integrity on top of
> backing data and integrity files. It then writes to the whole device
> with fio configured to generate integrity data. Then it reads back the
> whole device with fio configured to verify the integrity data.
> It also verifies that injected guard, reftag, and apptag corruptions are
> correctly detected.
> 
> Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> ---
>  tools/testing/selftests/ublk/Makefile        |   1 +
>  tools/testing/selftests/ublk/test_loop_08.sh | 111 +++++++++++++++++++
>  2 files changed, 112 insertions(+)
>  create mode 100755 tools/testing/selftests/ublk/test_loop_08.sh
> 
> diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
> index bfd68ae64142..ab745443fd58 100644
> --- a/tools/testing/selftests/ublk/Makefile
> +++ b/tools/testing/selftests/ublk/Makefile
> @@ -33,10 +33,11 @@ TEST_PROGS += test_loop_02.sh
>  TEST_PROGS += test_loop_03.sh
>  TEST_PROGS += test_loop_04.sh
>  TEST_PROGS += test_loop_05.sh
>  TEST_PROGS += test_loop_06.sh
>  TEST_PROGS += test_loop_07.sh
> +TEST_PROGS += test_loop_08.sh
>  TEST_PROGS += test_stripe_01.sh
>  TEST_PROGS += test_stripe_02.sh
>  TEST_PROGS += test_stripe_03.sh
>  TEST_PROGS += test_stripe_04.sh
>  TEST_PROGS += test_stripe_05.sh
> diff --git a/tools/testing/selftests/ublk/test_loop_08.sh b/tools/testing/selftests/ublk/test_loop_08.sh
> new file mode 100755
> index 000000000000..ca289cfb2ad4
> --- /dev/null
> +++ b/tools/testing/selftests/ublk/test_loop_08.sh
> @@ -0,0 +1,111 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +
> +. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
> +
> +if ! _have_program fio; then
> +	exit $UBLK_SKIP_CODE
> +fi
> +
> +fio_version=$(fio --version)
> +if [[ "$fio_version" =~ fio-[0-9]+\.[0-9]+$ ]]; then
> +	echo "Requires development fio version with https://github.com/axboe/fio/pull/1992"
> +	exit $UBLK_SKIP_CODE
> +fi
> +
> +TID=loop_08
> +
> +_prep_test "loop" "end-to-end integrity"
> +
> +_create_backfile 0 256M
> +_create_backfile 1 32M # 256M * (64 integrity bytes / 512 data bytes)
> +integrity_params="--integrity_capable --integrity_reftag
> +                  --metadata_size 64 --pi_offset 56 --csum_type t10dif"
> +dev_id=$(_add_ublk_dev -t loop -u $integrity_params "${UBLK_BACKFILES[@]}")

I tried above setting:

./kublk add -t loop --integrity_capable --integrity_reftag --metadata_size 64 --pi_offset 56 --csum_type t10dif --foreground -u /dev/sdb /dev/sdc
dev id 1: nr_hw_queues 2 queue_depth 128 block size 512 dev_capacity 8388608
	max rq size 1048576 daemon pid 38295 flags 0x160c2 state LIVE
	queue 0: affinity(0 )
	queue 1: affinity(8 )

However, IO error is always triggered:

[ 9202.316382] ublkb1: ref tag error at location 0 (rcvd 128)
[ 9202.317171] Buffer I/O error on dev ublkb1, logical block 0, async page read
[ 9202.319478] ublkb1: ref tag error at location 0 (rcvd 128)
[ 9202.319983] Buffer I/O error on dev ublkb1, logical block 0, async page read
[ 9202.326332] ublkb1: ref tag error at location 0 (rcvd 128)
[ 9202.326974] Buffer I/O error on dev ublkb1, logical block 0, async page read
[ 9202.327570] ldm_validate_partition_table(): Disk read failed.
[ 9202.336539] ublkb1: ref tag error at location 0 (rcvd 128)
[ 9202.337228] Buffer I/O error on dev ublkb1, logical block 0, async page read
[ 9202.339247] ublkb1: ref tag error at location 0 (rcvd 128)
[ 9202.339779] Buffer I/O error on dev ublkb1, logical block 0, async page read
[ 9202.344306] ublkb1: ref tag error at location 0 (rcvd 128)
[ 9202.344948] Buffer I/O error on dev ublkb1, logical block 0, async page read
[ 9202.347067] ublkb1: ref tag error at location 0 (rcvd 128)
[ 9202.347558] Buffer I/O error on dev ublkb1, logical block 0, async page read
[ 9202.348100] Dev ublkb1: unable to read RDB block 0
[ 9202.350159] ublkb1: ref tag error at location 0 (rcvd 128)
[ 9202.350642] Buffer I/O error on dev ublkb1, logical block 0, async page read
[ 9202.354977] ublkb1: ref tag error at location 0 (rcvd 128)
[ 9202.355539] Buffer I/O error on dev ublkb1, logical block 0, async page read
[ 9202.356280]  ublkb1: unable to read partition table



Thanks,
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 03/19] ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation
  2026-01-06 13:09   ` Ming Lei
@ 2026-01-06 16:32     ` Caleb Sander Mateos
  2026-01-07  0:15       ` Ming Lei
  0 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06 16:32 UTC (permalink / raw)
  To: Ming Lei
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 6, 2026 at 5:09 AM Ming Lei <ming.lei@redhat.com> wrote:
>
> On Mon, Jan 05, 2026 at 05:57:35PM -0700, Caleb Sander Mateos wrote:
> > From: Stanley Zhang <stazhang@purestorage.com>
> >
> > Add a feature flag UBLK_F_INTEGRITY for a ublk server to request
> > integrity/metadata support when creating a ublk device. The ublk server
> > can also check for the feature flag on the created device or the result
> > of UBLK_U_CMD_GET_FEATURES to tell if the ublk driver supports it.
> > UBLK_F_INTEGRITY requires UBLK_F_USER_COPY, as user copy is the only
> > data copy mode initially supported for integrity data.
> > Add UBLK_PARAM_TYPE_INTEGRITY and struct ublk_param_integrity to struct
> > ublk_params to specify the integrity params of a ublk device.
> > UBLK_PARAM_TYPE_INTEGRITY requires UBLK_F_INTEGRITY and a nonzero
> > metadata_size. The LBMD_PI_CAP_* and LBMD_PI_CSUM_* values from the
> > linux/fs.h UAPI header are used for the flags and csum_type fields.
> > If the UBLK_PARAM_TYPE_INTEGRITY flag is set, validate the integrity
> > parameters and apply them to the blk_integrity limits.
> > The struct ublk_param_integrity validations are based on the checks in
> > blk_validate_integrity_limits(). Any invalid parameters should be
> > rejected before being applied to struct blk_integrity.
> >
> > Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> > [csander: drop redundant pi_tuple_size field, use block metadata UAPI
> >  constants, add param validation]
> > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > ---
> >  drivers/block/ublk_drv.c      | 94 ++++++++++++++++++++++++++++++++++-
> >  include/uapi/linux/ublk_cmd.h | 18 +++++++
> >  2 files changed, 111 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> > index 8e3da9b2b93a..066c6ae062a0 100644
> > --- a/drivers/block/ublk_drv.c
> > +++ b/drivers/block/ublk_drv.c
> > @@ -42,10 +42,12 @@
> >  #include <linux/mm.h>
> >  #include <asm/page.h>
> >  #include <linux/task_work.h>
> >  #include <linux/namei.h>
> >  #include <linux/kref.h>
> > +#include <linux/blk-integrity.h>
> > +#include <uapi/linux/fs.h>
> >  #include <uapi/linux/ublk_cmd.h>
> >
> >  #define UBLK_MINORS          (1U << MINORBITS)
> >
> >  #define UBLK_INVALID_BUF_IDX         ((u16)-1)
> > @@ -81,11 +83,12 @@
> >
> >  /* All UBLK_PARAM_TYPE_* should be included here */
> >  #define UBLK_PARAM_TYPE_ALL                                \
> >       (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD | \
> >        UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED |    \
> > -      UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT)
> > +      UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT | \
> > +      UBLK_PARAM_TYPE_INTEGRITY)
> >
> >  struct ublk_uring_cmd_pdu {
> >       /*
> >        * Store requests in same batch temporarily for queuing them to
> >        * daemon context.
> > @@ -628,10 +631,57 @@ static void ublk_dev_param_basic_apply(struct ublk_device *ub)
> >               set_disk_ro(ub->ub_disk, true);
> >
> >       set_capacity(ub->ub_disk, p->dev_sectors);
> >  }
> >
> > +static int ublk_integrity_flags(u32 flags)
> > +{
> > +     int ret_flags = 0;
> > +
> > +     if (flags & LBMD_PI_CAP_INTEGRITY) {
> > +             flags &= ~LBMD_PI_CAP_INTEGRITY;
> > +             ret_flags |= BLK_INTEGRITY_DEVICE_CAPABLE;
> > +     }
> > +     if (flags & LBMD_PI_CAP_REFTAG) {
> > +             flags &= ~LBMD_PI_CAP_REFTAG;
> > +             ret_flags |= BLK_INTEGRITY_REF_TAG;
> > +     }
> > +     return flags ? -EINVAL : ret_flags;
> > +}
> > +
> > +static int ublk_integrity_pi_tuple_size(u8 csum_type)
> > +{
> > +     switch (csum_type) {
> > +     case LBMD_PI_CSUM_NONE:
> > +             return 0;
> > +     case LBMD_PI_CSUM_IP:
> > +     case LBMD_PI_CSUM_CRC16_T10DIF:
> > +             return 8;
> > +     case LBMD_PI_CSUM_CRC64_NVME:
> > +             return 16;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +}
> > +
> > +static enum blk_integrity_checksum ublk_integrity_csum_type(u8 csum_type)
> > +{
> > +     switch (csum_type) {
> > +     case LBMD_PI_CSUM_NONE:
> > +             return BLK_INTEGRITY_CSUM_NONE;
> > +     case LBMD_PI_CSUM_IP:
> > +             return BLK_INTEGRITY_CSUM_IP;
> > +     case LBMD_PI_CSUM_CRC16_T10DIF:
> > +             return BLK_INTEGRITY_CSUM_CRC;
> > +     case LBMD_PI_CSUM_CRC64_NVME:
> > +             return BLK_INTEGRITY_CSUM_CRC64;
> > +     default:
> > +             WARN_ON_ONCE(1);
> > +             return BLK_INTEGRITY_CSUM_NONE;
> > +     }
> > +}
> > +
> >  static int ublk_validate_params(const struct ublk_device *ub)
> >  {
> >       /* basic param is the only one which must be set */
> >       if (ub->params.types & UBLK_PARAM_TYPE_BASIC) {
> >               const struct ublk_param_basic *p = &ub->params.basic;
> > @@ -690,10 +740,33 @@ static int ublk_validate_params(const struct ublk_device *ub)
> >                       return -EINVAL;
> >               if (p->max_segment_size < UBLK_MIN_SEGMENT_SIZE)
> >                       return -EINVAL;
> >       }
> >
> > +     if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
> > +             const struct ublk_param_integrity *p = &ub->params.integrity;
> > +             int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
> > +             int flags = ublk_integrity_flags(p->flags);
> > +
> > +             if (!(ub->dev_info.flags & UBLK_F_INTEGRITY))
> > +                     return -EINVAL;
> > +             if (flags < 0)
> > +                     return flags;
> > +             if (pi_tuple_size < 0)
> > +                     return pi_tuple_size;
> > +             if (!p->metadata_size)
> > +                     return -EINVAL;
> > +             if (p->csum_type == LBMD_PI_CSUM_NONE &&
> > +                 p->flags & LBMD_PI_CAP_REFTAG)
> > +                     return -EINVAL;
> > +             if (p->pi_offset + pi_tuple_size > p->metadata_size)
> > +                     return -EINVAL;
> > +             if (p->interval_exp < SECTOR_SHIFT ||
> > +                 p->interval_exp > ub->params.basic.logical_bs_shift)
> > +                     return -EINVAL;
> > +     }
> > +
> >       return 0;
> >  }
> >
> >  static void ublk_apply_params(struct ublk_device *ub)
> >  {
> > @@ -2941,10 +3014,25 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub,
> >               lim.seg_boundary_mask = ub->params.seg.seg_boundary_mask;
> >               lim.max_segment_size = ub->params.seg.max_segment_size;
> >               lim.max_segments = ub->params.seg.max_segments;
> >       }
> >
> > +     if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
> > +             const struct ublk_param_integrity *p = &ub->params.integrity;
> > +             int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
> > +
> > +             lim.integrity = (struct blk_integrity) {
> > +                     .flags = ublk_integrity_flags(p->flags),
> > +                     .csum_type = ublk_integrity_csum_type(p->csum_type),
> > +                     .metadata_size = p->metadata_size,
> > +                     .pi_offset = p->pi_offset,
> > +                     .interval_exp = p->interval_exp,
> > +                     .tag_size = p->tag_size,
> > +                     .pi_tuple_size = pi_tuple_size,
> > +             };
> > +     }
> > +
> >       if (wait_for_completion_interruptible(&ub->completion) != 0)
> >               return -EINTR;
> >
> >       if (ub->ublksrv_tgid != ublksrv_pid)
> >               return -EINVAL;
> > @@ -3131,10 +3219,14 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
> >               if (info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
> >                                       UBLK_F_AUTO_BUF_REG))
> >                       return -EINVAL;
> >       }
> >
> > +     /* User copy is required to access integrity buffer */
> > +     if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY))
> > +             return -EINVAL;
> > +
> >       /* the created device is always owned by current user */
> >       ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
> >
> >       if (header->dev_id != info.dev_id) {
> >               pr_warn("%s: dev id not match %u %u\n",
> > diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
> > index ec77dabba45b..a54c47832fa2 100644
> > --- a/include/uapi/linux/ublk_cmd.h
> > +++ b/include/uapi/linux/ublk_cmd.h
> > @@ -309,10 +309,16 @@
> >   * 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)
> >
> > +/*
> > + * ublk device supports requests with integrity/metadata buffer.
> > + * Requires UBLK_F_USER_COPY.
> > + */
> > +#define UBLK_F_INTEGRITY (1ULL << 16)
> > +
> >  /* device state */
> >  #define UBLK_S_DEV_DEAD      0
> >  #define UBLK_S_DEV_LIVE      1
> >  #define UBLK_S_DEV_QUIESCED  2
> >  #define UBLK_S_DEV_FAIL_IO   3
> > @@ -598,10 +604,20 @@ struct ublk_param_segment {
> >       __u32   max_segment_size;
> >       __u16   max_segments;
> >       __u8    pad[2];
> >  };
> >
> > +struct ublk_param_integrity {
> > +     __u32   flags; /* LBMD_PI_CAP_* from linux/fs.h */
> > +     __u8    interval_exp;
> > +     __u8    metadata_size; /* UBLK_PARAM_TYPE_INTEGRITY requires nonzero */
> > +     __u8    pi_offset;
> > +     __u8    csum_type; /* LBMD_PI_CSUM_* from linux/fs.h */
> > +     __u8    tag_size;
> > +     __u8    pad[7];
> > +};
>
> Looks max_integrity_segments is missed, otherwise this patch is fine for me.

My thinking was that there isn't any reason why a ublk server would
need to limit the number of integrity segments. The request integrity
segments aren't directly exposed to the ublk server through user copy.
And since zero copy isn't supported for integrity data, there's no
concern about needing to align the ublk integrity limits with the
backing device limits. What do you think?

Thanks,
Caleb

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 19/19] selftests: ublk: add end-to-end integrity test
  2026-01-06 14:10   ` Ming Lei
@ 2026-01-06 17:15     ` Caleb Sander Mateos
  2026-01-07  0:21       ` Ming Lei
  0 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06 17:15 UTC (permalink / raw)
  To: Ming Lei
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 6, 2026 at 6:10 AM Ming Lei <ming.lei@redhat.com> wrote:
>
> On Mon, Jan 05, 2026 at 05:57:51PM -0700, Caleb Sander Mateos wrote:
> > Add test case loop_08 to verify the ublk integrity data flow. It uses
> > the kublk loop target to create a ublk device with integrity on top of
> > backing data and integrity files. It then writes to the whole device
> > with fio configured to generate integrity data. Then it reads back the
> > whole device with fio configured to verify the integrity data.
> > It also verifies that injected guard, reftag, and apptag corruptions are
> > correctly detected.
> >
> > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > ---
> >  tools/testing/selftests/ublk/Makefile        |   1 +
> >  tools/testing/selftests/ublk/test_loop_08.sh | 111 +++++++++++++++++++
> >  2 files changed, 112 insertions(+)
> >  create mode 100755 tools/testing/selftests/ublk/test_loop_08.sh
> >
> > diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
> > index bfd68ae64142..ab745443fd58 100644
> > --- a/tools/testing/selftests/ublk/Makefile
> > +++ b/tools/testing/selftests/ublk/Makefile
> > @@ -33,10 +33,11 @@ TEST_PROGS += test_loop_02.sh
> >  TEST_PROGS += test_loop_03.sh
> >  TEST_PROGS += test_loop_04.sh
> >  TEST_PROGS += test_loop_05.sh
> >  TEST_PROGS += test_loop_06.sh
> >  TEST_PROGS += test_loop_07.sh
> > +TEST_PROGS += test_loop_08.sh
> >  TEST_PROGS += test_stripe_01.sh
> >  TEST_PROGS += test_stripe_02.sh
> >  TEST_PROGS += test_stripe_03.sh
> >  TEST_PROGS += test_stripe_04.sh
> >  TEST_PROGS += test_stripe_05.sh
> > diff --git a/tools/testing/selftests/ublk/test_loop_08.sh b/tools/testing/selftests/ublk/test_loop_08.sh
> > new file mode 100755
> > index 000000000000..ca289cfb2ad4
> > --- /dev/null
> > +++ b/tools/testing/selftests/ublk/test_loop_08.sh
> > @@ -0,0 +1,111 @@
> > +#!/bin/bash
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
> > +
> > +if ! _have_program fio; then
> > +     exit $UBLK_SKIP_CODE
> > +fi
> > +
> > +fio_version=$(fio --version)
> > +if [[ "$fio_version" =~ fio-[0-9]+\.[0-9]+$ ]]; then
> > +     echo "Requires development fio version with https://github.com/axboe/fio/pull/1992"
> > +     exit $UBLK_SKIP_CODE
> > +fi
> > +
> > +TID=loop_08
> > +
> > +_prep_test "loop" "end-to-end integrity"
> > +
> > +_create_backfile 0 256M
> > +_create_backfile 1 32M # 256M * (64 integrity bytes / 512 data bytes)
> > +integrity_params="--integrity_capable --integrity_reftag
> > +                  --metadata_size 64 --pi_offset 56 --csum_type t10dif"
> > +dev_id=$(_add_ublk_dev -t loop -u $integrity_params "${UBLK_BACKFILES[@]}")
>
> I tried above setting:
>
> ./kublk add -t loop --integrity_capable --integrity_reftag --metadata_size 64 --pi_offset 56 --csum_type t10dif --foreground -u /dev/sdb /dev/sdc
> dev id 1: nr_hw_queues 2 queue_depth 128 block size 512 dev_capacity 8388608
>         max rq size 1048576 daemon pid 38295 flags 0x160c2 state LIVE
>         queue 0: affinity(0 )
>         queue 1: affinity(8 )
>
> However, IO error is always triggered:
>
> [ 9202.316382] ublkb1: ref tag error at location 0 (rcvd 128)
> [ 9202.317171] Buffer I/O error on dev ublkb1, logical block 0, async page read

Hmm, what are the initial contents of /dev/sdc? It looks like they are
nonzero, as the reftag being read for logical block 0 is 128 rather
than the expected 0 (the reftag would be read from bytes 60 to 63 of
/dev/sdc). In general, though, the partition scan may be expected to
fail the bio-integrity-auto checks if the integrity data hasn't been
initialized. I don't think this is an issue, since the partition scan
is looking for a partition table but there's no guarantee that one
exists.
You can disable the kernel integrity checks if you want by writing 0
to /sys/block/ublkb1/integrity/read_verify. However, I'm not sure it's
possible to do this soon enough to take effect before the partition
scan.
We could also use the UBLK_F_NO_AUTO_PART_SCAN feature, once it lands,
to suppress the partition scan and these error messages.

Best,
Caleb

> [ 9202.319478] ublkb1: ref tag error at location 0 (rcvd 128)
> [ 9202.319983] Buffer I/O error on dev ublkb1, logical block 0, async page read
> [ 9202.326332] ublkb1: ref tag error at location 0 (rcvd 128)
> [ 9202.326974] Buffer I/O error on dev ublkb1, logical block 0, async page read
> [ 9202.327570] ldm_validate_partition_table(): Disk read failed.
> [ 9202.336539] ublkb1: ref tag error at location 0 (rcvd 128)
> [ 9202.337228] Buffer I/O error on dev ublkb1, logical block 0, async page read
> [ 9202.339247] ublkb1: ref tag error at location 0 (rcvd 128)
> [ 9202.339779] Buffer I/O error on dev ublkb1, logical block 0, async page read
> [ 9202.344306] ublkb1: ref tag error at location 0 (rcvd 128)
> [ 9202.344948] Buffer I/O error on dev ublkb1, logical block 0, async page read
> [ 9202.347067] ublkb1: ref tag error at location 0 (rcvd 128)
> [ 9202.347558] Buffer I/O error on dev ublkb1, logical block 0, async page read
> [ 9202.348100] Dev ublkb1: unable to read RDB block 0
> [ 9202.350159] ublkb1: ref tag error at location 0 (rcvd 128)
> [ 9202.350642] Buffer I/O error on dev ublkb1, logical block 0, async page read
> [ 9202.354977] ublkb1: ref tag error at location 0 (rcvd 128)
> [ 9202.355539] Buffer I/O error on dev ublkb1, logical block 0, async page read
> [ 9202.356280]  ublkb1: unable to read partition table
>
>
>
> Thanks,
> Ming
>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 13/19] selftests: ublk: add utility to get block device metadata size
  2026-01-06 13:50   ` Ming Lei
@ 2026-01-06 17:18     ` Caleb Sander Mateos
  0 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06 17:18 UTC (permalink / raw)
  To: Ming Lei
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 6, 2026 at 5:50 AM Ming Lei <ming.lei@redhat.com> wrote:
>
> On Mon, Jan 05, 2026 at 05:57:45PM -0700, Caleb Sander Mateos wrote:
> > Some block device integrity parameters are available in sysfs, but
> > others are only accessible using the FS_IOC_GETLBMD_CAP ioctl. Add a
> > metadata_size utility program to print out the logical block metadata
> > size, PI offset, and PI size within the metadata. Example output:
> > $ metadata_size /dev/ublkb0
> > metadata_size: 64
> > pi_offset: 56
> > pi_tuple_size: 8
> >
> > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > ---
> >  tools/testing/selftests/ublk/Makefile        |  4 +--
> >  tools/testing/selftests/ublk/metadata_size.c | 36 ++++++++++++++++++++
> >  2 files changed, 38 insertions(+), 2 deletions(-)
> >  create mode 100644 tools/testing/selftests/ublk/metadata_size.c
> >
> > diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
> > index 06ba6fde098d..41f776bb86a6 100644
> > --- a/tools/testing/selftests/ublk/Makefile
> > +++ b/tools/testing/selftests/ublk/Makefile
> > @@ -47,14 +47,14 @@ TEST_PROGS += test_stress_03.sh
> >  TEST_PROGS += test_stress_04.sh
> >  TEST_PROGS += test_stress_05.sh
> >  TEST_PROGS += test_stress_06.sh
> >  TEST_PROGS += test_stress_07.sh
> >
> > -TEST_GEN_PROGS_EXTENDED = kublk
> > +TEST_GEN_PROGS_EXTENDED = kublk metadata_size
> >
> >  LOCAL_HDRS += $(wildcard *.h)
> >  include ../lib.mk
> >
> > -$(TEST_GEN_PROGS_EXTENDED): $(wildcard *.c)
> > +$(OUTPUT)/kublk: common.c fault_inject.c file_backed.c kublk.c null.c stripe.c
>
> I feel wildcard is pretty handy, can we avoid to kill it? Such as:
>
> STANDALONE_UTILS := metadata_size.c
> KUBLK_SRCS := $(filter-out $(STANDALONE_UTILS),$(wildcard *.c))

Sure, I wasn't aware of filter-out. I'm fine with that.

Thanks,
Caleb

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 09/19] ublk: implement integrity user copy
  2026-01-06 13:34   ` Ming Lei
@ 2026-01-06 18:20     ` Caleb Sander Mateos
  2026-01-07  0:28       ` Ming Lei
  0 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-06 18:20 UTC (permalink / raw)
  To: Ming Lei
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 6, 2026 at 5:34 AM Ming Lei <ming.lei@redhat.com> wrote:
>
> On Mon, Jan 05, 2026 at 05:57:41PM -0700, Caleb Sander Mateos wrote:
> > From: Stanley Zhang <stazhang@purestorage.com>
> >
> > Add a function ublk_copy_user_integrity() to copy integrity information
> > between a request and a user iov_iter. This mirrors the existing
> > ublk_copy_user_pages() but operates on request integrity data instead of
> > regular data. Check UBLKSRV_IO_INTEGRITY_FLAG in iocb->ki_pos in
> > ublk_user_copy() to choose between copying data or integrity data.
> >
> > Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> > [csander: change offset units from data bytes to integrity data bytes,
> >  test UBLKSRV_IO_INTEGRITY_FLAG after subtracting UBLKSRV_IO_BUF_OFFSET,
> >  fix CONFIG_BLK_DEV_INTEGRITY=n build,
> >  rebase on ublk user copy refactor]
> > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > ---
> >  drivers/block/ublk_drv.c      | 52 +++++++++++++++++++++++++++++++++--
> >  include/uapi/linux/ublk_cmd.h |  4 +++
> >  2 files changed, 53 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> > index e44ab9981ef4..9694a4c1caa7 100644
> > --- a/drivers/block/ublk_drv.c
> > +++ b/drivers/block/ublk_drv.c
> > @@ -621,10 +621,15 @@ static inline unsigned ublk_pos_to_tag(loff_t pos)
> >  {
> >       return ((pos - UBLKSRV_IO_BUF_OFFSET) >> UBLK_TAG_OFF) &
> >               UBLK_TAG_BITS_MASK;
> >  }
> >
> > +static inline bool ublk_pos_is_integrity(loff_t pos)
> > +{
> > +     return !!((pos - UBLKSRV_IO_BUF_OFFSET) & UBLKSRV_IO_INTEGRITY_FLAG);
> > +}
> > +
>
> It could be more readable to check UBLKSRV_IO_INTEGRITY_FLAG only.

That's assuming that UBLK_TAG_BITS = 16 has more bits than are
strictly required by UBLK_MAX_QUEUE_DEPTH = 4096? Otherwise, adding
UBLKSRV_IO_BUF_OFFSET = 1 << 31 to tag << UBLK_TAG_OFF could overflow
into the QID bits, which could then overflow into
UBLKSRV_IO_INTEGRITY_FLAG. That seems like a very fragile assumption.
And if you want to rely on this assumption, why bother subtracting
UBLKSRV_IO_BUF_OFFSET in ublk_pos_to_hwq() either? The compiler should
easily be able to deduplicate the iocb->ki_pos - UBLKSRV_IO_BUF_OFFSET
computations, so I can't imagine it matters for performance.

>
> >  static void ublk_dev_param_basic_apply(struct ublk_device *ub)
> >  {
> >       const struct ublk_param_basic *p = &ub->params.basic;
> >
> >       if (p->attrs & UBLK_ATTR_READ_ONLY)
> > @@ -1047,10 +1052,37 @@ static size_t ublk_copy_user_pages(const struct request *req,
> >                       break;
> >       }
> >       return done;
> >  }
> >
> > +#ifdef CONFIG_BLK_DEV_INTEGRITY
> > +static size_t ublk_copy_user_integrity(const struct request *req,
> > +             unsigned offset, struct iov_iter *uiter, int dir)
> > +{
> > +     size_t done = 0;
> > +     struct bio *bio = req->bio;
> > +     struct bvec_iter iter;
> > +     struct bio_vec iv;
> > +
> > +     if (!blk_integrity_rq(req))
> > +             return 0;
> > +
> > +     bio_for_each_integrity_vec(iv, bio, iter) {
> > +             if (!ublk_copy_user_bvec(&iv, &offset, uiter, dir, &done))
> > +                     break;
> > +     }
> > +
> > +     return done;
> > +}
> > +#else /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
> > +static size_t ublk_copy_user_integrity(const struct request *req,
> > +             unsigned offset, struct iov_iter *uiter, int dir)
> > +{
> > +     return 0;
> > +}
> > +#endif /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
> > +
> >  static inline bool ublk_need_map_req(const struct request *req)
> >  {
> >       return ublk_rq_has_data(req) && req_op(req) == REQ_OP_WRITE;
> >  }
> >
> > @@ -2654,10 +2686,12 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
> >  {
> >       struct ublk_device *ub = iocb->ki_filp->private_data;
> >       struct ublk_queue *ubq;
> >       struct request *req;
> >       struct ublk_io *io;
> > +     unsigned data_len;
> > +     bool is_integrity;
> >       size_t buf_off;
> >       u16 tag, q_id;
> >       ssize_t ret;
> >
> >       if (!user_backed_iter(iter))
> > @@ -2667,10 +2701,11 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
> >               return -EACCES;
> >
> >       tag = ublk_pos_to_tag(iocb->ki_pos);
> >       q_id = ublk_pos_to_hwq(iocb->ki_pos);
> >       buf_off = ublk_pos_to_buf_off(iocb->ki_pos);
> > +     is_integrity = ublk_pos_is_integrity(iocb->ki_pos);
>
> UBLKSRV_IO_INTEGRITY_FLAG can be set for device without UBLK_F_INTEGRITY,
> so UBLK_F_INTEGRITY need to be checked in case of `is_integrity`.

If UBLK_F_INTEGRITY isn't set, then UBLK_PARAM_TYPE_INTEGRITY isn't
allowed, so the ublk device won't support integrity data. Therefore,
blk_integrity_rq() will return false and ublk_copy_user_integrity()
will just return 0. Do you think it's important to return some error
code value instead? I would rather avoid the additional checks in the
hot path.

>
> >
> >       if (q_id >= ub->dev_info.nr_hw_queues)
> >               return -EINVAL;
> >
> >       ubq = ublk_get_queue(ub, q_id);
> > @@ -2683,21 +2718,31 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
> >       io = &ubq->ios[tag];
> >       req = __ublk_check_and_get_req(ub, q_id, tag, io);
> >       if (!req)
> >               return -EINVAL;
> >
> > -     if (buf_off > blk_rq_bytes(req)) {
> > +     if (is_integrity) {
> > +             struct blk_integrity *bi = &req->q->limits.integrity;
> > +
> > +             data_len = bio_integrity_bytes(bi, blk_rq_sectors(req));
> > +     } else {
> > +             data_len = blk_rq_bytes(req);
> > +     }
> > +     if (buf_off > data_len) {
> >               ret = -EINVAL;
> >               goto out;
> >       }
> >
> >       if (!ublk_check_ubuf_dir(req, dir)) {
> >               ret = -EACCES;
> >               goto out;
> >       }
> >
> > -     ret = ublk_copy_user_pages(req, buf_off, iter, dir);
> > +     if (is_integrity)
> > +             ret = ublk_copy_user_integrity(req, buf_off, iter, dir);
> > +     else
> > +             ret = ublk_copy_user_pages(req, buf_off, iter, dir);
> >
> >  out:
> >       ublk_put_req_ref(io, req);
> >       return ret;
> >  }
> > @@ -3931,11 +3976,12 @@ static struct miscdevice ublk_misc = {
> >  static int __init ublk_init(void)
> >  {
> >       int ret;
> >
> >       BUILD_BUG_ON((u64)UBLKSRV_IO_BUF_OFFSET +
> > -                     UBLKSRV_IO_BUF_TOTAL_SIZE < UBLKSRV_IO_BUF_OFFSET);
> > +                     UBLKSRV_IO_BUF_TOTAL_SIZE +
> > +                     UBLKSRV_IO_INTEGRITY_FLAG < UBLKSRV_IO_BUF_OFFSET);
>
> Maybe it can be simplified as:
>
> BUILD_BUG_ON(UBLK_INTEGRITY_FLAG_OFF >= 63);  /* Must fit in loff_t */

Okay, I think that works. Even if the addition of
UBLKSRV_IO_BUF_OFFSET causes an overflow to the next bit, it should
still fit within a 64-bit integer.

Thanks,
Caleb

>
> >       BUILD_BUG_ON(sizeof(struct ublk_auto_buf_reg) != 8);
> >
> >       init_waitqueue_head(&ublk_idr_wq);
> >
> >       ret = misc_register(&ublk_misc);
> > diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
> > index c1103ad5925b..3af7e3684834 100644
> > --- a/include/uapi/linux/ublk_cmd.h
> > +++ b/include/uapi/linux/ublk_cmd.h
> > @@ -132,10 +132,14 @@
> >  #define UBLK_MAX_NR_QUEUES   (1U << UBLK_QID_BITS)
> >
> >  #define UBLKSRV_IO_BUF_TOTAL_BITS    (UBLK_QID_OFF + UBLK_QID_BITS)
> >  #define UBLKSRV_IO_BUF_TOTAL_SIZE    (1ULL << UBLKSRV_IO_BUF_TOTAL_BITS)
> >
> > +/* Copy to/from request integrity buffer instead of data buffer */
> > +#define UBLK_INTEGRITY_FLAG_OFF UBLKSRV_IO_BUF_TOTAL_BITS
> > +#define UBLKSRV_IO_INTEGRITY_FLAG (1ULL << UBLK_INTEGRITY_FLAG_OFF)
> > +
> >  /*
> >   * ublk server can register data buffers for incoming I/O requests with a sparse
> >   * io_uring buffer table. The request buffer can then be used as the data buffer
> >   * for io_uring operations via the fixed buffer index.
> >   * Note that the ublk server can never directly access the request data memory.
> > --
> > 2.45.2
> >
>
> Thanks,
> Ming
>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 03/19] ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation
  2026-01-06 16:32     ` Caleb Sander Mateos
@ 2026-01-07  0:15       ` Ming Lei
  2026-01-07  2:20         ` Caleb Sander Mateos
  0 siblings, 1 reply; 40+ messages in thread
From: Ming Lei @ 2026-01-07  0:15 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 06, 2026 at 08:32:08AM -0800, Caleb Sander Mateos wrote:
> On Tue, Jan 6, 2026 at 5:09 AM Ming Lei <ming.lei@redhat.com> wrote:
> >
> > On Mon, Jan 05, 2026 at 05:57:35PM -0700, Caleb Sander Mateos wrote:
> > > From: Stanley Zhang <stazhang@purestorage.com>
> > >
> > > Add a feature flag UBLK_F_INTEGRITY for a ublk server to request
> > > integrity/metadata support when creating a ublk device. The ublk server
> > > can also check for the feature flag on the created device or the result
> > > of UBLK_U_CMD_GET_FEATURES to tell if the ublk driver supports it.
> > > UBLK_F_INTEGRITY requires UBLK_F_USER_COPY, as user copy is the only
> > > data copy mode initially supported for integrity data.
> > > Add UBLK_PARAM_TYPE_INTEGRITY and struct ublk_param_integrity to struct
> > > ublk_params to specify the integrity params of a ublk device.
> > > UBLK_PARAM_TYPE_INTEGRITY requires UBLK_F_INTEGRITY and a nonzero
> > > metadata_size. The LBMD_PI_CAP_* and LBMD_PI_CSUM_* values from the
> > > linux/fs.h UAPI header are used for the flags and csum_type fields.
> > > If the UBLK_PARAM_TYPE_INTEGRITY flag is set, validate the integrity
> > > parameters and apply them to the blk_integrity limits.
> > > The struct ublk_param_integrity validations are based on the checks in
> > > blk_validate_integrity_limits(). Any invalid parameters should be
> > > rejected before being applied to struct blk_integrity.
> > >
> > > Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> > > [csander: drop redundant pi_tuple_size field, use block metadata UAPI
> > >  constants, add param validation]
> > > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > > ---
> > >  drivers/block/ublk_drv.c      | 94 ++++++++++++++++++++++++++++++++++-
> > >  include/uapi/linux/ublk_cmd.h | 18 +++++++
> > >  2 files changed, 111 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> > > index 8e3da9b2b93a..066c6ae062a0 100644
> > > --- a/drivers/block/ublk_drv.c
> > > +++ b/drivers/block/ublk_drv.c
> > > @@ -42,10 +42,12 @@
> > >  #include <linux/mm.h>
> > >  #include <asm/page.h>
> > >  #include <linux/task_work.h>
> > >  #include <linux/namei.h>
> > >  #include <linux/kref.h>
> > > +#include <linux/blk-integrity.h>
> > > +#include <uapi/linux/fs.h>
> > >  #include <uapi/linux/ublk_cmd.h>
> > >
> > >  #define UBLK_MINORS          (1U << MINORBITS)
> > >
> > >  #define UBLK_INVALID_BUF_IDX         ((u16)-1)
> > > @@ -81,11 +83,12 @@
> > >
> > >  /* All UBLK_PARAM_TYPE_* should be included here */
> > >  #define UBLK_PARAM_TYPE_ALL                                \
> > >       (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD | \
> > >        UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED |    \
> > > -      UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT)
> > > +      UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT | \
> > > +      UBLK_PARAM_TYPE_INTEGRITY)
> > >
> > >  struct ublk_uring_cmd_pdu {
> > >       /*
> > >        * Store requests in same batch temporarily for queuing them to
> > >        * daemon context.
> > > @@ -628,10 +631,57 @@ static void ublk_dev_param_basic_apply(struct ublk_device *ub)
> > >               set_disk_ro(ub->ub_disk, true);
> > >
> > >       set_capacity(ub->ub_disk, p->dev_sectors);
> > >  }
> > >
> > > +static int ublk_integrity_flags(u32 flags)
> > > +{
> > > +     int ret_flags = 0;
> > > +
> > > +     if (flags & LBMD_PI_CAP_INTEGRITY) {
> > > +             flags &= ~LBMD_PI_CAP_INTEGRITY;
> > > +             ret_flags |= BLK_INTEGRITY_DEVICE_CAPABLE;
> > > +     }
> > > +     if (flags & LBMD_PI_CAP_REFTAG) {
> > > +             flags &= ~LBMD_PI_CAP_REFTAG;
> > > +             ret_flags |= BLK_INTEGRITY_REF_TAG;
> > > +     }
> > > +     return flags ? -EINVAL : ret_flags;
> > > +}
> > > +
> > > +static int ublk_integrity_pi_tuple_size(u8 csum_type)
> > > +{
> > > +     switch (csum_type) {
> > > +     case LBMD_PI_CSUM_NONE:
> > > +             return 0;
> > > +     case LBMD_PI_CSUM_IP:
> > > +     case LBMD_PI_CSUM_CRC16_T10DIF:
> > > +             return 8;
> > > +     case LBMD_PI_CSUM_CRC64_NVME:
> > > +             return 16;
> > > +     default:
> > > +             return -EINVAL;
> > > +     }
> > > +}
> > > +
> > > +static enum blk_integrity_checksum ublk_integrity_csum_type(u8 csum_type)
> > > +{
> > > +     switch (csum_type) {
> > > +     case LBMD_PI_CSUM_NONE:
> > > +             return BLK_INTEGRITY_CSUM_NONE;
> > > +     case LBMD_PI_CSUM_IP:
> > > +             return BLK_INTEGRITY_CSUM_IP;
> > > +     case LBMD_PI_CSUM_CRC16_T10DIF:
> > > +             return BLK_INTEGRITY_CSUM_CRC;
> > > +     case LBMD_PI_CSUM_CRC64_NVME:
> > > +             return BLK_INTEGRITY_CSUM_CRC64;
> > > +     default:
> > > +             WARN_ON_ONCE(1);
> > > +             return BLK_INTEGRITY_CSUM_NONE;
> > > +     }
> > > +}
> > > +
> > >  static int ublk_validate_params(const struct ublk_device *ub)
> > >  {
> > >       /* basic param is the only one which must be set */
> > >       if (ub->params.types & UBLK_PARAM_TYPE_BASIC) {
> > >               const struct ublk_param_basic *p = &ub->params.basic;
> > > @@ -690,10 +740,33 @@ static int ublk_validate_params(const struct ublk_device *ub)
> > >                       return -EINVAL;
> > >               if (p->max_segment_size < UBLK_MIN_SEGMENT_SIZE)
> > >                       return -EINVAL;
> > >       }
> > >
> > > +     if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
> > > +             const struct ublk_param_integrity *p = &ub->params.integrity;
> > > +             int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
> > > +             int flags = ublk_integrity_flags(p->flags);
> > > +
> > > +             if (!(ub->dev_info.flags & UBLK_F_INTEGRITY))
> > > +                     return -EINVAL;
> > > +             if (flags < 0)
> > > +                     return flags;
> > > +             if (pi_tuple_size < 0)
> > > +                     return pi_tuple_size;
> > > +             if (!p->metadata_size)
> > > +                     return -EINVAL;
> > > +             if (p->csum_type == LBMD_PI_CSUM_NONE &&
> > > +                 p->flags & LBMD_PI_CAP_REFTAG)
> > > +                     return -EINVAL;
> > > +             if (p->pi_offset + pi_tuple_size > p->metadata_size)
> > > +                     return -EINVAL;
> > > +             if (p->interval_exp < SECTOR_SHIFT ||
> > > +                 p->interval_exp > ub->params.basic.logical_bs_shift)
> > > +                     return -EINVAL;
> > > +     }
> > > +
> > >       return 0;
> > >  }
> > >
> > >  static void ublk_apply_params(struct ublk_device *ub)
> > >  {
> > > @@ -2941,10 +3014,25 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub,
> > >               lim.seg_boundary_mask = ub->params.seg.seg_boundary_mask;
> > >               lim.max_segment_size = ub->params.seg.max_segment_size;
> > >               lim.max_segments = ub->params.seg.max_segments;
> > >       }
> > >
> > > +     if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
> > > +             const struct ublk_param_integrity *p = &ub->params.integrity;
> > > +             int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
> > > +
> > > +             lim.integrity = (struct blk_integrity) {
> > > +                     .flags = ublk_integrity_flags(p->flags),
> > > +                     .csum_type = ublk_integrity_csum_type(p->csum_type),
> > > +                     .metadata_size = p->metadata_size,
> > > +                     .pi_offset = p->pi_offset,
> > > +                     .interval_exp = p->interval_exp,
> > > +                     .tag_size = p->tag_size,
> > > +                     .pi_tuple_size = pi_tuple_size,
> > > +             };
> > > +     }
> > > +
> > >       if (wait_for_completion_interruptible(&ub->completion) != 0)
> > >               return -EINTR;
> > >
> > >       if (ub->ublksrv_tgid != ublksrv_pid)
> > >               return -EINVAL;
> > > @@ -3131,10 +3219,14 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
> > >               if (info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
> > >                                       UBLK_F_AUTO_BUF_REG))
> > >                       return -EINVAL;
> > >       }
> > >
> > > +     /* User copy is required to access integrity buffer */
> > > +     if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY))
> > > +             return -EINVAL;
> > > +
> > >       /* the created device is always owned by current user */
> > >       ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
> > >
> > >       if (header->dev_id != info.dev_id) {
> > >               pr_warn("%s: dev id not match %u %u\n",
> > > diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
> > > index ec77dabba45b..a54c47832fa2 100644
> > > --- a/include/uapi/linux/ublk_cmd.h
> > > +++ b/include/uapi/linux/ublk_cmd.h
> > > @@ -309,10 +309,16 @@
> > >   * 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)
> > >
> > > +/*
> > > + * ublk device supports requests with integrity/metadata buffer.
> > > + * Requires UBLK_F_USER_COPY.
> > > + */
> > > +#define UBLK_F_INTEGRITY (1ULL << 16)
> > > +
> > >  /* device state */
> > >  #define UBLK_S_DEV_DEAD      0
> > >  #define UBLK_S_DEV_LIVE      1
> > >  #define UBLK_S_DEV_QUIESCED  2
> > >  #define UBLK_S_DEV_FAIL_IO   3
> > > @@ -598,10 +604,20 @@ struct ublk_param_segment {
> > >       __u32   max_segment_size;
> > >       __u16   max_segments;
> > >       __u8    pad[2];
> > >  };
> > >
> > > +struct ublk_param_integrity {
> > > +     __u32   flags; /* LBMD_PI_CAP_* from linux/fs.h */
> > > +     __u8    interval_exp;
> > > +     __u8    metadata_size; /* UBLK_PARAM_TYPE_INTEGRITY requires nonzero */
> > > +     __u8    pi_offset;
> > > +     __u8    csum_type; /* LBMD_PI_CSUM_* from linux/fs.h */
> > > +     __u8    tag_size;
> > > +     __u8    pad[7];
> > > +};
> >
> > Looks max_integrity_segments is missed, otherwise this patch is fine for me.
> 
> My thinking was that there isn't any reason why a ublk server would
> need to limit the number of integrity segments. The request integrity
> segments aren't directly exposed to the ublk server through user copy.
> And since zero copy isn't supported for integrity data, there's no
> concern about needing to align the ublk integrity limits with the
> backing device limits. What do you think?

Yeah, it isn't needed in current implementation, but things may change in
future.

How about adding it(__u16) to `ublk_param_integrity`? And document it is ignored for
user-copy based implementation. It is usually part of HW/SW interface wrt. PI.

Thanks,
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 19/19] selftests: ublk: add end-to-end integrity test
  2026-01-06 17:15     ` Caleb Sander Mateos
@ 2026-01-07  0:21       ` Ming Lei
  2026-01-07  1:32         ` Caleb Sander Mateos
  0 siblings, 1 reply; 40+ messages in thread
From: Ming Lei @ 2026-01-07  0:21 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 06, 2026 at 09:15:44AM -0800, Caleb Sander Mateos wrote:
> On Tue, Jan 6, 2026 at 6:10 AM Ming Lei <ming.lei@redhat.com> wrote:
> >
> > On Mon, Jan 05, 2026 at 05:57:51PM -0700, Caleb Sander Mateos wrote:
> > > Add test case loop_08 to verify the ublk integrity data flow. It uses
> > > the kublk loop target to create a ublk device with integrity on top of
> > > backing data and integrity files. It then writes to the whole device
> > > with fio configured to generate integrity data. Then it reads back the
> > > whole device with fio configured to verify the integrity data.
> > > It also verifies that injected guard, reftag, and apptag corruptions are
> > > correctly detected.
> > >
> > > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > > ---
> > >  tools/testing/selftests/ublk/Makefile        |   1 +
> > >  tools/testing/selftests/ublk/test_loop_08.sh | 111 +++++++++++++++++++
> > >  2 files changed, 112 insertions(+)
> > >  create mode 100755 tools/testing/selftests/ublk/test_loop_08.sh
> > >
> > > diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
> > > index bfd68ae64142..ab745443fd58 100644
> > > --- a/tools/testing/selftests/ublk/Makefile
> > > +++ b/tools/testing/selftests/ublk/Makefile
> > > @@ -33,10 +33,11 @@ TEST_PROGS += test_loop_02.sh
> > >  TEST_PROGS += test_loop_03.sh
> > >  TEST_PROGS += test_loop_04.sh
> > >  TEST_PROGS += test_loop_05.sh
> > >  TEST_PROGS += test_loop_06.sh
> > >  TEST_PROGS += test_loop_07.sh
> > > +TEST_PROGS += test_loop_08.sh
> > >  TEST_PROGS += test_stripe_01.sh
> > >  TEST_PROGS += test_stripe_02.sh
> > >  TEST_PROGS += test_stripe_03.sh
> > >  TEST_PROGS += test_stripe_04.sh
> > >  TEST_PROGS += test_stripe_05.sh
> > > diff --git a/tools/testing/selftests/ublk/test_loop_08.sh b/tools/testing/selftests/ublk/test_loop_08.sh
> > > new file mode 100755
> > > index 000000000000..ca289cfb2ad4
> > > --- /dev/null
> > > +++ b/tools/testing/selftests/ublk/test_loop_08.sh
> > > @@ -0,0 +1,111 @@
> > > +#!/bin/bash
> > > +# SPDX-License-Identifier: GPL-2.0
> > > +
> > > +. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
> > > +
> > > +if ! _have_program fio; then
> > > +     exit $UBLK_SKIP_CODE
> > > +fi
> > > +
> > > +fio_version=$(fio --version)
> > > +if [[ "$fio_version" =~ fio-[0-9]+\.[0-9]+$ ]]; then
> > > +     echo "Requires development fio version with https://github.com/axboe/fio/pull/1992"
> > > +     exit $UBLK_SKIP_CODE
> > > +fi
> > > +
> > > +TID=loop_08
> > > +
> > > +_prep_test "loop" "end-to-end integrity"
> > > +
> > > +_create_backfile 0 256M
> > > +_create_backfile 1 32M # 256M * (64 integrity bytes / 512 data bytes)
> > > +integrity_params="--integrity_capable --integrity_reftag
> > > +                  --metadata_size 64 --pi_offset 56 --csum_type t10dif"
> > > +dev_id=$(_add_ublk_dev -t loop -u $integrity_params "${UBLK_BACKFILES[@]}")
> >
> > I tried above setting:
> >
> > ./kublk add -t loop --integrity_capable --integrity_reftag --metadata_size 64 --pi_offset 56 --csum_type t10dif --foreground -u /dev/sdb /dev/sdc
> > dev id 1: nr_hw_queues 2 queue_depth 128 block size 512 dev_capacity 8388608
> >         max rq size 1048576 daemon pid 38295 flags 0x160c2 state LIVE
> >         queue 0: affinity(0 )
> >         queue 1: affinity(8 )
> >
> > However, IO error is always triggered:
> >
> > [ 9202.316382] ublkb1: ref tag error at location 0 (rcvd 128)
> > [ 9202.317171] Buffer I/O error on dev ublkb1, logical block 0, async page read
> 
> Hmm, what are the initial contents of /dev/sdc? It looks like they are
> nonzero, as the reftag being read for logical block 0 is 128 rather
> than the expected 0 (the reftag would be read from bytes 60 to 63 of
> /dev/sdc). In general, though, the partition scan may be expected to
> fail the bio-integrity-auto checks if the integrity data hasn't been
> initialized. I don't think this is an issue, since the partition scan
> is looking for a partition table but there's no guarantee that one
> exists.
> You can disable the kernel integrity checks if you want by writing 0
> to /sys/block/ublkb1/integrity/read_verify. However, I'm not sure it's
> possible to do this soon enough to take effect before the partition
> scan.
> We could also use the UBLK_F_NO_AUTO_PART_SCAN feature, once it lands,
> to suppress the partition scan and these error messages.

UBLK_F_NO_AUTO_PART_SCAN can't avoid the following read failure.

I guess the issue can be avoided by adding small superblock to the meta
data file, then format it in the 1st time when superblock doesn't exist.

This way will make it usable from test/verify purpose.


Thanks,
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 09/19] ublk: implement integrity user copy
  2026-01-06 18:20     ` Caleb Sander Mateos
@ 2026-01-07  0:28       ` Ming Lei
  2026-01-08  1:50         ` Caleb Sander Mateos
  0 siblings, 1 reply; 40+ messages in thread
From: Ming Lei @ 2026-01-07  0:28 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 06, 2026 at 10:20:14AM -0800, Caleb Sander Mateos wrote:
> On Tue, Jan 6, 2026 at 5:34 AM Ming Lei <ming.lei@redhat.com> wrote:
> >
> > On Mon, Jan 05, 2026 at 05:57:41PM -0700, Caleb Sander Mateos wrote:
> > > From: Stanley Zhang <stazhang@purestorage.com>
> > >
> > > Add a function ublk_copy_user_integrity() to copy integrity information
> > > between a request and a user iov_iter. This mirrors the existing
> > > ublk_copy_user_pages() but operates on request integrity data instead of
> > > regular data. Check UBLKSRV_IO_INTEGRITY_FLAG in iocb->ki_pos in
> > > ublk_user_copy() to choose between copying data or integrity data.
> > >
> > > Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> > > [csander: change offset units from data bytes to integrity data bytes,
> > >  test UBLKSRV_IO_INTEGRITY_FLAG after subtracting UBLKSRV_IO_BUF_OFFSET,
> > >  fix CONFIG_BLK_DEV_INTEGRITY=n build,
> > >  rebase on ublk user copy refactor]
> > > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > > ---
> > >  drivers/block/ublk_drv.c      | 52 +++++++++++++++++++++++++++++++++--
> > >  include/uapi/linux/ublk_cmd.h |  4 +++
> > >  2 files changed, 53 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> > > index e44ab9981ef4..9694a4c1caa7 100644
> > > --- a/drivers/block/ublk_drv.c
> > > +++ b/drivers/block/ublk_drv.c
> > > @@ -621,10 +621,15 @@ static inline unsigned ublk_pos_to_tag(loff_t pos)
> > >  {
> > >       return ((pos - UBLKSRV_IO_BUF_OFFSET) >> UBLK_TAG_OFF) &
> > >               UBLK_TAG_BITS_MASK;
> > >  }
> > >
> > > +static inline bool ublk_pos_is_integrity(loff_t pos)
> > > +{
> > > +     return !!((pos - UBLKSRV_IO_BUF_OFFSET) & UBLKSRV_IO_INTEGRITY_FLAG);
> > > +}
> > > +
> >
> > It could be more readable to check UBLKSRV_IO_INTEGRITY_FLAG only.
> 
> That's assuming that UBLK_TAG_BITS = 16 has more bits than are
> strictly required by UBLK_MAX_QUEUE_DEPTH = 4096? Otherwise, adding
> UBLKSRV_IO_BUF_OFFSET = 1 << 31 to tag << UBLK_TAG_OFF could overflow
> into the QID bits, which could then overflow into
> UBLKSRV_IO_INTEGRITY_FLAG. That seems like a very fragile assumption.
> And if you want to rely on this assumption, why bother subtracting
> UBLKSRV_IO_BUF_OFFSET in ublk_pos_to_hwq() either? The compiler should
> easily be able to deduplicate the iocb->ki_pos - UBLKSRV_IO_BUF_OFFSET
> computations, so I can't imagine it matters for performance.

UBLKSRV_IO_INTEGRITY_FLAG should be defined as one flag starting from top
bit(bit 62), then you will see it is just fine to check it directly.

But it isn't a big deal to subtract UBLKSRV_IO_BUF_OFFSET or not here, I
will leave it to you.

> 
> >
> > >  static void ublk_dev_param_basic_apply(struct ublk_device *ub)
> > >  {
> > >       const struct ublk_param_basic *p = &ub->params.basic;
> > >
> > >       if (p->attrs & UBLK_ATTR_READ_ONLY)
> > > @@ -1047,10 +1052,37 @@ static size_t ublk_copy_user_pages(const struct request *req,
> > >                       break;
> > >       }
> > >       return done;
> > >  }
> > >
> > > +#ifdef CONFIG_BLK_DEV_INTEGRITY
> > > +static size_t ublk_copy_user_integrity(const struct request *req,
> > > +             unsigned offset, struct iov_iter *uiter, int dir)
> > > +{
> > > +     size_t done = 0;
> > > +     struct bio *bio = req->bio;
> > > +     struct bvec_iter iter;
> > > +     struct bio_vec iv;
> > > +
> > > +     if (!blk_integrity_rq(req))
> > > +             return 0;
> > > +
> > > +     bio_for_each_integrity_vec(iv, bio, iter) {
> > > +             if (!ublk_copy_user_bvec(&iv, &offset, uiter, dir, &done))
> > > +                     break;
> > > +     }
> > > +
> > > +     return done;
> > > +}
> > > +#else /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
> > > +static size_t ublk_copy_user_integrity(const struct request *req,
> > > +             unsigned offset, struct iov_iter *uiter, int dir)
> > > +{
> > > +     return 0;
> > > +}
> > > +#endif /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
> > > +
> > >  static inline bool ublk_need_map_req(const struct request *req)
> > >  {
> > >       return ublk_rq_has_data(req) && req_op(req) == REQ_OP_WRITE;
> > >  }
> > >
> > > @@ -2654,10 +2686,12 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
> > >  {
> > >       struct ublk_device *ub = iocb->ki_filp->private_data;
> > >       struct ublk_queue *ubq;
> > >       struct request *req;
> > >       struct ublk_io *io;
> > > +     unsigned data_len;
> > > +     bool is_integrity;
> > >       size_t buf_off;
> > >       u16 tag, q_id;
> > >       ssize_t ret;
> > >
> > >       if (!user_backed_iter(iter))
> > > @@ -2667,10 +2701,11 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
> > >               return -EACCES;
> > >
> > >       tag = ublk_pos_to_tag(iocb->ki_pos);
> > >       q_id = ublk_pos_to_hwq(iocb->ki_pos);
> > >       buf_off = ublk_pos_to_buf_off(iocb->ki_pos);
> > > +     is_integrity = ublk_pos_is_integrity(iocb->ki_pos);
> >
> > UBLKSRV_IO_INTEGRITY_FLAG can be set for device without UBLK_F_INTEGRITY,
> > so UBLK_F_INTEGRITY need to be checked in case of `is_integrity`.
> 
> If UBLK_F_INTEGRITY isn't set, then UBLK_PARAM_TYPE_INTEGRITY isn't
> allowed, so the ublk device won't support integrity data. Therefore,
> blk_integrity_rq() will return false and ublk_copy_user_integrity()
> will just return 0. Do you think it's important to return some error
> code value instead? I would rather avoid the additional checks in the
> hot path.

The check could be zero cost, but better to fail the wrong usage than
returning 0 silently, which may often imply big issue.


Thanks, 
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 19/19] selftests: ublk: add end-to-end integrity test
  2026-01-07  0:21       ` Ming Lei
@ 2026-01-07  1:32         ` Caleb Sander Mateos
  2026-01-07  1:49           ` Ming Lei
  0 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-07  1:32 UTC (permalink / raw)
  To: Ming Lei
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 6, 2026 at 4:21 PM Ming Lei <ming.lei@redhat.com> wrote:
>
> On Tue, Jan 06, 2026 at 09:15:44AM -0800, Caleb Sander Mateos wrote:
> > On Tue, Jan 6, 2026 at 6:10 AM Ming Lei <ming.lei@redhat.com> wrote:
> > >
> > > On Mon, Jan 05, 2026 at 05:57:51PM -0700, Caleb Sander Mateos wrote:
> > > > Add test case loop_08 to verify the ublk integrity data flow. It uses
> > > > the kublk loop target to create a ublk device with integrity on top of
> > > > backing data and integrity files. It then writes to the whole device
> > > > with fio configured to generate integrity data. Then it reads back the
> > > > whole device with fio configured to verify the integrity data.
> > > > It also verifies that injected guard, reftag, and apptag corruptions are
> > > > correctly detected.
> > > >
> > > > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > > > ---
> > > >  tools/testing/selftests/ublk/Makefile        |   1 +
> > > >  tools/testing/selftests/ublk/test_loop_08.sh | 111 +++++++++++++++++++
> > > >  2 files changed, 112 insertions(+)
> > > >  create mode 100755 tools/testing/selftests/ublk/test_loop_08.sh
> > > >
> > > > diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
> > > > index bfd68ae64142..ab745443fd58 100644
> > > > --- a/tools/testing/selftests/ublk/Makefile
> > > > +++ b/tools/testing/selftests/ublk/Makefile
> > > > @@ -33,10 +33,11 @@ TEST_PROGS += test_loop_02.sh
> > > >  TEST_PROGS += test_loop_03.sh
> > > >  TEST_PROGS += test_loop_04.sh
> > > >  TEST_PROGS += test_loop_05.sh
> > > >  TEST_PROGS += test_loop_06.sh
> > > >  TEST_PROGS += test_loop_07.sh
> > > > +TEST_PROGS += test_loop_08.sh
> > > >  TEST_PROGS += test_stripe_01.sh
> > > >  TEST_PROGS += test_stripe_02.sh
> > > >  TEST_PROGS += test_stripe_03.sh
> > > >  TEST_PROGS += test_stripe_04.sh
> > > >  TEST_PROGS += test_stripe_05.sh
> > > > diff --git a/tools/testing/selftests/ublk/test_loop_08.sh b/tools/testing/selftests/ublk/test_loop_08.sh
> > > > new file mode 100755
> > > > index 000000000000..ca289cfb2ad4
> > > > --- /dev/null
> > > > +++ b/tools/testing/selftests/ublk/test_loop_08.sh
> > > > @@ -0,0 +1,111 @@
> > > > +#!/bin/bash
> > > > +# SPDX-License-Identifier: GPL-2.0
> > > > +
> > > > +. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
> > > > +
> > > > +if ! _have_program fio; then
> > > > +     exit $UBLK_SKIP_CODE
> > > > +fi
> > > > +
> > > > +fio_version=$(fio --version)
> > > > +if [[ "$fio_version" =~ fio-[0-9]+\.[0-9]+$ ]]; then
> > > > +     echo "Requires development fio version with https://github.com/axboe/fio/pull/1992"
> > > > +     exit $UBLK_SKIP_CODE
> > > > +fi
> > > > +
> > > > +TID=loop_08
> > > > +
> > > > +_prep_test "loop" "end-to-end integrity"
> > > > +
> > > > +_create_backfile 0 256M
> > > > +_create_backfile 1 32M # 256M * (64 integrity bytes / 512 data bytes)
> > > > +integrity_params="--integrity_capable --integrity_reftag
> > > > +                  --metadata_size 64 --pi_offset 56 --csum_type t10dif"
> > > > +dev_id=$(_add_ublk_dev -t loop -u $integrity_params "${UBLK_BACKFILES[@]}")
> > >
> > > I tried above setting:
> > >
> > > ./kublk add -t loop --integrity_capable --integrity_reftag --metadata_size 64 --pi_offset 56 --csum_type t10dif --foreground -u /dev/sdb /dev/sdc
> > > dev id 1: nr_hw_queues 2 queue_depth 128 block size 512 dev_capacity 8388608
> > >         max rq size 1048576 daemon pid 38295 flags 0x160c2 state LIVE
> > >         queue 0: affinity(0 )
> > >         queue 1: affinity(8 )
> > >
> > > However, IO error is always triggered:
> > >
> > > [ 9202.316382] ublkb1: ref tag error at location 0 (rcvd 128)
> > > [ 9202.317171] Buffer I/O error on dev ublkb1, logical block 0, async page read
> >
> > Hmm, what are the initial contents of /dev/sdc? It looks like they are
> > nonzero, as the reftag being read for logical block 0 is 128 rather
> > than the expected 0 (the reftag would be read from bytes 60 to 63 of
> > /dev/sdc). In general, though, the partition scan may be expected to
> > fail the bio-integrity-auto checks if the integrity data hasn't been
> > initialized. I don't think this is an issue, since the partition scan
> > is looking for a partition table but there's no guarantee that one
> > exists.
> > You can disable the kernel integrity checks if you want by writing 0
> > to /sys/block/ublkb1/integrity/read_verify. However, I'm not sure it's
> > possible to do this soon enough to take effect before the partition
> > scan.
> > We could also use the UBLK_F_NO_AUTO_PART_SCAN feature, once it lands,
> > to suppress the partition scan and these error messages.
>
> UBLK_F_NO_AUTO_PART_SCAN can't avoid the following read failure.

Are you saying the reads aren't coming from the partition scan? What
else would be issuing reads to the ublk device before fio is run on
it?

>
> I guess the issue can be avoided by adding small superblock to the meta
> data file, then format it in the 1st time when superblock doesn't exist.

Not sure the complexity of a superblock is necessary. Could just
initialize all the protection information with valid reftags and guard
tags.

>
> This way will make it usable from test/verify purpose.

I'm still not following why it's not "usable" currently. Only blocks
that have been written to (with protection information generated by
fio or the kernel) can be read back, but that's how block devices are
expected to be used. It seems fine to me for test purposes.
test_loop_08.sh writes to the full ublk device before verifying it, so
it shouldn't encounter any invalid reftags.

Best,
Caleb

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 19/19] selftests: ublk: add end-to-end integrity test
  2026-01-07  1:32         ` Caleb Sander Mateos
@ 2026-01-07  1:49           ` Ming Lei
  0 siblings, 0 replies; 40+ messages in thread
From: Ming Lei @ 2026-01-07  1:49 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 06, 2026 at 05:32:29PM -0800, Caleb Sander Mateos wrote:
> On Tue, Jan 6, 2026 at 4:21 PM Ming Lei <ming.lei@redhat.com> wrote:
> >
> > On Tue, Jan 06, 2026 at 09:15:44AM -0800, Caleb Sander Mateos wrote:
> > > On Tue, Jan 6, 2026 at 6:10 AM Ming Lei <ming.lei@redhat.com> wrote:
> > > >
> > > > On Mon, Jan 05, 2026 at 05:57:51PM -0700, Caleb Sander Mateos wrote:
> > > > > Add test case loop_08 to verify the ublk integrity data flow. It uses
> > > > > the kublk loop target to create a ublk device with integrity on top of
> > > > > backing data and integrity files. It then writes to the whole device
> > > > > with fio configured to generate integrity data. Then it reads back the
> > > > > whole device with fio configured to verify the integrity data.
> > > > > It also verifies that injected guard, reftag, and apptag corruptions are
> > > > > correctly detected.
> > > > >
> > > > > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > > > > ---
> > > > >  tools/testing/selftests/ublk/Makefile        |   1 +
> > > > >  tools/testing/selftests/ublk/test_loop_08.sh | 111 +++++++++++++++++++
> > > > >  2 files changed, 112 insertions(+)
> > > > >  create mode 100755 tools/testing/selftests/ublk/test_loop_08.sh
> > > > >
> > > > > diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
> > > > > index bfd68ae64142..ab745443fd58 100644
> > > > > --- a/tools/testing/selftests/ublk/Makefile
> > > > > +++ b/tools/testing/selftests/ublk/Makefile
> > > > > @@ -33,10 +33,11 @@ TEST_PROGS += test_loop_02.sh
> > > > >  TEST_PROGS += test_loop_03.sh
> > > > >  TEST_PROGS += test_loop_04.sh
> > > > >  TEST_PROGS += test_loop_05.sh
> > > > >  TEST_PROGS += test_loop_06.sh
> > > > >  TEST_PROGS += test_loop_07.sh
> > > > > +TEST_PROGS += test_loop_08.sh
> > > > >  TEST_PROGS += test_stripe_01.sh
> > > > >  TEST_PROGS += test_stripe_02.sh
> > > > >  TEST_PROGS += test_stripe_03.sh
> > > > >  TEST_PROGS += test_stripe_04.sh
> > > > >  TEST_PROGS += test_stripe_05.sh
> > > > > diff --git a/tools/testing/selftests/ublk/test_loop_08.sh b/tools/testing/selftests/ublk/test_loop_08.sh
> > > > > new file mode 100755
> > > > > index 000000000000..ca289cfb2ad4
> > > > > --- /dev/null
> > > > > +++ b/tools/testing/selftests/ublk/test_loop_08.sh
> > > > > @@ -0,0 +1,111 @@
> > > > > +#!/bin/bash
> > > > > +# SPDX-License-Identifier: GPL-2.0
> > > > > +
> > > > > +. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
> > > > > +
> > > > > +if ! _have_program fio; then
> > > > > +     exit $UBLK_SKIP_CODE
> > > > > +fi
> > > > > +
> > > > > +fio_version=$(fio --version)
> > > > > +if [[ "$fio_version" =~ fio-[0-9]+\.[0-9]+$ ]]; then
> > > > > +     echo "Requires development fio version with https://github.com/axboe/fio/pull/1992"
> > > > > +     exit $UBLK_SKIP_CODE
> > > > > +fi
> > > > > +
> > > > > +TID=loop_08
> > > > > +
> > > > > +_prep_test "loop" "end-to-end integrity"
> > > > > +
> > > > > +_create_backfile 0 256M
> > > > > +_create_backfile 1 32M # 256M * (64 integrity bytes / 512 data bytes)
> > > > > +integrity_params="--integrity_capable --integrity_reftag
> > > > > +                  --metadata_size 64 --pi_offset 56 --csum_type t10dif"
> > > > > +dev_id=$(_add_ublk_dev -t loop -u $integrity_params "${UBLK_BACKFILES[@]}")
> > > >
> > > > I tried above setting:
> > > >
> > > > ./kublk add -t loop --integrity_capable --integrity_reftag --metadata_size 64 --pi_offset 56 --csum_type t10dif --foreground -u /dev/sdb /dev/sdc
> > > > dev id 1: nr_hw_queues 2 queue_depth 128 block size 512 dev_capacity 8388608
> > > >         max rq size 1048576 daemon pid 38295 flags 0x160c2 state LIVE
> > > >         queue 0: affinity(0 )
> > > >         queue 1: affinity(8 )
> > > >
> > > > However, IO error is always triggered:
> > > >
> > > > [ 9202.316382] ublkb1: ref tag error at location 0 (rcvd 128)
> > > > [ 9202.317171] Buffer I/O error on dev ublkb1, logical block 0, async page read
> > >
> > > Hmm, what are the initial contents of /dev/sdc? It looks like they are
> > > nonzero, as the reftag being read for logical block 0 is 128 rather
> > > than the expected 0 (the reftag would be read from bytes 60 to 63 of
> > > /dev/sdc). In general, though, the partition scan may be expected to
> > > fail the bio-integrity-auto checks if the integrity data hasn't been
> > > initialized. I don't think this is an issue, since the partition scan
> > > is looking for a partition table but there's no guarantee that one
> > > exists.
> > > You can disable the kernel integrity checks if you want by writing 0
> > > to /sys/block/ublkb1/integrity/read_verify. However, I'm not sure it's
> > > possible to do this soon enough to take effect before the partition
> > > scan.
> > > We could also use the UBLK_F_NO_AUTO_PART_SCAN feature, once it lands,
> > > to suppress the partition scan and these error messages.
> >
> > UBLK_F_NO_AUTO_PART_SCAN can't avoid the following read failure.
> 
> Are you saying the reads aren't coming from the partition scan? What
> else would be issuing reads to the ublk device before fio is run on
> it?

I mean people can run fio read after disk is setup, or udev may read disk
in background.

> 
> >
> > I guess the issue can be avoided by adding small superblock to the meta
> > data file, then format it in the 1st time when superblock doesn't exist.
> 
> Not sure the complexity of a superblock is necessary. Could just
> initialize all the protection information with valid reftags and guard
> tags.

For verification purpose, the superblock can include only the info if PI meta
file is formatted or not. But it isn't a big deal if the meta backfile can
be formatted with tags in the specified setting.

> 
> >
> > This way will make it usable from test/verify purpose.
> 
> I'm still not following why it's not "usable" currently. Only blocks
> that have been written to (with protection information generated by
> fio or the kernel) can be read back, but that's how block devices are
> expected to be used. It seems fine to me for test purposes.
> test_loop_08.sh writes to the full ublk device before verifying it, so
> it shouldn't encounter any invalid reftags.

It triggers READ error easily, and will cause noisy report, just like
what I did...

Also you can't run FS test workload...


Thanks,
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 03/19] ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation
  2026-01-07  0:15       ` Ming Lei
@ 2026-01-07  2:20         ` Caleb Sander Mateos
  0 siblings, 0 replies; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-07  2:20 UTC (permalink / raw)
  To: Ming Lei
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 6, 2026 at 4:15 PM Ming Lei <ming.lei@redhat.com> wrote:
>
> On Tue, Jan 06, 2026 at 08:32:08AM -0800, Caleb Sander Mateos wrote:
> > On Tue, Jan 6, 2026 at 5:09 AM Ming Lei <ming.lei@redhat.com> wrote:
> > >
> > > On Mon, Jan 05, 2026 at 05:57:35PM -0700, Caleb Sander Mateos wrote:
> > > > From: Stanley Zhang <stazhang@purestorage.com>
> > > >
> > > > Add a feature flag UBLK_F_INTEGRITY for a ublk server to request
> > > > integrity/metadata support when creating a ublk device. The ublk server
> > > > can also check for the feature flag on the created device or the result
> > > > of UBLK_U_CMD_GET_FEATURES to tell if the ublk driver supports it.
> > > > UBLK_F_INTEGRITY requires UBLK_F_USER_COPY, as user copy is the only
> > > > data copy mode initially supported for integrity data.
> > > > Add UBLK_PARAM_TYPE_INTEGRITY and struct ublk_param_integrity to struct
> > > > ublk_params to specify the integrity params of a ublk device.
> > > > UBLK_PARAM_TYPE_INTEGRITY requires UBLK_F_INTEGRITY and a nonzero
> > > > metadata_size. The LBMD_PI_CAP_* and LBMD_PI_CSUM_* values from the
> > > > linux/fs.h UAPI header are used for the flags and csum_type fields.
> > > > If the UBLK_PARAM_TYPE_INTEGRITY flag is set, validate the integrity
> > > > parameters and apply them to the blk_integrity limits.
> > > > The struct ublk_param_integrity validations are based on the checks in
> > > > blk_validate_integrity_limits(). Any invalid parameters should be
> > > > rejected before being applied to struct blk_integrity.
> > > >
> > > > Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> > > > [csander: drop redundant pi_tuple_size field, use block metadata UAPI
> > > >  constants, add param validation]
> > > > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > > > ---
> > > >  drivers/block/ublk_drv.c      | 94 ++++++++++++++++++++++++++++++++++-
> > > >  include/uapi/linux/ublk_cmd.h | 18 +++++++
> > > >  2 files changed, 111 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> > > > index 8e3da9b2b93a..066c6ae062a0 100644
> > > > --- a/drivers/block/ublk_drv.c
> > > > +++ b/drivers/block/ublk_drv.c
> > > > @@ -42,10 +42,12 @@
> > > >  #include <linux/mm.h>
> > > >  #include <asm/page.h>
> > > >  #include <linux/task_work.h>
> > > >  #include <linux/namei.h>
> > > >  #include <linux/kref.h>
> > > > +#include <linux/blk-integrity.h>
> > > > +#include <uapi/linux/fs.h>
> > > >  #include <uapi/linux/ublk_cmd.h>
> > > >
> > > >  #define UBLK_MINORS          (1U << MINORBITS)
> > > >
> > > >  #define UBLK_INVALID_BUF_IDX         ((u16)-1)
> > > > @@ -81,11 +83,12 @@
> > > >
> > > >  /* All UBLK_PARAM_TYPE_* should be included here */
> > > >  #define UBLK_PARAM_TYPE_ALL                                \
> > > >       (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD | \
> > > >        UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED |    \
> > > > -      UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT)
> > > > +      UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT | \
> > > > +      UBLK_PARAM_TYPE_INTEGRITY)
> > > >
> > > >  struct ublk_uring_cmd_pdu {
> > > >       /*
> > > >        * Store requests in same batch temporarily for queuing them to
> > > >        * daemon context.
> > > > @@ -628,10 +631,57 @@ static void ublk_dev_param_basic_apply(struct ublk_device *ub)
> > > >               set_disk_ro(ub->ub_disk, true);
> > > >
> > > >       set_capacity(ub->ub_disk, p->dev_sectors);
> > > >  }
> > > >
> > > > +static int ublk_integrity_flags(u32 flags)
> > > > +{
> > > > +     int ret_flags = 0;
> > > > +
> > > > +     if (flags & LBMD_PI_CAP_INTEGRITY) {
> > > > +             flags &= ~LBMD_PI_CAP_INTEGRITY;
> > > > +             ret_flags |= BLK_INTEGRITY_DEVICE_CAPABLE;
> > > > +     }
> > > > +     if (flags & LBMD_PI_CAP_REFTAG) {
> > > > +             flags &= ~LBMD_PI_CAP_REFTAG;
> > > > +             ret_flags |= BLK_INTEGRITY_REF_TAG;
> > > > +     }
> > > > +     return flags ? -EINVAL : ret_flags;
> > > > +}
> > > > +
> > > > +static int ublk_integrity_pi_tuple_size(u8 csum_type)
> > > > +{
> > > > +     switch (csum_type) {
> > > > +     case LBMD_PI_CSUM_NONE:
> > > > +             return 0;
> > > > +     case LBMD_PI_CSUM_IP:
> > > > +     case LBMD_PI_CSUM_CRC16_T10DIF:
> > > > +             return 8;
> > > > +     case LBMD_PI_CSUM_CRC64_NVME:
> > > > +             return 16;
> > > > +     default:
> > > > +             return -EINVAL;
> > > > +     }
> > > > +}
> > > > +
> > > > +static enum blk_integrity_checksum ublk_integrity_csum_type(u8 csum_type)
> > > > +{
> > > > +     switch (csum_type) {
> > > > +     case LBMD_PI_CSUM_NONE:
> > > > +             return BLK_INTEGRITY_CSUM_NONE;
> > > > +     case LBMD_PI_CSUM_IP:
> > > > +             return BLK_INTEGRITY_CSUM_IP;
> > > > +     case LBMD_PI_CSUM_CRC16_T10DIF:
> > > > +             return BLK_INTEGRITY_CSUM_CRC;
> > > > +     case LBMD_PI_CSUM_CRC64_NVME:
> > > > +             return BLK_INTEGRITY_CSUM_CRC64;
> > > > +     default:
> > > > +             WARN_ON_ONCE(1);
> > > > +             return BLK_INTEGRITY_CSUM_NONE;
> > > > +     }
> > > > +}
> > > > +
> > > >  static int ublk_validate_params(const struct ublk_device *ub)
> > > >  {
> > > >       /* basic param is the only one which must be set */
> > > >       if (ub->params.types & UBLK_PARAM_TYPE_BASIC) {
> > > >               const struct ublk_param_basic *p = &ub->params.basic;
> > > > @@ -690,10 +740,33 @@ static int ublk_validate_params(const struct ublk_device *ub)
> > > >                       return -EINVAL;
> > > >               if (p->max_segment_size < UBLK_MIN_SEGMENT_SIZE)
> > > >                       return -EINVAL;
> > > >       }
> > > >
> > > > +     if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
> > > > +             const struct ublk_param_integrity *p = &ub->params.integrity;
> > > > +             int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
> > > > +             int flags = ublk_integrity_flags(p->flags);
> > > > +
> > > > +             if (!(ub->dev_info.flags & UBLK_F_INTEGRITY))
> > > > +                     return -EINVAL;
> > > > +             if (flags < 0)
> > > > +                     return flags;
> > > > +             if (pi_tuple_size < 0)
> > > > +                     return pi_tuple_size;
> > > > +             if (!p->metadata_size)
> > > > +                     return -EINVAL;
> > > > +             if (p->csum_type == LBMD_PI_CSUM_NONE &&
> > > > +                 p->flags & LBMD_PI_CAP_REFTAG)
> > > > +                     return -EINVAL;
> > > > +             if (p->pi_offset + pi_tuple_size > p->metadata_size)
> > > > +                     return -EINVAL;
> > > > +             if (p->interval_exp < SECTOR_SHIFT ||
> > > > +                 p->interval_exp > ub->params.basic.logical_bs_shift)
> > > > +                     return -EINVAL;
> > > > +     }
> > > > +
> > > >       return 0;
> > > >  }
> > > >
> > > >  static void ublk_apply_params(struct ublk_device *ub)
> > > >  {
> > > > @@ -2941,10 +3014,25 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub,
> > > >               lim.seg_boundary_mask = ub->params.seg.seg_boundary_mask;
> > > >               lim.max_segment_size = ub->params.seg.max_segment_size;
> > > >               lim.max_segments = ub->params.seg.max_segments;
> > > >       }
> > > >
> > > > +     if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
> > > > +             const struct ublk_param_integrity *p = &ub->params.integrity;
> > > > +             int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
> > > > +
> > > > +             lim.integrity = (struct blk_integrity) {
> > > > +                     .flags = ublk_integrity_flags(p->flags),
> > > > +                     .csum_type = ublk_integrity_csum_type(p->csum_type),
> > > > +                     .metadata_size = p->metadata_size,
> > > > +                     .pi_offset = p->pi_offset,
> > > > +                     .interval_exp = p->interval_exp,
> > > > +                     .tag_size = p->tag_size,
> > > > +                     .pi_tuple_size = pi_tuple_size,
> > > > +             };
> > > > +     }
> > > > +
> > > >       if (wait_for_completion_interruptible(&ub->completion) != 0)
> > > >               return -EINTR;
> > > >
> > > >       if (ub->ublksrv_tgid != ublksrv_pid)
> > > >               return -EINVAL;
> > > > @@ -3131,10 +3219,14 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
> > > >               if (info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
> > > >                                       UBLK_F_AUTO_BUF_REG))
> > > >                       return -EINVAL;
> > > >       }
> > > >
> > > > +     /* User copy is required to access integrity buffer */
> > > > +     if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY))
> > > > +             return -EINVAL;
> > > > +
> > > >       /* the created device is always owned by current user */
> > > >       ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
> > > >
> > > >       if (header->dev_id != info.dev_id) {
> > > >               pr_warn("%s: dev id not match %u %u\n",
> > > > diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
> > > > index ec77dabba45b..a54c47832fa2 100644
> > > > --- a/include/uapi/linux/ublk_cmd.h
> > > > +++ b/include/uapi/linux/ublk_cmd.h
> > > > @@ -309,10 +309,16 @@
> > > >   * 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)
> > > >
> > > > +/*
> > > > + * ublk device supports requests with integrity/metadata buffer.
> > > > + * Requires UBLK_F_USER_COPY.
> > > > + */
> > > > +#define UBLK_F_INTEGRITY (1ULL << 16)
> > > > +
> > > >  /* device state */
> > > >  #define UBLK_S_DEV_DEAD      0
> > > >  #define UBLK_S_DEV_LIVE      1
> > > >  #define UBLK_S_DEV_QUIESCED  2
> > > >  #define UBLK_S_DEV_FAIL_IO   3
> > > > @@ -598,10 +604,20 @@ struct ublk_param_segment {
> > > >       __u32   max_segment_size;
> > > >       __u16   max_segments;
> > > >       __u8    pad[2];
> > > >  };
> > > >
> > > > +struct ublk_param_integrity {
> > > > +     __u32   flags; /* LBMD_PI_CAP_* from linux/fs.h */
> > > > +     __u8    interval_exp;
> > > > +     __u8    metadata_size; /* UBLK_PARAM_TYPE_INTEGRITY requires nonzero */
> > > > +     __u8    pi_offset;
> > > > +     __u8    csum_type; /* LBMD_PI_CSUM_* from linux/fs.h */
> > > > +     __u8    tag_size;
> > > > +     __u8    pad[7];
> > > > +};
> > >
> > > Looks max_integrity_segments is missed, otherwise this patch is fine for me.
> >
> > My thinking was that there isn't any reason why a ublk server would
> > need to limit the number of integrity segments. The request integrity
> > segments aren't directly exposed to the ublk server through user copy.
> > And since zero copy isn't supported for integrity data, there's no
> > concern about needing to align the ublk integrity limits with the
> > backing device limits. What do you think?
>
> Yeah, it isn't needed in current implementation, but things may change in
> future.
>
> How about adding it(__u16) to `ublk_param_integrity`? And document it is ignored for
> user-copy based implementation. It is usually part of HW/SW interface wrt. PI.

Okay, may as well just plumb it through to the queue_limits then.

Best,
Caleb

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 09/19] ublk: implement integrity user copy
  2026-01-07  0:28       ` Ming Lei
@ 2026-01-08  1:50         ` Caleb Sander Mateos
  2026-01-08  2:11           ` Ming Lei
  0 siblings, 1 reply; 40+ messages in thread
From: Caleb Sander Mateos @ 2026-01-08  1:50 UTC (permalink / raw)
  To: Ming Lei
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Tue, Jan 6, 2026 at 4:28 PM Ming Lei <ming.lei@redhat.com> wrote:
>
> On Tue, Jan 06, 2026 at 10:20:14AM -0800, Caleb Sander Mateos wrote:
> > On Tue, Jan 6, 2026 at 5:34 AM Ming Lei <ming.lei@redhat.com> wrote:
> > >
> > > On Mon, Jan 05, 2026 at 05:57:41PM -0700, Caleb Sander Mateos wrote:
> > > > From: Stanley Zhang <stazhang@purestorage.com>
> > > >
> > > > Add a function ublk_copy_user_integrity() to copy integrity information
> > > > between a request and a user iov_iter. This mirrors the existing
> > > > ublk_copy_user_pages() but operates on request integrity data instead of
> > > > regular data. Check UBLKSRV_IO_INTEGRITY_FLAG in iocb->ki_pos in
> > > > ublk_user_copy() to choose between copying data or integrity data.
> > > >
> > > > Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> > > > [csander: change offset units from data bytes to integrity data bytes,
> > > >  test UBLKSRV_IO_INTEGRITY_FLAG after subtracting UBLKSRV_IO_BUF_OFFSET,
> > > >  fix CONFIG_BLK_DEV_INTEGRITY=n build,
> > > >  rebase on ublk user copy refactor]
> > > > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > > > ---
> > > >  drivers/block/ublk_drv.c      | 52 +++++++++++++++++++++++++++++++++--
> > > >  include/uapi/linux/ublk_cmd.h |  4 +++
> > > >  2 files changed, 53 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> > > > index e44ab9981ef4..9694a4c1caa7 100644
> > > > --- a/drivers/block/ublk_drv.c
> > > > +++ b/drivers/block/ublk_drv.c
> > > > @@ -621,10 +621,15 @@ static inline unsigned ublk_pos_to_tag(loff_t pos)
> > > >  {
> > > >       return ((pos - UBLKSRV_IO_BUF_OFFSET) >> UBLK_TAG_OFF) &
> > > >               UBLK_TAG_BITS_MASK;
> > > >  }
> > > >
> > > > +static inline bool ublk_pos_is_integrity(loff_t pos)
> > > > +{
> > > > +     return !!((pos - UBLKSRV_IO_BUF_OFFSET) & UBLKSRV_IO_INTEGRITY_FLAG);
> > > > +}
> > > > +
> > >
> > > It could be more readable to check UBLKSRV_IO_INTEGRITY_FLAG only.
> >
> > That's assuming that UBLK_TAG_BITS = 16 has more bits than are
> > strictly required by UBLK_MAX_QUEUE_DEPTH = 4096? Otherwise, adding
> > UBLKSRV_IO_BUF_OFFSET = 1 << 31 to tag << UBLK_TAG_OFF could overflow
> > into the QID bits, which could then overflow into
> > UBLKSRV_IO_INTEGRITY_FLAG. That seems like a very fragile assumption.
> > And if you want to rely on this assumption, why bother subtracting
> > UBLKSRV_IO_BUF_OFFSET in ublk_pos_to_hwq() either? The compiler should
> > easily be able to deduplicate the iocb->ki_pos - UBLKSRV_IO_BUF_OFFSET
> > computations, so I can't imagine it matters for performance.
>
> UBLKSRV_IO_INTEGRITY_FLAG should be defined as one flag starting from top
> bit(bit 62), then you will see it is just fine to check it directly.
>
> But it isn't a big deal to subtract UBLKSRV_IO_BUF_OFFSET or not here, I
> will leave it to you.
>
> >
> > >
> > > >  static void ublk_dev_param_basic_apply(struct ublk_device *ub)
> > > >  {
> > > >       const struct ublk_param_basic *p = &ub->params.basic;
> > > >
> > > >       if (p->attrs & UBLK_ATTR_READ_ONLY)
> > > > @@ -1047,10 +1052,37 @@ static size_t ublk_copy_user_pages(const struct request *req,
> > > >                       break;
> > > >       }
> > > >       return done;
> > > >  }
> > > >
> > > > +#ifdef CONFIG_BLK_DEV_INTEGRITY
> > > > +static size_t ublk_copy_user_integrity(const struct request *req,
> > > > +             unsigned offset, struct iov_iter *uiter, int dir)
> > > > +{
> > > > +     size_t done = 0;
> > > > +     struct bio *bio = req->bio;
> > > > +     struct bvec_iter iter;
> > > > +     struct bio_vec iv;
> > > > +
> > > > +     if (!blk_integrity_rq(req))
> > > > +             return 0;
> > > > +
> > > > +     bio_for_each_integrity_vec(iv, bio, iter) {
> > > > +             if (!ublk_copy_user_bvec(&iv, &offset, uiter, dir, &done))
> > > > +                     break;
> > > > +     }
> > > > +
> > > > +     return done;
> > > > +}
> > > > +#else /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
> > > > +static size_t ublk_copy_user_integrity(const struct request *req,
> > > > +             unsigned offset, struct iov_iter *uiter, int dir)
> > > > +{
> > > > +     return 0;
> > > > +}
> > > > +#endif /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
> > > > +
> > > >  static inline bool ublk_need_map_req(const struct request *req)
> > > >  {
> > > >       return ublk_rq_has_data(req) && req_op(req) == REQ_OP_WRITE;
> > > >  }
> > > >
> > > > @@ -2654,10 +2686,12 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
> > > >  {
> > > >       struct ublk_device *ub = iocb->ki_filp->private_data;
> > > >       struct ublk_queue *ubq;
> > > >       struct request *req;
> > > >       struct ublk_io *io;
> > > > +     unsigned data_len;
> > > > +     bool is_integrity;
> > > >       size_t buf_off;
> > > >       u16 tag, q_id;
> > > >       ssize_t ret;
> > > >
> > > >       if (!user_backed_iter(iter))
> > > > @@ -2667,10 +2701,11 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
> > > >               return -EACCES;
> > > >
> > > >       tag = ublk_pos_to_tag(iocb->ki_pos);
> > > >       q_id = ublk_pos_to_hwq(iocb->ki_pos);
> > > >       buf_off = ublk_pos_to_buf_off(iocb->ki_pos);
> > > > +     is_integrity = ublk_pos_is_integrity(iocb->ki_pos);
> > >
> > > UBLKSRV_IO_INTEGRITY_FLAG can be set for device without UBLK_F_INTEGRITY,
> > > so UBLK_F_INTEGRITY need to be checked in case of `is_integrity`.
> >
> > If UBLK_F_INTEGRITY isn't set, then UBLK_PARAM_TYPE_INTEGRITY isn't
> > allowed, so the ublk device won't support integrity data. Therefore,
> > blk_integrity_rq() will return false and ublk_copy_user_integrity()
> > will just return 0. Do you think it's important to return some error
> > code value instead? I would rather avoid the additional checks in the
> > hot path.
>
> The check could be zero cost, but better to fail the wrong usage than
> returning 0 silently, which may often imply big issue.

Not sure what you mean by "the check could be zero cost". It's 2
branches to check for UBLK_F_INTEGRITY in the ublk_device flags and to
check is_integrity. Even if the branches are predictable (and the
is_integrity one might not be), there's still some cost for computing
the conditions and taking up space in the branch history table.
A ublk server should already be checking that the return value from
the user copy syscall matches the passed in length. Otherwise, the
request's data was shorter than expected or a fault occurred while
accessing the userspace buffer. But if you feel strongly, I'll add an
explicit -EINVAL return code.

Best,
Caleb

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 09/19] ublk: implement integrity user copy
  2026-01-08  1:50         ` Caleb Sander Mateos
@ 2026-01-08  2:11           ` Ming Lei
  0 siblings, 0 replies; 40+ messages in thread
From: Ming Lei @ 2026-01-08  2:11 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Shuah Khan, linux-block, linux-kselftest,
	linux-kernel, Stanley Zhang, Uday Shankar, Martin K . Petersen

On Wed, Jan 07, 2026 at 05:50:04PM -0800, Caleb Sander Mateos wrote:
> On Tue, Jan 6, 2026 at 4:28 PM Ming Lei <ming.lei@redhat.com> wrote:
> >
> > On Tue, Jan 06, 2026 at 10:20:14AM -0800, Caleb Sander Mateos wrote:
> > > On Tue, Jan 6, 2026 at 5:34 AM Ming Lei <ming.lei@redhat.com> wrote:
> > > >
> > > > On Mon, Jan 05, 2026 at 05:57:41PM -0700, Caleb Sander Mateos wrote:
> > > > > From: Stanley Zhang <stazhang@purestorage.com>
> > > > >
> > > > > Add a function ublk_copy_user_integrity() to copy integrity information
> > > > > between a request and a user iov_iter. This mirrors the existing
> > > > > ublk_copy_user_pages() but operates on request integrity data instead of
> > > > > regular data. Check UBLKSRV_IO_INTEGRITY_FLAG in iocb->ki_pos in
> > > > > ublk_user_copy() to choose between copying data or integrity data.
> > > > >
> > > > > Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
> > > > > [csander: change offset units from data bytes to integrity data bytes,
> > > > >  test UBLKSRV_IO_INTEGRITY_FLAG after subtracting UBLKSRV_IO_BUF_OFFSET,
> > > > >  fix CONFIG_BLK_DEV_INTEGRITY=n build,
> > > > >  rebase on ublk user copy refactor]
> > > > > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > > > > ---
> > > > >  drivers/block/ublk_drv.c      | 52 +++++++++++++++++++++++++++++++++--
> > > > >  include/uapi/linux/ublk_cmd.h |  4 +++
> > > > >  2 files changed, 53 insertions(+), 3 deletions(-)
> > > > >
> > > > > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> > > > > index e44ab9981ef4..9694a4c1caa7 100644
> > > > > --- a/drivers/block/ublk_drv.c
> > > > > +++ b/drivers/block/ublk_drv.c
> > > > > @@ -621,10 +621,15 @@ static inline unsigned ublk_pos_to_tag(loff_t pos)
> > > > >  {
> > > > >       return ((pos - UBLKSRV_IO_BUF_OFFSET) >> UBLK_TAG_OFF) &
> > > > >               UBLK_TAG_BITS_MASK;
> > > > >  }
> > > > >
> > > > > +static inline bool ublk_pos_is_integrity(loff_t pos)
> > > > > +{
> > > > > +     return !!((pos - UBLKSRV_IO_BUF_OFFSET) & UBLKSRV_IO_INTEGRITY_FLAG);
> > > > > +}
> > > > > +
> > > >
> > > > It could be more readable to check UBLKSRV_IO_INTEGRITY_FLAG only.
> > >
> > > That's assuming that UBLK_TAG_BITS = 16 has more bits than are
> > > strictly required by UBLK_MAX_QUEUE_DEPTH = 4096? Otherwise, adding
> > > UBLKSRV_IO_BUF_OFFSET = 1 << 31 to tag << UBLK_TAG_OFF could overflow
> > > into the QID bits, which could then overflow into
> > > UBLKSRV_IO_INTEGRITY_FLAG. That seems like a very fragile assumption.
> > > And if you want to rely on this assumption, why bother subtracting
> > > UBLKSRV_IO_BUF_OFFSET in ublk_pos_to_hwq() either? The compiler should
> > > easily be able to deduplicate the iocb->ki_pos - UBLKSRV_IO_BUF_OFFSET
> > > computations, so I can't imagine it matters for performance.
> >
> > UBLKSRV_IO_INTEGRITY_FLAG should be defined as one flag starting from top
> > bit(bit 62), then you will see it is just fine to check it directly.
> >
> > But it isn't a big deal to subtract UBLKSRV_IO_BUF_OFFSET or not here, I
> > will leave it to you.
> >
> > >
> > > >
> > > > >  static void ublk_dev_param_basic_apply(struct ublk_device *ub)
> > > > >  {
> > > > >       const struct ublk_param_basic *p = &ub->params.basic;
> > > > >
> > > > >       if (p->attrs & UBLK_ATTR_READ_ONLY)
> > > > > @@ -1047,10 +1052,37 @@ static size_t ublk_copy_user_pages(const struct request *req,
> > > > >                       break;
> > > > >       }
> > > > >       return done;
> > > > >  }
> > > > >
> > > > > +#ifdef CONFIG_BLK_DEV_INTEGRITY
> > > > > +static size_t ublk_copy_user_integrity(const struct request *req,
> > > > > +             unsigned offset, struct iov_iter *uiter, int dir)
> > > > > +{
> > > > > +     size_t done = 0;
> > > > > +     struct bio *bio = req->bio;
> > > > > +     struct bvec_iter iter;
> > > > > +     struct bio_vec iv;
> > > > > +
> > > > > +     if (!blk_integrity_rq(req))
> > > > > +             return 0;
> > > > > +
> > > > > +     bio_for_each_integrity_vec(iv, bio, iter) {
> > > > > +             if (!ublk_copy_user_bvec(&iv, &offset, uiter, dir, &done))
> > > > > +                     break;
> > > > > +     }
> > > > > +
> > > > > +     return done;
> > > > > +}
> > > > > +#else /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
> > > > > +static size_t ublk_copy_user_integrity(const struct request *req,
> > > > > +             unsigned offset, struct iov_iter *uiter, int dir)
> > > > > +{
> > > > > +     return 0;
> > > > > +}
> > > > > +#endif /* #ifdef CONFIG_BLK_DEV_INTEGRITY */
> > > > > +
> > > > >  static inline bool ublk_need_map_req(const struct request *req)
> > > > >  {
> > > > >       return ublk_rq_has_data(req) && req_op(req) == REQ_OP_WRITE;
> > > > >  }
> > > > >
> > > > > @@ -2654,10 +2686,12 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
> > > > >  {
> > > > >       struct ublk_device *ub = iocb->ki_filp->private_data;
> > > > >       struct ublk_queue *ubq;
> > > > >       struct request *req;
> > > > >       struct ublk_io *io;
> > > > > +     unsigned data_len;
> > > > > +     bool is_integrity;
> > > > >       size_t buf_off;
> > > > >       u16 tag, q_id;
> > > > >       ssize_t ret;
> > > > >
> > > > >       if (!user_backed_iter(iter))
> > > > > @@ -2667,10 +2701,11 @@ ublk_user_copy(struct kiocb *iocb, struct iov_iter *iter, int dir)
> > > > >               return -EACCES;
> > > > >
> > > > >       tag = ublk_pos_to_tag(iocb->ki_pos);
> > > > >       q_id = ublk_pos_to_hwq(iocb->ki_pos);
> > > > >       buf_off = ublk_pos_to_buf_off(iocb->ki_pos);
> > > > > +     is_integrity = ublk_pos_is_integrity(iocb->ki_pos);
> > > >
> > > > UBLKSRV_IO_INTEGRITY_FLAG can be set for device without UBLK_F_INTEGRITY,
> > > > so UBLK_F_INTEGRITY need to be checked in case of `is_integrity`.
> > >
> > > If UBLK_F_INTEGRITY isn't set, then UBLK_PARAM_TYPE_INTEGRITY isn't
> > > allowed, so the ublk device won't support integrity data. Therefore,
> > > blk_integrity_rq() will return false and ublk_copy_user_integrity()
> > > will just return 0. Do you think it's important to return some error
> > > code value instead? I would rather avoid the additional checks in the
> > > hot path.
> >
> > The check could be zero cost, but better to fail the wrong usage than
> > returning 0 silently, which may often imply big issue.
> 
> Not sure what you mean by "the check could be zero cost". It's 2
> branches to check for UBLK_F_INTEGRITY in the ublk_device flags and to
> check is_integrity. Even if the branches are predictable (and the
> is_integrity one might not be), there's still some cost for computing
> the conditions and taking up space in the branch history table.

ub->dev_info.nr_hw_queues is fetched for validating `q_id`, so
ub->dev_info.flags is always hit from the same cache line.

> A ublk server should already be checking that the return value from
> the user copy syscall matches the passed in length. Otherwise, the
> request's data was shorter than expected or a fault occurred while
> accessing the userspace buffer. But if you feel strongly, I'll add an
> explicit -EINVAL return code.

It is absolutely userspace fault or bug, I think it is better to fast fail.
Otherwise, it has to be documented clearly.


Thanks,
Ming


^ permalink raw reply	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2026-01-08  2:11 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-06  0:57 [PATCH v3 00/19] ublk: add support for integrity data Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 01/19] blk-integrity: take const pointer in blk_integrity_rq() Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 02/19] ublk: move ublk flag check functions earlier Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 03/19] ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation Caleb Sander Mateos
2026-01-06 13:09   ` Ming Lei
2026-01-06 16:32     ` Caleb Sander Mateos
2026-01-07  0:15       ` Ming Lei
2026-01-07  2:20         ` Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 04/19] ublk: set UBLK_IO_F_INTEGRITY in ublksrv_io_desc Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 05/19] ublk: add ublk_copy_user_bvec() helper Caleb Sander Mateos
2026-01-06 13:14   ` Ming Lei
2026-01-06  0:57 ` [PATCH v3 06/19] ublk: split out ublk_user_copy() helper Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 07/19] ublk: inline ublk_check_and_get_req() into ublk_user_copy() Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 08/19] ublk: move offset check out of __ublk_check_and_get_req() Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 09/19] ublk: implement integrity user copy Caleb Sander Mateos
2026-01-06 13:34   ` Ming Lei
2026-01-06 18:20     ` Caleb Sander Mateos
2026-01-07  0:28       ` Ming Lei
2026-01-08  1:50         ` Caleb Sander Mateos
2026-01-08  2:11           ` Ming Lei
2026-01-06 13:46   ` Ming Lei
2026-01-06  0:57 ` [PATCH v3 10/19] ublk: support UBLK_F_INTEGRITY Caleb Sander Mateos
2026-01-06 13:36   ` Ming Lei
2026-01-06  0:57 ` [PATCH v3 11/19] ublk: optimize ublk_user_copy() on daemon task Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 12/19] selftests: ublk: display UBLK_F_INTEGRITY support Caleb Sander Mateos
2026-01-06 13:38   ` Ming Lei
2026-01-06  0:57 ` [PATCH v3 13/19] selftests: ublk: add utility to get block device metadata size Caleb Sander Mateos
2026-01-06 13:50   ` Ming Lei
2026-01-06 17:18     ` Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 14/19] selftests: ublk: add kublk support for integrity params Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 15/19] selftests: ublk: implement integrity user copy in kublk Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 16/19] selftests: ublk: support non-O_DIRECT backing files Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 17/19] selftests: ublk: add integrity data support to loop target Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 18/19] selftests: ublk: add integrity params test Caleb Sander Mateos
2026-01-06  0:57 ` [PATCH v3 19/19] selftests: ublk: add end-to-end integrity test Caleb Sander Mateos
2026-01-06 14:10   ` Ming Lei
2026-01-06 17:15     ` Caleb Sander Mateos
2026-01-07  0:21       ` Ming Lei
2026-01-07  1:32         ` Caleb Sander Mateos
2026-01-07  1:49           ` Ming Lei

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).