Blame view

block/blk-integrity.c 11.4 KB
7ba1ba12e   Martin K. Petersen   block: Block laye...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  /*
   * blk-integrity.c - Block layer data integrity extensions
   *
   * Copyright (C) 2007, 2008 Oracle Corporation
   * Written by: Martin K. Petersen <martin.petersen@oracle.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License version
   * 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; see the file COPYING.  If not, write to
   * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
   * USA.
   *
   */
  
  #include <linux/blkdev.h>
  #include <linux/mempool.h>
  #include <linux/bio.h>
  #include <linux/scatterlist.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
27
  #include <linux/slab.h>
7ba1ba12e   Martin K. Petersen   block: Block laye...
28
29
30
31
  
  #include "blk.h"
  
  static struct kmem_cache *integrity_cachep;
a63a5cf84   Mike Snitzer   dm: improve block...
32
  static const char *bi_unsupported_name = "unsupported";
7ba1ba12e   Martin K. Petersen   block: Block laye...
33
34
  /**
   * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
35
36
   * @q:		request queue
   * @bio:	bio with integrity metadata attached
7ba1ba12e   Martin K. Petersen   block: Block laye...
37
38
   *
   * Description: Returns the number of elements required in a
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
39
   * scatterlist corresponding to the integrity metadata in a bio.
7ba1ba12e   Martin K. Petersen   block: Block laye...
40
   */
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
41
  int blk_rq_count_integrity_sg(struct request_queue *q, struct bio *bio)
7ba1ba12e   Martin K. Petersen   block: Block laye...
42
  {
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
43
44
45
46
  	struct bio_vec *iv, *ivprv = NULL;
  	unsigned int segments = 0;
  	unsigned int seg_size = 0;
  	unsigned int i = 0;
7ba1ba12e   Martin K. Petersen   block: Block laye...
47

13f05c8d8   Martin K. Petersen   block/scsi: Provi...
48
  	bio_for_each_integrity_vec(iv, bio, i) {
7ba1ba12e   Martin K. Petersen   block: Block laye...
49

13f05c8d8   Martin K. Petersen   block/scsi: Provi...
50
51
52
53
54
55
56
57
58
  		if (ivprv) {
  			if (!BIOVEC_PHYS_MERGEABLE(ivprv, iv))
  				goto new_segment;
  
  			if (!BIOVEC_SEG_BOUNDARY(q, ivprv, iv))
  				goto new_segment;
  
  			if (seg_size + iv->bv_len > queue_max_segment_size(q))
  				goto new_segment;
7ba1ba12e   Martin K. Petersen   block: Block laye...
59

13f05c8d8   Martin K. Petersen   block/scsi: Provi...
60
61
62
  			seg_size += iv->bv_len;
  		} else {
  new_segment:
7ba1ba12e   Martin K. Petersen   block: Block laye...
63
  			segments++;
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
64
65
  			seg_size = iv->bv_len;
  		}
7ba1ba12e   Martin K. Petersen   block: Block laye...
66
67
68
69
70
71
72
73
74
75
  
  		ivprv = iv;
  	}
  
  	return segments;
  }
  EXPORT_SYMBOL(blk_rq_count_integrity_sg);
  
  /**
   * blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
76
77
   * @q:		request queue
   * @bio:	bio with integrity metadata attached
7ba1ba12e   Martin K. Petersen   block: Block laye...
78
79
80
81
82
83
   * @sglist:	target scatterlist
   *
   * Description: Map the integrity vectors in request into a
   * scatterlist.  The scatterlist must be big enough to hold all
   * elements.  I.e. sized using blk_rq_count_integrity_sg().
   */
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
84
85
  int blk_rq_map_integrity_sg(struct request_queue *q, struct bio *bio,
  			    struct scatterlist *sglist)
