Blame view

block/blk-integrity.c 11.5 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>
d5decd3b9   Paul Gortmaker   block: add export...
27
  #include <linux/export.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
28
  #include <linux/slab.h>
7ba1ba12e   Martin K. Petersen   block: Block laye...
29
30
31
32
  
  #include "blk.h"
  
  static struct kmem_cache *integrity_cachep;
a63a5cf84   Mike Snitzer   dm: improve block...
33
  static const char *bi_unsupported_name = "unsupported";
7ba1ba12e   Martin K. Petersen   block: Block laye...
34
35
  /**
   * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
36
37
   * @q:		request queue
   * @bio:	bio with integrity metadata attached
7ba1ba12e   Martin K. Petersen   block: Block laye...
38
39
   *
   * Description: Returns the number of elements required in a
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
40
   * scatterlist corresponding to the integrity metadata in a bio.
7ba1ba12e   Martin K. Petersen   block: Block laye...
41
   */
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
42
  int blk_rq_count_integrity_sg(struct request_queue *q, struct bio *bio)
7ba1ba12e   Martin K. Petersen   block: Block laye...
43
  {
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
44
45
46
47
  	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...
48

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

13f05c8d8   Martin K. Petersen   block/scsi: Provi...
51
52
53
54
55
56
57
58
59
  		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...
60

13f05c8d8   Martin K. Petersen   block/scsi: Provi...
61
62
63
  			seg_size += iv->bv_len;
  		} else {
  new_segment:
7ba1ba12e   Martin K. Petersen   block: Block laye...
64
  			segments++;
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
65
66
  			seg_size = iv->bv_len;
  		}
7ba1ba12e   Martin K. Petersen   block: Block laye...
67
68
69
70
71
72
73
74
75
76
  
  		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...
77
78
   * @q:		request queue
   * @bio:	bio with integrity metadata attached
7ba1ba12e   Martin K. Petersen   block: Block laye...
79
80
81
82
83
84
   * @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...
85
86
  int blk_rq_map_integrity_sg(struct request_queue *q, struct bio *bio,
  			    struct scatterlist *sglist)
