aboutsummaryrefslogtreecommitdiffstats
path: root/block
diff options
authorChristian Brauner <brauner@kernel.org>2026-03-10 09:37:48 +0100
committerChristian Brauner <brauner@kernel.org>2026-03-10 09:37:48 +0100
commit969ebebc30fff0b9756130e3b4f6f3036e7c53ab (patch)
tree31fee4af06796a7987c1c805812d413b7960df11 /block
parent1f318b96cc84d7c2ab792fcc0bfd42a7ca890681 (diff)
parenta9aa6045abde87b94168c3ba034b953417e27272 (diff)
downloadlinux-next-history-969ebebc30fff0b9756130e3b4f6f3036e7c53ab.tar.gz
Merge branch 'for-7.1/block-integrity'
Bring in the shared branch with the block layer. * 'for-7.1/block-integrity' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/axboe/linux: block: pass a maxlen argument to bio_iov_iter_bounce block: add fs_bio_integrity helpers block: make max_integrity_io_size public block: prepare generation / verification helpers for fs usage block: add a bdev_has_integrity_csum helper block: factor out a bio_integrity_setup_default helper block: factor out a bio_integrity_action helper Signed-off-by: Christian Brauner <brauner@kernel.org>
Diffstat (limited to 'block')
-rw-r--r--block/Makefile2
-rw-r--r--block/bio-integrity-auto.c80
-rw-r--r--block/bio-integrity-fs.c81
-rw-r--r--block/bio-integrity.c64
-rw-r--r--block/bio.c17
-rw-r--r--block/blk-mq.c6
-rw-r--r--block/blk-settings.c13
-rw-r--r--block/blk.h6
-rw-r--r--block/t10-pi.c12
9 files changed, 181 insertions, 100 deletions
diff --git a/block/Makefile b/block/Makefile
index c65f4da937026..7dce2e44276c4 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-auto.c b/block/bio-integrity-auto.c
index 44dcdf7520c56..ebd17f47e0f9e 100644
--- a/block/bio-integrity-auto.c
+++ b/block/bio-integrity-auto.c
@@ -39,7 +39,7 @@ static void bio_integrity_verify_fn(struct work_struct *work)
container_of(work, struct bio_integrity_data, work);
struct bio *bio = bid->bio;
- blk_integrity_verify_iter(bio, &bid->saved_bio_iter);
+ bio->bi_status = bio_integrity_verify(bio, &bid->saved_bio_iter);
bio_integrity_finish(bid);
bio_endio(bio);
}
@@ -50,11 +50,6 @@ static bool bip_should_check(struct bio_integrity_payload *bip)
return bip->bip_flags & BIP_CHECK_FLAGS;
}
-static bool bi_offload_capable(struct blk_integrity *bi)
-{
- return bi->metadata_size == bi->pi_tuple_size;
-}
-
/**
* __bio_integrity_endio - Integrity I/O completion function
* @bio: Protected bio
@@ -84,83 +79,30 @@ bool __bio_integrity_endio(struct bio *bio)
/**
* bio_integrity_prep - Prepare bio for integrity I/O
* @bio: bio to prepare
+ * @action: preparation action needed (BI_ACT_*)
+ *
+ * Allocate the integrity payload. For writes, generate the integrity metadata
+ * and for reads, setup the completion handler to verify the metadata.
*
- * Checks if the bio already has an integrity payload attached. If it does, the
- * payload has been generated by another kernel subsystem, and we just pass it
- * through.
- * Otherwise allocates integrity payload and for writes the integrity metadata
- * will be generated. For reads, the completion handler will verify the
- * metadata.
+ * This is used for bios that do not have user integrity payloads attached.
*/
-bool bio_integrity_prep(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;
- bool set_flags = true;
- gfp_t gfp = GFP_NOIO;
-
- if (!bi)
- return true;
-
- if (!bio_sectors(bio))
- return true;
-
- /* Already protected? */
- if (bio_integrity(bio))
- return true;
-
- switch (bio_op(bio)) {
- case REQ_OP_READ:
- if (bi->flags & BLK_INTEGRITY_NOVERIFY) {
- if (bi_offload_capable(bi))
- return true;
- set_flags = false;
- }
- break;
- case REQ_OP_WRITE:
- /*
- * Zero the memory allocated to not leak uninitialized kernel
- * memory to disk for non-integrity metadata where nothing else
- * initializes the memory.
- */
- if (bi->flags & BLK_INTEGRITY_NOGENERATE) {
- if (bi_offload_capable(bi))
- return true;
- set_flags = false;
- gfp |= __GFP_ZERO;
- } else if (bi->metadata_size > bi->pi_tuple_size)
- gfp |= __GFP_ZERO;
- break;
- default:
- return true;
- }
-
- if (WARN_ON_ONCE(bio_has_crypt_ctx(bio)))
- return true;
bid = mempool_alloc(&bid_pool, GFP_NOIO);
bio_integrity_init(bio, &bid->bip, &bid->bvec, 1);
bid->bio = bio;
bid->bip.bip_flags |= BIP_BLOCK_INTEGRITY;
- bio_integrity_alloc_buf(bio, gfp & __GFP_ZERO);
-
- bip_set_seed(&bid->bip, bio->bi_iter.bi_sector);
-
- if (set_flags) {
- 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;
- }
+ bio_integrity_alloc_buf(bio, action & BI_ACT_ZERO);
+ 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))
- blk_integrity_generate(bio);
+ bio_integrity_generate(bio);
else
bid->saved_bio_iter = bio->bi_iter;
- return true;
}
EXPORT_SYMBOL(bio_integrity_prep);
diff --git a/block/bio-integrity-fs.c b/block/bio-integrity-fs.c
new file mode 100644
index 0000000000000..acb1e5f270d2b
--- /dev/null
+++ b/block/bio-integrity-fs.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Christoph Hellwig.
+ */
+#include <linux/blk-integrity.h>
+#include <linux/bio-integrity.h>
+#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/block/bio-integrity.c b/block/bio-integrity.c
index 20f5d301d32dd..e79eaf0477943 100644
--- a/block/bio-integrity.c
+++ b/block/bio-integrity.c
@@ -7,6 +7,7 @@
*/
#include <linux/blk-integrity.h>
+#include <linux/t10-pi.h>
#include "blk.h"
struct bio_integrity_alloc {
@@ -16,6 +17,53 @@ struct bio_integrity_alloc {
static mempool_t integrity_buf_pool;
+static bool bi_offload_capable(struct blk_integrity *bi)
+{
+ return bi->metadata_size == bi->pi_tuple_size;
+}
+
+unsigned int __bio_integrity_action(struct bio *bio)
+{
+ struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
+
+ if (WARN_ON_ONCE(bio_has_crypt_ctx(bio)))
+ return 0;
+
+ switch (bio_op(bio)) {
+ case REQ_OP_READ:
+ if (bi->flags & BLK_INTEGRITY_NOVERIFY) {
+ if (bi_offload_capable(bi))
+ return 0;
+ return BI_ACT_BUFFER;
+ }
+ return BI_ACT_BUFFER | BI_ACT_CHECK;
+ case REQ_OP_WRITE:
+ /*
+ * Flush masquerading as write?
+ */
+ if (!bio_sectors(bio))
+ return 0;
+
+ /*
+ * Zero the memory allocated to not leak uninitialized kernel
+ * memory to disk for non-integrity metadata where nothing else
+ * initializes the memory.
+ */
+ if (bi->flags & BLK_INTEGRITY_NOGENERATE) {
+ if (bi_offload_capable(bi))
+ return 0;
+ return BI_ACT_BUFFER | BI_ACT_ZERO;
+ }
+
+ if (bi->metadata_size > bi->pi_tuple_size)
+ return BI_ACT_BUFFER | BI_ACT_CHECK | BI_ACT_ZERO;
+ return BI_ACT_BUFFER | BI_ACT_CHECK;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(__bio_integrity_action);
+
void bio_integrity_alloc_buf(struct bio *bio, bool zero_buffer)
{
struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
@@ -53,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/block/bio.c b/block/bio.c
index d80d5d26804e3..784d2a66d3aef 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/block/blk-mq.c b/block/blk-mq.c
index 3da2215b29125..a047faf3b0ec9 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -3143,6 +3143,7 @@ void blk_mq_submit_bio(struct bio *bio)
struct request_queue *q = bdev_get_queue(bio->bi_bdev);
struct blk_plug *plug = current->plug;
const int is_sync = op_is_sync(bio->bi_opf);
+ unsigned int integrity_action;
struct blk_mq_hw_ctx *hctx;
unsigned int nr_segs;
struct request *rq;
@@ -3195,8 +3196,9 @@ void blk_mq_submit_bio(struct bio *bio)
if (!bio)
goto queue_exit;
- if (!bio_integrity_prep(bio))
- goto queue_exit;
+ integrity_action = bio_integrity_action(bio);
+ if (integrity_action)
+ bio_integrity_prep(bio, integrity_action);
blk_mq_bio_issue_init(q, bio);
if (blk_mq_attempt_bio_merge(q, bio, nr_segs))
diff --git a/block/blk-settings.c b/block/blk-settings.c
index a9e65dc090dae..dabfab97fbab0 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/block/blk.h b/block/blk.h
index f6053e9dd2aac..c5b2115b9ea43 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -699,8 +699,10 @@ int bdev_open(struct block_device *bdev, blk_mode_t mode, void *holder,
const struct blk_holder_ops *hops, struct file *bdev_file);
int bdev_permission(dev_t dev, blk_mode_t mode, void *holder);
-void blk_integrity_generate(struct bio *bio);
-void blk_integrity_verify_iter(struct bio *bio, struct bvec_iter *saved_iter);
+void bio_integrity_generate(struct bio *bio);
+blk_status_t bio_integrity_verify(struct bio *bio,
+ struct bvec_iter *saved_iter);
+
void blk_integrity_prepare(struct request *rq);
void blk_integrity_complete(struct request *rq, unsigned int nr_bytes);
diff --git a/block/t10-pi.c b/block/t10-pi.c
index 0c4ed97021460..d27be6041fd31 100644
--- a/block/t10-pi.c
+++ b/block/t10-pi.c
@@ -372,7 +372,7 @@ static void ext_pi_type1_complete(struct request *rq, unsigned int nr_bytes)
}
}
-void blk_integrity_generate(struct bio *bio)
+void bio_integrity_generate(struct bio *bio)
{
struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
struct bio_integrity_payload *bip = bio_integrity(bio);
@@ -404,7 +404,7 @@ void blk_integrity_generate(struct bio *bio)
}
}
-void blk_integrity_verify_iter(struct bio *bio, struct bvec_iter *saved_iter)
+blk_status_t bio_integrity_verify(struct bio *bio, struct bvec_iter *saved_iter)
{
struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
struct bio_integrity_payload *bip = bio_integrity(bio);
@@ -439,11 +439,11 @@ void blk_integrity_verify_iter(struct bio *bio, struct bvec_iter *saved_iter)
}
kunmap_local(kaddr);
- if (ret) {
- bio->bi_status = ret;
- return;
- }
+ if (ret)
+ return ret;
}
+
+ return BLK_STS_OK;
}
void blk_integrity_prepare(struct request *rq)