xref: /linux/block/blk-integrity.c (revision 8be007c8e0911d0450b402ca8cbb1a8cbd00e8f2)
18c16567dSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0
27ba1ba12SMartin K. Petersen /*
37ba1ba12SMartin K. Petersen  * blk-integrity.c - Block layer data integrity extensions
47ba1ba12SMartin K. Petersen  *
57ba1ba12SMartin K. Petersen  * Copyright (C) 2007, 2008 Oracle Corporation
67ba1ba12SMartin K. Petersen  * Written by: Martin K. Petersen <martin.petersen@oracle.com>
77ba1ba12SMartin K. Petersen  */
87ba1ba12SMartin K. Petersen 
9fe45e630SChristoph Hellwig #include <linux/blk-integrity.h>
1066114cadSTejun Heo #include <linux/backing-dev.h>
117ba1ba12SMartin K. Petersen #include <linux/mempool.h>
127ba1ba12SMartin K. Petersen #include <linux/bio.h>
137ba1ba12SMartin K. Petersen #include <linux/scatterlist.h>
14d5decd3bSPaul Gortmaker #include <linux/export.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
167ba1ba12SMartin K. Petersen 
177ba1ba12SMartin K. Petersen #include "blk.h"
187ba1ba12SMartin K. Petersen 
197ba1ba12SMartin K. Petersen /**
207ba1ba12SMartin K. Petersen  * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements
2113f05c8dSMartin K. Petersen  * @q:		request queue
2213f05c8dSMartin K. Petersen  * @bio:	bio with integrity metadata attached
237ba1ba12SMartin K. Petersen  *
247ba1ba12SMartin K. Petersen  * Description: Returns the number of elements required in a
2513f05c8dSMartin K. Petersen  * scatterlist corresponding to the integrity metadata in a bio.
267ba1ba12SMartin K. Petersen  */
blk_rq_count_integrity_sg(struct request_queue * q,struct bio * bio)2713f05c8dSMartin K. Petersen int blk_rq_count_integrity_sg(struct request_queue *q, struct bio *bio)
287ba1ba12SMartin K. Petersen {
29d57a5f7cSKent Overstreet 	struct bio_vec iv, ivprv = { NULL };
3013f05c8dSMartin K. Petersen 	unsigned int segments = 0;
3113f05c8dSMartin K. Petersen 	unsigned int seg_size = 0;
32d57a5f7cSKent Overstreet 	struct bvec_iter iter;
33d57a5f7cSKent Overstreet 	int prev = 0;
347ba1ba12SMartin K. Petersen 
35d57a5f7cSKent Overstreet 	bio_for_each_integrity_vec(iv, bio, iter) {
367ba1ba12SMartin K. Petersen 
37d57a5f7cSKent Overstreet 		if (prev) {
383dccdae5SChristoph Hellwig 			if (!biovec_phys_mergeable(q, &ivprv, &iv))
3913f05c8dSMartin K. Petersen 				goto new_segment;
40d57a5f7cSKent Overstreet 			if (seg_size + iv.bv_len > queue_max_segment_size(q))
4113f05c8dSMartin K. Petersen 				goto new_segment;
4213f05c8dSMartin K. Petersen 
43d57a5f7cSKent Overstreet 			seg_size += iv.bv_len;
4413f05c8dSMartin K. Petersen 		} else {
4513f05c8dSMartin K. Petersen new_segment:
467ba1ba12SMartin K. Petersen 			segments++;
47d57a5f7cSKent Overstreet 			seg_size = iv.bv_len;
4813f05c8dSMartin K. Petersen 		}
497ba1ba12SMartin K. Petersen 
50d57a5f7cSKent Overstreet 		prev = 1;
517ba1ba12SMartin K. Petersen 		ivprv = iv;
527ba1ba12SMartin K. Petersen 	}
537ba1ba12SMartin K. Petersen 
547ba1ba12SMartin K. Petersen 	return segments;
557ba1ba12SMartin K. Petersen }
567ba1ba12SMartin K. Petersen 
577ba1ba12SMartin K. Petersen /**
587ba1ba12SMartin K. Petersen  * blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist
59*8be007c8SKeith Busch  * @rq:		request to map
607ba1ba12SMartin K. Petersen  * @sglist:	target scatterlist
617ba1ba12SMartin K. Petersen  *
627ba1ba12SMartin K. Petersen  * Description: Map the integrity vectors in request into a
637ba1ba12SMartin K. Petersen  * scatterlist.  The scatterlist must be big enough to hold all
6476c313f6SKeith Busch  * elements.  I.e. sized using blk_rq_count_integrity_sg() or
6576c313f6SKeith Busch  * rq->nr_integrity_segments.
667ba1ba12SMartin K. Petersen  */
blk_rq_map_integrity_sg(struct request * rq,struct scatterlist * sglist)6776c313f6SKeith Busch int blk_rq_map_integrity_sg(struct request *rq, struct scatterlist *sglist)
687ba1ba12SMartin K. Petersen {
69d57a5f7cSKent Overstreet 	struct bio_vec iv, ivprv = { NULL };
7076c313f6SKeith Busch 	struct request_queue *q = rq->q;
7113f05c8dSMartin K. Petersen 	struct scatterlist *sg = NULL;
7276c313f6SKeith Busch 	struct bio *bio = rq->bio;
7313f05c8dSMartin K. Petersen 	unsigned int segments = 0;
74d57a5f7cSKent Overstreet 	struct bvec_iter iter;
75d57a5f7cSKent Overstreet 	int prev = 0;
767ba1ba12SMartin K. Petersen 
77d57a5f7cSKent Overstreet 	bio_for_each_integrity_vec(iv, bio, iter) {
78d57a5f7cSKent Overstreet 		if (prev) {
793dccdae5SChristoph Hellwig 			if (!biovec_phys_mergeable(q, &ivprv, &iv))
807ba1ba12SMartin K. Petersen 				goto new_segment;
81d57a5f7cSKent Overstreet 			if (sg->length + iv.bv_len > queue_max_segment_size(q))
8213f05c8dSMartin K. Petersen 				goto new_segment;
8313f05c8dSMartin K. Petersen 
84d57a5f7cSKent Overstreet 			sg->length += iv.bv_len;
857ba1ba12SMartin K. Petersen 		} else {
867ba1ba12SMartin K. Petersen new_segment:
877ba1ba12SMartin K. Petersen 			if (!sg)
887ba1ba12SMartin K. Petersen 				sg = sglist;
897ba1ba12SMartin K. Petersen 			else {
90c8164d89SPaolo Bonzini 				sg_unmark_end(sg);
917ba1ba12SMartin K. Petersen 				sg = sg_next(sg);
927ba1ba12SMartin K. Petersen 			}
937ba1ba12SMartin K. Petersen 
94d57a5f7cSKent Overstreet 			sg_set_page(sg, iv.bv_page, iv.bv_len, iv.bv_offset);
957ba1ba12SMartin K. Petersen 			segments++;
967ba1ba12SMartin K. Petersen 		}
977ba1ba12SMartin K. Petersen 
98d57a5f7cSKent Overstreet 		prev = 1;
997ba1ba12SMartin K. Petersen 		ivprv = iv;
1007ba1ba12SMartin K. Petersen 	}
1017ba1ba12SMartin K. Petersen 
1027ba1ba12SMartin K. Petersen 	if (sg)
1037ba1ba12SMartin K. Petersen 		sg_mark_end(sg);
1047ba1ba12SMartin K. Petersen 
10576c313f6SKeith Busch 	/*
10676c313f6SKeith Busch 	 * Something must have been wrong if the figured number of segment
10776c313f6SKeith Busch 	 * is bigger than number of req's physical integrity segments
10876c313f6SKeith Busch 	 */
10976c313f6SKeith Busch 	BUG_ON(segments > rq->nr_integrity_segments);
11076c313f6SKeith Busch 	BUG_ON(segments > queue_max_integrity_segments(q));
1117ba1ba12SMartin K. Petersen 	return segments;
1127ba1ba12SMartin K. Petersen }
1137ba1ba12SMartin K. Petersen EXPORT_SYMBOL(blk_rq_map_integrity_sg);
1147ba1ba12SMartin K. Petersen 
blk_rq_integrity_map_user(struct request * rq,void __user * ubuf,ssize_t bytes,u32 seed)115d2c5b1faSKeith Busch int blk_rq_integrity_map_user(struct request *rq, void __user *ubuf,
116d2c5b1faSKeith Busch 			      ssize_t bytes, u32 seed)
117d2c5b1faSKeith Busch {
118d2c5b1faSKeith Busch 	int ret = bio_integrity_map_user(rq->bio, ubuf, bytes, seed);
119d2c5b1faSKeith Busch 
120d2c5b1faSKeith Busch 	if (ret)
121d2c5b1faSKeith Busch 		return ret;
122d2c5b1faSKeith Busch 
123d2c5b1faSKeith Busch 	rq->nr_integrity_segments = blk_rq_count_integrity_sg(rq->q, rq->bio);
124d2c5b1faSKeith Busch 	rq->cmd_flags |= REQ_INTEGRITY;
125d2c5b1faSKeith Busch 	return 0;
126d2c5b1faSKeith Busch }
127d2c5b1faSKeith Busch EXPORT_SYMBOL_GPL(blk_rq_integrity_map_user);
128d2c5b1faSKeith Busch 
blk_integrity_merge_rq(struct request_queue * q,struct request * req,struct request * next)1294eaf99beSMartin K. Petersen bool blk_integrity_merge_rq(struct request_queue *q, struct request *req,
13013f05c8dSMartin K. Petersen 			    struct request *next)
13113f05c8dSMartin K. Petersen {
1324eaf99beSMartin K. Petersen 	if (blk_integrity_rq(req) == 0 && blk_integrity_rq(next) == 0)
1334eaf99beSMartin K. Petersen 		return true;
1344eaf99beSMartin K. Petersen 
1354eaf99beSMartin K. Petersen 	if (blk_integrity_rq(req) == 0 || blk_integrity_rq(next) == 0)
1364eaf99beSMartin K. Petersen 		return false;
1374eaf99beSMartin K. Petersen 
1384eaf99beSMartin K. Petersen 	if (bio_integrity(req->bio)->bip_flags !=
1394eaf99beSMartin K. Petersen 	    bio_integrity(next->bio)->bip_flags)
1404eaf99beSMartin K. Petersen 		return false;
14113f05c8dSMartin K. Petersen 
14213f05c8dSMartin K. Petersen 	if (req->nr_integrity_segments + next->nr_integrity_segments >
14313f05c8dSMartin K. Petersen 	    q->limits.max_integrity_segments)
1444eaf99beSMartin K. Petersen 		return false;
14513f05c8dSMartin K. Petersen 
1467f39add3SSagi Grimberg 	if (integrity_req_gap_back_merge(req, next->bio))
1477f39add3SSagi Grimberg 		return false;
1487f39add3SSagi Grimberg 
1494eaf99beSMartin K. Petersen 	return true;
15013f05c8dSMartin K. Petersen }
15113f05c8dSMartin K. Petersen 
blk_integrity_merge_bio(struct request_queue * q,struct request * req,struct bio * bio)1524eaf99beSMartin K. Petersen bool blk_integrity_merge_bio(struct request_queue *q, struct request *req,
15313f05c8dSMartin K. Petersen 			     struct bio *bio)
15413f05c8dSMartin K. Petersen {
15513f05c8dSMartin K. Petersen 	int nr_integrity_segs;
15613f05c8dSMartin K. Petersen 
1574eaf99beSMartin K. Petersen 	if (blk_integrity_rq(req) == 0 && bio_integrity(bio) == NULL)
1584eaf99beSMartin K. Petersen 		return true;
1594eaf99beSMartin K. Petersen 
1604eaf99beSMartin K. Petersen 	if (blk_integrity_rq(req) == 0 || bio_integrity(bio) == NULL)
1614eaf99beSMartin K. Petersen 		return false;
1624eaf99beSMartin K. Petersen 
1634eaf99beSMartin K. Petersen 	if (bio_integrity(req->bio)->bip_flags != bio_integrity(bio)->bip_flags)
1644eaf99beSMartin K. Petersen 		return false;
1654eaf99beSMartin K. Petersen 
16613f05c8dSMartin K. Petersen 	nr_integrity_segs = blk_rq_count_integrity_sg(q, bio);
16713f05c8dSMartin K. Petersen 	if (req->nr_integrity_segments + nr_integrity_segs >
16813f05c8dSMartin K. Petersen 	    q->limits.max_integrity_segments)
1694eaf99beSMartin K. Petersen 		return false;
17013f05c8dSMartin K. Petersen 
1714eaf99beSMartin K. Petersen 	return true;
17213f05c8dSMartin K. Petersen }
17313f05c8dSMartin K. Petersen 
dev_to_bi(struct device * dev)17476b8c319SThomas Weißschuh static inline struct blk_integrity *dev_to_bi(struct device *dev)
1757ba1ba12SMartin K. Petersen {
176c6e56cf6SChristoph Hellwig 	return &dev_to_disk(dev)->queue->limits.integrity;
17776b8c319SThomas Weißschuh }
17876b8c319SThomas Weißschuh 
blk_integrity_profile_name(struct blk_integrity * bi)179e9f5f44aSChristoph Hellwig const char *blk_integrity_profile_name(struct blk_integrity *bi)
180e9f5f44aSChristoph Hellwig {
181e9f5f44aSChristoph Hellwig 	switch (bi->csum_type) {
182e9f5f44aSChristoph Hellwig 	case BLK_INTEGRITY_CSUM_IP:
183e9f5f44aSChristoph Hellwig 		if (bi->flags & BLK_INTEGRITY_REF_TAG)
184e9f5f44aSChristoph Hellwig 			return "T10-DIF-TYPE1-IP";
185e9f5f44aSChristoph Hellwig 		return "T10-DIF-TYPE3-IP";
186e9f5f44aSChristoph Hellwig 	case BLK_INTEGRITY_CSUM_CRC:
187e9f5f44aSChristoph Hellwig 		if (bi->flags & BLK_INTEGRITY_REF_TAG)
188e9f5f44aSChristoph Hellwig 			return "T10-DIF-TYPE1-CRC";
189e9f5f44aSChristoph Hellwig 		return "T10-DIF-TYPE3-CRC";
190e9f5f44aSChristoph Hellwig 	case BLK_INTEGRITY_CSUM_CRC64:
191e9f5f44aSChristoph Hellwig 		if (bi->flags & BLK_INTEGRITY_REF_TAG)
192e9f5f44aSChristoph Hellwig 			return "EXT-DIF-TYPE1-CRC64";
193e9f5f44aSChristoph Hellwig 		return "EXT-DIF-TYPE3-CRC64";
194e9f5f44aSChristoph Hellwig 	case BLK_INTEGRITY_CSUM_NONE:
195e9f5f44aSChristoph Hellwig 		break;
196e9f5f44aSChristoph Hellwig 	}
197e9f5f44aSChristoph Hellwig 
198e9f5f44aSChristoph Hellwig 	return "nop";
199e9f5f44aSChristoph Hellwig }
200e9f5f44aSChristoph Hellwig EXPORT_SYMBOL_GPL(blk_integrity_profile_name);
201e9f5f44aSChristoph Hellwig 
flag_store(struct device * dev,const char * page,size_t count,unsigned char flag)202b83bd486SKanchan Joshi static ssize_t flag_store(struct device *dev, const char *page, size_t count,
203b83bd486SKanchan Joshi 		unsigned char flag)
2041366251aSChristoph Hellwig {
205c6e56cf6SChristoph Hellwig 	struct request_queue *q = dev_to_disk(dev)->queue;
206c6e56cf6SChristoph Hellwig 	struct queue_limits lim;
2071d59857eSChristoph Hellwig 	unsigned long val;
2081d59857eSChristoph Hellwig 	int err;
2091d59857eSChristoph Hellwig 
2101d59857eSChristoph Hellwig 	err = kstrtoul(page, 10, &val);
2111d59857eSChristoph Hellwig 	if (err)
2121d59857eSChristoph Hellwig 		return err;
2131366251aSChristoph Hellwig 
214c6e56cf6SChristoph Hellwig 	/* note that the flags are inverted vs the values in the sysfs files */
215c6e56cf6SChristoph Hellwig 	lim = queue_limits_start_update(q);
2161366251aSChristoph Hellwig 	if (val)
217c6e56cf6SChristoph Hellwig 		lim.integrity.flags &= ~flag;
2189f4aa46fSChristoph Hellwig 	else
219c6e56cf6SChristoph Hellwig 		lim.integrity.flags |= flag;
220c6e56cf6SChristoph Hellwig 
221c6e56cf6SChristoph Hellwig 	blk_mq_freeze_queue(q);
222c6e56cf6SChristoph Hellwig 	err = queue_limits_commit_update(q, &lim);
223c6e56cf6SChristoph Hellwig 	blk_mq_unfreeze_queue(q);
224c6e56cf6SChristoph Hellwig 	if (err)
225c6e56cf6SChristoph Hellwig 		return err;
2261366251aSChristoph Hellwig 	return count;
2271366251aSChristoph Hellwig }
2281366251aSChristoph Hellwig 
flag_show(struct device * dev,char * page,unsigned char flag)229b83bd486SKanchan Joshi static ssize_t flag_show(struct device *dev, char *page, unsigned char flag)
2301366251aSChristoph Hellwig {
2311366251aSChristoph Hellwig 	struct blk_integrity *bi = dev_to_bi(dev);
2321366251aSChristoph Hellwig 
2339f4aa46fSChristoph Hellwig 	return sysfs_emit(page, "%d\n", !(bi->flags & flag));
2341366251aSChristoph Hellwig }
2351366251aSChristoph Hellwig 
format_show(struct device * dev,struct device_attribute * attr,char * page)23676b8c319SThomas Weißschuh static ssize_t format_show(struct device *dev, struct device_attribute *attr,
23776b8c319SThomas Weißschuh 			   char *page)
23876b8c319SThomas Weißschuh {
23976b8c319SThomas Weißschuh 	struct blk_integrity *bi = dev_to_bi(dev);
24076b8c319SThomas Weißschuh 
241e9f5f44aSChristoph Hellwig 	if (!bi->tuple_size)
2423315e169SThomas Weißschuh 		return sysfs_emit(page, "none\n");
243e9f5f44aSChristoph Hellwig 	return sysfs_emit(page, "%s\n", blk_integrity_profile_name(bi));
2447ba1ba12SMartin K. Petersen }
2457ba1ba12SMartin K. Petersen 
tag_size_show(struct device * dev,struct device_attribute * attr,char * page)24676b8c319SThomas Weißschuh static ssize_t tag_size_show(struct device *dev, struct device_attribute *attr,
24776b8c319SThomas Weißschuh 			     char *page)
2487ba1ba12SMartin K. Petersen {
24976b8c319SThomas Weißschuh 	struct blk_integrity *bi = dev_to_bi(dev);
25076b8c319SThomas Weißschuh 
2513315e169SThomas Weißschuh 	return sysfs_emit(page, "%u\n", bi->tag_size);
2527ba1ba12SMartin K. Petersen }
2537ba1ba12SMartin K. Petersen 
protection_interval_bytes_show(struct device * dev,struct device_attribute * attr,char * page)25476b8c319SThomas Weißschuh static ssize_t protection_interval_bytes_show(struct device *dev,
25576b8c319SThomas Weißschuh 					      struct device_attribute *attr,
25676b8c319SThomas Weißschuh 					      char *page)
2574c241d08SMartin K. Petersen {
25876b8c319SThomas Weißschuh 	struct blk_integrity *bi = dev_to_bi(dev);
25976b8c319SThomas Weißschuh 
2603315e169SThomas Weißschuh 	return sysfs_emit(page, "%u\n",
26125520d55SMartin K. Petersen 			  bi->interval_exp ? 1 << bi->interval_exp : 0);
2624c241d08SMartin K. Petersen }
2634c241d08SMartin K. Petersen 
read_verify_store(struct device * dev,struct device_attribute * attr,const char * page,size_t count)26476b8c319SThomas Weißschuh static ssize_t read_verify_store(struct device *dev,
26576b8c319SThomas Weißschuh 				 struct device_attribute *attr,
2667ba1ba12SMartin K. Petersen 				 const char *page, size_t count)
2677ba1ba12SMartin K. Petersen {
268b83bd486SKanchan Joshi 	return flag_store(dev, page, count, BLK_INTEGRITY_NOVERIFY);
2697ba1ba12SMartin K. Petersen }
2707ba1ba12SMartin K. Petersen 
read_verify_show(struct device * dev,struct device_attribute * attr,char * page)27176b8c319SThomas Weißschuh static ssize_t read_verify_show(struct device *dev,
27276b8c319SThomas Weißschuh 				struct device_attribute *attr, char *page)
2737ba1ba12SMartin K. Petersen {
274b83bd486SKanchan Joshi 	return flag_show(dev, page, BLK_INTEGRITY_NOVERIFY);
2757ba1ba12SMartin K. Petersen }
2767ba1ba12SMartin K. Petersen 
write_generate_store(struct device * dev,struct device_attribute * attr,const char * page,size_t count)27776b8c319SThomas Weißschuh static ssize_t write_generate_store(struct device *dev,
27876b8c319SThomas Weißschuh 				    struct device_attribute *attr,
2797ba1ba12SMartin K. Petersen 				    const char *page, size_t count)
2807ba1ba12SMartin K. Petersen {
281b83bd486SKanchan Joshi 	return flag_store(dev, page, count, BLK_INTEGRITY_NOGENERATE);
2827ba1ba12SMartin K. Petersen }
2837ba1ba12SMartin K. Petersen 
write_generate_show(struct device * dev,struct device_attribute * attr,char * page)28476b8c319SThomas Weißschuh static ssize_t write_generate_show(struct device *dev,
28576b8c319SThomas Weißschuh 				   struct device_attribute *attr, char *page)
2867ba1ba12SMartin K. Petersen {
287b83bd486SKanchan Joshi 	return flag_show(dev, page, BLK_INTEGRITY_NOGENERATE);
2887ba1ba12SMartin K. Petersen }
2897ba1ba12SMartin K. Petersen 
device_is_integrity_capable_show(struct device * dev,struct device_attribute * attr,char * page)29076b8c319SThomas Weißschuh static ssize_t device_is_integrity_capable_show(struct device *dev,
29176b8c319SThomas Weißschuh 						struct device_attribute *attr,
29276b8c319SThomas Weißschuh 						char *page)
2933aec2f41SMartin K. Petersen {
29476b8c319SThomas Weißschuh 	struct blk_integrity *bi = dev_to_bi(dev);
29576b8c319SThomas Weißschuh 
2963315e169SThomas Weißschuh 	return sysfs_emit(page, "%u\n",
2973315e169SThomas Weißschuh 			  !!(bi->flags & BLK_INTEGRITY_DEVICE_CAPABLE));
2983aec2f41SMartin K. Petersen }
2993aec2f41SMartin K. Petersen 
30076b8c319SThomas Weißschuh static DEVICE_ATTR_RO(format);
30176b8c319SThomas Weißschuh static DEVICE_ATTR_RO(tag_size);
30276b8c319SThomas Weißschuh static DEVICE_ATTR_RO(protection_interval_bytes);
30376b8c319SThomas Weißschuh static DEVICE_ATTR_RW(read_verify);
30476b8c319SThomas Weißschuh static DEVICE_ATTR_RW(write_generate);
30576b8c319SThomas Weißschuh static DEVICE_ATTR_RO(device_is_integrity_capable);
3063aec2f41SMartin K. Petersen 
3077ba1ba12SMartin K. Petersen static struct attribute *integrity_attrs[] = {
30876b8c319SThomas Weißschuh 	&dev_attr_format.attr,
30976b8c319SThomas Weißschuh 	&dev_attr_tag_size.attr,
31076b8c319SThomas Weißschuh 	&dev_attr_protection_interval_bytes.attr,
31176b8c319SThomas Weißschuh 	&dev_attr_read_verify.attr,
31276b8c319SThomas Weißschuh 	&dev_attr_write_generate.attr,
31376b8c319SThomas Weißschuh 	&dev_attr_device_is_integrity_capable.attr,
31476b8c319SThomas Weißschuh 	NULL
3157ba1ba12SMartin K. Petersen };
3167ba1ba12SMartin K. Petersen 
317ff53cd52SThomas Weißschuh const struct attribute_group blk_integrity_attr_group = {
318ff53cd52SThomas Weißschuh 	.name = "integrity",
319ff53cd52SThomas Weißschuh 	.attrs = integrity_attrs,
3207ba1ba12SMartin K. Petersen };
321