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>
This commit is contained in:
Christian Brauner
2026-03-10 09:37:48 +01:00
15 changed files with 247 additions and 118 deletions

View File

@@ -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

View File

@@ -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_*)
*
* 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.
* Allocate the integrity payload. For writes, generate the integrity metadata
* and for reads, setup the completion handler to 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);

81
block/bio-integrity-fs.c Normal file
View File

@@ -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);

View File

@@ -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

View File

@@ -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)

View File

@@ -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))

View File

@@ -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;

View File

@@ -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);

View File

@@ -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)

View File

@@ -1435,14 +1435,16 @@ static void btt_submit_bio(struct bio *bio)
{
struct bio_integrity_payload *bip = bio_integrity(bio);
struct btt *btt = bio->bi_bdev->bd_disk->private_data;
unsigned int integrity_action;
struct bvec_iter iter;
unsigned long start;
struct bio_vec bvec;
int err = 0;
bool do_acct;
if (!bio_integrity_prep(bio))
return;
integrity_action = bio_integrity_action(bio);
if (integrity_action)
bio_integrity_prep(bio, integrity_action);
do_acct = blk_queue_io_stat(bio->bi_bdev->bd_disk->queue);
if (do_acct)

View File

@@ -351,7 +351,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);

View File

@@ -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,
@@ -144,5 +143,12 @@ 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);
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 */

View File

@@ -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,

View File

@@ -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,
@@ -180,4 +175,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 */

View File

@@ -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_has_integrity_csum(struct block_device *bdev)
{
struct queue_limits *lim = bdev_limits(bdev);
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)
{
struct request_queue *q = bdev_get_queue(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 bdev_has_integrity_csum(bdev) ||
(bdev_limits(bdev)->features & BLK_FEAT_STABLE_WRITES);
}
static inline bool blk_queue_write_cache(struct request_queue *q)
@@ -1877,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 */