Blame view

block/t10-pi.c 7.92 KB
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
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
27
28
29
  /*
   * t10_pi.c - Functions for generating and verifying T10 Protection
   *	      Information.
   *
   * Copyright (C) 2007, 2008, 2014 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/t10-pi.h>
  #include <linux/blkdev.h>
  #include <linux/crc-t10dif.h>
  #include <net/checksum.h>
  
  typedef __be16 (csum_fn) (void *, unsigned int);
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  static __be16 t10_pi_crc_fn(void *data, unsigned int len)
  {
  	return cpu_to_be16(crc_t10dif(data, len));
  }
  
  static __be16 t10_pi_ip_fn(void *data, unsigned int len)
  {
  	return (__force __be16)ip_compute_csum(data, len);
  }
  
  /*
   * Type 1 and Type 2 protection use the same format: 16 bit guard tag,
   * 16 bit app tag, 32 bit reference tag. Type 3 does not define the ref
   * tag.
   */
4e4cbee93   Christoph Hellwig   block: switch bio...
45
46
  static blk_status_t t10_pi_generate(struct blk_integrity_iter *iter,
  		csum_fn *fn, unsigned int type)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  {
  	unsigned int i;
  
  	for (i = 0 ; i < iter->data_size ; i += iter->interval) {
  		struct t10_pi_tuple *pi = iter->prot_buf;
  
  		pi->guard_tag = fn(iter->data_buf, iter->interval);
  		pi->app_tag = 0;
  
  		if (type == 1)
  			pi->ref_tag = cpu_to_be32(lower_32_bits(iter->seed));
  		else
  			pi->ref_tag = 0;
  
  		iter->data_buf += iter->interval;
  		iter->prot_buf += sizeof(struct t10_pi_tuple);
  		iter->seed++;
  	}
4e4cbee93   Christoph Hellwig   block: switch bio...
65
  	return BLK_STS_OK;
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
66
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
67
68
  static blk_status_t t10_pi_verify(struct blk_integrity_iter *iter,
  		csum_fn *fn, unsigned int type)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
69
70
71
72
73
74
75
76
77
78
  {
  	unsigned int i;
  
  	for (i = 0 ; i < iter->data_size ; i += iter->interval) {
  		struct t10_pi_tuple *pi = iter->prot_buf;
  		__be16 csum;
  
  		switch (type) {
  		case 1:
  		case 2:
128b6f9fd   Dmitry Monakhov   t10-pi: Move open...
79
  			if (pi->app_tag == T10_PI_APP_ESCAPE)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
80
81
82
83
84
85
86
87
88
  				goto next;
  
  			if (be32_to_cpu(pi->ref_tag) !=
  			    lower_32_bits(iter->seed)) {
  				pr_err("%s: ref tag error at location %llu " \
  				       "(rcvd %u)
  ", iter->disk_name,
  				       (unsigned long long)
  				       iter->seed, be32_to_cpu(pi->ref_tag));
a462b9508   Bart Van Assche   block: Dedicated ...
89
  				return BLK_STS_PROTECTION;
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
90
91
92
  			}
  			break;
  		case 3:
128b6f9fd   Dmitry Monakhov   t10-pi: Move open...
93
94
  			if (pi->app_tag == T10_PI_APP_ESCAPE &&
  			    pi->ref_tag == T10_PI_REF_ESCAPE)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
95
96
97
98
99
100
101
102
103
104
105
106
  				goto next;
  			break;
  		}
  
  		csum = fn(iter->data_buf, iter->interval);
  
  		if (pi->guard_tag != csum) {
  			pr_err("%s: guard tag error at sector %llu " \
  			       "(rcvd %04x, want %04x)
  ", iter->disk_name,
  			       (unsigned long long)iter->seed,
  			       be16_to_cpu(pi->guard_tag), be16_to_cpu(csum));
4e4cbee93   Christoph Hellwig   block: switch bio...
107
  			return BLK_STS_PROTECTION;
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
108
109
110
111
112
113
114
  		}
  
  next:
  		iter->data_buf += iter->interval;
  		iter->prot_buf += sizeof(struct t10_pi_tuple);
  		iter->seed++;
  	}
4e4cbee93   Christoph Hellwig   block: switch bio...
115
  	return BLK_STS_OK;
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
116
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
117
  static blk_status_t t10_pi_type1_generate_crc(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
118
119
120
  {
  	return t10_pi_generate(iter, t10_pi_crc_fn, 1);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
121
  static blk_status_t t10_pi_type1_generate_ip(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
122
123
124
  {
  	return t10_pi_generate(iter, t10_pi_ip_fn, 1);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
125
  static blk_status_t t10_pi_type1_verify_crc(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
126
127
128
  {
  	return t10_pi_verify(iter, t10_pi_crc_fn, 1);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
129
  static blk_status_t t10_pi_type1_verify_ip(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
130
131
132
  {
  	return t10_pi_verify(iter, t10_pi_ip_fn, 1);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
133
  static blk_status_t t10_pi_type3_generate_crc(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
134
135
136
  {
  	return t10_pi_generate(iter, t10_pi_crc_fn, 3);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
137
  static blk_status_t t10_pi_type3_generate_ip(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
138
139
140
  {
  	return t10_pi_generate(iter, t10_pi_ip_fn, 3);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
141
  static blk_status_t t10_pi_type3_verify_crc(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
142
143
144
  {
  	return t10_pi_verify(iter, t10_pi_crc_fn, 3);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
145
  static blk_status_t t10_pi_type3_verify_ip(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
146
147
148
  {
  	return t10_pi_verify(iter, t10_pi_ip_fn, 3);
  }
869ab90f0   Eric Biggers   block: constify s...
149
  const struct blk_integrity_profile t10_pi_type1_crc = {
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
150
151
152
  	.name			= "T10-DIF-TYPE1-CRC",
  	.generate_fn		= t10_pi_type1_generate_crc,
  	.verify_fn		= t10_pi_type1_verify_crc,
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
153
154
  };
  EXPORT_SYMBOL(t10_pi_type1_crc);
869ab90f0   Eric Biggers   block: constify s...
155
  const struct blk_integrity_profile t10_pi_type1_ip = {
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
156
157
158
  	.name			= "T10-DIF-TYPE1-IP",
  	.generate_fn		= t10_pi_type1_generate_ip,
  	.verify_fn		= t10_pi_type1_verify_ip,
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
159
160
  };
  EXPORT_SYMBOL(t10_pi_type1_ip);
869ab90f0   Eric Biggers   block: constify s...
161
  const struct blk_integrity_profile t10_pi_type3_crc = {
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
162
163
164
  	.name			= "T10-DIF-TYPE3-CRC",
  	.generate_fn		= t10_pi_type3_generate_crc,
  	.verify_fn		= t10_pi_type3_verify_crc,
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
165
166
  };
  EXPORT_SYMBOL(t10_pi_type3_crc);
869ab90f0   Eric Biggers   block: constify s...
167
  const struct blk_integrity_profile t10_pi_type3_ip = {
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
168
169
170
  	.name			= "T10-DIF-TYPE3-IP",
  	.generate_fn		= t10_pi_type3_generate_ip,
  	.verify_fn		= t10_pi_type3_verify_ip,
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
171
172
  };
  EXPORT_SYMBOL(t10_pi_type3_ip);
10c41ddd6   Max Gurtovoy   block: move dif_p...
173
174
175
176
177
178
179
180
181
182
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
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
275
276
277
278
279
280
281
282
  
  /**
   * t10_pi_prepare - prepare PI prior submitting request to device
   * @rq:              request with PI that should be prepared
   * @protection_type: PI type (Type 1/Type 2/Type 3)
   *
   * For Type 1/Type 2, the virtual start sector is the one that was
   * originally submitted by the block layer for the ref_tag usage. Due to
   * partitioning, MD/DM cloning, etc. the actual physical start sector is
   * likely to be different. Remap protection information to match the
   * physical LBA.
   *
   * Type 3 does not have a reference tag so no remapping is required.
   */
  void t10_pi_prepare(struct request *rq, u8 protection_type)
  {
  	const int tuple_sz = rq->q->integrity.tuple_size;
  	u32 ref_tag = t10_pi_ref_tag(rq);
  	struct bio *bio;
  
  	if (protection_type == T10_PI_TYPE3_PROTECTION)
  		return;
  
  	__rq_for_each_bio(bio, rq) {
  		struct bio_integrity_payload *bip = bio_integrity(bio);
  		u32 virt = bip_get_seed(bip) & 0xffffffff;
  		struct bio_vec iv;
  		struct bvec_iter iter;
  
  		/* Already remapped? */
  		if (bip->bip_flags & BIP_MAPPED_INTEGRITY)
  			break;
  
  		bip_for_each_vec(iv, bip, iter) {
  			void *p, *pmap;
  			unsigned int j;
  
  			pmap = kmap_atomic(iv.bv_page);
  			p = pmap + iv.bv_offset;
  			for (j = 0; j < iv.bv_len; j += tuple_sz) {
  				struct t10_pi_tuple *pi = p;
  
  				if (be32_to_cpu(pi->ref_tag) == virt)
  					pi->ref_tag = cpu_to_be32(ref_tag);
  				virt++;
  				ref_tag++;
  				p += tuple_sz;
  			}
  
  			kunmap_atomic(pmap);
  		}
  
  		bip->bip_flags |= BIP_MAPPED_INTEGRITY;
  	}
  }
  EXPORT_SYMBOL(t10_pi_prepare);
  
  /**
   * t10_pi_complete - prepare PI prior returning request to the block layer
   * @rq:              request with PI that should be prepared
   * @protection_type: PI type (Type 1/Type 2/Type 3)
   * @intervals:       total elements to prepare
   *
   * For Type 1/Type 2, the virtual start sector is the one that was
   * originally submitted by the block layer for the ref_tag usage. Due to
   * partitioning, MD/DM cloning, etc. the actual physical start sector is
   * likely to be different. Since the physical start sector was submitted
   * to the device, we should remap it back to virtual values expected by the
   * block layer.
   *
   * Type 3 does not have a reference tag so no remapping is required.
   */
  void t10_pi_complete(struct request *rq, u8 protection_type,
  		     unsigned int intervals)
  {
  	const int tuple_sz = rq->q->integrity.tuple_size;
  	u32 ref_tag = t10_pi_ref_tag(rq);
  	struct bio *bio;
  
  	if (protection_type == T10_PI_TYPE3_PROTECTION)
  		return;
  
  	__rq_for_each_bio(bio, rq) {
  		struct bio_integrity_payload *bip = bio_integrity(bio);
  		u32 virt = bip_get_seed(bip) & 0xffffffff;
  		struct bio_vec iv;
  		struct bvec_iter iter;
  
  		bip_for_each_vec(iv, bip, iter) {
  			void *p, *pmap;
  			unsigned int j;
  
  			pmap = kmap_atomic(iv.bv_page);
  			p = pmap + iv.bv_offset;
  			for (j = 0; j < iv.bv_len && intervals; j += tuple_sz) {
  				struct t10_pi_tuple *pi = p;
  
  				if (be32_to_cpu(pi->ref_tag) == ref_tag)
  					pi->ref_tag = cpu_to_be32(virt);
  				virt++;
  				ref_tag++;
  				intervals--;
  				p += tuple_sz;
  			}
  
  			kunmap_atomic(pmap);
  		}
  	}
  }
  EXPORT_SYMBOL(t10_pi_complete);