7ba1ba12e   Martin K. Petersen   block: Block laye...
87
  {
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
88
89
90
91
  	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...
92

13f05c8d8   Martin K. Petersen   block/scsi: Provi...
93
  	bio_for_each_integrity_vec(iv, bio, i) {
7ba1ba12e   Martin K. Petersen   block: Block laye...
94
95
96
97
  
  		if (ivprv) {
  			if (!BIOVEC_PHYS_MERGEABLE(ivprv, iv))
  				goto new_segment;
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
98
99
100
101
102
  			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...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  			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...
128
129
130
   * blk_integrity_compare - Compare integrity profile of two disks
   * @gd1:	Disk to compare
   * @gd2:	Disk to compare
7ba1ba12e   Martin K. Petersen   block: Block laye...
131
132
133
134
   *
   * 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...
135
   * function can be used to check whether two gendisk devices have
7ba1ba12e   Martin K. Petersen   block: Block laye...
136
137
   * compatible integrity formats.
   */
ad7fce931   Martin K. Petersen   block: Switch blk...
138
  int blk_integrity_compare(struct gendisk *gd1, struct gendisk *gd2)
7ba1ba12e   Martin K. Petersen   block: Block laye...
139
  {
ad7fce931   Martin K. Petersen   block: Switch blk...
140
141
  	struct blk_integrity *b1 = gd1->integrity;
  	struct blk_integrity *b2 = gd2->integrity;
7ba1ba12e   Martin K. Petersen   block: Block laye...
142

ad7fce931   Martin K. Petersen   block: Switch blk...
143
144
  	if (!b1 && !b2)
  		return 0;
7ba1ba12e   Martin K. Petersen   block: Block laye...
145
146
  
  	if (!b1 || !b2)
ad7fce931   Martin K. Petersen   block: Switch blk...
147
  		return -1;
7ba1ba12e   Martin K. Petersen   block: Block laye...
148
149
150
151
  
  	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...
152
  		       gd1->disk_name, gd2->disk_name,
7ba1ba12e   Martin K. Petersen   block: Block laye...
153
154
155
156
157
158
159
  		       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...
160
  		       gd1->disk_name, gd2->disk_name,
7ba1ba12e   Martin K. Petersen   block: Block laye...
161
162
163
164
165
166
167
  		       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...
168
  		       gd1->disk_name, gd2->disk_name,
7ba1ba12e   Martin K. Petersen   block: Block laye...
169
170
171
172
173
174
175
  		       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...
176
  		       gd1->disk_name, gd2->disk_name,
7ba1ba12e   Martin K. Petersen   block: Block laye...
177
178
179
180
181
182
183
  		       b1->name, b2->name);
  		return -1;
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL(blk_integrity_compare);
13f05c8d8   Martin K. Petersen   block/scsi: Provi...
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
216
  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...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  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 ...
233
234
235
  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...
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
275
  {
  	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 ...
276
  		bi->flags |= INTEGRITY_FLAG_READ;
7ba1ba12e   Martin K. Petersen   block: Block laye...
277
  	else
b24498d47   Jens Axboe   block: integrity ...
278
  		bi->flags &= ~INTEGRITY_FLAG_READ;
7ba1ba12e   Martin K. Petersen   block: Block laye...
279
280
281
282
283
284
  
  	return count;
  }
  
  static ssize_t integrity_read_show(struct blk_integrity *bi, char *page)
  {
b24498d47   Jens Axboe   block: integrity ...
285
286
  	return sprintf(page, "%d
  ", (bi->flags & INTEGRITY_FLAG_READ) != 0);
7ba1ba12e   Martin K. Petersen   block: Block laye...
287
288
289
290
291
292
293
294
295
  }
  
  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 ...
296
  		bi->flags |= INTEGRITY_FLAG_WRITE;
7ba1ba12e   Martin K. Petersen   block: Block laye...
297
  	else
b24498d47   Jens Axboe   block: integrity ...
298
  		bi->flags &= ~INTEGRITY_FLAG_WRITE;
7ba1ba12e   Martin K. Petersen   block: Block laye...
299
300
301
302
303
304
  
  	return count;
  }
  
  static ssize_t integrity_write_show(struct blk_integrity *bi, char *page)
  {
b24498d47   Jens Axboe   block: integrity ...
305
306
  	return sprintf(page, "%d
  ", (bi->flags & INTEGRITY_FLAG_WRITE) != 0);
7ba1ba12e   Martin K. Petersen   block: Block laye...
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
337
  }
  
  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...
338
  static const struct sysfs_ops integrity_ops = {
7ba1ba12e   Martin K. Petersen   block: Block laye...
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
364
  	.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...
365
366
367
368
369
370
371
  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...
372
373
374
  /**
   * 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...
375
   * @template:	optional integrity profile to register
7ba1ba12e   Martin K. Petersen   block: Block laye...
376
377
378
379
380
   *
   * 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...
381
382
   * 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...
383
384
385
386
387
388
   */
  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...
389
390
  
  	if (disk->integrity == NULL) {
b984679ef   Jens Axboe   block: integrity ...
391
  		bi = kmem_cache_alloc(integrity_cachep,
322316385   Martin K. Petersen   block: Allow empt...
392
  				      GFP_KERNEL | __GFP_ZERO);
7ba1ba12e   Martin K. Petersen   block: Block laye...
393
394
395
396
  		if (!bi)
  			return -1;
  
  		if (kobject_init_and_add(&bi->kobj, &integrity_ktype,
ed9e19823   Tejun Heo   block: implement ...
397
398
  					 &disk_to_dev(disk)->kobj,
  					 "%s", "integrity")) {
7ba1ba12e   Martin K. Petersen   block: Block laye...
399
400
401
402
403
  			kmem_cache_free(integrity_cachep, bi);
  			return -1;
  		}
  
  		kobject_uevent(&bi->kobj, KOBJ_ADD);
b24498d47   Jens Axboe   block: integrity ...
404
  		bi->flags |= INTEGRITY_FLAG_READ | INTEGRITY_FLAG_WRITE;
e1defc4ff   Martin K. Petersen   block: Do away wi...
405
  		bi->sector_size = queue_logical_block_size(disk->queue);
7ba1ba12e   Martin K. Petersen   block: Block laye...
406
407
408
409
410
  		disk->integrity = bi;
  	} else
  		bi = disk->integrity;
  
  	/* Use the provided profile as template */
322316385   Martin K. Petersen   block: Allow empt...
411
412
413
414
415
416
417
418
419
  	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...
420
  		bi->name = bi_unsupported_name;
7ba1ba12e   Martin K. Petersen   block: Block laye...
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  
  	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...
444
  	kobject_put(&bi->kobj);
0c032ab88   Martin K. Petersen   block: Fix double...
445
  	disk->integrity = NULL;
7ba1ba12e   Martin K. Petersen   block: Block laye...
446
447
  }
  EXPORT_SYMBOL(blk_integrity_unregister);