7ba1ba12e   Martin K. Petersen   block: Block laye...
86
  {
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
87
88
89
90
  	struct bio_vec *iv, *ivprv = NULL;
  	struct scatterlist *sg = NULL;
  	unsigned int segments = 0;
  	unsigned int i = 0;
7ba1ba12e   Martin K. Petersen   block: Block laye...
91

13f05c8d8   Martin K. Petersen   block/scsi: Provi...
92
  	bio_for_each_integrity_vec(iv, bio, i) {
7ba1ba12e   Martin K. Petersen   block: Block laye...
93
94
95
96
  
  		if (ivprv) {
  			if (!BIOVEC_PHYS_MERGEABLE(ivprv, iv))
  				goto new_segment;
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
97
98
99
100
101
  			if (!BIOVEC_SEG_BOUNDARY(q, ivprv, iv))
  				goto new_segment;
  
  			if (sg->length + iv->bv_len > queue_max_segment_size(q))
  				goto new_segment;
7ba1ba12e   Martin K. Petersen   block: Block laye...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  			sg->length += iv->bv_len;
  		} else {
  new_segment:
  			if (!sg)
  				sg = sglist;
  			else {
  				sg->page_link &= ~0x02;
  				sg = sg_next(sg);
  			}
  
  			sg_set_page(sg, iv->bv_page, iv->bv_len, iv->bv_offset);
  			segments++;
  		}
  
  		ivprv = iv;
  	}
  
  	if (sg)
  		sg_mark_end(sg);
  
  	return segments;
  }
  EXPORT_SYMBOL(blk_rq_map_integrity_sg);
  
  /**
ad7fce931   Martin K. Petersen   block: Switch blk...
127
128
129
   * blk_integrity_compare - Compare integrity profile of two disks
   * @gd1:	Disk to compare
   * @gd2:	Disk to compare
7ba1ba12e   Martin K. Petersen   block: Block laye...
130
131
132
133
   *
   * Description: Meta-devices like DM and MD need to verify that all
   * sub-devices use the same integrity format before advertising to
   * upper layers that they can send/receive integrity metadata.  This
ad7fce931   Martin K. Petersen   block: Switch blk...
134
   * function can be used to check whether two gendisk devices have
7ba1ba12e   Martin K. Petersen   block: Block laye...
135
136
   * compatible integrity formats.
   */
ad7fce931   Martin K. Petersen   block: Switch blk...
137
  int blk_integrity_compare(struct gendisk *gd1, struct gendisk *gd2)
