xref: /openbmc/linux/block/t10-pi.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
18c16567dSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0
22341c2f8SMartin K. Petersen /*
32341c2f8SMartin K. Petersen  * t10_pi.c - Functions for generating and verifying T10 Protection
42341c2f8SMartin K. Petersen  *	      Information.
52341c2f8SMartin K. Petersen  */
62341c2f8SMartin K. Petersen 
72341c2f8SMartin K. Petersen #include <linux/t10-pi.h>
8fe45e630SChristoph Hellwig #include <linux/blk-integrity.h>
92341c2f8SMartin K. Petersen #include <linux/crc-t10dif.h>
10*a7d4383fSKeith Busch #include <linux/crc64.h>
11a754bd5fSHerbert Xu #include <linux/module.h>
122341c2f8SMartin K. Petersen #include <net/checksum.h>
13*a7d4383fSKeith Busch #include <asm/unaligned.h>
142341c2f8SMartin K. Petersen 
152341c2f8SMartin K. Petersen typedef __be16 (csum_fn) (void *, unsigned int);
162341c2f8SMartin K. Petersen 
t10_pi_crc_fn(void * data,unsigned int len)172341c2f8SMartin K. Petersen static __be16 t10_pi_crc_fn(void *data, unsigned int len)
182341c2f8SMartin K. Petersen {
192341c2f8SMartin K. Petersen 	return cpu_to_be16(crc_t10dif(data, len));
202341c2f8SMartin K. Petersen }
212341c2f8SMartin K. Petersen 
t10_pi_ip_fn(void * data,unsigned int len)222341c2f8SMartin K. Petersen static __be16 t10_pi_ip_fn(void *data, unsigned int len)
232341c2f8SMartin K. Petersen {
242341c2f8SMartin K. Petersen 	return (__force __be16)ip_compute_csum(data, len);
252341c2f8SMartin K. Petersen }
262341c2f8SMartin K. Petersen 
272341c2f8SMartin K. Petersen /*
282341c2f8SMartin K. Petersen  * Type 1 and Type 2 protection use the same format: 16 bit guard tag,
292341c2f8SMartin K. Petersen  * 16 bit app tag, 32 bit reference tag. Type 3 does not define the ref
302341c2f8SMartin K. Petersen  * tag.
312341c2f8SMartin K. Petersen  */
t10_pi_generate(struct blk_integrity_iter * iter,csum_fn * fn,enum t10_dif_type type)324e4cbee9SChristoph Hellwig static blk_status_t t10_pi_generate(struct blk_integrity_iter *iter,
335eaed68dSMax Gurtovoy 		csum_fn *fn, enum t10_dif_type type)
342341c2f8SMartin K. Petersen {
352341c2f8SMartin K. Petersen 	unsigned int i;
362341c2f8SMartin K. Petersen 
372341c2f8SMartin K. Petersen 	for (i = 0 ; i < iter->data_size ; i += iter->interval) {
382341c2f8SMartin K. Petersen 		struct t10_pi_tuple *pi = iter->prot_buf;
392341c2f8SMartin K. Petersen 
402341c2f8SMartin K. Petersen 		pi->guard_tag = fn(iter->data_buf, iter->interval);
412341c2f8SMartin K. Petersen 		pi->app_tag = 0;
422341c2f8SMartin K. Petersen 
435eaed68dSMax Gurtovoy 		if (type == T10_PI_TYPE1_PROTECTION)
442341c2f8SMartin K. Petersen 			pi->ref_tag = cpu_to_be32(lower_32_bits(iter->seed));
452341c2f8SMartin K. Petersen 		else
462341c2f8SMartin K. Petersen 			pi->ref_tag = 0;
472341c2f8SMartin K. Petersen 
482341c2f8SMartin K. Petersen 		iter->data_buf += iter->interval;
49c340b990SKeith Busch 		iter->prot_buf += iter->tuple_size;
502341c2f8SMartin K. Petersen 		iter->seed++;
512341c2f8SMartin K. Petersen 	}
522341c2f8SMartin K. Petersen 
534e4cbee9SChristoph Hellwig 	return BLK_STS_OK;
542341c2f8SMartin K. Petersen }
552341c2f8SMartin K. Petersen 
t10_pi_verify(struct blk_integrity_iter * iter,csum_fn * fn,enum t10_dif_type type)564e4cbee9SChristoph Hellwig static blk_status_t t10_pi_verify(struct blk_integrity_iter *iter,
575eaed68dSMax Gurtovoy 		csum_fn *fn, enum t10_dif_type type)
582341c2f8SMartin K. Petersen {
592341c2f8SMartin K. Petersen 	unsigned int i;
602341c2f8SMartin K. Petersen 
61be21683eSMax Gurtovoy 	BUG_ON(type == T10_PI_TYPE0_PROTECTION);
62be21683eSMax Gurtovoy 
632341c2f8SMartin K. Petersen 	for (i = 0 ; i < iter->data_size ; i += iter->interval) {
642341c2f8SMartin K. Petersen 		struct t10_pi_tuple *pi = iter->prot_buf;
652341c2f8SMartin K. Petersen 		__be16 csum;
662341c2f8SMartin K. Petersen 
67be21683eSMax Gurtovoy 		if (type == T10_PI_TYPE1_PROTECTION ||
68be21683eSMax Gurtovoy 		    type == T10_PI_TYPE2_PROTECTION) {
69128b6f9fSDmitry Monakhov 			if (pi->app_tag == T10_PI_APP_ESCAPE)
702341c2f8SMartin K. Petersen 				goto next;
712341c2f8SMartin K. Petersen 
722341c2f8SMartin K. Petersen 			if (be32_to_cpu(pi->ref_tag) !=
732341c2f8SMartin K. Petersen 			    lower_32_bits(iter->seed)) {
742341c2f8SMartin K. Petersen 				pr_err("%s: ref tag error at location %llu " \
752341c2f8SMartin K. Petersen 				       "(rcvd %u)\n", iter->disk_name,
762341c2f8SMartin K. Petersen 				       (unsigned long long)
772341c2f8SMartin K. Petersen 				       iter->seed, be32_to_cpu(pi->ref_tag));
78a462b950SBart Van Assche 				return BLK_STS_PROTECTION;
792341c2f8SMartin K. Petersen 			}
80be21683eSMax Gurtovoy 		} else if (type == T10_PI_TYPE3_PROTECTION) {
81128b6f9fSDmitry Monakhov 			if (pi->app_tag == T10_PI_APP_ESCAPE &&
82128b6f9fSDmitry Monakhov 			    pi->ref_tag == T10_PI_REF_ESCAPE)
832341c2f8SMartin K. Petersen 				goto next;
842341c2f8SMartin K. Petersen 		}
852341c2f8SMartin K. Petersen 
862341c2f8SMartin K. Petersen 		csum = fn(iter->data_buf, iter->interval);
872341c2f8SMartin K. Petersen 
882341c2f8SMartin K. Petersen 		if (pi->guard_tag != csum) {
892341c2f8SMartin K. Petersen 			pr_err("%s: guard tag error at sector %llu " \
902341c2f8SMartin K. Petersen 			       "(rcvd %04x, want %04x)\n", iter->disk_name,
912341c2f8SMartin K. Petersen 			       (unsigned long long)iter->seed,
922341c2f8SMartin K. Petersen 			       be16_to_cpu(pi->guard_tag), be16_to_cpu(csum));
934e4cbee9SChristoph Hellwig 			return BLK_STS_PROTECTION;
942341c2f8SMartin K. Petersen 		}
952341c2f8SMartin K. Petersen 
962341c2f8SMartin K. Petersen next:
972341c2f8SMartin K. Petersen 		iter->data_buf += iter->interval;
98c340b990SKeith Busch 		iter->prot_buf += iter->tuple_size;
992341c2f8SMartin K. Petersen 		iter->seed++;
1002341c2f8SMartin K. Petersen 	}
1012341c2f8SMartin K. Petersen 
1024e4cbee9SChristoph Hellwig 	return BLK_STS_OK;
1032341c2f8SMartin K. Petersen }
1042341c2f8SMartin K. Petersen 
t10_pi_type1_generate_crc(struct blk_integrity_iter * iter)1054e4cbee9SChristoph Hellwig static blk_status_t t10_pi_type1_generate_crc(struct blk_integrity_iter *iter)
1062341c2f8SMartin K. Petersen {
1075eaed68dSMax Gurtovoy 	return t10_pi_generate(iter, t10_pi_crc_fn, T10_PI_TYPE1_PROTECTION);
1082341c2f8SMartin K. Petersen }
1092341c2f8SMartin K. Petersen 
t10_pi_type1_generate_ip(struct blk_integrity_iter * iter)1104e4cbee9SChristoph Hellwig static blk_status_t t10_pi_type1_generate_ip(struct blk_integrity_iter *iter)
1112341c2f8SMartin K. Petersen {
1125eaed68dSMax Gurtovoy 	return t10_pi_generate(iter, t10_pi_ip_fn, T10_PI_TYPE1_PROTECTION);
1132341c2f8SMartin K. Petersen }
1142341c2f8SMartin K. Petersen 
t10_pi_type1_verify_crc(struct blk_integrity_iter * iter)1154e4cbee9SChristoph Hellwig static blk_status_t t10_pi_type1_verify_crc(struct blk_integrity_iter *iter)
1162341c2f8SMartin K. Petersen {
1175eaed68dSMax Gurtovoy 	return t10_pi_verify(iter, t10_pi_crc_fn, T10_PI_TYPE1_PROTECTION);
1182341c2f8SMartin K. Petersen }
1192341c2f8SMartin K. Petersen 
t10_pi_type1_verify_ip(struct blk_integrity_iter * iter)1204e4cbee9SChristoph Hellwig static blk_status_t t10_pi_type1_verify_ip(struct blk_integrity_iter *iter)
1212341c2f8SMartin K. Petersen {
1225eaed68dSMax Gurtovoy 	return t10_pi_verify(iter, t10_pi_ip_fn, T10_PI_TYPE1_PROTECTION);
1232341c2f8SMartin K. Petersen }
1242341c2f8SMartin K. Petersen 
12510c41dddSMax Gurtovoy /**
12654d4e6abSMax Gurtovoy  * t10_pi_type1_prepare - prepare PI prior submitting request to device
12710c41dddSMax Gurtovoy  * @rq:              request with PI that should be prepared
12810c41dddSMax Gurtovoy  *
12910c41dddSMax Gurtovoy  * For Type 1/Type 2, the virtual start sector is the one that was
13010c41dddSMax Gurtovoy  * originally submitted by the block layer for the ref_tag usage. Due to
13110c41dddSMax Gurtovoy  * partitioning, MD/DM cloning, etc. the actual physical start sector is
13210c41dddSMax Gurtovoy  * likely to be different. Remap protection information to match the
13310c41dddSMax Gurtovoy  * physical LBA.
13410c41dddSMax Gurtovoy  */
t10_pi_type1_prepare(struct request * rq)13554d4e6abSMax Gurtovoy static void t10_pi_type1_prepare(struct request *rq)
13610c41dddSMax Gurtovoy {
13710c41dddSMax Gurtovoy 	const int tuple_sz = rq->q->integrity.tuple_size;
13810c41dddSMax Gurtovoy 	u32 ref_tag = t10_pi_ref_tag(rq);
13910c41dddSMax Gurtovoy 	struct bio *bio;
14010c41dddSMax Gurtovoy 
14110c41dddSMax Gurtovoy 	__rq_for_each_bio(bio, rq) {
14210c41dddSMax Gurtovoy 		struct bio_integrity_payload *bip = bio_integrity(bio);
14310c41dddSMax Gurtovoy 		u32 virt = bip_get_seed(bip) & 0xffffffff;
14410c41dddSMax Gurtovoy 		struct bio_vec iv;
14510c41dddSMax Gurtovoy 		struct bvec_iter iter;
14610c41dddSMax Gurtovoy 
14710c41dddSMax Gurtovoy 		/* Already remapped? */
14810c41dddSMax Gurtovoy 		if (bip->bip_flags & BIP_MAPPED_INTEGRITY)
14910c41dddSMax Gurtovoy 			break;
15010c41dddSMax Gurtovoy 
15110c41dddSMax Gurtovoy 		bip_for_each_vec(iv, bip, iter) {
15210c41dddSMax Gurtovoy 			unsigned int j;
1538aec120aSChristoph Hellwig 			void *p;
15410c41dddSMax Gurtovoy 
1558aec120aSChristoph Hellwig 			p = bvec_kmap_local(&iv);
15610c41dddSMax Gurtovoy 			for (j = 0; j < iv.bv_len; j += tuple_sz) {
15710c41dddSMax Gurtovoy 				struct t10_pi_tuple *pi = p;
15810c41dddSMax Gurtovoy 
15910c41dddSMax Gurtovoy 				if (be32_to_cpu(pi->ref_tag) == virt)
16010c41dddSMax Gurtovoy 					pi->ref_tag = cpu_to_be32(ref_tag);
16110c41dddSMax Gurtovoy 				virt++;
16210c41dddSMax Gurtovoy 				ref_tag++;
16310c41dddSMax Gurtovoy 				p += tuple_sz;
16410c41dddSMax Gurtovoy 			}
1658aec120aSChristoph Hellwig 			kunmap_local(p);
16610c41dddSMax Gurtovoy 		}
16710c41dddSMax Gurtovoy 
16810c41dddSMax Gurtovoy 		bip->bip_flags |= BIP_MAPPED_INTEGRITY;
16910c41dddSMax Gurtovoy 	}
17010c41dddSMax Gurtovoy }
17110c41dddSMax Gurtovoy 
17210c41dddSMax Gurtovoy /**
17354d4e6abSMax Gurtovoy  * t10_pi_type1_complete - prepare PI prior returning request to the blk layer
17410c41dddSMax Gurtovoy  * @rq:              request with PI that should be prepared
17554d4e6abSMax Gurtovoy  * @nr_bytes:        total bytes to prepare
17610c41dddSMax Gurtovoy  *
17710c41dddSMax Gurtovoy  * For Type 1/Type 2, the virtual start sector is the one that was
17810c41dddSMax Gurtovoy  * originally submitted by the block layer for the ref_tag usage. Due to
17910c41dddSMax Gurtovoy  * partitioning, MD/DM cloning, etc. the actual physical start sector is
18010c41dddSMax Gurtovoy  * likely to be different. Since the physical start sector was submitted
18110c41dddSMax Gurtovoy  * to the device, we should remap it back to virtual values expected by the
18210c41dddSMax Gurtovoy  * block layer.
18310c41dddSMax Gurtovoy  */
t10_pi_type1_complete(struct request * rq,unsigned int nr_bytes)18454d4e6abSMax Gurtovoy static void t10_pi_type1_complete(struct request *rq, unsigned int nr_bytes)
18510c41dddSMax Gurtovoy {
18654d4e6abSMax Gurtovoy 	unsigned intervals = nr_bytes >> rq->q->integrity.interval_exp;
18710c41dddSMax Gurtovoy 	const int tuple_sz = rq->q->integrity.tuple_size;
18810c41dddSMax Gurtovoy 	u32 ref_tag = t10_pi_ref_tag(rq);
18910c41dddSMax Gurtovoy 	struct bio *bio;
19010c41dddSMax Gurtovoy 
19110c41dddSMax Gurtovoy 	__rq_for_each_bio(bio, rq) {
19210c41dddSMax Gurtovoy 		struct bio_integrity_payload *bip = bio_integrity(bio);
19310c41dddSMax Gurtovoy 		u32 virt = bip_get_seed(bip) & 0xffffffff;
19410c41dddSMax Gurtovoy 		struct bio_vec iv;
19510c41dddSMax Gurtovoy 		struct bvec_iter iter;
19610c41dddSMax Gurtovoy 
19710c41dddSMax Gurtovoy 		bip_for_each_vec(iv, bip, iter) {
19810c41dddSMax Gurtovoy 			unsigned int j;
1998aec120aSChristoph Hellwig 			void *p;
20010c41dddSMax Gurtovoy 
2018aec120aSChristoph Hellwig 			p = bvec_kmap_local(&iv);
20210c41dddSMax Gurtovoy 			for (j = 0; j < iv.bv_len && intervals; j += tuple_sz) {
20310c41dddSMax Gurtovoy 				struct t10_pi_tuple *pi = p;
20410c41dddSMax Gurtovoy 
20510c41dddSMax Gurtovoy 				if (be32_to_cpu(pi->ref_tag) == ref_tag)
20610c41dddSMax Gurtovoy 					pi->ref_tag = cpu_to_be32(virt);
20710c41dddSMax Gurtovoy 				virt++;
20810c41dddSMax Gurtovoy 				ref_tag++;
20910c41dddSMax Gurtovoy 				intervals--;
21010c41dddSMax Gurtovoy 				p += tuple_sz;
21110c41dddSMax Gurtovoy 			}
2128aec120aSChristoph Hellwig 			kunmap_local(p);
21310c41dddSMax Gurtovoy 		}
21410c41dddSMax Gurtovoy 	}
21510c41dddSMax Gurtovoy }
21654d4e6abSMax Gurtovoy 
t10_pi_type3_generate_crc(struct blk_integrity_iter * iter)21754d4e6abSMax Gurtovoy static blk_status_t t10_pi_type3_generate_crc(struct blk_integrity_iter *iter)
21854d4e6abSMax Gurtovoy {
21954d4e6abSMax Gurtovoy 	return t10_pi_generate(iter, t10_pi_crc_fn, T10_PI_TYPE3_PROTECTION);
22054d4e6abSMax Gurtovoy }
22154d4e6abSMax Gurtovoy 
t10_pi_type3_generate_ip(struct blk_integrity_iter * iter)22254d4e6abSMax Gurtovoy static blk_status_t t10_pi_type3_generate_ip(struct blk_integrity_iter *iter)
22354d4e6abSMax Gurtovoy {
22454d4e6abSMax Gurtovoy 	return t10_pi_generate(iter, t10_pi_ip_fn, T10_PI_TYPE3_PROTECTION);
22554d4e6abSMax Gurtovoy }
22654d4e6abSMax Gurtovoy 
t10_pi_type3_verify_crc(struct blk_integrity_iter * iter)22754d4e6abSMax Gurtovoy static blk_status_t t10_pi_type3_verify_crc(struct blk_integrity_iter *iter)
22854d4e6abSMax Gurtovoy {
22954d4e6abSMax Gurtovoy 	return t10_pi_verify(iter, t10_pi_crc_fn, T10_PI_TYPE3_PROTECTION);
23054d4e6abSMax Gurtovoy }
23154d4e6abSMax Gurtovoy 
t10_pi_type3_verify_ip(struct blk_integrity_iter * iter)23254d4e6abSMax Gurtovoy static blk_status_t t10_pi_type3_verify_ip(struct blk_integrity_iter *iter)
23354d4e6abSMax Gurtovoy {
23454d4e6abSMax Gurtovoy 	return t10_pi_verify(iter, t10_pi_ip_fn, T10_PI_TYPE3_PROTECTION);
23554d4e6abSMax Gurtovoy }
23654d4e6abSMax Gurtovoy 
23798e54402SBart Van Assche /* Type 3 does not have a reference tag so no remapping is required. */
t10_pi_type3_prepare(struct request * rq)23854d4e6abSMax Gurtovoy static void t10_pi_type3_prepare(struct request *rq)
23954d4e6abSMax Gurtovoy {
24054d4e6abSMax Gurtovoy }
24154d4e6abSMax Gurtovoy 
24298e54402SBart Van Assche /* Type 3 does not have a reference tag so no remapping is required. */
t10_pi_type3_complete(struct request * rq,unsigned int nr_bytes)24354d4e6abSMax Gurtovoy static void t10_pi_type3_complete(struct request *rq, unsigned int nr_bytes)
24454d4e6abSMax Gurtovoy {
24554d4e6abSMax Gurtovoy }
24654d4e6abSMax Gurtovoy 
24754d4e6abSMax Gurtovoy const struct blk_integrity_profile t10_pi_type1_crc = {
24854d4e6abSMax Gurtovoy 	.name			= "T10-DIF-TYPE1-CRC",
24954d4e6abSMax Gurtovoy 	.generate_fn		= t10_pi_type1_generate_crc,
25054d4e6abSMax Gurtovoy 	.verify_fn		= t10_pi_type1_verify_crc,
25154d4e6abSMax Gurtovoy 	.prepare_fn		= t10_pi_type1_prepare,
25254d4e6abSMax Gurtovoy 	.complete_fn		= t10_pi_type1_complete,
25354d4e6abSMax Gurtovoy };
25454d4e6abSMax Gurtovoy EXPORT_SYMBOL(t10_pi_type1_crc);
25554d4e6abSMax Gurtovoy 
25654d4e6abSMax Gurtovoy const struct blk_integrity_profile t10_pi_type1_ip = {
25754d4e6abSMax Gurtovoy 	.name			= "T10-DIF-TYPE1-IP",
25854d4e6abSMax Gurtovoy 	.generate_fn		= t10_pi_type1_generate_ip,
25954d4e6abSMax Gurtovoy 	.verify_fn		= t10_pi_type1_verify_ip,
26054d4e6abSMax Gurtovoy 	.prepare_fn		= t10_pi_type1_prepare,
26154d4e6abSMax Gurtovoy 	.complete_fn		= t10_pi_type1_complete,
26254d4e6abSMax Gurtovoy };
26354d4e6abSMax Gurtovoy EXPORT_SYMBOL(t10_pi_type1_ip);
26454d4e6abSMax Gurtovoy 
26554d4e6abSMax Gurtovoy const struct blk_integrity_profile t10_pi_type3_crc = {
26654d4e6abSMax Gurtovoy 	.name			= "T10-DIF-TYPE3-CRC",
26754d4e6abSMax Gurtovoy 	.generate_fn		= t10_pi_type3_generate_crc,
26854d4e6abSMax Gurtovoy 	.verify_fn		= t10_pi_type3_verify_crc,
26954d4e6abSMax Gurtovoy 	.prepare_fn		= t10_pi_type3_prepare,
27054d4e6abSMax Gurtovoy 	.complete_fn		= t10_pi_type3_complete,
27154d4e6abSMax Gurtovoy };
27254d4e6abSMax Gurtovoy EXPORT_SYMBOL(t10_pi_type3_crc);
27354d4e6abSMax Gurtovoy 
27454d4e6abSMax Gurtovoy const struct blk_integrity_profile t10_pi_type3_ip = {
27554d4e6abSMax Gurtovoy 	.name			= "T10-DIF-TYPE3-IP",
27654d4e6abSMax Gurtovoy 	.generate_fn		= t10_pi_type3_generate_ip,
27754d4e6abSMax Gurtovoy 	.verify_fn		= t10_pi_type3_verify_ip,
27854d4e6abSMax Gurtovoy 	.prepare_fn		= t10_pi_type3_prepare,
27954d4e6abSMax Gurtovoy 	.complete_fn		= t10_pi_type3_complete,
28054d4e6abSMax Gurtovoy };
28154d4e6abSMax Gurtovoy EXPORT_SYMBOL(t10_pi_type3_ip);
282a754bd5fSHerbert Xu 
ext_pi_crc64(void * data,unsigned int len)283*a7d4383fSKeith Busch static __be64 ext_pi_crc64(void *data, unsigned int len)
284*a7d4383fSKeith Busch {
285*a7d4383fSKeith Busch 	return cpu_to_be64(crc64_rocksoft(data, len));
286*a7d4383fSKeith Busch }
287*a7d4383fSKeith Busch 
ext_pi_crc64_generate(struct blk_integrity_iter * iter,enum t10_dif_type type)288*a7d4383fSKeith Busch static blk_status_t ext_pi_crc64_generate(struct blk_integrity_iter *iter,
289*a7d4383fSKeith Busch 					enum t10_dif_type type)
290*a7d4383fSKeith Busch {
291*a7d4383fSKeith Busch 	unsigned int i;
292*a7d4383fSKeith Busch 
293*a7d4383fSKeith Busch 	for (i = 0 ; i < iter->data_size ; i += iter->interval) {
294*a7d4383fSKeith Busch 		struct crc64_pi_tuple *pi = iter->prot_buf;
295*a7d4383fSKeith Busch 
296*a7d4383fSKeith Busch 		pi->guard_tag = ext_pi_crc64(iter->data_buf, iter->interval);
297*a7d4383fSKeith Busch 		pi->app_tag = 0;
298*a7d4383fSKeith Busch 
299*a7d4383fSKeith Busch 		if (type == T10_PI_TYPE1_PROTECTION)
300*a7d4383fSKeith Busch 			put_unaligned_be48(iter->seed, pi->ref_tag);
301*a7d4383fSKeith Busch 		else
302*a7d4383fSKeith Busch 			put_unaligned_be48(0ULL, pi->ref_tag);
303*a7d4383fSKeith Busch 
304*a7d4383fSKeith Busch 		iter->data_buf += iter->interval;
305*a7d4383fSKeith Busch 		iter->prot_buf += iter->tuple_size;
306*a7d4383fSKeith Busch 		iter->seed++;
307*a7d4383fSKeith Busch 	}
308*a7d4383fSKeith Busch 
309*a7d4383fSKeith Busch 	return BLK_STS_OK;
310*a7d4383fSKeith Busch }
311*a7d4383fSKeith Busch 
ext_pi_ref_escape(u8 * ref_tag)312*a7d4383fSKeith Busch static bool ext_pi_ref_escape(u8 *ref_tag)
313*a7d4383fSKeith Busch {
314*a7d4383fSKeith Busch 	static u8 ref_escape[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
315*a7d4383fSKeith Busch 
316*a7d4383fSKeith Busch 	return memcmp(ref_tag, ref_escape, sizeof(ref_escape)) == 0;
317*a7d4383fSKeith Busch }
318*a7d4383fSKeith Busch 
ext_pi_crc64_verify(struct blk_integrity_iter * iter,enum t10_dif_type type)319*a7d4383fSKeith Busch static blk_status_t ext_pi_crc64_verify(struct blk_integrity_iter *iter,
320*a7d4383fSKeith Busch 				      enum t10_dif_type type)
321*a7d4383fSKeith Busch {
322*a7d4383fSKeith Busch 	unsigned int i;
323*a7d4383fSKeith Busch 
324*a7d4383fSKeith Busch 	for (i = 0; i < iter->data_size; i += iter->interval) {
325*a7d4383fSKeith Busch 		struct crc64_pi_tuple *pi = iter->prot_buf;
326*a7d4383fSKeith Busch 		u64 ref, seed;
327*a7d4383fSKeith Busch 		__be64 csum;
328*a7d4383fSKeith Busch 
329*a7d4383fSKeith Busch 		if (type == T10_PI_TYPE1_PROTECTION) {
330*a7d4383fSKeith Busch 			if (pi->app_tag == T10_PI_APP_ESCAPE)
331*a7d4383fSKeith Busch 				goto next;
332*a7d4383fSKeith Busch 
333*a7d4383fSKeith Busch 			ref = get_unaligned_be48(pi->ref_tag);
334*a7d4383fSKeith Busch 			seed = lower_48_bits(iter->seed);
335*a7d4383fSKeith Busch 			if (ref != seed) {
336*a7d4383fSKeith Busch 				pr_err("%s: ref tag error at location %llu (rcvd %llu)\n",
337*a7d4383fSKeith Busch 					iter->disk_name, seed, ref);
338*a7d4383fSKeith Busch 				return BLK_STS_PROTECTION;
339*a7d4383fSKeith Busch 			}
340*a7d4383fSKeith Busch 		} else if (type == T10_PI_TYPE3_PROTECTION) {
341*a7d4383fSKeith Busch 			if (pi->app_tag == T10_PI_APP_ESCAPE &&
342*a7d4383fSKeith Busch 			    ext_pi_ref_escape(pi->ref_tag))
343*a7d4383fSKeith Busch 				goto next;
344*a7d4383fSKeith Busch 		}
345*a7d4383fSKeith Busch 
346*a7d4383fSKeith Busch 		csum = ext_pi_crc64(iter->data_buf, iter->interval);
347*a7d4383fSKeith Busch 		if (pi->guard_tag != csum) {
348*a7d4383fSKeith Busch 			pr_err("%s: guard tag error at sector %llu " \
349*a7d4383fSKeith Busch 			       "(rcvd %016llx, want %016llx)\n",
350*a7d4383fSKeith Busch 				iter->disk_name, (unsigned long long)iter->seed,
351*a7d4383fSKeith Busch 				be64_to_cpu(pi->guard_tag), be64_to_cpu(csum));
352*a7d4383fSKeith Busch 			return BLK_STS_PROTECTION;
353*a7d4383fSKeith Busch 		}
354*a7d4383fSKeith Busch 
355*a7d4383fSKeith Busch next:
356*a7d4383fSKeith Busch 		iter->data_buf += iter->interval;
357*a7d4383fSKeith Busch 		iter->prot_buf += iter->tuple_size;
358*a7d4383fSKeith Busch 		iter->seed++;
359*a7d4383fSKeith Busch 	}
360*a7d4383fSKeith Busch 
361*a7d4383fSKeith Busch 	return BLK_STS_OK;
362*a7d4383fSKeith Busch }
363*a7d4383fSKeith Busch 
ext_pi_type1_verify_crc64(struct blk_integrity_iter * iter)364*a7d4383fSKeith Busch static blk_status_t ext_pi_type1_verify_crc64(struct blk_integrity_iter *iter)
365*a7d4383fSKeith Busch {
366*a7d4383fSKeith Busch 	return ext_pi_crc64_verify(iter, T10_PI_TYPE1_PROTECTION);
367*a7d4383fSKeith Busch }
368*a7d4383fSKeith Busch 
ext_pi_type1_generate_crc64(struct blk_integrity_iter * iter)369*a7d4383fSKeith Busch static blk_status_t ext_pi_type1_generate_crc64(struct blk_integrity_iter *iter)
370*a7d4383fSKeith Busch {
371*a7d4383fSKeith Busch 	return ext_pi_crc64_generate(iter, T10_PI_TYPE1_PROTECTION);
372*a7d4383fSKeith Busch }
373*a7d4383fSKeith Busch 
ext_pi_type1_prepare(struct request * rq)374*a7d4383fSKeith Busch static void ext_pi_type1_prepare(struct request *rq)
375*a7d4383fSKeith Busch {
376*a7d4383fSKeith Busch 	const int tuple_sz = rq->q->integrity.tuple_size;
377*a7d4383fSKeith Busch 	u64 ref_tag = ext_pi_ref_tag(rq);
378*a7d4383fSKeith Busch 	struct bio *bio;
379*a7d4383fSKeith Busch 
380*a7d4383fSKeith Busch 	__rq_for_each_bio(bio, rq) {
381*a7d4383fSKeith Busch 		struct bio_integrity_payload *bip = bio_integrity(bio);
382*a7d4383fSKeith Busch 		u64 virt = lower_48_bits(bip_get_seed(bip));
383*a7d4383fSKeith Busch 		struct bio_vec iv;
384*a7d4383fSKeith Busch 		struct bvec_iter iter;
385*a7d4383fSKeith Busch 
386*a7d4383fSKeith Busch 		/* Already remapped? */
387*a7d4383fSKeith Busch 		if (bip->bip_flags & BIP_MAPPED_INTEGRITY)
388*a7d4383fSKeith Busch 			break;
389*a7d4383fSKeith Busch 
390*a7d4383fSKeith Busch 		bip_for_each_vec(iv, bip, iter) {
391*a7d4383fSKeith Busch 			unsigned int j;
392*a7d4383fSKeith Busch 			void *p;
393*a7d4383fSKeith Busch 
394*a7d4383fSKeith Busch 			p = bvec_kmap_local(&iv);
395*a7d4383fSKeith Busch 			for (j = 0; j < iv.bv_len; j += tuple_sz) {
396*a7d4383fSKeith Busch 				struct crc64_pi_tuple *pi = p;
397*a7d4383fSKeith Busch 				u64 ref = get_unaligned_be48(pi->ref_tag);
398*a7d4383fSKeith Busch 
399*a7d4383fSKeith Busch 				if (ref == virt)
400*a7d4383fSKeith Busch 					put_unaligned_be48(ref_tag, pi->ref_tag);
401*a7d4383fSKeith Busch 				virt++;
402*a7d4383fSKeith Busch 				ref_tag++;
403*a7d4383fSKeith Busch 				p += tuple_sz;
404*a7d4383fSKeith Busch 			}
405*a7d4383fSKeith Busch 			kunmap_local(p);
406*a7d4383fSKeith Busch 		}
407*a7d4383fSKeith Busch 
408*a7d4383fSKeith Busch 		bip->bip_flags |= BIP_MAPPED_INTEGRITY;
409*a7d4383fSKeith Busch 	}
410*a7d4383fSKeith Busch }
411*a7d4383fSKeith Busch 
ext_pi_type1_complete(struct request * rq,unsigned int nr_bytes)412*a7d4383fSKeith Busch static void ext_pi_type1_complete(struct request *rq, unsigned int nr_bytes)
413*a7d4383fSKeith Busch {
414*a7d4383fSKeith Busch 	unsigned intervals = nr_bytes >> rq->q->integrity.interval_exp;
415*a7d4383fSKeith Busch 	const int tuple_sz = rq->q->integrity.tuple_size;
416*a7d4383fSKeith Busch 	u64 ref_tag = ext_pi_ref_tag(rq);
417*a7d4383fSKeith Busch 	struct bio *bio;
418*a7d4383fSKeith Busch 
419*a7d4383fSKeith Busch 	__rq_for_each_bio(bio, rq) {
420*a7d4383fSKeith Busch 		struct bio_integrity_payload *bip = bio_integrity(bio);
421*a7d4383fSKeith Busch 		u64 virt = lower_48_bits(bip_get_seed(bip));
422*a7d4383fSKeith Busch 		struct bio_vec iv;
423*a7d4383fSKeith Busch 		struct bvec_iter iter;
424*a7d4383fSKeith Busch 
425*a7d4383fSKeith Busch 		bip_for_each_vec(iv, bip, iter) {
426*a7d4383fSKeith Busch 			unsigned int j;
427*a7d4383fSKeith Busch 			void *p;
428*a7d4383fSKeith Busch 
429*a7d4383fSKeith Busch 			p = bvec_kmap_local(&iv);
430*a7d4383fSKeith Busch 			for (j = 0; j < iv.bv_len && intervals; j += tuple_sz) {
431*a7d4383fSKeith Busch 				struct crc64_pi_tuple *pi = p;
432*a7d4383fSKeith Busch 				u64 ref = get_unaligned_be48(pi->ref_tag);
433*a7d4383fSKeith Busch 
434*a7d4383fSKeith Busch 				if (ref == ref_tag)
435*a7d4383fSKeith Busch 					put_unaligned_be48(virt, pi->ref_tag);
436*a7d4383fSKeith Busch 				virt++;
437*a7d4383fSKeith Busch 				ref_tag++;
438*a7d4383fSKeith Busch 				intervals--;
439*a7d4383fSKeith Busch 				p += tuple_sz;
440*a7d4383fSKeith Busch 			}
441*a7d4383fSKeith Busch 			kunmap_local(p);
442*a7d4383fSKeith Busch 		}
443*a7d4383fSKeith Busch 	}
444*a7d4383fSKeith Busch }
445*a7d4383fSKeith Busch 
ext_pi_type3_verify_crc64(struct blk_integrity_iter * iter)446*a7d4383fSKeith Busch static blk_status_t ext_pi_type3_verify_crc64(struct blk_integrity_iter *iter)
447*a7d4383fSKeith Busch {
448*a7d4383fSKeith Busch 	return ext_pi_crc64_verify(iter, T10_PI_TYPE3_PROTECTION);
449*a7d4383fSKeith Busch }
450*a7d4383fSKeith Busch 
ext_pi_type3_generate_crc64(struct blk_integrity_iter * iter)451*a7d4383fSKeith Busch static blk_status_t ext_pi_type3_generate_crc64(struct blk_integrity_iter *iter)
452*a7d4383fSKeith Busch {
453*a7d4383fSKeith Busch 	return ext_pi_crc64_generate(iter, T10_PI_TYPE3_PROTECTION);
454*a7d4383fSKeith Busch }
455*a7d4383fSKeith Busch 
456*a7d4383fSKeith Busch const struct blk_integrity_profile ext_pi_type1_crc64 = {
457*a7d4383fSKeith Busch 	.name			= "EXT-DIF-TYPE1-CRC64",
458*a7d4383fSKeith Busch 	.generate_fn		= ext_pi_type1_generate_crc64,
459*a7d4383fSKeith Busch 	.verify_fn		= ext_pi_type1_verify_crc64,
460*a7d4383fSKeith Busch 	.prepare_fn		= ext_pi_type1_prepare,
461*a7d4383fSKeith Busch 	.complete_fn		= ext_pi_type1_complete,
462*a7d4383fSKeith Busch };
463*a7d4383fSKeith Busch EXPORT_SYMBOL_GPL(ext_pi_type1_crc64);
464*a7d4383fSKeith Busch 
465*a7d4383fSKeith Busch const struct blk_integrity_profile ext_pi_type3_crc64 = {
466*a7d4383fSKeith Busch 	.name			= "EXT-DIF-TYPE3-CRC64",
467*a7d4383fSKeith Busch 	.generate_fn		= ext_pi_type3_generate_crc64,
468*a7d4383fSKeith Busch 	.verify_fn		= ext_pi_type3_verify_crc64,
469*a7d4383fSKeith Busch 	.prepare_fn		= t10_pi_type3_prepare,
470*a7d4383fSKeith Busch 	.complete_fn		= t10_pi_type3_complete,
471*a7d4383fSKeith Busch };
472*a7d4383fSKeith Busch EXPORT_SYMBOL_GPL(ext_pi_type3_crc64);
473*a7d4383fSKeith Busch 
474*a7d4383fSKeith Busch MODULE_LICENSE("GPL");
475a754bd5fSHerbert Xu MODULE_LICENSE("GPL");
476