public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/6] block: fix integrity offset/length conversions
@ 2026-04-17  1:57 Caleb Sander Mateos
  2026-04-17  1:57 ` [PATCH v3 1/6] block: use integrity interval instead of sector as seed Caleb Sander Mateos
                   ` (6 more replies)
  0 siblings, 7 replies; 9+ messages in thread
From: Caleb Sander Mateos @ 2026-04-17  1:57 UTC (permalink / raw)
  To: Jens Axboe, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
	Martin K. Petersen
  Cc: Anuj Gupta, linux-block, linux-nvme, linux-scsi, target-devel,
	linux-kernel, Caleb Sander Mateos

The block layer's integrity code currently sets the seed (initial
reference tag) in units of 512-byte sectors but increments it in units
of integrity intervals. Not only do the T10 DIF formats require ref tags
to be the lower bits of the logical block address, but mixing the two
units means the ref tags used for a particular logical block vary based
on its offset within a read/write request. This looks to be a
longstanding bug affecting block devices that support integrity with
block sizes > 512 bytes; I'm surprised it wasn't noticed before.

Also fix the newly added fs_bio_integrity_verify() to pass
bio_integrity_verify() a struct bdev_iter representing the data instead
of the integrity. Most of the integrity data is currently being skipped.

v3:
- Drop bi and bip arguments to bip_set_seed() (Christoph)

v2:
- Reorder fixes before refactoring commits
- Use u64, SECTOR_SHIFT (Christoph)
- Don't take sector_t in bip_set_seed() (Christoph)

Caleb Sander Mateos (6):
  block: use integrity interval instead of sector as seed
  bio-integrity-fs: pass data iter to bio_integrity_verify()
  blk-integrity: take u64 in bio_integrity_intervals()
  bio-integrity-fs: use integrity interval instead of sector as seed
  t10-pi: use bio_integrity_intervals() helper
  blk-integrity: avoid sector_t in bip_{get,set}_seed()

 block/bio-integrity-fs.c            |  8 ++++++--
 block/bio-integrity.c               |  4 ++--
 block/t10-pi.c                      |  7 ++++---
 drivers/nvme/target/io-cmd-bdev.c   |  3 +--
 drivers/target/target_core_iblock.c |  3 +--
 include/linux/bio-integrity.h       | 11 -----------
 include/linux/blk-integrity.h       | 27 ++++++++++++++++++++-------
 include/linux/bvec.h                |  1 +
 8 files changed, 35 insertions(+), 29 deletions(-)

-- 
2.45.2


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

* [PATCH v3 1/6] block: use integrity interval instead of sector as seed
  2026-04-17  1:57 [PATCH v3 0/6] block: fix integrity offset/length conversions Caleb Sander Mateos
@ 2026-04-17  1:57 ` Caleb Sander Mateos
  2026-04-17  1:57 ` [PATCH v3 2/6] bio-integrity-fs: pass data iter to bio_integrity_verify() Caleb Sander Mateos
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Caleb Sander Mateos @ 2026-04-17  1:57 UTC (permalink / raw)
  To: Jens Axboe, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
	Martin K. Petersen
  Cc: Anuj Gupta, linux-block, linux-nvme, linux-scsi, target-devel,
	linux-kernel, Caleb Sander Mateos

bio_integrity_setup_default() and blk_integrity_iterate() set the
integrity seed (initial reference tag) to the absolute address in the
block device in units of 512-byte sectors. The seed is correctly
incremented/decremented in units of integrity intervals in
bio_integrity_map_iter(), bio_integrity_advance(), and
blk_integrity_interval(). As a result, the ref tag written or read to a
particular integrity interval on a block device with integrity interval
size > 512 bytes varies with the starting offset of the read/write.

Convert the initial seed to units of integrity intervals so a consistent
ref tag is used for each integrity interval.

Fixes: 3be91c4a3d09 ("block: Deprecate the use of the term sector in the context of block integrity")
Fixes: 63573e359d05 ("bio-integrity: Restore original iterator on verify stage")
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Anuj Gupta <anuj20.g@samsung.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 block/bio-integrity.c | 3 ++-
 block/t10-pi.c        | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/block/bio-integrity.c b/block/bio-integrity.c