7ba1ba12e   Martin K. Petersen   block: Block laye...
138
  {
ad7fce931   Martin K. Petersen   block: Switch blk...
139
140
  	struct blk_integrity *b1 = gd1->integrity;
  	struct blk_integrity *b2 = gd2->integrity;
7ba1ba12e   Martin K. Petersen   block: Block laye...
141

ad7fce931   Martin K. Petersen   block: Switch blk...
142
143
  	if (!b1 && !b2)
  		return 0;
7ba1ba12e   Martin K. Petersen   block: Block laye...
144
145
  
  	if (!b1 || !b2)
ad7fce931   Martin K. Petersen   block: Switch blk...
146
  		return -1;
7ba1ba12e   Martin K. Petersen   block: Block laye...
147
148
149
150
  
  	if (b1->sector_size != b2->sector_size) {
  		printk(KERN_ERR "%s: %s/%s sector sz %u != %u
  ", __func__,
ad7fce931   Martin K. Petersen   block: Switch blk...
151
  		       gd1->disk_name, gd2->disk_name,
7ba1ba12e   Martin K. Petersen   block: Block laye...
152
153
154
155
156
157
158
  		       b1->sector_size, b2->sector_size);
  		return -1;
  	}
  
  	if (b1->tuple_size != b2->tuple_size) {
  		printk(KERN_ERR "%s: %s/%s tuple sz %u != %u
  ", __func__,
ad7fce931   Martin K. Petersen   block: Switch blk...
159
  		       gd1->disk_name, gd2->disk_name,
7ba1ba12e   Martin K. Petersen   block: Block laye...
160
161
162
163
164
165
166
  		       b1->tuple_size, b2->tuple_size);
  		return -1;
  	}
  
  	if (b1->tag_size && b2->tag_size && (b1->tag_size != b2->tag_size)) {
  		printk(KERN_ERR "%s: %s/%s tag sz %u != %u
  ", __func__,
ad7fce931   Martin K. Petersen   block: Switch blk...
167
  		       gd1->disk_name, gd2->disk_name,
7ba1ba12e   Martin K. Petersen   block: Block laye...
168
169
170
171
172
173
174
  		       b1->tag_size, b2->tag_size);
  		return -1;
  	}
  
  	if (strcmp(b1->name, b2->name)) {
  		printk(KERN_ERR "%s: %s/%s type %s != %s
  ", __func__,
ad7fce931   Martin K. Petersen   block: Switch blk...
175
  		       gd1->disk_name, gd2->disk_name,
7ba1ba12e   Martin K. Petersen   block: Block laye...
176
177
178
179
180
181
182
  		       b1->name, b2->name);
  		return -1;
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL(blk_integrity_compare);
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  int blk_integrity_merge_rq(struct request_queue *q, struct request *req,
  			   struct request *next)
  {
  	if (blk_integrity_rq(req) != blk_integrity_rq(next))
  		return -1;
  
  	if (req->nr_integrity_segments + next->nr_integrity_segments >
  	    q->limits.max_integrity_segments)
  		return -1;
  
  	return 0;
  }
  EXPORT_SYMBOL(blk_integrity_merge_rq);
  
  int blk_integrity_merge_bio(struct request_queue *q, struct request *req,
  			    struct bio *bio)
  {
  	int nr_integrity_segs;
  	struct bio *next = bio->bi_next;
  
  	bio->bi_next = NULL;
  	nr_integrity_segs = blk_rq_count_integrity_sg(q, bio);
  	bio->bi_next = next;
  
  	if (req->nr_integrity_segments + nr_integrity_segs >
  	    q->limits.max_integrity_segments)
  		return -1;
  
  	req->nr_integrity_segments += nr_integrity_segs;
  
  	return 0;
  }
  EXPORT_SYMBOL(blk_integrity_merge_bio);
7ba1ba12e   Martin K. Petersen   block: Block laye...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
  struct integrity_sysfs_entry {
  	struct attribute attr;
  	ssize_t (*show)(struct blk_integrity *, char *);
  	ssize_t (*store)(struct blk_integrity *, const char *, size_t);
  };
  
  static ssize_t integrity_attr_show(struct kobject *kobj, struct attribute *attr,
  				   char *page)
  {
  	struct blk_integrity *bi =
  		container_of(kobj, struct blk_integrity, kobj);
  	struct integrity_sysfs_entry *entry =
  		container_of(attr, struct integrity_sysfs_entry, attr);
  
  	return entry->show(bi, page);
  }
b984679ef   Jens Axboe   block: integrity ...
232
233
234
  static ssize_t integrity_attr_store(struct kobject *kobj,
  				    struct attribute *attr, const char *page,
  				    size_t count)
7ba1ba12e   Martin K. Petersen   block: Block laye...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  {
  	struct blk_integrity *bi =
  		container_of(kobj, struct blk_integrity, kobj);
  	struct integrity_sysfs_entry *entry =
  		container_of(attr, struct integrity_sysfs_entry, attr);
  	ssize_t ret = 0;
  
  	if (entry->store)
  		ret = entry->store(bi, page, count);
  
  	return ret;
  }
  
  static ssize_t integrity_format_show(struct blk_integrity *bi, char *page)
  {
  	if (bi != NULL && bi->name != NULL)
  		return sprintf(page, "%s
  ", bi->name);
  	else
  		return sprintf(page, "none
  ");
  }
  
  static ssize_t integrity_tag_size_show(struct blk_integrity *bi, char *page)
  {
  	if (bi != NULL)
  		return sprintf(page, "%u
  ", bi->tag_size);
  	else
  		return sprintf(page, "0
  ");
  }
  
  static ssize_t integrity_read_store(struct blk_integrity *bi,
  				    const char *page, size_t count)
  {
  	char *p = (char *) page;
  	unsigned long val = simple_strtoul(p, &p, 10);
  
  	if (val)
b24498d47   Jens Axboe   block: integrity ...
275
  		bi->flags |= INTEGRITY_FLAG_READ;
7ba1ba12e   Martin K. Petersen   block: Block laye...
276
  	else
b24498d47   Jens Axboe   block: integrity ...
277
  		bi->flags &= ~INTEGRITY_FLAG_READ;
7ba1ba12e   Martin K. Petersen   block: Block laye...
278
279
280
281
282
283
  
  	return count;
  }
  
  static ssize_t integrity_read_show(struct blk_integrity *bi, char *page)
  {
b24498d47   Jens Axboe   block: integrity ...
284
285
  	return sprintf(page, "%d
  ", (bi->flags & INTEGRITY_FLAG_READ) != 0);
7ba1ba12e   Martin K. Petersen   block: Block laye...
286
287
288
289
290
291
292
293
294
  }
  
  static ssize_t integrity_write_store(struct blk_integrity *bi,
  				     const char *page, size_t count)
  {
  	char *p = (char *) page;
  	unsigned long val = simple_strtoul(p, &p, 10);
  
  	if (val)
b24498d47   Jens Axboe   block: integrity ...
295
  		bi->flags |= INTEGRITY_FLAG_WRITE;
7ba1ba12e   Martin K. Petersen   block: Block laye...
296
  	else
b24498d47   Jens Axboe   block: integrity ...
297
  		bi->flags &= ~INTEGRITY_FLAG_WRITE;
7ba1ba12e   Martin K. Petersen   block: Block laye...
298
299
300
301
302
303
  
  	return count;
  }
  
  static ssize_t integrity_write_show(struct blk_integrity *bi, char *page)
  {
b24498d47   Jens Axboe   block: integrity ...
304
305
  	return sprintf(page, "%d
  ", (bi->flags & INTEGRITY_FLAG_WRITE) != 0);
7ba1ba12e   Martin K. Petersen   block: Block laye...
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
  }
  
  static struct integrity_sysfs_entry integrity_format_entry = {
  	.attr = { .name = "format", .mode = S_IRUGO },
  	.show = integrity_format_show,
  };
  
  static struct integrity_sysfs_entry integrity_tag_size_entry = {
  	.attr = { .name = "tag_size", .mode = S_IRUGO },
  	.show = integrity_tag_size_show,
  };
  
  static struct integrity_sysfs_entry integrity_read_entry = {
  	.attr = { .name = "read_verify", .mode = S_IRUGO | S_IWUSR },
  	.show = integrity_read_show,
  	.store = integrity_read_store,
  };
  
  static struct integrity_sysfs_entry integrity_write_entry = {
  	.attr = { .name = "write_generate", .mode = S_IRUGO | S_IWUSR },
  	.show = integrity_write_show,
  	.store = integrity_write_store,
  };
  
  static struct attribute *integrity_attrs[] = {
  	&integrity_format_entry.attr,
  	&integrity_tag_size_entry.attr,
  	&integrity_read_entry.attr,
  	&integrity_write_entry.attr,
  	NULL,
  };
52cf25d0a   Emese Revfy   Driver core: Cons...
337
  static const struct sysfs_ops integrity_ops = {
7ba1ba12e   Martin K. Petersen   block: Block laye...
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
  	.show	= &integrity_attr_show,
  	.store	= &integrity_attr_store,
  };
  
  static int __init blk_dev_integrity_init(void)
  {
  	integrity_cachep = kmem_cache_create("blkdev_integrity",
  					     sizeof(struct blk_integrity),
  					     0, SLAB_PANIC, NULL);
  	return 0;
  }
  subsys_initcall(blk_dev_integrity_init);
  
  static void blk_integrity_release(struct kobject *kobj)
  {
  	struct blk_integrity *bi =
  		container_of(kobj, struct blk_integrity, kobj);
  
  	kmem_cache_free(integrity_cachep, bi);
  }
  
  static struct kobj_type integrity_ktype = {
  	.default_attrs	= integrity_attrs,
  	.sysfs_ops	= &integrity_ops,
  	.release	= blk_integrity_release,
  };
a63a5cf84   Mike Snitzer   dm: improve block...
364
365
366
367
368
369
370
  bool blk_integrity_is_initialized(struct gendisk *disk)
  {
  	struct blk_integrity *bi = blk_get_integrity(disk);
  
  	return (bi && bi->name && strcmp(bi->name, bi_unsupported_name) != 0);
  }
  EXPORT_SYMBOL(blk_integrity_is_initialized);
7ba1ba12e   Martin K. Petersen   block: Block laye...
371
372
373
  /**
   * blk_integrity_register - Register a gendisk as being integrity-capable
   * @disk:	struct gendisk pointer to make integrity-aware
322316385   Martin K. Petersen   block: Allow empt...
374
   * @template:	optional integrity profile to register
7ba1ba12e   Martin K. Petersen   block: Block laye...
375
376
377
378
379
   *
   * Description: When a device needs to advertise itself as being able
   * to send/receive integrity metadata it must use this function to
   * register the capability with the block layer.  The template is a
   * blk_integrity struct with values appropriate for the underlying
322316385   Martin K. Petersen   block: Allow empt...
380
381
   * hardware.  If template is NULL the new profile is allocated but
   * not filled out. See Documentation/block/data-integrity.txt.
7ba1ba12e   Martin K. Petersen   block: Block laye...
382
383
384
385
386
387
   */
  int blk_integrity_register(struct gendisk *disk, struct blk_integrity *template)
  {
  	struct blk_integrity *bi;
  
  	BUG_ON(disk == NULL);
7ba1ba12e   Martin K. Petersen   block: Block laye...
388
389
  
  	if (disk->integrity == NULL) {
b984679ef   Jens Axboe   block: integrity ...
390
  		bi = kmem_cache_alloc(integrity_cachep,
322316385   Martin K. Petersen   block: Allow empt...
391
  				      GFP_KERNEL | __GFP_ZERO);
7ba1ba12e   Martin K. Petersen   block: Block laye...
392
393
394
395
  		if (!bi)
  			return -1;
  
  		if (kobject_init_and_add(&bi->kobj, &integrity_ktype,
ed9e19823   Tejun Heo   block: implement ...
396
397
  					 &disk_to_dev(disk)->kobj,
  					 "%s", "integrity")) {
7ba1ba12e   Martin K. Petersen   block: Block laye...
398
399
400
401
402
  			kmem_cache_free(integrity_cachep, bi);
  			return -1;
  		}
  
  		kobject_uevent(&bi->kobj, KOBJ_ADD);
b24498d47   Jens Axboe   block: integrity ...
403
  		bi->flags |= INTEGRITY_FLAG_READ | INTEGRITY_FLAG_WRITE;
e1defc4ff   Martin K. Petersen   block: Do away wi...
404
  		bi->sector_size = queue_logical_block_size(disk->queue);
7ba1ba12e   Martin K. Petersen   block: Block laye...
405
406
407
408
409
  		disk->integrity = bi;
  	} else
  		bi = disk->integrity;
  
  	/* Use the provided profile as template */
322316385   Martin K. Petersen   block: Allow empt...
410
411
412
413
414
415
416
417
418
  	if (template != NULL) {
  		bi->name = template->name;
  		bi->generate_fn = template->generate_fn;
  		bi->verify_fn = template->verify_fn;
  		bi->tuple_size = template->tuple_size;
  		bi->set_tag_fn = template->set_tag_fn;
  		bi->get_tag_fn = template->get_tag_fn;
  		bi->tag_size = template->tag_size;
  	} else
a63a5cf84   Mike Snitzer   dm: improve block...
419
  		bi->name = bi_unsupported_name;
7ba1ba12e   Martin K. Petersen   block: Block laye...
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
  
  	return 0;
  }
  EXPORT_SYMBOL(blk_integrity_register);
  
  /**
   * blk_integrity_unregister - Remove block integrity profile
   * @disk:	disk whose integrity profile to deallocate
   *
   * Description: This function frees all memory used by the block
   * integrity profile.  To be called at device teardown.
   */
  void blk_integrity_unregister(struct gendisk *disk)
  {
  	struct blk_integrity *bi;
  
  	if (!disk || !disk->integrity)
  		return;
  
  	bi = disk->integrity;
  
  	kobject_uevent(&bi->kobj, KOBJ_REMOVE);
  	kobject_del(&bi->kobj);
3839e4b29   Xiaotian Feng   block: fix improp...
443
  	kobject_put(&bi->kobj);
0c032ab88   Martin K. Petersen   block: Fix double...
444
  	disk->integrity = NULL;
7ba1ba12e   Martin K. Petersen   block: Block laye...
445
446
  }
  EXPORT_SYMBOL(blk_integrity_unregister);