Blame view

block/t10-pi.c 7.2 KB
8c16567d8   Christoph Hellwig   block: switch all...
1
  // SPDX-License-Identifier: GPL-2.0
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
2
3
4
  /*
   * t10_pi.c - Functions for generating and verifying T10 Protection
   *	      Information.
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
5
6
7
8
9
10
11
12
   */
  
  #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...
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  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...
28
29
  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...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  {
  	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...
48
  	return BLK_STS_OK;
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
49
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
50
51
  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...
52
53
54
55
56
57
58
59
60
61
  {
  	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...
62
  			if (pi->app_tag == T10_PI_APP_ESCAPE)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
63
64
65
66
67
68
69
70
71
  				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 ...
72
  				return BLK_STS_PROTECTION;
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
73
74
75
  			}
  			break;
  		case 3:
128b6f9fd   Dmitry Monakhov   t10-pi: Move open...
76
77
  			if (pi->app_tag == T10_PI_APP_ESCAPE &&
  			    pi->ref_tag == T10_PI_REF_ESCAPE)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
78
79
80
81
82
83
84
85
86
87
88
89
  				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...
90
  			return BLK_STS_PROTECTION;
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
91
92
93
94
95
96
97
  		}
  
  next:
  		iter->data_buf += iter->interval;
  		iter->prot_buf += sizeof(struct t10_pi_tuple);
  		iter->seed++;
  	}
4e4cbee93   Christoph Hellwig   block: switch bio...
98
  	return BLK_STS_OK;
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
99
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
100
  static blk_status_t t10_pi_type1_generate_crc(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
101
102
103
  {
  	return t10_pi_generate(iter, t10_pi_crc_fn, 1);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
104
  static blk_status_t t10_pi_type1_generate_ip(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
105
106
107
  {
  	return t10_pi_generate(iter, t10_pi_ip_fn, 1);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
108
  static blk_status_t t10_pi_type1_verify_crc(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
109
110
111
  {
  	return t10_pi_verify(iter, t10_pi_crc_fn, 1);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
112
  static blk_status_t t10_pi_type1_verify_ip(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
113
114
115
  {
  	return t10_pi_verify(iter, t10_pi_ip_fn, 1);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
116
  static blk_status_t t10_pi_type3_generate_crc(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
117
118
119
  {
  	return t10_pi_generate(iter, t10_pi_crc_fn, 3);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
120
  static blk_status_t t10_pi_type3_generate_ip(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
121
122
123
  {
  	return t10_pi_generate(iter, t10_pi_ip_fn, 3);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
124
  static blk_status_t t10_pi_type3_verify_crc(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
125
126
127
  {
  	return t10_pi_verify(iter, t10_pi_crc_fn, 3);
  }
4e4cbee93   Christoph Hellwig   block: switch bio...
128
  static blk_status_t t10_pi_type3_verify_ip(struct blk_integrity_iter *iter)
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
129
130
131
  {
  	return t10_pi_verify(iter, t10_pi_ip_fn, 3);
  }
869ab90f0   Eric Biggers   block: constify s...
132
  const struct blk_integrity_profile t10_pi_type1_crc = {
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
133
134
135
  	.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...
136
137
  };
  EXPORT_SYMBOL(t10_pi_type1_crc);
869ab90f0   Eric Biggers   block: constify s...
138
  const struct blk_integrity_profile t10_pi_type1_ip = {
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
139
140
141
  	.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...
142
143
  };
  EXPORT_SYMBOL(t10_pi_type1_ip);
869ab90f0   Eric Biggers   block: constify s...
144
  const struct blk_integrity_profile t10_pi_type3_crc = {
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
145
146
147
  	.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...
148
149
  };
  EXPORT_SYMBOL(t10_pi_type3_crc);
869ab90f0   Eric Biggers   block: constify s...
150
  const struct blk_integrity_profile t10_pi_type3_ip = {
2341c2f8c   Martin K. Petersen   block: Add T10 Pr...
151
152
153
  	.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...
154
155
  };
  EXPORT_SYMBOL(t10_pi_type3_ip);
10c41ddd6   Max Gurtovoy   block: move dif_p...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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
  
  /**
   * 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);