linux/block/bio-integrity-fs.c
Christoph Hellwig 7de79a5ffe block,iomap: fix protection information verification with initial bvec offset
When reconstructing a bvec_iter from an ioend for protection information
verification, iomap currently ignores the offset into the initial
bio_vec.

This can't happen for buffered I/O or direct I/O to user addresses, but
is exercised by splice on O_DIRECT file descriptors or when using the
loop driver in direct I/O mode.

Fortunately the only file system PI user (XFS) currently always bounce
buffers, so this can't actually be triggered yet.  But we'll want to make
the bounce buffering conditional soon, for which this needs to be fixed.

Store the initial offset in struct iomap_ioend, and pass a
pre-constructed bvec_iter to fs_bio_integrity_verify.  For the
synchronous read case the fix is even simpler as this path can
simply stash away the original bvec_iter.

Fixes: 0bde8a12b5 ("block: add fs_bio_integrity helpers")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Link: https://patch.msgid.link/20260909060924.1102037-10-hch@lst.de
Signed-off-by: Jens Axboe <axboe@kernel.dk>
2026-09-14 07:42:09 -06:00

89 lines
2.4 KiB
C

// 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_NOFS);
bio_integrity_init(bio, &iib->bip, &iib->bvec, 1);
bio_integrity_alloc_buf(bio, GFP_NOFS, action & BI_ACT_ZERO);
if (action & BI_ACT_CHECK)
bio_integrity_setup_default(bio);
return action;
}
EXPORT_SYMBOL_GPL(fs_bio_integrity_alloc);
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;
}
EXPORT_SYMBOL_GPL(fs_bio_integrity_free);
void fs_bio_integrity_generate(struct bio *bio)
{
if (fs_bio_integrity_alloc(bio) &&
(bio_integrity(bio)->bip_flags & BIP_CHECK_FLAGS))
bio_integrity_generate(bio);
}
EXPORT_SYMBOL_GPL(fs_bio_integrity_generate);
int fs_bio_integrity_verify(struct bio *bio, struct bvec_iter *data_iter)
{
struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
struct bio_integrity_payload *bip = bio_integrity(bio);
if (!bip || !(bip->bip_flags & BIP_CHECK_FLAGS))
return 0;
/*
* 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 = data_iter->bi_sector;
bip->bip_iter.bi_size =
bio_integrity_bytes(bi, data_iter->bi_size >> SECTOR_SHIFT);
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",
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);