mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
A bio segment may have partial interval block data with the rest continuing into the next segments because direct-io data payloads only need to align in memory to the device's DMA limits. At the same time, the protection information may also be split in multiple segments. The most likely way that may happen is if two requests merge, or if we're directly using the io_uring user metadata. The generate/verify, however, only ever accessed the first bip_vec. Further, it may be possible to unalign the protection fields from the user space buffer, or if there are odd additional opaque bytes in front or in back of the protection information metadata region. Change up the iteration to allow spanning multiple segments. This patch is mostly a re-write of the protection information handling to allow any arbitrary alignments, so it's probably easier to review the end result rather than the diff. Many controllers are not able to handle interval data composed of multiple segments when PI is used, so this patch introduces a new integrity limit that a low level driver can set to notify that it is capable, default to false. The nvme driver is the first one to enable it in this patch. Everyone else will force DMA alignment to the logical block size as before to ensure interval data is always aligned within a single segment. Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Keith Busch <kbusch@kernel.org> Link: https://patch.msgid.link/20260313144701.1221652-2-kbusch@meta.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
568 lines
14 KiB
C
568 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* t10_pi.c - Functions for generating and verifying T10 Protection
|
|
* Information.
|
|
*/
|
|
|
|
#include <linux/t10-pi.h>
|
|
#include <linux/blk-integrity.h>
|
|
#include <linux/crc-t10dif.h>
|
|
#include <linux/crc64.h>
|
|
#include <net/checksum.h>
|
|
#include <linux/unaligned.h>
|
|
#include "blk.h"
|
|
|
|
#define APP_TAG_ESCAPE 0xffff
|
|
#define REF_TAG_ESCAPE 0xffffffff
|
|
|
|
/*
|
|
* This union is used for onstack allocations when the pi field is split across
|
|
* segments. blk_validate_integrity_limits() guarantees pi_tuple_size matches
|
|
* the sizeof one of these two types.
|
|
*/
|
|
union pi_tuple {
|
|
struct crc64_pi_tuple crc64_pi;
|
|
struct t10_pi_tuple t10_pi;
|
|
};
|
|
|
|
struct blk_integrity_iter {
|
|
struct bio *bio;
|
|
struct bio_integrity_payload *bip;
|
|
struct blk_integrity *bi;
|
|
struct bvec_iter data_iter;
|
|
struct bvec_iter prot_iter;
|
|
unsigned int interval_remaining;
|
|
u64 seed;
|
|
u64 csum;
|
|
};
|
|
|
|
static void blk_calculate_guard(struct blk_integrity_iter *iter, void *data,
|
|
unsigned int len)
|
|
{
|
|
switch (iter->bi->csum_type) {
|
|
case BLK_INTEGRITY_CSUM_CRC64:
|
|
iter->csum = crc64_nvme(iter->csum, data, len);
|
|
break;
|
|
case BLK_INTEGRITY_CSUM_CRC:
|
|
iter->csum = crc_t10dif_update(iter->csum, data, len);
|
|
break;
|
|
case BLK_INTEGRITY_CSUM_IP:
|
|
iter->csum = (__force u32)csum_partial(data, len,
|
|
(__force __wsum)iter->csum);
|
|
break;
|
|
default:
|
|
WARN_ON_ONCE(1);
|
|
iter->csum = U64_MAX;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void blk_integrity_csum_finish(struct blk_integrity_iter *iter)
|
|
{
|
|
switch (iter->bi->csum_type) {
|
|
case BLK_INTEGRITY_CSUM_IP:
|
|
iter->csum = (__force u16)csum_fold((__force __wsum)iter->csum);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update the csum for formats that have metadata padding in front of the data
|
|
* integrity field
|
|
*/
|
|
static void blk_integrity_csum_offset(struct blk_integrity_iter *iter)
|
|
{
|
|
unsigned int offset = iter->bi->pi_offset;
|
|
struct bio_vec *bvec = iter->bip->bip_vec;
|
|
|
|
while (offset > 0) {
|
|
struct bio_vec pbv = bvec_iter_bvec(bvec, iter->prot_iter);
|
|
unsigned int len = min(pbv.bv_len, offset);
|
|
void *prot_buf = bvec_kmap_local(&pbv);
|
|
|
|
blk_calculate_guard(iter, prot_buf, len);
|
|
kunmap_local(prot_buf);
|
|
offset -= len;
|
|
bvec_iter_advance_single(bvec, &iter->prot_iter, len);
|
|
}
|
|
blk_integrity_csum_finish(iter);
|
|
}
|
|
|
|
static void blk_integrity_copy_from_tuple(struct bio_integrity_payload *bip,
|
|
struct bvec_iter *iter, void *tuple,
|
|
unsigned int tuple_size)
|
|
{
|
|
while (tuple_size) {
|
|
struct bio_vec pbv = bvec_iter_bvec(bip->bip_vec, *iter);
|
|
unsigned int len = min(tuple_size, pbv.bv_len);
|
|
void *prot_buf = bvec_kmap_local(&pbv);
|
|
|
|
memcpy(prot_buf, tuple, len);
|
|
kunmap_local(prot_buf);
|
|
bvec_iter_advance_single(bip->bip_vec, iter, len);
|
|
tuple_size -= len;
|
|
tuple += len;
|
|
}
|
|
}
|
|
|
|
static void blk_integrity_copy_to_tuple(struct bio_integrity_payload *bip,
|
|
struct bvec_iter *iter, void *tuple,
|
|
unsigned int tuple_size)
|
|
{
|
|
while (tuple_size) {
|
|
struct bio_vec pbv = bvec_iter_bvec(bip->bip_vec, *iter);
|
|
unsigned int len = min(tuple_size, pbv.bv_len);
|
|
void *prot_buf = bvec_kmap_local(&pbv);
|
|
|
|
memcpy(tuple, prot_buf, len);
|
|
kunmap_local(prot_buf);
|
|
bvec_iter_advance_single(bip->bip_vec, iter, len);
|
|
tuple_size -= len;
|
|
tuple += len;
|
|
}
|
|
}
|
|
|
|
static bool ext_pi_ref_escape(const u8 ref_tag[6])
|
|
{
|
|
static const u8 ref_escape[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
|
|
return memcmp(ref_tag, ref_escape, sizeof(ref_escape)) == 0;
|
|
}
|
|
|
|
static blk_status_t blk_verify_ext_pi(struct blk_integrity_iter *iter,
|
|
struct crc64_pi_tuple *pi)
|
|
{
|
|
u64 seed = lower_48_bits(iter->seed);
|
|
u64 guard = get_unaligned_be64(&pi->guard_tag);
|
|
u64 ref = get_unaligned_be48(pi->ref_tag);
|
|
u16 app = get_unaligned_be16(&pi->app_tag);
|
|
|
|
if (iter->bi->flags & BLK_INTEGRITY_REF_TAG) {
|
|
if (app == APP_TAG_ESCAPE)
|
|
return BLK_STS_OK;
|
|
if (ref != seed) {
|
|
pr_err("%s: ref tag error at location %llu (rcvd %llu)\n",
|
|
iter->bio->bi_bdev->bd_disk->disk_name, seed,
|
|
ref);
|
|
return BLK_STS_PROTECTION;
|
|
}
|
|
} else if (app == APP_TAG_ESCAPE && ext_pi_ref_escape(pi->ref_tag)) {
|
|
return BLK_STS_OK;
|
|
}
|
|
|
|
if (guard != iter->csum) {
|
|
pr_err("%s: guard tag error at sector %llu (rcvd %016llx, want %016llx)\n",
|
|
iter->bio->bi_bdev->bd_disk->disk_name, iter->seed,
|
|
guard, iter->csum);
|
|
return BLK_STS_PROTECTION;
|
|
}
|
|
|
|
return BLK_STS_OK;
|
|
}
|
|
|
|
static blk_status_t blk_verify_pi(struct blk_integrity_iter *iter,
|
|
struct t10_pi_tuple *pi, u16 guard)
|
|
{
|
|
u32 seed = lower_32_bits(iter->seed);
|
|
u32 ref = get_unaligned_be32(&pi->ref_tag);
|
|
u16 app = get_unaligned_be16(&pi->app_tag);
|
|
|
|
if (iter->bi->flags & BLK_INTEGRITY_REF_TAG) {
|
|
if (app == APP_TAG_ESCAPE)
|
|
return BLK_STS_OK;
|
|
if (ref != seed) {
|
|
pr_err("%s: ref tag error at location %u (rcvd %u)\n",
|
|
iter->bio->bi_bdev->bd_disk->disk_name, seed,
|
|
ref);
|
|
return BLK_STS_PROTECTION;
|
|
}
|
|
} else if (app == APP_TAG_ESCAPE && ref == REF_TAG_ESCAPE) {
|
|
return BLK_STS_OK;
|
|
}
|
|
|
|
if (guard != (u16)iter->csum) {
|
|
pr_err("%s: guard tag error at sector %llu (rcvd %04x, want %04x)\n",
|
|
iter->bio->bi_bdev->bd_disk->disk_name, iter->seed,
|
|
guard, (u16)iter->csum);
|
|
return BLK_STS_PROTECTION;
|
|
}
|
|
|
|
return BLK_STS_OK;
|
|
}
|
|
|
|
static blk_status_t blk_verify_t10_pi(struct blk_integrity_iter *iter,
|
|
struct t10_pi_tuple *pi)
|
|
{
|
|
u16 guard = get_unaligned_be16(&pi->guard_tag);
|
|
|
|
return blk_verify_pi(iter, pi, guard);
|
|
}
|
|
|
|
static blk_status_t blk_verify_ip_pi(struct blk_integrity_iter *iter,
|
|
struct t10_pi_tuple *pi)
|
|
{
|
|
u16 guard = get_unaligned((u16 *)&pi->guard_tag);
|
|
|
|
return blk_verify_pi(iter, pi, guard);
|
|
}
|
|
|
|
static blk_status_t blk_integrity_verify(struct blk_integrity_iter *iter,
|
|
union pi_tuple *tuple)
|
|
{
|
|
switch (iter->bi->csum_type) {
|
|
case BLK_INTEGRITY_CSUM_CRC64:
|
|
return blk_verify_ext_pi(iter, &tuple->crc64_pi);
|
|
case BLK_INTEGRITY_CSUM_CRC:
|
|
return blk_verify_t10_pi(iter, &tuple->t10_pi);
|
|
case BLK_INTEGRITY_CSUM_IP:
|
|
return blk_verify_ip_pi(iter, &tuple->t10_pi);
|
|
default:
|
|
return BLK_STS_OK;
|
|
}
|
|
}
|
|
|
|
static void blk_set_ext_pi(struct blk_integrity_iter *iter,
|
|
struct crc64_pi_tuple *pi)
|
|
{
|
|
put_unaligned_be64(iter->csum, &pi->guard_tag);
|
|
put_unaligned_be16(0, &pi->app_tag);
|
|
put_unaligned_be48(iter->seed, &pi->ref_tag);
|
|
}
|
|
|
|
static void blk_set_pi(struct blk_integrity_iter *iter,
|
|
struct t10_pi_tuple *pi, __be16 csum)
|
|
{
|
|
put_unaligned(csum, &pi->guard_tag);
|
|
put_unaligned_be16(0, &pi->app_tag);
|
|
put_unaligned_be32(iter->seed, &pi->ref_tag);
|
|
}
|
|
|
|
static void blk_set_t10_pi(struct blk_integrity_iter *iter,
|
|
struct t10_pi_tuple *pi)
|
|
{
|
|
blk_set_pi(iter, pi, cpu_to_be16((u16)iter->csum));
|
|
}
|
|
|
|
static void blk_set_ip_pi(struct blk_integrity_iter *iter,
|
|
struct t10_pi_tuple *pi)
|
|
{
|
|
blk_set_pi(iter, pi, (__force __be16)(u16)iter->csum);
|
|
}
|
|
|
|
static void blk_integrity_set(struct blk_integrity_iter *iter,
|
|
union pi_tuple *tuple)
|
|
{
|
|
switch (iter->bi->csum_type) {
|
|
case BLK_INTEGRITY_CSUM_CRC64:
|
|
return blk_set_ext_pi(iter, &tuple->crc64_pi);
|
|
case BLK_INTEGRITY_CSUM_CRC:
|
|
return blk_set_t10_pi(iter, &tuple->t10_pi);
|
|
case BLK_INTEGRITY_CSUM_IP:
|
|
return blk_set_ip_pi(iter, &tuple->t10_pi);
|
|
default:
|
|
WARN_ON_ONCE(1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static blk_status_t blk_integrity_interval(struct blk_integrity_iter *iter,
|
|
bool verify)
|
|
{
|
|
blk_status_t ret = BLK_STS_OK;
|
|
union pi_tuple tuple;
|
|
void *ptuple = &tuple;
|
|
struct bio_vec pbv;
|
|
|
|
blk_integrity_csum_offset(iter);
|
|
pbv = bvec_iter_bvec(iter->bip->bip_vec, iter->prot_iter);
|
|
if (pbv.bv_len >= iter->bi->pi_tuple_size) {
|
|
ptuple = bvec_kmap_local(&pbv);
|
|
bvec_iter_advance_single(iter->bip->bip_vec, &iter->prot_iter,
|
|
iter->bi->metadata_size - iter->bi->pi_offset);
|
|
} else if (verify) {
|
|
blk_integrity_copy_to_tuple(iter->bip, &iter->prot_iter,
|
|
ptuple, iter->bi->pi_tuple_size);
|
|
}
|
|
|
|
if (verify)
|
|
ret = blk_integrity_verify(iter, ptuple);
|
|
else
|
|
blk_integrity_set(iter, ptuple);
|
|
|
|
if (likely(ptuple != &tuple)) {
|
|
kunmap_local(ptuple);
|
|
} else if (!verify) {
|
|
blk_integrity_copy_from_tuple(iter->bip, &iter->prot_iter,
|
|
ptuple, iter->bi->pi_tuple_size);
|
|
}
|
|
|
|
iter->interval_remaining = 1 << iter->bi->interval_exp;
|
|
iter->csum = 0;
|
|
iter->seed++;
|
|
return ret;
|
|
}
|
|
|
|
static blk_status_t blk_integrity_iterate(struct bio *bio,
|
|
struct bvec_iter *data_iter,
|
|
bool verify)
|
|
{
|
|
struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
|
|
struct bio_integrity_payload *bip = bio_integrity(bio);
|
|
struct blk_integrity_iter iter = {
|
|
.bio = bio,
|
|
.bip = bip,
|
|
.bi = bi,
|
|
.data_iter = *data_iter,
|
|
.prot_iter = bip->bip_iter,
|
|
.interval_remaining = 1 << bi->interval_exp,
|
|
.seed = data_iter->bi_sector,
|
|
.csum = 0,
|
|
};
|
|
blk_status_t ret = BLK_STS_OK;
|
|
|
|
while (iter.data_iter.bi_size && ret == BLK_STS_OK) {
|
|
struct bio_vec bv = bvec_iter_bvec(iter.bio->bi_io_vec,
|
|
iter.data_iter);
|
|
void *kaddr = bvec_kmap_local(&bv);
|
|
void *data = kaddr;
|
|
unsigned int len;
|
|
|
|
bvec_iter_advance_single(iter.bio->bi_io_vec, &iter.data_iter,
|
|
bv.bv_len);
|
|
while (bv.bv_len && ret == BLK_STS_OK) {
|
|
len = min(iter.interval_remaining, bv.bv_len);
|
|
blk_calculate_guard(&iter, data, len);
|
|
bv.bv_len -= len;
|
|
data += len;
|
|
iter.interval_remaining -= len;
|
|
if (!iter.interval_remaining)
|
|
ret = blk_integrity_interval(&iter, verify);
|
|
}
|
|
kunmap_local(kaddr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void bio_integrity_generate(struct bio *bio)
|
|
{
|
|
struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
|
|
|
|
switch (bi->csum_type) {
|
|
case BLK_INTEGRITY_CSUM_CRC64:
|
|
case BLK_INTEGRITY_CSUM_CRC:
|
|
case BLK_INTEGRITY_CSUM_IP:
|
|
blk_integrity_iterate(bio, &bio->bi_iter, false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
switch (bi->csum_type) {
|
|
case BLK_INTEGRITY_CSUM_CRC64:
|
|
case BLK_INTEGRITY_CSUM_CRC:
|
|
case BLK_INTEGRITY_CSUM_IP:
|
|
return blk_integrity_iterate(bio, saved_iter, true);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return BLK_STS_OK;
|
|
}
|
|
|
|
/*
|
|
* Advance @iter past the protection offset for protection formats that
|
|
* contain front padding on the metadata region.
|
|
*/
|
|
static void blk_pi_advance_offset(struct blk_integrity *bi,
|
|
struct bio_integrity_payload *bip,
|
|
struct bvec_iter *iter)
|
|
{
|
|
unsigned int offset = bi->pi_offset;
|
|
|
|
while (offset > 0) {
|
|
struct bio_vec bv = mp_bvec_iter_bvec(bip->bip_vec, *iter);
|
|
unsigned int len = min(bv.bv_len, offset);
|
|
|
|
bvec_iter_advance_single(bip->bip_vec, iter, len);
|
|
offset -= len;
|
|
}
|
|
}
|
|
|
|
static void *blk_tuple_remap_begin(union pi_tuple *tuple,
|
|
struct blk_integrity *bi,
|
|
struct bio_integrity_payload *bip,
|
|
struct bvec_iter *iter)
|
|
{
|
|
struct bvec_iter titer;
|
|
struct bio_vec pbv;
|
|
|
|
blk_pi_advance_offset(bi, bip, iter);
|
|
pbv = bvec_iter_bvec(bip->bip_vec, *iter);
|
|
if (likely(pbv.bv_len >= bi->pi_tuple_size))
|
|
return bvec_kmap_local(&pbv);
|
|
|
|
/*
|
|
* We need to preserve the state of the original iter for the
|
|
* copy_from_tuple at the end, so make a temp iter for here.
|
|
*/
|
|
titer = *iter;
|
|
blk_integrity_copy_to_tuple(bip, &titer, tuple, bi->pi_tuple_size);
|
|
return tuple;
|
|
}
|
|
|
|
static void blk_tuple_remap_end(union pi_tuple *tuple, void *ptuple,
|
|
struct blk_integrity *bi,
|
|
struct bio_integrity_payload *bip,
|
|
struct bvec_iter *iter)
|
|
{
|
|
unsigned int len = bi->metadata_size - bi->pi_offset;
|
|
|
|
if (likely(ptuple != tuple)) {
|
|
kunmap_local(ptuple);
|
|
} else {
|
|
blk_integrity_copy_from_tuple(bip, iter, ptuple,
|
|
bi->pi_tuple_size);
|
|
len -= bi->pi_tuple_size;
|
|
}
|
|
|
|
bvec_iter_advance(bip->bip_vec, iter, len);
|
|
}
|
|
|
|
static void blk_set_ext_unmap_ref(struct crc64_pi_tuple *pi, u64 virt,
|
|
u64 ref_tag)
|
|
{
|
|
u64 ref = get_unaligned_be48(&pi->ref_tag);
|
|
|
|
if (ref == lower_48_bits(ref_tag) && ref != lower_48_bits(virt))
|
|
put_unaligned_be48(virt, pi->ref_tag);
|
|
}
|
|
|
|
static void blk_set_t10_unmap_ref(struct t10_pi_tuple *pi, u32 virt,
|
|
u32 ref_tag)
|
|
{
|
|
u32 ref = get_unaligned_be32(&pi->ref_tag);
|
|
|
|
if (ref == ref_tag && ref != virt)
|
|
put_unaligned_be32(virt, &pi->ref_tag);
|
|
}
|
|
|
|
static void blk_reftag_remap_complete(struct blk_integrity *bi,
|
|
union pi_tuple *tuple, u64 virt, u64 ref)
|
|
{
|
|
switch (bi->csum_type) {
|
|
case BLK_INTEGRITY_CSUM_CRC64:
|
|
blk_set_ext_unmap_ref(&tuple->crc64_pi, virt, ref);
|
|
break;
|
|
case BLK_INTEGRITY_CSUM_CRC:
|
|
case BLK_INTEGRITY_CSUM_IP:
|
|
blk_set_t10_unmap_ref(&tuple->t10_pi, virt, ref);
|
|
break;
|
|
default:
|
|
WARN_ON_ONCE(1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void blk_set_ext_map_ref(struct crc64_pi_tuple *pi, u64 virt,
|
|
u64 ref_tag)
|
|
{
|
|
u64 ref = get_unaligned_be48(&pi->ref_tag);
|
|
|
|
if (ref == lower_48_bits(virt) && ref != ref_tag)
|
|
put_unaligned_be48(ref_tag, pi->ref_tag);
|
|
}
|
|
|
|
static void blk_set_t10_map_ref(struct t10_pi_tuple *pi, u32 virt, u32 ref_tag)
|
|
{
|
|
u32 ref = get_unaligned_be32(&pi->ref_tag);
|
|
|
|
if (ref == virt && ref != ref_tag)
|
|
put_unaligned_be32(ref_tag, &pi->ref_tag);
|
|
}
|
|
|
|
static void blk_reftag_remap_prepare(struct blk_integrity *bi,
|
|
union pi_tuple *tuple,
|
|
u64 virt, u64 ref)
|
|
{
|
|
switch (bi->csum_type) {
|
|
case BLK_INTEGRITY_CSUM_CRC64:
|
|
blk_set_ext_map_ref(&tuple->crc64_pi, virt, ref);
|
|
break;
|
|
case BLK_INTEGRITY_CSUM_CRC:
|
|
case BLK_INTEGRITY_CSUM_IP:
|
|
blk_set_t10_map_ref(&tuple->t10_pi, virt, ref);
|
|
break;
|
|
default:
|
|
WARN_ON_ONCE(1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void __blk_reftag_remap(struct bio *bio, struct blk_integrity *bi,
|
|
unsigned *intervals, u64 *ref, bool prep)
|
|
{
|
|
struct bio_integrity_payload *bip = bio_integrity(bio);
|
|
struct bvec_iter iter = bip->bip_iter;
|
|
u64 virt = bip_get_seed(bip);
|
|
union pi_tuple *ptuple;
|
|
union pi_tuple tuple;
|
|
|
|
if (prep && bip->bip_flags & BIP_MAPPED_INTEGRITY) {
|
|
*ref += bio->bi_iter.bi_size >> bi->interval_exp;
|
|
return;
|
|
}
|
|
|
|
while (iter.bi_size && *intervals) {
|
|
ptuple = blk_tuple_remap_begin(&tuple, bi, bip, &iter);
|
|
|
|
if (prep)
|
|
blk_reftag_remap_prepare(bi, ptuple, virt, *ref);
|
|
else
|
|
blk_reftag_remap_complete(bi, ptuple, virt, *ref);
|
|
|
|
blk_tuple_remap_end(&tuple, ptuple, bi, bip, &iter);
|
|
(*intervals)--;
|
|
(*ref)++;
|
|
virt++;
|
|
}
|
|
|
|
if (prep)
|
|
bip->bip_flags |= BIP_MAPPED_INTEGRITY;
|
|
}
|
|
|
|
static void blk_integrity_remap(struct request *rq, unsigned int nr_bytes,
|
|
bool prep)
|
|
{
|
|
struct blk_integrity *bi = &rq->q->limits.integrity;
|
|
u64 ref = blk_rq_pos(rq) >> (bi->interval_exp - SECTOR_SHIFT);
|
|
unsigned intervals = nr_bytes >> bi->interval_exp;
|
|
struct bio *bio;
|
|
|
|
if (!(bi->flags & BLK_INTEGRITY_REF_TAG))
|
|
return;
|
|
|
|
__rq_for_each_bio(bio, rq) {
|
|
__blk_reftag_remap(bio, bi, &intervals, &ref, prep);
|
|
if (!intervals)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void blk_integrity_prepare(struct request *rq)
|
|
{
|
|
blk_integrity_remap(rq, blk_rq_bytes(rq), true);
|
|
}
|
|
|
|
void blk_integrity_complete(struct request *rq, unsigned int nr_bytes)
|
|
{
|
|
blk_integrity_remap(rq, nr_bytes, false);
|
|
}
|