index e79eaf047794..3ad6a6799f17 100644
--- a/block/bio-integrity.c
+++ b/block/bio-integrity.c
@@ -103,12 +103,13 @@ void bio_integrity_free_buf(struct bio_integrity_payload *bip)
 
 void bio_integrity_setup_default(struct bio *bio)
 {
 	struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
 	struct bio_integrity_payload *bip = bio_integrity(bio);
+	u64 seed = bio->bi_iter.bi_sector >> (bi->interval_exp - SECTOR_SHIFT);
 
-	bip_set_seed(bip, bio->bi_iter.bi_sector);
+	bip_set_seed(bip, seed);
 
 	if (bi->csum_type) {
 		bip->bip_flags |= BIP_CHECK_GUARD;
 		if (bi->csum_type == BLK_INTEGRITY_CSUM_IP)
 			bip->bip_flags |= BIP_IP_CHECKSUM;
diff --git a/block/t10-pi.c b/block/t10-pi.c
index a19b4e102a83..e58d5eb6cefb 100644
--- a/block/t10-pi.c
+++ b/block/t10-pi.c
@@ -308,18 +308,19 @@ static blk_status_t blk_integrity_iterate(struct bio *bio,
 					  struct bvec_iter *data_iter,
 					  bool verify)
 {
 	struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
 	struct bio_integrity_payload *bip = bio_integrity(bio);
+	u64 seed = data_iter->bi_sector >> (bi->interval_exp - SECTOR_SHIFT);
 	struct blk_integrity_iter iter = {
 		.bio = bio,
 		.bip = bip,
 		.bi = bi,
 		.data_iter = *data_iter,
 		.prot_iter = bip->bip_iter,
 		.interval_remaining = 1 << bi->interval_exp,
-		.seed = data_iter->bi_sector,
+		.seed = seed,
 		.csum = 0,
 	};
 	blk_status_t ret = BLK_STS_OK;
 
 	while (iter.data_iter.bi_size && ret == BLK_STS_OK) {
-- 
2.45.2


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

* [PATCH v3 2/6] bio-integrity-fs: pass data iter to bio_integrity_verify()
  2026-04-17  1:57 [PATCH v3 0/6] block: fix integrity offset/length conversions Caleb Sander Mateos
  2026-04-17  1:57 ` [PATCH v3 1/6] block: use integrity interval instead of sector as seed Caleb Sander Mateos
@ 2026-04-17  1:57 ` Caleb Sander Mateos
  2026-04-17  1:57 ` [PATCH v3 3/6] blk-integrity: take u64 in bio_integrity_intervals() Caleb Sander Mateos
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Caleb Sander Mateos @ 2026-04-17  1:57 UTC (permalink / raw)
  To: Jens Axboe, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
	Martin K. Petersen
  Cc: Anuj Gupta, linux-block, linux-nvme, linux-scsi, target-devel,
	linux-kernel, Caleb Sander Mateos

bio_integrity_verify() expects the passed struct bvec_iter to be an
iterator over bio data, not integrity. So construct a separate data
bvec_iter without the bio_integrity_bytes() conversion and pass it to
bio_integrity_verify() instead of bip_iter.

Fixes: 0bde8a12b554 ("block: add fs_bio_integrity helpers")
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Anuj Gupta <anuj20.g@samsung.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 block/bio-integrity-fs.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/block/bio-integrity-fs.c b/block/bio-integrity-fs.c
index acb1e5f270d2..0daa42d9ead7 100644
--- a/block/bio-integrity-fs.c
+++ b/block/bio-integrity-fs.c
@@ -53,21 +53,25 @@ EXPORT_SYMBOL_GPL(fs_bio_integrity_generate);
 
 int fs_bio_integrity_verify(struct bio *bio, sector_t sector, unsigned int size)
 {
 	struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
 	struct bio_integrity_payload *bip = bio_integrity(bio);
+	struct bvec_iter data_iter = {
+		.bi_sector	= sector,
+		.bi_size	= size,
+	};
 
 	/*
 	 * Reinitialize bip->bip_iter.
 	 *
 	 * This is for use in the submitter after the driver is done with the
 	 * bio.  Requires the submitter to remember the sector and the size.
 	 */
 	memset(&bip->bip_iter, 0, sizeof(bip->bip_iter));
 	bip->bip_iter.bi_sector = sector;
 	bip->bip_iter.bi_size = bio_integrity_bytes(bi, size >> SECTOR_SHIFT);
-	return blk_status_to_errno(bio_integrity_verify(bio, &bip->bip_iter));
+	return blk_status_to_errno(bio_integrity_verify(bio, &data_iter));
 }
 
 static int __init fs_bio_integrity_init(void)
 {
 	fs_bio_integrity_cache = kmem_cache_create("fs_bio_integrity",
-- 
2.45.2


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

* [PATCH v3 3/6] blk-integrity: take u64 in bio_integrity_intervals()
  2026-04-17  1:57 [PATCH v3 0/6] block: fix integrity offset/length conversions Caleb Sander Mateos
  2026-04-17  1:57 ` [PATCH v3 1/6] block: use integrity interval instead of sector as seed Caleb Sander Mateos
  2026-04-17  1:57 ` [PATCH v3 2/6] bio-integrity-fs: pass data iter to bio_integrity_verify() Caleb Sander Mateos
@ 2026-04-17  1:57 ` Caleb Sander Mateos
  2026-04-17  1:57 ` [PATCH v3 4/6] bio-integrity-fs: use integrity interval instead of sector as seed Caleb Sander Mateos
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Caleb Sander Mateos @ 2026-04-17  1:57 UTC (permalink / raw)
  To: Jens Axboe, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
	Martin K. Petersen
  Cc: Anuj Gupta, linux-block, linux-nvme, linux-scsi, target-devel,
	linux-kernel, Caleb Sander Mateos

To allow bio_integrity_intervals() to convert an absolute sector to an
absolute integrity interval, use u64 for its argument and return types.
Also use SECTOR_SHIFT instead of the magic constant 9.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Anuj Gupta <anuj20.g@samsung.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 include/linux/blk-integrity.h | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/include/linux/blk-integrity.h b/include/linux/blk-integrity.h
index b1b530613c34..825d777c078b 100644
--- a/include/linux/blk-integrity.h
+++ b/include/linux/blk-integrity.h
@@ -64,23 +64,23 @@ queue_max_integrity_segments(const struct request_queue *q)
 {
 	return q->limits.max_integrity_segments;
 }
 
 /**
- * bio_integrity_intervals - Return number of integrity intervals for a bio
+ * bio_integrity_intervals - Convert sectors to integrity intervals
  * @bi:		blk_integrity profile for device
- * @sectors:	Size of the bio in 512-byte sectors
+ * @sectors:	Number of 512-byte sectors
  *
  * Description: The block layer calculates everything in 512 byte
  * sectors but integrity metadata is done in terms of the data integrity
  * interval size of the storage device.  Convert the block layer sectors
  * to the appropriate number of integrity intervals.
  */
-static inline unsigned int bio_integrity_intervals(struct blk_integrity *bi,
-						   unsigned int sectors)
+static inline u64 bio_integrity_intervals(const struct blk_integrity *bi,
+					  u64 sectors)
 {
-	return sectors >> (bi->interval_exp - 9);
+	return sectors >> (bi->interval_exp - SECTOR_SHIFT);
 }
 
 static inline unsigned int bio_integrity_bytes(struct blk_integrity *bi,
 					       unsigned int sectors)
 {
@@ -151,12 +151,12 @@ static inline unsigned short
 queue_max_integrity_segments(const struct request_queue *q)
 {
 	return 0;
 }
 
-static inline unsigned int bio_integrity_intervals(struct blk_integrity *bi,
-						   unsigned int sectors)
+static inline u64 bio_integrity_intervals(const struct blk_integrity *bi,
+					  u64 sectors)
 {
 	return 0;
 }
 
 static inline unsigned int bio_integrity_bytes(struct blk_integrity *bi,
-- 
2.45.2


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

* [PATCH v3 4/6] bio-integrity-fs: use integrity interval instead of sector as seed
  2026-04-17  1:57 [PATCH v3 0/6] block: fix integrity offset/length conversions Caleb Sander Mateos
                   ` (2 preceding siblings ...)
  2026-04-17  1:57 ` [PATCH v3 3/6] blk-integrity: take u64 in bio_integrity_intervals() Caleb Sander Mateos
@ 2026-04-17  1:57 ` Caleb Sander Mateos
  2026-04-17  1:57 ` [PATCH v3 5/6] t10-pi: use bio_integrity_intervals() helper Caleb Sander Mateos
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Caleb Sander Mateos @ 2026-04-17  1:57 UTC (permalink / raw)
  To: Jens Axboe, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
	Martin K. Petersen
  Cc: Anuj Gupta, linux-block, linux-nvme, linux-scsi, target-devel,
	linux-kernel, Caleb Sander Mateos

bip_iter.bi_sector is meant to be in units of integrity intervals rather
than 512-byte sectors. bio_integrity_verify() doesn't actually use it
currently (it uses the passed in struct bvec_iter's bi_sector instead).
But let's set it to the expected value for consistency.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Anuj Gupta <anuj20.g@samsung.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 block/bio-integrity-fs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/block/bio-integrity-fs.c b/block/bio-integrity-fs.c
index 0daa42d9ead7..0ad1c5ab64e9 100644
--- a/block/bio-integrity-fs.c
+++ b/block/bio-integrity-fs.c
@@ -65,11 +65,11 @@ int fs_bio_integrity_verify(struct bio *bio, sector_t sector, unsigned int size)
 	 *
 	 * This is for use in the submitter after the driver is done with the
 	 * bio.  Requires the submitter to remember the sector and the size.
 	 */
 	memset(&bip->bip_iter, 0, sizeof(bip->bip_iter));
-	bip->bip_iter.bi_sector = sector;
+	bip->bip_iter.bi_sector = bio_integrity_intervals(bi, sector);
 	bip->bip_iter.bi_size = bio_integrity_bytes(bi, size >> SECTOR_SHIFT);
 	return blk_status_to_errno(bio_integrity_verify(bio, &data_iter));
 }
 
 static int __init fs_bio_integrity_init(void)
-- 
2.45.2


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

* [PATCH v3 5/6] t10-pi: use bio_integrity_intervals() helper
  2026-04-17  1:57 [PATCH v3 0/6] block: fix integrity offset/length conversions Caleb Sander Mateos
                   ` (3 preceding siblings ...)
  2026-04-17  1:57 ` [PATCH v3 4/6] bio-integrity-fs: use integrity interval instead of sector as seed Caleb Sander Mateos
@ 2026-04-17  1:57 ` Caleb Sander Mateos
  2026-04-17  1:57 ` [PATCH v3 6/6] blk-integrity: avoid sector_t in bip_{get,set}_seed() Caleb Sander Mateos
  2026-04-17  3:26 ` [PATCH v3 0/6] block: fix integrity offset/length conversions Martin K. Petersen
  6 siblings, 0 replies; 9+ messages in thread
From: Caleb Sander Mateos @ 2026-04-17  1:57 UTC (permalink / raw)
  To: Jens Axboe, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
	Martin K. Petersen
  Cc: Anuj Gupta, linux-block, linux-nvme, linux-scsi, target-devel,
	linux-kernel, Caleb Sander Mateos

Use bio_integrity_intervals() to convert blk_rq_pos(rq) to integrity
intervals to reduce code duplication.

Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Anuj Gupta <anuj20.g@samsung.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
 block/t10-pi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/block/t10-pi.c b/block/t10-pi.c
index e58d5eb6cefb..787950dec50a 100644
--- a/block/t10-pi.c
+++ b/block/t10-pi.c
@@ -541,11 +541,11 @@ static void __blk_reftag_remap(struct bio *bio, struct blk_integrity *bi,
 
 static void blk_integrity_remap(struct request *rq, unsigned int nr_bytes,
 				bool prep)
 {
 	struct blk_integrity *bi = &rq->q->limits.integrity;
-	u64 ref = blk_rq_pos(rq) >> (bi->interval_exp - SECTOR_SHIFT);
+	u64 ref = bio_integrity_intervals(bi, blk_rq_pos(rq));
 	unsigned intervals = nr_bytes >> bi->interval_exp;
 	struct bio *bio;
 
 	if (!(bi->flags & BLK_INTEGRITY_REF_TAG))
 		return;
-- 
2.45.2


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

* [PATCH v3 6/6] blk-integrity: avoid sector_t in bip_{get,set}_seed()
  2026-04-17  1:57 [PATCH v3 0/6] block: fix integrity offset/length conversions Caleb Sander Mateos
                   ` (4 preceding siblings ...)
  2026-04-17  1:57 ` [PATCH v3 5/6] t10-pi: use bio_integrity_intervals() helper Caleb Sander Mateos
@ 2026-04-17  1:57 ` Caleb Sander Mateos
  2026-04-17  3:26 ` [PATCH v3 0/6] block: fix integrity offset/length conversions Martin K. Petersen
  6 siblings, 0 replies; 9+ messages in thread
From: Caleb Sander Mateos @ 2026-04-17  1:57 UTC (permalink / raw)
  To: Jens Axboe, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
	Martin K. Petersen
  Cc: Anuj Gupta, linux-block, linux-nvme, linux-scsi, target-devel,
	linux-kernel, Caleb Sander Mateos, Christoph Hellwig

bip_set_seed() and big_get_seed() take/return a sector_t value that's
actually an integrity interval number. This is confusing, so pass
struct bio instead to bip_set_seed() and convert the bio's device
address to integrity intervals.

Open-code the access to bip->bip_iter.bi_sector in the one caller of
bip_set_seed() that doesn't use the bio device address for the seed.
Open-code bip_get_seed() in its one caller.

Add a comment to struct bvec_iter's bi_sector field explaining its
alternate use for bip_iter.

Suggested-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
 block/bio-integrity.c               |  5 ++---
 block/t10-pi.c                      |  2 +-
 drivers/nvme/target/io-cmd-bdev.c   |  3 +--
 drivers/target/target_core_iblock.c |  3 +--
 include/linux/bio-integrity.h       | 11 -----------
 include/linux/blk-integrity.h       | 13 +++++++++++++
 include/linux/bvec.h                |  1 +
 7 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/block/bio-integrity.c b/block/bio-integrity.c
index 3ad6a6799f17..8549d9148b0b 100644
--- a/block/bio-integrity.c
+++ b/block/bio-integrity.c
@@ -103,13 +103,12 @@ void bio_integrity_free_buf(struct bio_integrity_payload *bip)
 
 void bio_integrity_setup_default(struct bio *bio)
 {
 	struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
 	struct bio_integrity_payload *bip = bio_integrity(bio);
-	u64 seed = bio->bi_iter.bi_sector >> (bi->interval_exp - SECTOR_SHIFT);
 
-	bip_set_seed(bip, seed);
+	bip_set_seed(bio);
 
 	if (bi->csum_type) {
 		bip->bip_flags |= BIP_CHECK_GUARD;
 		if (bi->csum_type == BLK_INTEGRITY_CSUM_IP)
 			bip->bip_flags |= BIP_IP_CHECKSUM;
@@ -472,11 +471,11 @@ int bio_integrity_map_iter(struct bio *bio, struct uio_meta *meta)
 
 	it.count = integrity_bytes;
 	ret = bio_integrity_map_user(bio, &it);
 	if (!ret) {
 		bio_uio_meta_to_bip(bio, meta);
-		bip_set_seed(bio_integrity(bio), meta->seed);
+		bio_integrity(bio)->bip_iter.bi_sector = meta->seed;
 		iov_iter_advance(&meta->iter, integrity_bytes);
 		meta->seed += bio_integrity_intervals(bi, bio_sectors(bio));
 	}
 	return ret;
 }
diff --git a/block/t10-pi.c b/block/t10-pi.c
index 787950dec50a..71367fd082bd 100644
--- a/block/t10-pi.c
+++ b/block/t10-pi.c
@@ -510,11 +510,11 @@ static void blk_reftag_remap_prepare(struct blk_integrity *bi,
 static void __blk_reftag_remap(struct bio *bio, struct blk_integrity *bi,
 			       unsigned *intervals, u64 *ref, bool prep)
 {
 	struct bio_integrity_payload *bip = bio_integrity(bio);
 	struct bvec_iter iter = bip->bip_iter;
-	u64 virt = bip_get_seed(bip);
+	u64 virt = bip->bip_iter.bi_sector;
 	union pi_tuple *ptuple;
 	union pi_tuple tuple;
 
 	if (prep && bip->bip_flags & BIP_MAPPED_INTEGRITY) {
 		*ref += bio->bi_iter.bi_size >> bi->interval_exp;
diff --git a/drivers/nvme/target/io-cmd-bdev.c b/drivers/nvme/target/io-cmd-bdev.c
index f2d9e8901df4..65fce51b024a 100644
--- a/drivers/nvme/target/io-cmd-bdev.c
+++ b/drivers/nvme/target/io-cmd-bdev.c
@@ -218,12 +218,11 @@ static int nvmet_bdev_alloc_bip(struct nvmet_req *req, struct bio *bio,
 		pr_err("Unable to allocate bio_integrity_payload\n");
 		return PTR_ERR(bip);
 	}
 
 	/* virtual start sector must be in integrity interval units */
-	bip_set_seed(bip, bio->bi_iter.bi_sector >>
-		     (bi->interval_exp - SECTOR_SHIFT));
+	bip_set_seed(bio);
 
 	resid = bio_integrity_bytes(bi, bio_sectors(bio));
 	while (resid > 0 && sg_miter_next(miter)) {
 		len = min_t(size_t, miter->length, resid);
 		rc = bio_integrity_add_page(bio, miter->page, len,
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
index 1087d1d17c36..1aeb5be529a8 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -706,12 +706,11 @@ iblock_alloc_bip(struct se_cmd *cmd, struct bio *bio,
 		pr_err("Unable to allocate bio_integrity_payload\n");
 		return PTR_ERR(bip);
 	}
 
 	/* virtual start sector must be in integrity interval units */
-	bip_set_seed(bip, bio->bi_iter.bi_sector >>
-				  (bi->interval_exp - SECTOR_SHIFT));
+	bip_set_seed(bio);
 
 	pr_debug("IBLOCK BIP Size: %u Sector: %llu\n", bip->bip_iter.bi_size,
 		 (unsigned long long)bip->bip_iter.bi_sector);
 
 	resid = bio_integrity_bytes(bi, bio_sectors(bio));
diff --git a/include/linux/bio-integrity.h b/include/linux/bio-integrity.h
index af5178434ec6..edcd0855abba 100644
--- a/include/linux/bio-integrity.h
+++ b/include/linux/bio-integrity.h
@@ -56,21 +56,10 @@ static inline bool bio_integrity_flagged(struct bio *bio, enum bip_flags flag)
 		return bip->bip_flags & flag;
 
 	return false;
 }
 
-static inline sector_t bip_get_seed(struct bio_integrity_payload *bip)
-{
-	return bip->bip_iter.bi_sector;
-}
-
-static inline void bip_set_seed(struct bio_integrity_payload *bip,
-				sector_t seed)
-{
-	bip->bip_iter.bi_sector = seed;
-}
-
 void bio_integrity_init(struct bio *bio, struct bio_integrity_payload *bip,
 		struct bio_vec *bvecs, unsigned int nr_vecs);
 struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, gfp_t gfp,
 		unsigned int nr);
 int bio_integrity_add_page(struct bio *bio, struct page *page, unsigned int len,
diff --git a/include/linux/blk-integrity.h b/include/linux/blk-integrity.h
index 825d777c078b..c82b2f6fe194 100644
--- a/include/linux/blk-integrity.h
+++ b/include/linux/blk-integrity.h
@@ -85,10 +85,23 @@ static inline unsigned int bio_integrity_bytes(struct blk_integrity *bi,
 					       unsigned int sectors)
 {
 	return bio_integrity_intervals(bi, sectors) * bi->metadata_size;
 }
 
+/**
+ * bip_set_seed - Set bip reference tag seed from bio device address
+ * @bio:	struct bio whose integrity payload's ref tag seed to initialize
+ */
+static inline void bip_set_seed(struct bio *bio)
+{
+	struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
+	struct bio_integrity_payload *bip = bio_integrity(bio);
+
+	bip->bip_iter.bi_sector =
+		bio_integrity_intervals(bi, bio->bi_iter.bi_sector);
+}
+
 static inline bool blk_integrity_rq(const struct request *rq)
 {
 	return rq->cmd_flags & REQ_INTEGRITY;
 }
 
diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index d36dd476feda..3dc88f5cd367 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -76,10 +76,11 @@ static inline void bvec_set_virt(struct bio_vec *bv, void *vaddr,
 
 struct bvec_iter {
 	/*
 	 * Current device address in 512 byte sectors. Only updated by the bio
 	 * iter wrappers and not the bvec iterator helpers themselves.
+	 * For bip_iter, this is overloaded to represent the integrity interval.
 	 */
 	sector_t		bi_sector;
 
 	/*
 	 * Remaining size in bytes.
-- 
2.45.2


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

* Re: [PATCH v3 0/6] block: fix integrity offset/length conversions
  2026-04-17  1:57 [PATCH v3 0/6] block: fix integrity offset/length conversions Caleb Sander Mateos
                   ` (5 preceding siblings ...)
  2026-04-17  1:57 ` [PATCH v3 6/6] blk-integrity: avoid sector_t in bip_{get,set}_seed() Caleb Sander Mateos
@ 2026-04-17  3:26 ` Martin K. Petersen
  2026-04-17 15:10   ` Caleb Sander Mateos
  6 siblings, 1 reply; 9+ messages in thread
From: Martin K. Petersen @ 2026-04-17  3:26 UTC (permalink / raw)
  To: Caleb Sander Mateos
  Cc: Jens Axboe, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
	Martin K. Petersen, Anuj Gupta, linux-block, linux-nvme,
	linux-scsi, target-devel, linux-kernel


Hi Caleb!

> The block layer's integrity code currently sets the seed (initial
> reference tag) in units of 512-byte sectors but increments it in units
> of integrity intervals

I don't necessarily agree with the premise that the seed needs to be
expressed in any particular unit. The seed is a start value, nothing
more.

We happen to set it to the block number in the block layer since we need
to be able to know what to compare against on completion (for Type 1 +
the restrictive Linux implementation of Type 2). But that does not imply
that the seed needs to be specified in any particular unit. Submitters
set the seed to whichever value makes sense to them (i.e. it could be
the offset within a file as opposed to the eventual LBA on the backend
device). And then that seed is incremented by 1 for each integrity
interval of data in the PI sent to/received from the device. The
conversion between the submitter's view of what the first ref tag should
be (i.e. seed) and what is required by the hardware (for instance lower
32 bits of device LBA) is the reason we perform remapping. The seed is
intentionally different in the submitter's protection envelope compared
to the device's protection envelope.

Using the block layer block number as seed was just a convenience since
that provided a predictable value for any I/O that had its PI
autogenerated. I never intended for the actual LBA to be used as seed
value on a 4Kn device. Initially we just used 0 as the seed. Leveraging
the block number just added a bit of additional protection.

I confess I haven't tested 4Kn in a while since things sort of converged
on 512e. But I used to run nightly tests on a SCSI storage with 4Kn
blocks just fine.

> This looks to be a longstanding bug affecting block devices that
> support integrity with block sizes > 512 bytes; I'm surprised it
> wasn't noticed before.

Are you seeing this with NVMe or SCSI?

-- 
Martin K. Petersen

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

* Re: [PATCH v3 0/6] block: fix integrity offset/length conversions
  2026-04-17  3:26 ` [PATCH v3 0/6] block: fix integrity offset/length conversions Martin K. Petersen
@ 2026-04-17 15:10   ` Caleb Sander Mateos
  0 siblings, 0 replies; 9+ messages in thread
From: Caleb Sander Mateos @ 2026-04-17 15:10 UTC (permalink / raw)
  To: Martin K. Petersen
  Cc: Jens Axboe, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
	Anuj Gupta, linux-block, linux-nvme, linux-scsi, target-devel,
	linux-kernel

On Thu, Apr 16, 2026 at 8:26 PM Martin K. Petersen
<martin.petersen@oracle.com> wrote:
>
>
> Hi Caleb!
>
> > The block layer's integrity code currently sets the seed (initial
> > reference tag) in units of 512-byte sectors but increments it in units
> > of integrity intervals
>
> I don't necessarily agree with the premise that the seed needs to be
> expressed in any particular unit. The seed is a start value, nothing
> more.

NVM Command Set specification 1.1 section 5.3.3 requires the reference
tag to increment by 1 per logical block, so that seems to determine
the increment unit:

> If the Reference Tag Check bit of the PRCHK field is set to ‘1’ and the namespace is
> formatted for Type 1 or Type 2 protection, then the controller compares the Logical Block
> Reference Tag to the computed reference tag. The computed reference tag depends on
> the Protection Type:
> ▪ If the namespace is formatted for Type 1 protection, the value of the computed
> reference tag for the first logical block of the command is the value contained in
> the Initial Logical Block Reference Tag (ILBRT) or Expected Initial Logical Block
> Reference Tag (EILBRT) field in the command, and the computed reference tag is
> incremented for each subsequent logical block. The controller shall complete the
> command with a status of Invalid Protection Information if the ILBRT field or the
> EILBRT field does not match the value of the least significant bits of the SLBA field
> sized to the number of bits in the Logical Block Reference Tag (refer to section
> 5.3.1.4).
> Note: Unlike SCSI Protection Information Type 1 protection which implicitly uses
> the least significant four bytes of the LBA, the controller always uses the ILBRT or
> EILBRT field and requires the host to initialize the ILBRT or EILBRT field to the
> least significant bits of the LBA sized to the number of bits in the Logical Block
> Reference Tag when Type 1 protection is used.
> ▪ If the namespace is formatted for Type 2 protection, the value of the computed
> reference tag for the first logical block of the command is the value contained in
> the Initial Logical Block Reference Tag (ILBRT) or Expected Initial Logical Block
> Reference Tag (EILBRT) field in the command, and the computed reference tag is
> incremented for each subsequent logical block.

The ref tag used for a particular block needs to be consistent. And
since reftag(block N) can be computed as the reftag(M) + N - M if
block N is accessed as part of an I/O that begins at block M, the
function must be of the form reftag(block N) = N + c for some constant
c. Thus, the ref tag seed needs to be computed in units of logical
blocks (integrity intervals); no other unit (e.g. 512-byte sectors)
works.
To see the issue with the current approach, consider an example
accessing LBA 1 on a device with a 4 KB block size. If the block is
written as part of a write that begins at LBA 0, its ref tag in the
generated PI will be 1 (sector 0 + 1 integrity interval). If it's
later read by a read starting at LBA 1, its expected ref tag will be 8
(sector 8 + 0 integrity intervals), and the auto-integrity code will
fail the read due to a reftag mismatch. This seems completely
unworkable for a block storage device.

>
> We happen to set it to the block number in the block layer since we need
> to be able to know what to compare against on completion (for Type 1 +
> the restrictive Linux implementation of Type 2). But that does not imply
> that the seed needs to be specified in any particular unit. Submitters
> set the seed to whichever value makes sense to them (i.e. it could be
> the offset within a file as opposed to the eventual LBA on the backend

I agree, the seed doesn't need to match the final LBA, but it does
need to be in *units* of logical blocks, plus some constant offset.

> device). And then that seed is incremented by 1 for each integrity
> interval of data in the PI sent to/received from the device. The
> conversion between the submitter's view of what the first ref tag should
> be (i.e. seed) and what is required by the hardware (for instance lower
> 32 bits of device LBA) is the reason we perform remapping. The seed is
> intentionally different in the submitter's protection envelope compared
> to the device's protection envelope.
>
> Using the block layer block number as seed was just a convenience since
> that provided a predictable value for any I/O that had its PI
> autogenerated. I never intended for the actual LBA to be used as seed
> value on a 4Kn device. Initially we just used 0 as the seed. Leveraging
> the block number just added a bit of additional protection.
>
> I confess I haven't tested 4Kn in a while since things sort of converged
> on 512e. But I used to run nightly tests on a SCSI storage with 4Kn
> blocks just fine.
>
> > This looks to be a longstanding bug affecting block devices that
> > support integrity with block sizes > 512 bytes; I'm surprised it
> > wasn't noticed before.
>
> Are you seeing this with NVMe or SCSI?

With a ublk device. It should affect any block device that supports
integrity and has a logical block size > 512.

Best,
Caleb

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

end of thread, other threads:[~2026-04-17 15:10 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-17  1:57 [PATCH v3 0/6] block: fix integrity offset/length conversions Caleb Sander Mateos
2026-04-17  1:57 ` [PATCH v3 1/6] block: use integrity interval instead of sector as seed Caleb Sander Mateos
2026-04-17  1:57 ` [PATCH v3 2/6] bio-integrity-fs: pass data iter to bio_integrity_verify() Caleb Sander Mateos
2026-04-17  1:57 ` [PATCH v3 3/6] blk-integrity: take u64 in bio_integrity_intervals() Caleb Sander Mateos
2026-04-17  1:57 ` [PATCH v3 4/6] bio-integrity-fs: use integrity interval instead of sector as seed Caleb Sander Mateos
2026-04-17  1:57 ` [PATCH v3 5/6] t10-pi: use bio_integrity_intervals() helper Caleb Sander Mateos
2026-04-17  1:57 ` [PATCH v3 6/6] blk-integrity: avoid sector_t in bip_{get,set}_seed() Caleb Sander Mateos
2026-04-17  3:26 ` [PATCH v3 0/6] block: fix integrity offset/length conversions Martin K. Petersen
2026-04-17 15:10   ` Caleb Sander Mateos

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox