From 7ea25eaad5ae3a6c837a3df9bdb822194f002565 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Feb 2026 05:20:01 -0800 Subject: block: factor out a bio_integrity_action helper Split the logic to see if a bio needs integrity metadata from bio_integrity_prep into a reusable helper than can be called from file system code. Signed-off-by: Christoph Hellwig Reviewed-by: Anuj Gupta Reviewed-by: Kanchan Joshi Reviewed-by: Martin K. Petersen Reviewed-by: Darrick J. Wong Tested-by: Anuj Gupta Signed-off-by: Jens Axboe --- include/linux/bio-integrity.h | 5 ++--- include/linux/blk-integrity.h | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bio-integrity.h b/include/linux/bio-integrity.h index 21e4652dcfd2..276cbbdd2c9d 100644 --- a/include/linux/bio-integrity.h +++ b/include/linux/bio-integrity.h @@ -78,7 +78,7 @@ int bio_integrity_add_page(struct bio *bio, struct page *page, unsigned int len, int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter); int bio_integrity_map_iter(struct bio *bio, struct uio_meta *meta); void bio_integrity_unmap_user(struct bio *bio); -bool bio_integrity_prep(struct bio *bio); +void bio_integrity_prep(struct bio *bio, unsigned int action); void bio_integrity_advance(struct bio *bio, unsigned int bytes_done); void bio_integrity_trim(struct bio *bio); int bio_integrity_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp_mask); @@ -104,9 +104,8 @@ static inline void bio_integrity_unmap_user(struct bio *bio) { } -static inline bool bio_integrity_prep(struct bio *bio) +static inline void bio_integrity_prep(struct bio *bio, unsigned int action) { - return true; } static inline int bio_integrity_clone(struct bio *bio, struct bio *bio_src, diff --git a/include/linux/blk-integrity.h b/include/linux/blk-integrity.h index c15b1ac62765..fd3f3c8c0fcd 100644 --- a/include/linux/blk-integrity.h +++ b/include/linux/blk-integrity.h @@ -180,4 +180,27 @@ static inline struct bio_vec rq_integrity_vec(struct request *rq) } #endif /* CONFIG_BLK_DEV_INTEGRITY */ +enum bio_integrity_action { + BI_ACT_BUFFER = (1u << 0), /* allocate buffer */ + BI_ACT_CHECK = (1u << 1), /* generate / verify PI */ + BI_ACT_ZERO = (1u << 2), /* zero buffer */ +}; + +/** + * bio_integrity_action - return the integrity action needed for a bio + * @bio: bio to operate on + * + * Returns the mask of integrity actions (BI_ACT_*) that need to be performed + * for @bio. + */ +unsigned int __bio_integrity_action(struct bio *bio); +static inline unsigned int bio_integrity_action(struct bio *bio) +{ + if (!blk_get_integrity(bio->bi_bdev->bd_disk)) + return 0; + if (bio_integrity(bio)) + return 0; + return __bio_integrity_action(bio); +} + #endif /* _LINUX_BLK_INTEGRITY_H */ -- cgit v1.2.3 From a936655697cd8d1bab2fd5189e2c33dd6356a266 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Feb 2026 05:20:02 -0800 Subject: block: factor out a bio_integrity_setup_default helper Add a helper to set the seed and check flag based on useful defaults from the profile. Note that this includes a small behavior change, as we now only set the seed if any action is set, which is fine as nothing will look at it otherwise. Signed-off-by: Christoph Hellwig Reviewed-by: Anuj Gupta Reviewed-by: Kanchan Joshi Reviewed-by: Martin K. Petersen Reviewed-by: Darrick J. Wong Tested-by: Anuj Gupta Signed-off-by: Jens Axboe --- block/bio-integrity-auto.c | 14 ++------------ block/bio-integrity.c | 16 ++++++++++++++++ include/linux/bio-integrity.h | 1 + 3 files changed, 19 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/block/bio-integrity-auto.c b/block/bio-integrity-auto.c index e16f669dbf1e..b64c71a7fc82 100644 --- a/block/bio-integrity-auto.c +++ b/block/bio-integrity-auto.c @@ -88,7 +88,6 @@ bool __bio_integrity_endio(struct bio *bio) */ void bio_integrity_prep(struct bio *bio, unsigned int action) { - struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk); struct bio_integrity_data *bid; bid = mempool_alloc(&bid_pool, GFP_NOIO); @@ -96,17 +95,8 @@ void bio_integrity_prep(struct bio *bio, unsigned int action) bid->bio = bio; bid->bip.bip_flags |= BIP_BLOCK_INTEGRITY; bio_integrity_alloc_buf(bio, action & BI_ACT_ZERO); - - bip_set_seed(&bid->bip, bio->bi_iter.bi_sector); - - if (action & BI_ACT_CHECK) { - if (bi->csum_type == BLK_INTEGRITY_CSUM_IP) - bid->bip.bip_flags |= BIP_IP_CHECKSUM; - if (bi->csum_type) - bid->bip.bip_flags |= BIP_CHECK_GUARD; - if (bi->flags & BLK_INTEGRITY_REF_TAG) - bid->bip.bip_flags |= BIP_CHECK_REFTAG; - } + if (action & BI_ACT_CHECK) + bio_integrity_setup_default(bio); /* Auto-generate integrity metadata if this is a write */ if (bio_data_dir(bio) == WRITE && bip_should_check(&bid->bip)) diff --git a/block/bio-integrity.c b/block/bio-integrity.c index 0955be90038b..e79eaf047794 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -101,6 +101,22 @@ void bio_integrity_free_buf(struct bio_integrity_payload *bip) kfree(bvec_virt(bv)); } +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); + + bip_set_seed(bip, bio->bi_iter.bi_sector); + + if (bi->csum_type) { + bip->bip_flags |= BIP_CHECK_GUARD; + if (bi->csum_type == BLK_INTEGRITY_CSUM_IP) + bip->bip_flags |= BIP_IP_CHECKSUM; + } + if (bi->flags & BLK_INTEGRITY_REF_TAG) + bip->bip_flags |= BIP_CHECK_REFTAG; +} + /** * bio_integrity_free - Free bio integrity payload * @bio: bio containing bip to be freed diff --git a/include/linux/bio-integrity.h b/include/linux/bio-integrity.h index 276cbbdd2c9d..232b86b9bbcb 100644 --- a/include/linux/bio-integrity.h +++ b/include/linux/bio-integrity.h @@ -143,5 +143,6 @@ static inline int bio_integrity_add_page(struct bio *bio, struct page *page, void bio_integrity_alloc_buf(struct bio *bio, bool zero_buffer); void bio_integrity_free_buf(struct bio_integrity_payload *bip); +void bio_integrity_setup_default(struct bio *bio); #endif /* _LINUX_BIO_INTEGRITY_H */ -- cgit v1.2.3 From 7afe93946dff63aa57c6db81f5eb43ac8233364e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Feb 2026 05:20:03 -0800 Subject: block: add a bdev_has_integrity_csum helper Factor out a helper to see if the block device has an integrity checksum from bdev_stable_writes so that it can be reused for other checks. Signed-off-by: Christoph Hellwig Reviewed-by: Anuj Gupta Reviewed-by: Kanchan Joshi Reviewed-by: Martin K. Petersen Reviewed-by: Darrick J. Wong Tested-by: Anuj Gupta Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d463b9b5a0a5..dec0acaed6e6 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1477,14 +1477,18 @@ static inline bool bdev_synchronous(struct block_device *bdev) return bdev->bd_disk->queue->limits.features & BLK_FEAT_SYNCHRONOUS; } -static inline bool bdev_stable_writes(struct block_device *bdev) +static inline bool bdev_has_integrity_csum(struct block_device *bdev) { - struct request_queue *q = bdev_get_queue(bdev); + struct queue_limits *lim = bdev_limits(bdev); - if (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) && - q->limits.integrity.csum_type != BLK_INTEGRITY_CSUM_NONE) - return true; - return q->limits.features & BLK_FEAT_STABLE_WRITES; + return IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) && + lim->integrity.csum_type != BLK_INTEGRITY_CSUM_NONE; +} + +static inline bool bdev_stable_writes(struct block_device *bdev) +{ + return bdev_has_integrity_csum(bdev) || + (bdev_limits(bdev)->features & BLK_FEAT_STABLE_WRITES); } static inline bool blk_queue_write_cache(struct request_queue *q) -- cgit v1.2.3 From 8c56ef10150ed7650cf4105539242c94c156148c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Feb 2026 05:20:05 -0800 Subject: block: make max_integrity_io_size public File systems that generate integrity will need this, so move it out of the block private or blk-mq specific headers. Signed-off-by: Christoph Hellwig Reviewed-by: Anuj Gupta Reviewed-by: Kanchan Joshi Reviewed-by: Martin K. Petersen Reviewed-by: Darrick J. Wong Tested-by: Anuj Gupta Signed-off-by: Jens Axboe --- block/blk-settings.c | 13 ------------- include/linux/blk-integrity.h | 5 ----- include/linux/blkdev.h | 18 ++++++++++++++++++ 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/block/blk-settings.c b/block/blk-settings.c index a9e65dc090da..dabfab97fbab 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -123,19 +123,6 @@ static int blk_validate_zoned_limits(struct queue_limits *lim) return 0; } -/* - * Maximum size of I/O that needs a block layer integrity buffer. Limited - * by the number of intervals for which we can fit the integrity buffer into - * the buffer size. Because the buffer is a single segment it is also limited - * by the maximum segment size. - */ -static inline unsigned int max_integrity_io_size(struct queue_limits *lim) -{ - return min_t(unsigned int, lim->max_segment_size, - (BLK_INTEGRITY_MAX_SIZE / lim->integrity.metadata_size) << - lim->integrity.interval_exp); -} - static int blk_validate_integrity_limits(struct queue_limits *lim) { struct blk_integrity *bi = &lim->integrity; diff --git a/include/linux/blk-integrity.h b/include/linux/blk-integrity.h index fd3f3c8c0fcd..ea6d7d322ae3 100644 --- a/include/linux/blk-integrity.h +++ b/include/linux/blk-integrity.h @@ -8,11 +8,6 @@ struct request; -/* - * Maximum contiguous integrity buffer allocation. - */ -#define BLK_INTEGRITY_MAX_SIZE SZ_2M - enum blk_integrity_flags { BLK_INTEGRITY_NOVERIFY = 1 << 0, BLK_INTEGRITY_NOGENERATE = 1 << 1, diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index dec0acaed6e6..11857ae13d10 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1881,6 +1881,24 @@ static inline int bio_split_rw_at(struct bio *bio, return bio_split_io_at(bio, lim, segs, max_bytes, lim->dma_alignment); } +/* + * Maximum contiguous integrity buffer allocation. + */ +#define BLK_INTEGRITY_MAX_SIZE SZ_2M + +/* + * Maximum size of I/O that needs a block layer integrity buffer. Limited + * by the number of intervals for which we can fit the integrity buffer into + * the buffer size. Because the buffer is a single segment it is also limited + * by the maximum segment size. + */ +static inline unsigned int max_integrity_io_size(struct queue_limits *lim) +{ + return min_t(unsigned int, lim->max_segment_size, + (BLK_INTEGRITY_MAX_SIZE / lim->integrity.metadata_size) << + lim->integrity.interval_exp); +} + #define DEFINE_IO_COMP_BATCH(name) struct io_comp_batch name = { } #endif /* _LINUX_BLKDEV_H */ -- cgit v1.2.3 From 0bde8a12b5540572a7fd6d2867bee6de15e4f289 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Feb 2026 05:20:06 -0800 Subject: block: add fs_bio_integrity helpers Add a set of helpers for file system initiated integrity information. These include mempool backed allocations and verifying based on a passed in sector and size which is often available from file system completion routines. Signed-off-by: Christoph Hellwig Reviewed-by: Anuj Gupta Reviewed-by: Kanchan Joshi Reviewed-by: Martin K. Petersen Reviewed-by: Darrick J. Wong Tested-by: Anuj Gupta Signed-off-by: Jens Axboe --- block/Makefile | 2 +- block/bio-integrity-fs.c | 81 +++++++++++++++++++++++++++++++++++++++++++ include/linux/bio-integrity.h | 6 ++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 block/bio-integrity-fs.c (limited to 'include/linux') diff --git a/block/Makefile b/block/Makefile index c65f4da93702..7dce2e44276c 100644 --- a/block/Makefile +++ b/block/Makefile @@ -26,7 +26,7 @@ bfq-y := bfq-iosched.o bfq-wf2q.o bfq-cgroup.o obj-$(CONFIG_IOSCHED_BFQ) += bfq.o obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o blk-integrity.o t10-pi.o \ - bio-integrity-auto.o + bio-integrity-auto.o bio-integrity-fs.o obj-$(CONFIG_BLK_DEV_ZONED) += blk-zoned.o obj-$(CONFIG_BLK_WBT) += blk-wbt.o obj-$(CONFIG_BLK_DEBUG_FS) += blk-mq-debugfs.o diff --git a/block/bio-integrity-fs.c b/block/bio-integrity-fs.c new file mode 100644 index 000000000000..acb1e5f270d2 --- /dev/null +++ b/block/bio-integrity-fs.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Christoph Hellwig. + */ +#include +#include +#include "blk.h" + +struct fs_bio_integrity_buf { + struct bio_integrity_payload bip; + struct bio_vec bvec; +}; + +static struct kmem_cache *fs_bio_integrity_cache; +static mempool_t fs_bio_integrity_pool; + +unsigned int fs_bio_integrity_alloc(struct bio *bio) +{ + struct fs_bio_integrity_buf *iib; + unsigned int action; + + action = bio_integrity_action(bio); + if (!action) + return 0; + + iib = mempool_alloc(&fs_bio_integrity_pool, GFP_NOIO); + bio_integrity_init(bio, &iib->bip, &iib->bvec, 1); + + bio_integrity_alloc_buf(bio, action & BI_ACT_ZERO); + if (action & BI_ACT_CHECK) + bio_integrity_setup_default(bio); + return action; +} + +void fs_bio_integrity_free(struct bio *bio) +{ + struct bio_integrity_payload *bip = bio_integrity(bio); + + bio_integrity_free_buf(bip); + mempool_free(container_of(bip, struct fs_bio_integrity_buf, bip), + &fs_bio_integrity_pool); + + bio->bi_integrity = NULL; + bio->bi_opf &= ~REQ_INTEGRITY; +} + +void fs_bio_integrity_generate(struct bio *bio) +{ + if (fs_bio_integrity_alloc(bio)) + bio_integrity_generate(bio); +} +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); + + /* + * 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)); +} + +static int __init fs_bio_integrity_init(void) +{ + fs_bio_integrity_cache = kmem_cache_create("fs_bio_integrity", + sizeof(struct fs_bio_integrity_buf), 0, + SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); + if (mempool_init_slab_pool(&fs_bio_integrity_pool, BIO_POOL_SIZE, + fs_bio_integrity_cache)) + panic("fs_bio_integrity: can't create pool\n"); + return 0; +} +fs_initcall(fs_bio_integrity_init); diff --git a/include/linux/bio-integrity.h b/include/linux/bio-integrity.h index 232b86b9bbcb..af5178434ec6 100644 --- a/include/linux/bio-integrity.h +++ b/include/linux/bio-integrity.h @@ -145,4 +145,10 @@ void bio_integrity_alloc_buf(struct bio *bio, bool zero_buffer); void bio_integrity_free_buf(struct bio_integrity_payload *bip); void bio_integrity_setup_default(struct bio *bio); +unsigned int fs_bio_integrity_alloc(struct bio *bio); +void fs_bio_integrity_free(struct bio *bio); +void fs_bio_integrity_generate(struct bio *bio); +int fs_bio_integrity_verify(struct bio *bio, sector_t sector, + unsigned int size); + #endif /* _LINUX_BIO_INTEGRITY_H */ -- cgit v1.2.3 From a9aa6045abde87b94168c3ba034b953417e27272 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Feb 2026 05:20:07 -0800 Subject: block: pass a maxlen argument to bio_iov_iter_bounce Allow the file system to limit the size processed in a single bounce operation. This is needed when generating integrity data so that the size of a single integrity segment can't overflow. Signed-off-by: Christoph Hellwig Reviewed-by: Anuj Gupta Reviewed-by: Kanchan Joshi Reviewed-by: Martin K. Petersen Reviewed-by: Darrick J. Wong Tested-by: Anuj Gupta Signed-off-by: Jens Axboe --- block/bio.c | 17 ++++++++++------- fs/iomap/direct-io.c | 2 +- include/linux/bio.h | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/block/bio.c b/block/bio.c index d80d5d26804e..784d2a66d3ae 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1327,9 +1327,10 @@ static void bio_free_folios(struct bio *bio) } } -static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter) +static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, + size_t maxlen) { - size_t total_len = iov_iter_count(iter); + size_t total_len = min(maxlen, iov_iter_count(iter)); if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED))) return -EINVAL; @@ -1367,9 +1368,10 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter) return 0; } -static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter) +static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, + size_t maxlen) { - size_t len = min(iov_iter_count(iter), SZ_1M); + size_t len = min3(iov_iter_count(iter), maxlen, SZ_1M); struct folio *folio; folio = folio_alloc_greedy(GFP_KERNEL, &len); @@ -1408,6 +1410,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter) * bio_iov_iter_bounce - bounce buffer data from an iter into a bio * @bio: bio to send * @iter: iter to read from / write into + * @maxlen: maximum size to bounce * * Helper for direct I/O implementations that need to bounce buffer because * we need to checksum the data or perform other operations that require @@ -1415,11 +1418,11 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter) * copies the data into it. Needs to be paired with bio_iov_iter_unbounce() * called on completion. */ -int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter) +int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen) { if (op_is_write(bio_op(bio))) - return bio_iov_iter_bounce_write(bio, iter); - return bio_iov_iter_bounce_read(bio, iter); + return bio_iov_iter_bounce_write(bio, iter, maxlen); + return bio_iov_iter_bounce_read(bio, iter, maxlen); } static void bvec_unpin(struct bio_vec *bv, bool mark_dirty) diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index 95254aa1b654..21d4fad2eeb8 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -338,7 +338,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, bio->bi_end_io = iomap_dio_bio_end_io; if (dio->flags & IOMAP_DIO_BOUNCE) - ret = bio_iov_iter_bounce(bio, dio->submit.iter); + ret = bio_iov_iter_bounce(bio, dio->submit.iter, BIO_MAX_SIZE); else ret = bio_iov_iter_get_pages(bio, dio->submit.iter, alignment - 1); diff --git a/include/linux/bio.h b/include/linux/bio.h index 36a3f2275ecd..9693a0d6fefe 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -474,7 +474,7 @@ void __bio_release_pages(struct bio *bio, bool mark_dirty); extern void bio_set_pages_dirty(struct bio *bio); extern void bio_check_pages_dirty(struct bio *bio); -int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter); +int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen); void bio_iov_iter_unbounce(struct bio *bio, bool is_error, bool mark_dirty); extern void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter, -- cgit v1.2.3