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