Merge tag 'nfs-for-6.12-1' of git://git.linux-nfs.org/projects/anna/linux-nfs
[linux-2.6-block.git] / block / blk-integrity.c
CommitLineData
8c16567d 1// SPDX-License-Identifier: GPL-2.0
7ba1ba12
MP
2/*
3 * blk-integrity.c - Block layer data integrity extensions
4 *
5 * Copyright (C) 2007, 2008 Oracle Corporation
6 * Written by: Martin K. Petersen <martin.petersen@oracle.com>
7ba1ba12
MP
7 */
8
fe45e630 9#include <linux/blk-integrity.h>
66114cad 10#include <linux/backing-dev.h>
7ba1ba12
MP
11#include <linux/mempool.h>
12#include <linux/bio.h>
13#include <linux/scatterlist.h>
d5decd3b 14#include <linux/export.h>
5a0e3ad6 15#include <linux/slab.h>
7ba1ba12
MP
16
17#include "blk.h"
18
7ba1ba12
MP
19/**
20 * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements
13f05c8d
MP
21 * @q: request queue
22 * @bio: bio with integrity metadata attached
7ba1ba12
MP
23 *
24 * Description: Returns the number of elements required in a
13f05c8d 25 * scatterlist corresponding to the integrity metadata in a bio.
7ba1ba12 26 */
13f05c8d 27int blk_rq_count_integrity_sg(struct request_queue *q, struct bio *bio)
7ba1ba12 28{
d57a5f7c 29 struct bio_vec iv, ivprv = { NULL };
13f05c8d
MP
30 unsigned int segments = 0;
31 unsigned int seg_size = 0;
d57a5f7c
KO
32 struct bvec_iter iter;
33 int prev = 0;
7ba1ba12 34
d57a5f7c 35 bio_for_each_integrity_vec(iv, bio, iter) {
7ba1ba12 36
d57a5f7c 37 if (prev) {
3dccdae5 38 if (!biovec_phys_mergeable(q, &ivprv, &iv))
13f05c8d 39 goto new_segment;
d57a5f7c 40 if (seg_size + iv.bv_len > queue_max_segment_size(q))
13f05c8d 41 goto new_segment;
7ba1ba12 42
d57a5f7c 43 seg_size += iv.bv_len;
13f05c8d
MP
44 } else {
45new_segment:
7ba1ba12 46 segments++;
d57a5f7c 47 seg_size = iv.bv_len;
13f05c8d 48 }
7ba1ba12 49
d57a5f7c 50 prev = 1;
7ba1ba12
MP
51 ivprv = iv;
52 }
53
54 return segments;
55}
56EXPORT_SYMBOL(blk_rq_count_integrity_sg);
57
58/**
59 * blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist
13f05c8d
MP
60 * @q: request queue
61 * @bio: bio with integrity metadata attached
7ba1ba12
MP
62 * @sglist: target scatterlist
63 *
64 * Description: Map the integrity vectors in request into a
65 * scatterlist. The scatterlist must be big enough to hold all
66 * elements. I.e. sized using blk_rq_count_integrity_sg().
67 */
13f05c8d
MP
68int blk_rq_map_integrity_sg(struct request_queue *q, struct bio *bio,
69 struct scatterlist *sglist)
7ba1ba12 70{
d57a5f7c 71 struct bio_vec iv, ivprv = { NULL };
13f05c8d
MP
72 struct scatterlist *sg = NULL;
73 unsigned int segments = 0;
d57a5f7c
KO
74 struct bvec_iter iter;
75 int prev = 0;
7ba1ba12 76
d57a5f7c 77 bio_for_each_integrity_vec(iv, bio, iter) {
7ba1ba12 78
d57a5f7c 79 if (prev) {
3dccdae5 80 if (!biovec_phys_mergeable(q, &ivprv, &iv))
7ba1ba12 81 goto new_segment;
d57a5f7c 82 if (sg->length + iv.bv_len > queue_max_segment_size(q))
13f05c8d
MP
83 goto new_segment;
84
d57a5f7c 85 sg->length += iv.bv_len;
7ba1ba12
MP
86 } else {
87new_segment:
88 if (!sg)
89 sg = sglist;
90 else {
c8164d89 91 sg_unmark_end(sg);
7ba1ba12
MP
92 sg = sg_next(sg);
93 }
94
d57a5f7c 95 sg_set_page(sg, iv.bv_page, iv.bv_len, iv.bv_offset);
7ba1ba12
MP
96 segments++;
97 }
98
d57a5f7c 99 prev = 1;
7ba1ba12
MP
100 ivprv = iv;
101 }
102
103 if (sg)
104 sg_mark_end(sg);
105
106 return segments;
107}
108EXPORT_SYMBOL(blk_rq_map_integrity_sg);
109
4eaf99be
MP
110bool blk_integrity_merge_rq(struct request_queue *q, struct request *req,
111 struct request *next)
13f05c8d 112{
4eaf99be
MP
113 if (blk_integrity_rq(req) == 0 && blk_integrity_rq(next) == 0)
114 return true;
115
116 if (blk_integrity_rq(req) == 0 || blk_integrity_rq(next) == 0)
117 return false;
118
119 if (bio_integrity(req->bio)->bip_flags !=
120 bio_integrity(next->bio)->bip_flags)
121 return false;
13f05c8d
MP
122
123 if (req->nr_integrity_segments + next->nr_integrity_segments >
124 q->limits.max_integrity_segments)
4eaf99be 125 return false;
13f05c8d 126
7f39add3
SG
127 if (integrity_req_gap_back_merge(req, next->bio))
128 return false;
129
4eaf99be 130 return true;
13f05c8d 131}
13f05c8d 132
4eaf99be
MP
133bool blk_integrity_merge_bio(struct request_queue *q, struct request *req,
134 struct bio *bio)
13f05c8d
MP
135{
136 int nr_integrity_segs;
137 struct bio *next = bio->bi_next;
138
4eaf99be
MP
139 if (blk_integrity_rq(req) == 0 && bio_integrity(bio) == NULL)
140 return true;
141
142 if (blk_integrity_rq(req) == 0 || bio_integrity(bio) == NULL)
143 return false;
144
145 if (bio_integrity(req->bio)->bip_flags != bio_integrity(bio)->bip_flags)
146 return false;
147
13f05c8d
MP
148 bio->bi_next = NULL;
149 nr_integrity_segs = blk_rq_count_integrity_sg(q, bio);
150 bio->bi_next = next;
151
152 if (req->nr_integrity_segments + nr_integrity_segs >
153 q->limits.max_integrity_segments)
4eaf99be 154 return false;
13f05c8d
MP
155
156 req->nr_integrity_segments += nr_integrity_segs;
157
4eaf99be 158 return true;
13f05c8d 159}
13f05c8d 160
76b8c319
TW
161static inline struct blk_integrity *dev_to_bi(struct device *dev)
162{
c6e56cf6 163 return &dev_to_disk(dev)->queue->limits.integrity;
7ba1ba12
MP
164}
165
e9f5f44a
CH
166const char *blk_integrity_profile_name(struct blk_integrity *bi)
167{
168 switch (bi->csum_type) {
169 case BLK_INTEGRITY_CSUM_IP:
170 if (bi->flags & BLK_INTEGRITY_REF_TAG)
171 return "T10-DIF-TYPE1-IP";
172 return "T10-DIF-TYPE3-IP";
173 case BLK_INTEGRITY_CSUM_CRC:
174 if (bi->flags & BLK_INTEGRITY_REF_TAG)
175 return "T10-DIF-TYPE1-CRC";
176 return "T10-DIF-TYPE3-CRC";
177 case BLK_INTEGRITY_CSUM_CRC64:
178 if (bi->flags & BLK_INTEGRITY_REF_TAG)
179 return "EXT-DIF-TYPE1-CRC64";
180 return "EXT-DIF-TYPE3-CRC64";
181 case BLK_INTEGRITY_CSUM_NONE:
182 break;
183 }
184
185 return "nop";
186}
187EXPORT_SYMBOL_GPL(blk_integrity_profile_name);
188
b83bd486
KJ
189static ssize_t flag_store(struct device *dev, const char *page, size_t count,
190 unsigned char flag)
1366251a 191{
c6e56cf6
CH
192 struct request_queue *q = dev_to_disk(dev)->queue;
193 struct queue_limits lim;
1d59857e
CH
194 unsigned long val;
195 int err;
196
197 err = kstrtoul(page, 10, &val);
198 if (err)
199 return err;
1366251a 200
c6e56cf6
CH
201 /* note that the flags are inverted vs the values in the sysfs files */
202 lim = queue_limits_start_update(q);
1366251a 203 if (val)
c6e56cf6 204 lim.integrity.flags &= ~flag;
9f4aa46f 205 else
c6e56cf6
CH
206 lim.integrity.flags |= flag;
207
208 blk_mq_freeze_queue(q);
209 err = queue_limits_commit_update(q, &lim);
210 blk_mq_unfreeze_queue(q);
211 if (err)
212 return err;
1366251a
CH
213 return count;
214}
215
b83bd486 216static ssize_t flag_show(struct device *dev, char *page, unsigned char flag)
1366251a
CH
217{
218 struct blk_integrity *bi = dev_to_bi(dev);
219
9f4aa46f 220 return sysfs_emit(page, "%d\n", !(bi->flags & flag));
1366251a
CH
221}
222
76b8c319
TW
223static ssize_t format_show(struct device *dev, struct device_attribute *attr,
224 char *page)
7ba1ba12 225{
76b8c319
TW
226 struct blk_integrity *bi = dev_to_bi(dev);
227
e9f5f44a
CH
228 if (!bi->tuple_size)
229 return sysfs_emit(page, "none\n");
230 return sysfs_emit(page, "%s\n", blk_integrity_profile_name(bi));
7ba1ba12
MP
231}
232
76b8c319
TW
233static ssize_t tag_size_show(struct device *dev, struct device_attribute *attr,
234 char *page)
7ba1ba12 235{
76b8c319
TW
236 struct blk_integrity *bi = dev_to_bi(dev);
237
3315e169 238 return sysfs_emit(page, "%u\n", bi->tag_size);
7ba1ba12
MP
239}
240
76b8c319
TW
241static ssize_t protection_interval_bytes_show(struct device *dev,
242 struct device_attribute *attr,
243 char *page)
4c241d08 244{
76b8c319
TW
245 struct blk_integrity *bi = dev_to_bi(dev);
246
3315e169
TW
247 return sysfs_emit(page, "%u\n",
248 bi->interval_exp ? 1 << bi->interval_exp : 0);
4c241d08
MP
249}
250
76b8c319
TW
251static ssize_t read_verify_store(struct device *dev,
252 struct device_attribute *attr,
253 const char *page, size_t count)
7ba1ba12 254{
b83bd486 255 return flag_store(dev, page, count, BLK_INTEGRITY_NOVERIFY);
7ba1ba12
MP
256}
257
76b8c319
TW
258static ssize_t read_verify_show(struct device *dev,
259 struct device_attribute *attr, char *page)
7ba1ba12 260{
b83bd486 261 return flag_show(dev, page, BLK_INTEGRITY_NOVERIFY);
7ba1ba12
MP
262}
263
76b8c319
TW
264static ssize_t write_generate_store(struct device *dev,
265 struct device_attribute *attr,
266 const char *page, size_t count)
7ba1ba12 267{
b83bd486 268 return flag_store(dev, page, count, BLK_INTEGRITY_NOGENERATE);
7ba1ba12
MP
269}
270
76b8c319
TW
271static ssize_t write_generate_show(struct device *dev,
272 struct device_attribute *attr, char *page)
7ba1ba12 273{
b83bd486 274 return flag_show(dev, page, BLK_INTEGRITY_NOGENERATE);
7ba1ba12
MP
275}
276
76b8c319
TW
277static ssize_t device_is_integrity_capable_show(struct device *dev,
278 struct device_attribute *attr,
279 char *page)
3aec2f41 280{
76b8c319
TW
281 struct blk_integrity *bi = dev_to_bi(dev);
282
3315e169
TW
283 return sysfs_emit(page, "%u\n",
284 !!(bi->flags & BLK_INTEGRITY_DEVICE_CAPABLE));
3aec2f41
MP
285}
286
76b8c319
TW
287static DEVICE_ATTR_RO(format);
288static DEVICE_ATTR_RO(tag_size);
289static DEVICE_ATTR_RO(protection_interval_bytes);
290static DEVICE_ATTR_RW(read_verify);
291static DEVICE_ATTR_RW(write_generate);
292static DEVICE_ATTR_RO(device_is_integrity_capable);
3aec2f41 293
7ba1ba12 294static struct attribute *integrity_attrs[] = {
76b8c319
TW
295 &dev_attr_format.attr,
296 &dev_attr_tag_size.attr,
297 &dev_attr_protection_interval_bytes.attr,
298 &dev_attr_read_verify.attr,
299 &dev_attr_write_generate.attr,
300 &dev_attr_device_is_integrity_capable.attr,
301 NULL
7ba1ba12
MP
302};
303
ff53cd52
TW
304const struct attribute_group blk_integrity_attr_group = {
305 .name = "integrity",
306 .attrs = integrity_attrs,
7ba1ba12 307};