mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-08-26 21:52:20 +00:00

The tuple_size field in blk_integrity currently represents the total size of metadata associated with each data interval. To make the meaning more explicit, rename tuple_size to metadata_size. This is a purely mechanical rename with no functional changes. Suggested-by: Martin K. Petersen <martin.petersen@oracle.com> Signed-off-by: Anuj Gupta <anuj20.g@samsung.com> Link: https://lore.kernel.org/20250630090548.3317-2-anuj20.g@samsung.com Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com> Signed-off-by: Christian Brauner <brauner@kernel.org>
224 lines
5.9 KiB
C
224 lines
5.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2007, 2008, 2009 Oracle Corporation
|
|
* Written by: Martin K. Petersen <martin.petersen@oracle.com>
|
|
*
|
|
* Automatically generate and verify integrity data on PI capable devices if the
|
|
* bio submitter didn't provide PI itself. This ensures that kernel verifies
|
|
* data integrity even if the file system (or other user of the block device) is
|
|
* not aware of PI.
|
|
*/
|
|
#include <linux/blk-integrity.h>
|
|
#include <linux/t10-pi.h>
|
|
#include <linux/workqueue.h>
|
|
#include "blk.h"
|
|
|
|
struct bio_integrity_data {
|
|
struct bio *bio;
|
|
struct bvec_iter saved_bio_iter;
|
|
struct work_struct work;
|
|
struct bio_integrity_payload bip;
|
|
struct bio_vec bvec;
|
|
};
|
|
|
|
static struct kmem_cache *bid_slab;
|
|
static mempool_t bid_pool;
|
|
static struct workqueue_struct *kintegrityd_wq;
|
|
|
|
static void bio_integrity_finish(struct bio_integrity_data *bid)
|
|
{
|
|
bid->bio->bi_integrity = NULL;
|
|
bid->bio->bi_opf &= ~REQ_INTEGRITY;
|
|
kfree(bvec_virt(bid->bip.bip_vec));
|
|
mempool_free(bid, &bid_pool);
|
|
}
|
|
|
|
static void bio_integrity_verify_fn(struct work_struct *work)
|
|
{
|
|
struct bio_integrity_data *bid =
|
|
container_of(work, struct bio_integrity_data, work);
|
|
struct bio *bio = bid->bio;
|
|
|
|
blk_integrity_verify_iter(bio, &bid->saved_bio_iter);
|
|
bio_integrity_finish(bid);
|
|
bio_endio(bio);
|
|
}
|
|
|
|
#define BIP_CHECK_FLAGS (BIP_CHECK_GUARD | BIP_CHECK_REFTAG | BIP_CHECK_APPTAG)
|
|
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)
|
|
{
|
|
switch (bi->csum_type) {
|
|
case BLK_INTEGRITY_CSUM_CRC64:
|
|
return bi->metadata_size == sizeof(struct crc64_pi_tuple);
|
|
case BLK_INTEGRITY_CSUM_CRC:
|
|
case BLK_INTEGRITY_CSUM_IP:
|
|
return bi->metadata_size == sizeof(struct t10_pi_tuple);
|
|
default:
|
|
pr_warn_once("%s: unknown integrity checksum type:%d\n",
|
|
__func__, bi->csum_type);
|
|
fallthrough;
|
|
case BLK_INTEGRITY_CSUM_NONE:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* __bio_integrity_endio - Integrity I/O completion function
|
|
* @bio: Protected bio
|
|
*
|
|
* Normally I/O completion is done in interrupt context. However, verifying I/O
|
|
* integrity is a time-consuming task which must be run in process context.
|
|
*
|
|
* This function postpones completion accordingly.
|
|
*/
|
|
bool __bio_integrity_endio(struct bio *bio)
|
|
{
|
|
struct bio_integrity_payload *bip = bio_integrity(bio);
|
|
struct bio_integrity_data *bid =
|
|
container_of(bip, struct bio_integrity_data, bip);
|
|
|
|
if (bio_op(bio) == REQ_OP_READ && !bio->bi_status &&
|
|
bip_should_check(bip)) {
|
|
INIT_WORK(&bid->work, bio_integrity_verify_fn);
|
|
queue_work(kintegrityd_wq, &bid->work);
|
|
return false;
|
|
}
|
|
|
|
bio_integrity_finish(bid);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* bio_integrity_prep - Prepare bio for integrity I/O
|
|
* @bio: bio to prepare
|
|
*
|
|
* 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.
|
|
*/
|
|
bool bio_integrity_prep(struct bio *bio)
|
|
{
|
|
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;
|
|
unsigned int len;
|
|
void *buf;
|
|
|
|
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->csum_type == BLK_INTEGRITY_CSUM_NONE)
|
|
gfp |= __GFP_ZERO;
|
|
break;
|
|
default:
|
|
return true;
|
|
}
|
|
|
|
if (WARN_ON_ONCE(bio_has_crypt_ctx(bio)))
|
|
return true;
|
|
|
|
/* Allocate kernel buffer for protection data */
|
|
len = bio_integrity_bytes(bi, bio_sectors(bio));
|
|
buf = kmalloc(len, gfp);
|
|
if (!buf)
|
|
goto err_end_io;
|
|
bid = mempool_alloc(&bid_pool, GFP_NOIO);
|
|
if (!bid)
|
|
goto err_free_buf;
|
|
bio_integrity_init(bio, &bid->bip, &bid->bvec, 1);
|
|
|
|
bid->bio = bio;
|
|
|
|
bid->bip.bip_flags |= BIP_BLOCK_INTEGRITY;
|
|
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;
|
|
}
|
|
|
|
if (bio_integrity_add_page(bio, virt_to_page(buf), len,
|
|
offset_in_page(buf)) < len)
|
|
goto err_end_io;
|
|
|
|
/* Auto-generate integrity metadata if this is a write */
|
|
if (bio_data_dir(bio) == WRITE && bip_should_check(&bid->bip))
|
|
blk_integrity_generate(bio);
|
|
else
|
|
bid->saved_bio_iter = bio->bi_iter;
|
|
return true;
|
|
|
|
err_free_buf:
|
|
kfree(buf);
|
|
err_end_io:
|
|
bio->bi_status = BLK_STS_RESOURCE;
|
|
bio_endio(bio);
|
|
return false;
|
|
}
|
|
EXPORT_SYMBOL(bio_integrity_prep);
|
|
|
|
void blk_flush_integrity(void)
|
|
{
|
|
flush_workqueue(kintegrityd_wq);
|
|
}
|
|
|
|
static int __init blk_integrity_auto_init(void)
|
|
{
|
|
bid_slab = kmem_cache_create("bio_integrity_data",
|
|
sizeof(struct bio_integrity_data), 0,
|
|
SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
|
|
|
|
if (mempool_init_slab_pool(&bid_pool, BIO_POOL_SIZE, bid_slab))
|
|
panic("bio: can't create integrity pool\n");
|
|
|
|
/*
|
|
* kintegrityd won't block much but may burn a lot of CPU cycles.
|
|
* Make it highpri CPU intensive wq with max concurrency of 1.
|
|
*/
|
|
kintegrityd_wq = alloc_workqueue("kintegrityd", WQ_MEM_RECLAIM |
|
|
WQ_HIGHPRI | WQ_CPU_INTENSIVE, 1);
|
|
if (!kintegrityd_wq)
|
|
panic("Failed to create kintegrityd\n");
|
|
return 0;
|
|
}
|
|
subsys_initcall(blk_integrity_auto_init);
|