Commit 89d10b78 authored by Jens Axboe's avatar Jens Axboe
Browse files

Merge branch 'for-7.1/block-integrity' into for-7.1/block

Merge in integrity changes which are also landing in the VFS tree as
dependencies for fs related changes.

* for-7.1/block-integrity:
  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
parents ecd92cfe a9aa6045
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+11 −69
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
// 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);
+64 −0
Original line number Diff line number Diff line
@@ -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
+10 −7
Original line number Diff line number Diff line
@@ -1326,9 +1326,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;
@@ -1366,9 +1367,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);
@@ -1407,6 +1409,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
@@ -1414,11 +1417,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)
Loading