Line data Source code
1 : /* SPDX-License-Identifier: BSD-3-Clause
2 : * Copyright (C) 2022 Intel Corporation.
3 : * All rights reserved.
4 : */
5 :
6 : #include "spdk/dif.h"
7 : #include "spdk/crc16.h"
8 : #include "spdk/crc32.h"
9 : #include "spdk/crc64.h"
10 : #include "spdk/endian.h"
11 : #include "spdk/log.h"
12 : #include "spdk/util.h"
13 :
14 : #define REFTAG_MASK_16 0x00000000FFFFFFFF
15 : #define REFTAG_MASK_32 0xFFFFFFFFFFFFFFFF
16 : #define REFTAG_MASK_64 0x0000FFFFFFFFFFFF
17 :
18 : /* The variable size Storage Tag and Reference Tag is not supported yet,
19 : * so the maximum size of the Reference Tag is assumed.
20 : */
21 : struct spdk_dif {
22 : union {
23 : struct {
24 : uint16_t guard;
25 : uint16_t app_tag;
26 : uint32_t stor_ref_space;
27 : } g16;
28 : struct {
29 : uint32_t guard;
30 : uint16_t app_tag;
31 : uint16_t stor_ref_space_p1;
32 : uint64_t stor_ref_space_p2;
33 : } g32;
34 : struct {
35 : uint64_t guard;
36 : uint16_t app_tag;
37 : uint16_t stor_ref_space_p1;
38 : uint32_t stor_ref_space_p2;
39 : } g64;
40 : };
41 : };
42 : SPDK_STATIC_ASSERT(SPDK_SIZEOF_MEMBER(struct spdk_dif, g16) == 8, "Incorrect size");
43 : SPDK_STATIC_ASSERT(SPDK_SIZEOF_MEMBER(struct spdk_dif, g32) == 16, "Incorrect size");
44 : SPDK_STATIC_ASSERT(SPDK_SIZEOF_MEMBER(struct spdk_dif, g64) == 16, "Incorrect size");
45 :
46 : /* Context to iterate or create a iovec array.
47 : * Each sgl is either iterated or created at a time.
48 : */
49 : struct _dif_sgl {
50 : /* Current iovec in the iteration or creation */
51 : struct iovec *iov;
52 :
53 : /* Remaining count of iovecs in the iteration or creation. */
54 : int iovcnt;
55 :
56 : /* Current offset in the iovec */
57 : uint32_t iov_offset;
58 :
59 : /* Size of the created iovec array in bytes */
60 : uint32_t total_size;
61 : };
62 :
63 : static inline void
64 2396 : _dif_sgl_init(struct _dif_sgl *s, struct iovec *iovs, int iovcnt)
65 : {
66 2396 : s->iov = iovs;
67 2396 : s->iovcnt = iovcnt;
68 2396 : s->iov_offset = 0;
69 2396 : s->total_size = 0;
70 2396 : }
71 :
72 : static void
73 12765 : _dif_sgl_advance(struct _dif_sgl *s, uint32_t step)
74 : {
75 12765 : s->iov_offset += step;
76 17904 : while (s->iovcnt != 0) {
77 16227 : if (s->iov_offset < s->iov->iov_len) {
78 11088 : break;
79 : }
80 :
81 5139 : s->iov_offset -= s->iov->iov_len;
82 5139 : s->iov++;
83 5139 : s->iovcnt--;
84 : }
85 12765 : }
86 :
87 : static inline void
88 12625 : _dif_sgl_get_buf(struct _dif_sgl *s, uint8_t **_buf, uint32_t *_buf_len)
89 : {
90 12625 : if (_buf != NULL) {
91 12625 : *_buf = (uint8_t *)s->iov->iov_base + s->iov_offset;
92 : }
93 12625 : if (_buf_len != NULL) {
94 7926 : *_buf_len = s->iov->iov_len - s->iov_offset;
95 : }
96 12625 : }
97 :
98 : static inline bool
99 120 : _dif_sgl_append(struct _dif_sgl *s, uint8_t *data, uint32_t data_len)
100 : {
101 120 : assert(s->iovcnt > 0);
102 120 : s->iov->iov_base = data;
103 120 : s->iov->iov_len = data_len;
104 120 : s->total_size += data_len;
105 120 : s->iov++;
106 120 : s->iovcnt--;
107 :
108 120 : if (s->iovcnt > 0) {
109 100 : return true;
110 : } else {
111 20 : return false;
112 : }
113 : }
114 :
115 : static inline bool
116 104 : _dif_sgl_append_split(struct _dif_sgl *dst, struct _dif_sgl *src, uint32_t data_len)
117 : {
118 104 : uint8_t *buf;
119 104 : uint32_t buf_len;
120 :
121 204 : while (data_len != 0) {
122 120 : _dif_sgl_get_buf(src, &buf, &buf_len);
123 120 : buf_len = spdk_min(buf_len, data_len);
124 :
125 120 : if (!_dif_sgl_append(dst, buf, buf_len)) {
126 20 : return false;
127 : }
128 :
129 100 : _dif_sgl_advance(src, buf_len);
130 100 : data_len -= buf_len;
131 : }
132 :
133 84 : return true;
134 : }
135 :
136 : /* This function must be used before starting iteration. */
137 : static bool
138 834 : _dif_sgl_is_bytes_multiple(struct _dif_sgl *s, uint32_t bytes)
139 : {
140 : int i;
141 :
142 2089 : for (i = 0; i < s->iovcnt; i++) {
143 1689 : if (s->iov[i].iov_len % bytes) {
144 434 : return false;
145 : }
146 : }
147 :
148 400 : return true;
149 : }
150 :
151 : static bool
152 156 : _dif_sgl_is_valid_block_aligned(struct _dif_sgl *s, uint32_t num_blocks, uint32_t block_size)
153 : {
154 156 : uint32_t count = 0;
155 : int i;
156 :
157 312 : for (i = 0; i < s->iovcnt; i++) {
158 156 : if (s->iov[i].iov_len % block_size) {
159 0 : return false;
160 : }
161 156 : count += s->iov[i].iov_len / block_size;
162 : }
163 :
164 156 : return count >= num_blocks;
165 : }
166 :
167 : /* This function must be used before starting iteration. */
168 : static bool
169 2175 : _dif_sgl_is_valid(struct _dif_sgl *s, uint32_t bytes)
170 : {
171 2175 : uint64_t total = 0;
172 : int i;
173 :
174 8156 : for (i = 0; i < s->iovcnt; i++) {
175 5981 : total += s->iov[i].iov_len;
176 : }
177 :
178 2175 : return total >= bytes;
179 : }
180 :
181 : static void
182 72 : _dif_sgl_copy(struct _dif_sgl *to, struct _dif_sgl *from)
183 : {
184 72 : memcpy(to, from, sizeof(struct _dif_sgl));
185 72 : }
186 :
187 : static bool
188 842 : _dif_is_disabled(enum spdk_dif_type dif_type)
189 : {
190 842 : if (dif_type == SPDK_DIF_DISABLE) {
191 2 : return true;
192 : } else {
193 840 : return false;
194 : }
195 : }
196 :
197 : static inline size_t
198 3401 : _dif_size(enum spdk_dif_pi_format dif_pi_format)
199 : {
200 : uint8_t size;
201 :
202 3401 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
203 1235 : size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g16);
204 2166 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
205 1104 : size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g32);
206 : } else {
207 1062 : size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g64);
208 : }
209 :
210 3401 : return size;
211 : }
212 :
213 : static uint32_t
214 489 : _get_guard_interval(uint32_t block_size, uint32_t md_size, bool dif_loc, bool md_interleave,
215 : size_t dif_size)
216 : {
217 489 : if (!dif_loc) {
218 : /* For metadata formats with more than 8/16 bytes (depending on
219 : * the PI format), if the DIF is contained in the last 8/16 bytes
220 : * of metadata, then the CRC covers all metadata up to but excluding
221 : * these last 8/16 bytes.
222 : */
223 287 : if (md_interleave) {
224 227 : return block_size - dif_size;
225 : } else {
226 60 : return md_size - dif_size;
227 : }
228 : } else {
229 : /* For metadata formats with more than 8/16 bytes (depending on
230 : * the PI format), if the DIF is contained in the first 8/16 bytes
231 : * of metadata, then the CRC does not cover any metadata.
232 : */
233 202 : if (md_interleave) {
234 166 : return block_size - md_size;
235 : } else {
236 36 : return 0;
237 : }
238 : }
239 : }
240 :
241 : static inline uint8_t
242 180 : _dif_guard_size(enum spdk_dif_pi_format dif_pi_format)
243 : {
244 : uint8_t size;
245 :
246 180 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
247 60 : size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g16.guard);
248 120 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
249 60 : size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g32.guard);
250 : } else {
251 60 : size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g64.guard);
252 : }
253 :
254 180 : return size;
255 : }
256 :
257 : static inline void
258 1577 : _dif_set_guard(struct spdk_dif *dif, uint64_t guard, enum spdk_dif_pi_format dif_pi_format)
259 : {
260 1577 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
261 533 : to_be16(&(dif->g16.guard), (uint16_t)guard);
262 1044 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
263 531 : to_be32(&(dif->g32.guard), (uint32_t)guard);
264 : } else {
265 513 : to_be64(&(dif->g64.guard), guard);
266 : }
267 1577 : }
268 :
269 : static inline uint64_t
270 1142 : _dif_get_guard(struct spdk_dif *dif, enum spdk_dif_pi_format dif_pi_format)
271 : {
272 : uint64_t guard;
273 :
274 1142 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
275 385 : guard = (uint64_t)from_be16(&(dif->g16.guard));
276 757 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
277 378 : guard = (uint64_t)from_be32(&(dif->g32.guard));
278 : } else {
279 379 : guard = from_be64(&(dif->g64.guard));
280 : }
281 :
282 1142 : return guard;
283 : }
284 :
285 : static inline uint64_t
286 4000 : _dif_generate_guard(uint64_t guard_seed, void *buf, size_t buf_len,
287 : enum spdk_dif_pi_format dif_pi_format)
288 : {
289 : uint64_t guard;
290 :
291 4000 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
292 1355 : guard = (uint64_t)spdk_crc16_t10dif((uint16_t)guard_seed, buf, buf_len);
293 2645 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
294 1337 : guard = (uint64_t)spdk_crc32c_nvme(buf, buf_len, guard_seed);
295 : } else {
296 1308 : guard = spdk_crc64_nvme(buf, buf_len, guard_seed);
297 : }
298 :
299 4000 : return guard;
300 : }
301 :
302 : static inline uint64_t
303 750 : _dif_generate_guard_copy(uint64_t guard_seed, void *dst, void *src, size_t buf_len,
304 : enum spdk_dif_pi_format dif_pi_format)
305 : {
306 : uint64_t guard;
307 :
308 750 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
309 250 : guard = (uint64_t)spdk_crc16_t10dif_copy((uint16_t)guard_seed, dst, src, buf_len);
310 500 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
311 250 : memcpy(dst, src, buf_len);
312 250 : guard = (uint64_t)spdk_crc32c_nvme(src, buf_len, guard_seed);
313 : } else {
314 250 : memcpy(dst, src, buf_len);
315 250 : guard = spdk_crc64_nvme(src, buf_len, guard_seed);
316 : }
317 :
318 750 : return guard;
319 : }
320 :
321 : static inline uint8_t
322 120 : _dif_apptag_offset(enum spdk_dif_pi_format dif_pi_format)
323 : {
324 120 : return _dif_guard_size(dif_pi_format);
325 : }
326 :
327 : static inline uint8_t
328 120 : _dif_apptag_size(void)
329 : {
330 120 : return SPDK_SIZEOF_MEMBER(struct spdk_dif, g16.app_tag);
331 : }
332 :
333 : static inline void
334 1569 : _dif_set_apptag(struct spdk_dif *dif, uint16_t app_tag, enum spdk_dif_pi_format dif_pi_format)
335 : {
336 1569 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
337 532 : to_be16(&(dif->g16.app_tag), app_tag);
338 1037 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
339 528 : to_be16(&(dif->g32.app_tag), app_tag);
340 : } else {
341 509 : to_be16(&(dif->g64.app_tag), app_tag);
342 : }
343 1569 : }
344 :
345 : static inline uint16_t
346 2628 : _dif_get_apptag(struct spdk_dif *dif, enum spdk_dif_pi_format dif_pi_format)
347 : {
348 : uint16_t app_tag;
349 :
350 2628 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
351 891 : app_tag = from_be16(&(dif->g16.app_tag));
352 1737 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
353 869 : app_tag = from_be16(&(dif->g32.app_tag));
354 : } else {
355 868 : app_tag = from_be16(&(dif->g64.app_tag));
356 : }
357 :
358 2628 : return app_tag;
359 : }
360 :
361 : static inline bool
362 1618 : _dif_apptag_ignore(struct spdk_dif *dif, enum spdk_dif_pi_format dif_pi_format)
363 : {
364 1618 : return _dif_get_apptag(dif, dif_pi_format) == SPDK_DIF_APPTAG_IGNORE;
365 : }
366 :
367 : static inline uint8_t
368 60 : _dif_reftag_offset(enum spdk_dif_pi_format dif_pi_format)
369 : {
370 : uint8_t offset;
371 :
372 60 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
373 20 : offset = _dif_apptag_offset(dif_pi_format) + _dif_apptag_size();
374 40 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
375 20 : offset = _dif_apptag_offset(dif_pi_format) + _dif_apptag_size()
376 : + SPDK_SIZEOF_MEMBER(struct spdk_dif, g32.stor_ref_space_p1);
377 : } else {
378 20 : offset = _dif_apptag_offset(dif_pi_format) + _dif_apptag_size();
379 : }
380 :
381 60 : return offset;
382 : }
383 :
384 : static inline uint8_t
385 60 : _dif_reftag_size(enum spdk_dif_pi_format dif_pi_format)
386 : {
387 : uint8_t size;
388 :
389 60 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
390 20 : size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g16.stor_ref_space);
391 40 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
392 20 : size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g32.stor_ref_space_p2);
393 : } else {
394 20 : size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g64.stor_ref_space_p1) +
395 : SPDK_SIZEOF_MEMBER(struct spdk_dif, g64.stor_ref_space_p2);
396 : }
397 :
398 60 : return size;
399 : }
400 :
401 : static inline void
402 1710 : _dif_set_reftag(struct spdk_dif *dif, uint64_t ref_tag, enum spdk_dif_pi_format dif_pi_format)
403 : {
404 1710 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
405 579 : to_be32(&(dif->g16.stor_ref_space), (uint32_t)ref_tag);
406 1131 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
407 574 : to_be64(&(dif->g32.stor_ref_space_p2), ref_tag);
408 : } else {
409 557 : to_be16(&(dif->g64.stor_ref_space_p1), (uint16_t)(ref_tag >> 32));
410 557 : to_be32(&(dif->g64.stor_ref_space_p2), (uint32_t)ref_tag);
411 : }
412 1710 : }
413 :
414 : static inline uint64_t
415 1156 : _dif_get_reftag(struct spdk_dif *dif, enum spdk_dif_pi_format dif_pi_format)
416 : {
417 : uint64_t ref_tag;
418 :
419 1156 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
420 394 : ref_tag = (uint64_t)from_be32(&(dif->g16.stor_ref_space));
421 762 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
422 381 : ref_tag = from_be64(&(dif->g32.stor_ref_space_p2));
423 : } else {
424 381 : ref_tag = (uint64_t)from_be16(&(dif->g64.stor_ref_space_p1));
425 381 : ref_tag <<= 32;
426 381 : ref_tag |= (uint64_t)from_be32(&(dif->g64.stor_ref_space_p2));
427 : }
428 :
429 1156 : return ref_tag;
430 : }
431 :
432 : static inline bool
433 1088 : _dif_reftag_match(struct spdk_dif *dif, uint64_t ref_tag,
434 : enum spdk_dif_pi_format dif_pi_format)
435 : {
436 : uint64_t _ref_tag;
437 : bool match;
438 :
439 1088 : _ref_tag = _dif_get_reftag(dif, dif_pi_format);
440 :
441 1088 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
442 370 : match = (_ref_tag == (ref_tag & REFTAG_MASK_16));
443 718 : } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
444 359 : match = (_ref_tag == ref_tag);
445 : } else {
446 359 : match = (_ref_tag == (ref_tag & REFTAG_MASK_64));
447 : }
448 :
449 1088 : return match;
450 : }
451 :
452 : static inline bool
453 7 : _dif_reftag_ignore(struct spdk_dif *dif, enum spdk_dif_pi_format dif_pi_format)
454 : {
455 7 : return _dif_reftag_match(dif, REFTAG_MASK_32, dif_pi_format);
456 : }
457 :
458 : int
459 474 : spdk_dif_ctx_init(struct spdk_dif_ctx *ctx, uint32_t block_size, uint32_t md_size,
460 : bool md_interleave, bool dif_loc, enum spdk_dif_type dif_type, uint32_t dif_flags,
461 : uint32_t init_ref_tag, uint16_t apptag_mask, uint16_t app_tag,
462 : uint32_t data_offset, uint64_t guard_seed, struct spdk_dif_ctx_init_ext_opts *opts)
463 : {
464 : uint32_t data_block_size;
465 474 : enum spdk_dif_pi_format dif_pi_format = SPDK_DIF_PI_FORMAT_16;
466 :
467 474 : if (opts != NULL) {
468 474 : if (opts->dif_pi_format != SPDK_DIF_PI_FORMAT_16 &&
469 302 : opts->dif_pi_format != SPDK_DIF_PI_FORMAT_32 &&
470 146 : opts->dif_pi_format != SPDK_DIF_PI_FORMAT_64) {
471 0 : SPDK_ERRLOG("No valid DIF PI format provided.\n");
472 0 : return -EINVAL;
473 : }
474 :
475 474 : dif_pi_format = opts->dif_pi_format;
476 : }
477 :
478 474 : if (md_size < _dif_size(dif_pi_format)) {
479 4 : SPDK_ERRLOG("Metadata size is smaller than DIF size.\n");
480 4 : return -EINVAL;
481 : }
482 :
483 470 : if (md_interleave) {
484 372 : if (block_size < md_size) {
485 0 : SPDK_ERRLOG("Block size is smaller than DIF size.\n");
486 0 : return -EINVAL;
487 : }
488 372 : data_block_size = block_size - md_size;
489 : } else {
490 98 : if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
491 32 : if (block_size == 0 || (block_size % 512) != 0) {
492 0 : SPDK_ERRLOG("Zero block size is not allowed and should be a multiple of 512B\n");
493 0 : return -EINVAL;
494 : }
495 : } else {
496 66 : if (block_size == 0 || (block_size % 4096) != 0) {
497 2 : SPDK_ERRLOG("Zero block size is not allowed and should be a multiple of 4kB\n");
498 2 : return -EINVAL;
499 : }
500 : }
501 :
502 96 : data_block_size = block_size;
503 : }
504 :
505 468 : ctx->block_size = block_size;
506 468 : ctx->md_size = md_size;
507 468 : ctx->md_interleave = md_interleave;
508 468 : ctx->dif_pi_format = dif_pi_format;
509 468 : ctx->guard_interval = _get_guard_interval(block_size, md_size, dif_loc, md_interleave,
510 468 : _dif_size(ctx->dif_pi_format));
511 468 : ctx->dif_type = dif_type;
512 468 : ctx->dif_flags = dif_flags;
513 468 : ctx->init_ref_tag = init_ref_tag;
514 468 : ctx->apptag_mask = apptag_mask;
515 468 : ctx->app_tag = app_tag;
516 468 : ctx->data_offset = data_offset;
517 468 : ctx->ref_tag_offset = data_offset / data_block_size;
518 468 : ctx->last_guard = guard_seed;
519 468 : ctx->guard_seed = guard_seed;
520 468 : ctx->remapped_init_ref_tag = 0;
521 :
522 468 : return 0;
523 : }
524 :
525 : void
526 42 : spdk_dif_ctx_set_data_offset(struct spdk_dif_ctx *ctx, uint32_t data_offset)
527 : {
528 : uint32_t data_block_size;
529 :
530 42 : if (ctx->md_interleave) {
531 42 : data_block_size = ctx->block_size - ctx->md_size;
532 : } else {
533 0 : data_block_size = ctx->block_size;
534 : }
535 :
536 42 : ctx->data_offset = data_offset;
537 42 : ctx->ref_tag_offset = data_offset / data_block_size;
538 42 : }
539 :
540 : void
541 21 : spdk_dif_ctx_set_remapped_init_ref_tag(struct spdk_dif_ctx *ctx,
542 : uint32_t remapped_init_ref_tag)
543 : {
544 21 : ctx->remapped_init_ref_tag = remapped_init_ref_tag;
545 21 : }
546 :
547 : static void
548 1907 : _dif_generate(void *_dif, uint64_t guard, uint32_t offset_blocks,
549 : const struct spdk_dif_ctx *ctx)
550 : {
551 1907 : struct spdk_dif *dif = _dif;
552 : uint64_t ref_tag;
553 :
554 1907 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
555 1577 : _dif_set_guard(dif, guard, ctx->dif_pi_format);
556 : }
557 :
558 1907 : if (ctx->dif_flags & SPDK_DIF_FLAGS_APPTAG_CHECK) {
559 1569 : _dif_set_apptag(dif, ctx->app_tag, ctx->dif_pi_format);
560 : }
561 :
562 1907 : if (ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK) {
563 : /* For type 1 and 2, the reference tag is incremented for each
564 : * subsequent logical block. For type 3, the reference tag
565 : * remains the same as the initial reference tag.
566 : */
567 1566 : if (ctx->dif_type != SPDK_DIF_TYPE3) {
568 1559 : ref_tag = ctx->init_ref_tag + ctx->ref_tag_offset + offset_blocks;
569 : } else {
570 7 : ref_tag = ctx->init_ref_tag + ctx->ref_tag_offset;
571 : }
572 :
573 : /* Overwrite reference tag if initialization reference tag is SPDK_DIF_REFTAG_IGNORE */
574 1566 : if (ctx->init_ref_tag == SPDK_DIF_REFTAG_IGNORE) {
575 2 : if (ctx->dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
576 2 : ref_tag = REFTAG_MASK_16;
577 0 : } else if (ctx->dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
578 0 : ref_tag = REFTAG_MASK_32;
579 : } else {
580 0 : ref_tag = REFTAG_MASK_64;
581 : }
582 : }
583 :
584 1566 : _dif_set_reftag(dif, ref_tag, ctx->dif_pi_format);
585 : }
586 1907 : }
587 :
588 : static void
589 100 : dif_generate(struct _dif_sgl *sgl, uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
590 : {
591 100 : uint32_t offset_blocks = 0;
592 100 : uint8_t *buf;
593 100 : uint64_t guard = 0;
594 :
595 677 : while (offset_blocks < num_blocks) {
596 577 : _dif_sgl_get_buf(sgl, &buf, NULL);
597 :
598 577 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
599 475 : guard = _dif_generate_guard(ctx->guard_seed, buf, ctx->guard_interval, ctx->dif_pi_format);
600 : }
601 :
602 577 : _dif_generate(buf + ctx->guard_interval, guard, offset_blocks, ctx);
603 :
604 577 : _dif_sgl_advance(sgl, ctx->block_size);
605 577 : offset_blocks++;
606 : }
607 100 : }
608 :
609 : static uint64_t
610 262 : _dif_generate_split(struct _dif_sgl *sgl, uint32_t offset_in_block, uint32_t data_len,
611 : uint64_t guard, uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
612 : {
613 262 : uint32_t offset_in_dif, buf_len;
614 262 : uint8_t *buf;
615 262 : struct spdk_dif dif = {};
616 :
617 262 : assert(offset_in_block < ctx->guard_interval);
618 262 : assert(offset_in_block + data_len < ctx->guard_interval ||
619 : offset_in_block + data_len == ctx->block_size);
620 :
621 : /* Compute CRC over split logical block data. */
622 704 : while (data_len != 0 && offset_in_block < ctx->guard_interval) {
623 442 : _dif_sgl_get_buf(sgl, &buf, &buf_len);
624 442 : buf_len = spdk_min(buf_len, data_len);
625 442 : buf_len = spdk_min(buf_len, ctx->guard_interval - offset_in_block);
626 :
627 442 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
628 442 : guard = _dif_generate_guard(guard, buf, buf_len, ctx->dif_pi_format);
629 : }
630 :
631 442 : _dif_sgl_advance(sgl, buf_len);
632 442 : offset_in_block += buf_len;
633 442 : data_len -= buf_len;
634 : }
635 :
636 262 : if (offset_in_block < ctx->guard_interval) {
637 39 : return guard;
638 : }
639 :
640 : /* If a whole logical block data is parsed, generate DIF
641 : * and save it to the temporary DIF area.
642 : */
643 223 : _dif_generate(&dif, guard, offset_blocks, ctx);
644 :
645 : /* Copy generated DIF field to the split DIF field, and then
646 : * skip metadata field after DIF field (if any).
647 : */
648 616 : while (offset_in_block < ctx->block_size) {
649 393 : _dif_sgl_get_buf(sgl, &buf, &buf_len);
650 :
651 393 : if (offset_in_block < ctx->guard_interval + _dif_size(ctx->dif_pi_format)) {
652 309 : offset_in_dif = offset_in_block - ctx->guard_interval;
653 309 : buf_len = spdk_min(buf_len, _dif_size(ctx->dif_pi_format) - offset_in_dif);
654 :
655 309 : memcpy(buf, ((uint8_t *)&dif) + offset_in_dif, buf_len);
656 : } else {
657 84 : buf_len = spdk_min(buf_len, ctx->block_size - offset_in_block);
658 : }
659 :
660 393 : _dif_sgl_advance(sgl, buf_len);
661 393 : offset_in_block += buf_len;
662 : }
663 :
664 223 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
665 223 : guard = ctx->guard_seed;
666 : }
667 :
668 223 : return guard;
669 : }
670 :
671 : static void
672 152 : dif_generate_split(struct _dif_sgl *sgl, uint32_t num_blocks,
673 : const struct spdk_dif_ctx *ctx)
674 : {
675 : uint32_t offset_blocks;
676 152 : uint64_t guard = 0;
677 :
678 152 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
679 152 : guard = ctx->guard_seed;
680 : }
681 :
682 329 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
683 177 : _dif_generate_split(sgl, 0, ctx->block_size, guard, offset_blocks, ctx);
684 : }
685 152 : }
686 :
687 : int
688 244 : spdk_dif_generate(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
689 : const struct spdk_dif_ctx *ctx)
690 : {
691 244 : struct _dif_sgl sgl;
692 :
693 244 : _dif_sgl_init(&sgl, iovs, iovcnt);
694 :
695 244 : if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
696 0 : SPDK_ERRLOG("Size of iovec array is not valid.\n");
697 0 : return -EINVAL;
698 : }
699 :
700 244 : if (_dif_is_disabled(ctx->dif_type)) {
701 1 : return 0;
702 : }
703 :
704 243 : if (_dif_sgl_is_bytes_multiple(&sgl, ctx->block_size)) {
705 91 : dif_generate(&sgl, num_blocks, ctx);
706 : } else {
707 152 : dif_generate_split(&sgl, num_blocks, ctx);
708 : }
709 :
710 243 : return 0;
711 : }
712 :
713 : static void
714 262 : _dif_error_set(struct spdk_dif_error *err_blk, uint8_t err_type,
715 : uint64_t expected, uint64_t actual, uint32_t err_offset)
716 : {
717 262 : if (err_blk) {
718 253 : err_blk->err_type = err_type;
719 253 : err_blk->expected = expected;
720 253 : err_blk->actual = actual;
721 253 : err_blk->err_offset = err_offset;
722 : }
723 262 : }
724 :
725 : static int
726 1474 : _dif_verify(void *_dif, uint64_t guard, uint32_t offset_blocks,
727 : const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
728 : {
729 1474 : struct spdk_dif *dif = _dif;
730 : uint64_t _guard;
731 : uint16_t _app_tag;
732 : uint64_t ref_tag, _ref_tag;
733 :
734 1474 : switch (ctx->dif_type) {
735 1467 : case SPDK_DIF_TYPE1:
736 : case SPDK_DIF_TYPE2:
737 : /* If Type 1 or 2 is used, then all DIF checks are disabled when
738 : * the Application Tag is 0xFFFF.
739 : */
740 1467 : if (_dif_apptag_ignore(dif, ctx->dif_pi_format)) {
741 6 : return 0;
742 : }
743 1461 : break;
744 7 : case SPDK_DIF_TYPE3:
745 : /* If Type 3 is used, then all DIF checks are disabled when the
746 : * Application Tag is 0xFFFF and the Reference Tag is 0xFFFFFFFF
747 : * or 0xFFFFFFFFFFFFFFFF depending on the PI format.
748 : */
749 :
750 14 : if (_dif_apptag_ignore(dif, ctx->dif_pi_format) &&
751 7 : _dif_reftag_ignore(dif, ctx->dif_pi_format)) {
752 4 : return 0;
753 : }
754 3 : break;
755 0 : default:
756 0 : break;
757 : }
758 :
759 : /* For type 1 and 2, the reference tag is incremented for each
760 : * subsequent logical block. For type 3, the reference tag
761 : * remains the same as the initial reference tag.
762 : */
763 1464 : if (ctx->dif_type != SPDK_DIF_TYPE3) {
764 1461 : ref_tag = ctx->init_ref_tag + ctx->ref_tag_offset + offset_blocks;
765 : } else {
766 3 : ref_tag = ctx->init_ref_tag + ctx->ref_tag_offset;
767 : }
768 :
769 1464 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
770 : /* Compare the DIF Guard field to the CRC computed over the logical
771 : * block data.
772 : */
773 1132 : _guard = _dif_get_guard(dif, ctx->dif_pi_format);
774 1132 : if (_guard != guard) {
775 124 : _dif_error_set(err_blk, SPDK_DIF_GUARD_ERROR, _guard, guard,
776 : offset_blocks);
777 124 : SPDK_ERRLOG("Failed to compare Guard: LBA=%" PRIu64 "," \
778 : " Expected=%lx, Actual=%lx\n",
779 : ref_tag, _guard, guard);
780 124 : return -1;
781 : }
782 : }
783 :
784 1340 : if (ctx->dif_flags & SPDK_DIF_FLAGS_APPTAG_CHECK) {
785 : /* Compare unmasked bits in the DIF Application Tag field to the
786 : * passed Application Tag.
787 : */
788 1008 : _app_tag = _dif_get_apptag(dif, ctx->dif_pi_format);
789 1008 : if ((_app_tag & ctx->apptag_mask) != (ctx->app_tag & ctx->apptag_mask)) {
790 72 : _dif_error_set(err_blk, SPDK_DIF_APPTAG_ERROR, ctx->app_tag,
791 72 : (_app_tag & ctx->apptag_mask), offset_blocks);
792 72 : SPDK_ERRLOG("Failed to compare App Tag: LBA=%" PRIu64 "," \
793 : " Expected=%x, Actual=%x\n",
794 : ref_tag, ctx->app_tag, (_app_tag & ctx->apptag_mask));
795 72 : return -1;
796 : }
797 : }
798 :
799 1268 : if (ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK) {
800 937 : switch (ctx->dif_type) {
801 937 : case SPDK_DIF_TYPE1:
802 : case SPDK_DIF_TYPE2:
803 : /* Compare the DIF Reference Tag field to the passed Reference Tag.
804 : * The passed Reference Tag will be the least significant 4 bytes
805 : * or 8 bytes (depending on the PI format)
806 : * of the LBA when Type 1 is used, and application specific value
807 : * if Type 2 is used.
808 : */
809 937 : if (!_dif_reftag_match(dif, ref_tag, ctx->dif_pi_format)) {
810 66 : _ref_tag = _dif_get_reftag(dif, ctx->dif_pi_format);
811 66 : _dif_error_set(err_blk, SPDK_DIF_REFTAG_ERROR, ref_tag,
812 : _ref_tag, offset_blocks);
813 66 : SPDK_ERRLOG("Failed to compare Ref Tag: LBA=%" PRIu64 "," \
814 : " Expected=%lx, Actual=%lx\n",
815 : ref_tag, ref_tag, _ref_tag);
816 66 : return -1;
817 : }
818 871 : break;
819 0 : case SPDK_DIF_TYPE3:
820 : /* For Type 3, computed Reference Tag remains unchanged.
821 : * Hence ignore the Reference Tag field.
822 : */
823 0 : break;
824 0 : default:
825 0 : break;
826 : }
827 331 : }
828 :
829 1202 : return 0;
830 : }
831 :
832 : static int
833 105 : dif_verify(struct _dif_sgl *sgl, uint32_t num_blocks,
834 : const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
835 : {
836 105 : uint32_t offset_blocks = 0;
837 : int rc;
838 105 : uint8_t *buf;
839 105 : uint64_t guard = 0;
840 :
841 542 : while (offset_blocks < num_blocks) {
842 474 : _dif_sgl_get_buf(sgl, &buf, NULL);
843 :
844 474 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
845 370 : guard = _dif_generate_guard(ctx->guard_seed, buf, ctx->guard_interval, ctx->dif_pi_format);
846 : }
847 :
848 474 : rc = _dif_verify(buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
849 474 : if (rc != 0) {
850 37 : return rc;
851 : }
852 :
853 437 : _dif_sgl_advance(sgl, ctx->block_size);
854 437 : offset_blocks++;
855 : }
856 :
857 68 : return 0;
858 : }
859 :
860 : static int
861 196 : _dif_verify_split(struct _dif_sgl *sgl, uint32_t offset_in_block, uint32_t data_len,
862 : uint64_t *_guard, uint32_t offset_blocks,
863 : const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
864 : {
865 196 : uint32_t offset_in_dif, buf_len;
866 196 : uint8_t *buf;
867 : uint64_t guard;
868 196 : struct spdk_dif dif = {};
869 : int rc;
870 :
871 196 : assert(_guard != NULL);
872 196 : assert(offset_in_block < ctx->guard_interval);
873 196 : assert(offset_in_block + data_len < ctx->guard_interval ||
874 : offset_in_block + data_len == ctx->block_size);
875 :
876 196 : guard = *_guard;
877 :
878 : /* Compute CRC over split logical block data. */
879 557 : while (data_len != 0 && offset_in_block < ctx->guard_interval) {
880 361 : _dif_sgl_get_buf(sgl, &buf, &buf_len);
881 361 : buf_len = spdk_min(buf_len, data_len);
882 361 : buf_len = spdk_min(buf_len, ctx->guard_interval - offset_in_block);
883 :
884 361 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
885 361 : guard = _dif_generate_guard(guard, buf, buf_len, ctx->dif_pi_format);
886 : }
887 :
888 361 : _dif_sgl_advance(sgl, buf_len);
889 361 : offset_in_block += buf_len;
890 361 : data_len -= buf_len;
891 : }
892 :
893 196 : if (offset_in_block < ctx->guard_interval) {
894 15 : *_guard = guard;
895 15 : return 0;
896 : }
897 :
898 : /* Copy the split DIF field to the temporary DIF buffer, and then
899 : * skip metadata field after DIF field (if any). */
900 529 : while (offset_in_block < ctx->block_size) {
901 348 : _dif_sgl_get_buf(sgl, &buf, &buf_len);
902 :
903 348 : if (offset_in_block < ctx->guard_interval + _dif_size(ctx->dif_pi_format)) {
904 264 : offset_in_dif = offset_in_block - ctx->guard_interval;
905 264 : buf_len = spdk_min(buf_len, _dif_size(ctx->dif_pi_format) - offset_in_dif);
906 :
907 264 : memcpy((uint8_t *)&dif + offset_in_dif, buf, buf_len);
908 : } else {
909 84 : buf_len = spdk_min(buf_len, ctx->block_size - offset_in_block);
910 : }
911 348 : _dif_sgl_advance(sgl, buf_len);
912 348 : offset_in_block += buf_len;
913 : }
914 :
915 181 : rc = _dif_verify(&dif, guard, offset_blocks, ctx, err_blk);
916 181 : if (rc != 0) {
917 120 : return rc;
918 : }
919 :
920 61 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
921 61 : guard = ctx->guard_seed;
922 : }
923 :
924 61 : *_guard = guard;
925 61 : return 0;
926 : }
927 :
928 : static int
929 150 : dif_verify_split(struct _dif_sgl *sgl, uint32_t num_blocks,
930 : const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
931 : {
932 : uint32_t offset_blocks;
933 150 : uint64_t guard = 0;
934 : int rc;
935 :
936 150 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
937 150 : guard = ctx->guard_seed;
938 : }
939 :
940 199 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
941 169 : rc = _dif_verify_split(sgl, 0, ctx->block_size, &guard, offset_blocks,
942 : ctx, err_blk);
943 169 : if (rc != 0) {
944 120 : return rc;
945 : }
946 : }
947 :
948 30 : return 0;
949 : }
950 :
951 : int
952 247 : spdk_dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
953 : const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
954 : {
955 247 : struct _dif_sgl sgl;
956 :
957 247 : _dif_sgl_init(&sgl, iovs, iovcnt);
958 :
959 247 : if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
960 0 : SPDK_ERRLOG("Size of iovec array is not valid.\n");
961 0 : return -EINVAL;
962 : }
963 :
964 247 : if (_dif_is_disabled(ctx->dif_type)) {
965 1 : return 0;
966 : }
967 :
968 246 : if (_dif_sgl_is_bytes_multiple(&sgl, ctx->block_size)) {
969 96 : return dif_verify(&sgl, num_blocks, ctx, err_blk);
970 : } else {
971 150 : return dif_verify_split(&sgl, num_blocks, ctx, err_blk);
972 : }
973 : }
974 :
975 : static uint32_t
976 9 : dif_update_crc32c(struct _dif_sgl *sgl, uint32_t num_blocks,
977 : uint32_t crc32c, const struct spdk_dif_ctx *ctx)
978 : {
979 : uint32_t offset_blocks;
980 9 : uint8_t *buf;
981 :
982 45 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
983 36 : _dif_sgl_get_buf(sgl, &buf, NULL);
984 :
985 36 : crc32c = spdk_crc32c_update(buf, ctx->block_size - ctx->md_size, crc32c);
986 :
987 36 : _dif_sgl_advance(sgl, ctx->block_size);
988 : }
989 :
990 9 : return crc32c;
991 : }
992 :
993 : static uint32_t
994 51 : _dif_update_crc32c_split(struct _dif_sgl *sgl, uint32_t offset_in_block, uint32_t data_len,
995 : uint32_t crc32c, const struct spdk_dif_ctx *ctx)
996 : {
997 51 : uint32_t data_block_size, buf_len;
998 51 : uint8_t *buf;
999 :
1000 51 : data_block_size = ctx->block_size - ctx->md_size;
1001 :
1002 51 : assert(offset_in_block + data_len <= ctx->block_size);
1003 :
1004 174 : while (data_len != 0) {
1005 123 : _dif_sgl_get_buf(sgl, &buf, &buf_len);
1006 123 : buf_len = spdk_min(buf_len, data_len);
1007 :
1008 123 : if (offset_in_block < data_block_size) {
1009 69 : buf_len = spdk_min(buf_len, data_block_size - offset_in_block);
1010 69 : crc32c = spdk_crc32c_update(buf, buf_len, crc32c);
1011 : }
1012 :
1013 123 : _dif_sgl_advance(sgl, buf_len);
1014 123 : offset_in_block += buf_len;
1015 123 : data_len -= buf_len;
1016 : }
1017 :
1018 51 : return crc32c;
1019 : }
1020 :
1021 : static uint32_t
1022 6 : dif_update_crc32c_split(struct _dif_sgl *sgl, uint32_t num_blocks,
1023 : uint32_t crc32c, const struct spdk_dif_ctx *ctx)
1024 : {
1025 : uint32_t offset_blocks;
1026 :
1027 30 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
1028 24 : crc32c = _dif_update_crc32c_split(sgl, 0, ctx->block_size, crc32c, ctx);
1029 : }
1030 :
1031 6 : return crc32c;
1032 : }
1033 :
1034 : int
1035 15 : spdk_dif_update_crc32c(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
1036 : uint32_t *_crc32c, const struct spdk_dif_ctx *ctx)
1037 : {
1038 15 : struct _dif_sgl sgl;
1039 :
1040 15 : if (_crc32c == NULL) {
1041 0 : return -EINVAL;
1042 : }
1043 :
1044 15 : _dif_sgl_init(&sgl, iovs, iovcnt);
1045 :
1046 15 : if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
1047 0 : SPDK_ERRLOG("Size of iovec array is not valid.\n");
1048 0 : return -EINVAL;
1049 : }
1050 :
1051 15 : if (_dif_sgl_is_bytes_multiple(&sgl, ctx->block_size)) {
1052 9 : *_crc32c = dif_update_crc32c(&sgl, num_blocks, *_crc32c, ctx);
1053 : } else {
1054 6 : *_crc32c = dif_update_crc32c_split(&sgl, num_blocks, *_crc32c, ctx);
1055 : }
1056 :
1057 15 : return 0;
1058 : }
1059 :
1060 : static void
1061 48 : dif_generate_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
1062 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
1063 : {
1064 48 : uint32_t offset_blocks = 0, data_block_size;
1065 48 : uint8_t *src, *dst;
1066 : uint64_t guard;
1067 :
1068 48 : data_block_size = ctx->block_size - ctx->md_size;
1069 :
1070 492 : while (offset_blocks < num_blocks) {
1071 444 : _dif_sgl_get_buf(src_sgl, &src, NULL);
1072 444 : _dif_sgl_get_buf(dst_sgl, &dst, NULL);
1073 :
1074 444 : guard = 0;
1075 444 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1076 330 : guard = _dif_generate_guard_copy(ctx->guard_seed, dst, src, data_block_size,
1077 330 : ctx->dif_pi_format);
1078 330 : guard = _dif_generate_guard(guard, dst + data_block_size,
1079 330 : ctx->guard_interval - data_block_size, ctx->dif_pi_format);
1080 : } else {
1081 114 : memcpy(dst, src, data_block_size);
1082 : }
1083 :
1084 444 : _dif_generate(dst + ctx->guard_interval, guard, offset_blocks, ctx);
1085 :
1086 444 : _dif_sgl_advance(src_sgl, data_block_size);
1087 444 : _dif_sgl_advance(dst_sgl, ctx->block_size);
1088 444 : offset_blocks++;
1089 : }
1090 48 : }
1091 :
1092 : static void
1093 63 : _dif_generate_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
1094 : uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
1095 : {
1096 63 : uint32_t offset_in_block, src_len, data_block_size;
1097 63 : uint8_t *src, *dst;
1098 63 : uint64_t guard = 0;
1099 :
1100 63 : _dif_sgl_get_buf(dst_sgl, &dst, NULL);
1101 :
1102 63 : data_block_size = ctx->block_size - ctx->md_size;
1103 :
1104 63 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1105 63 : guard = ctx->guard_seed;
1106 : }
1107 63 : offset_in_block = 0;
1108 :
1109 192 : while (offset_in_block < data_block_size) {
1110 : /* Compute CRC over split logical block data and copy
1111 : * data to bounce buffer.
1112 : */
1113 129 : _dif_sgl_get_buf(src_sgl, &src, &src_len);
1114 129 : src_len = spdk_min(src_len, data_block_size - offset_in_block);
1115 :
1116 129 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1117 129 : guard = _dif_generate_guard_copy(guard, dst + offset_in_block,
1118 129 : src, src_len, ctx->dif_pi_format);
1119 : } else {
1120 0 : memcpy(dst + offset_in_block, src, src_len);
1121 : }
1122 :
1123 129 : _dif_sgl_advance(src_sgl, src_len);
1124 129 : offset_in_block += src_len;
1125 : }
1126 :
1127 63 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1128 63 : guard = _dif_generate_guard(guard, dst + data_block_size,
1129 63 : ctx->guard_interval - data_block_size, ctx->dif_pi_format);
1130 : }
1131 :
1132 63 : _dif_sgl_advance(dst_sgl, ctx->block_size);
1133 :
1134 63 : _dif_generate(dst + ctx->guard_interval, guard, offset_blocks, ctx);
1135 63 : }
1136 :
1137 : static void
1138 30 : dif_generate_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
1139 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
1140 : {
1141 : uint32_t offset_blocks;
1142 :
1143 93 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
1144 63 : _dif_generate_copy_split(src_sgl, dst_sgl, offset_blocks, ctx);
1145 : }
1146 30 : }
1147 :
1148 : int
1149 78 : spdk_dif_generate_copy(struct iovec *iovs, int iovcnt, struct iovec *bounce_iovs,
1150 : int bounce_iovcnt, uint32_t num_blocks,
1151 : const struct spdk_dif_ctx *ctx)
1152 : {
1153 78 : struct _dif_sgl src_sgl, dst_sgl;
1154 : uint32_t data_block_size;
1155 :
1156 78 : _dif_sgl_init(&src_sgl, iovs, iovcnt);
1157 78 : _dif_sgl_init(&dst_sgl, bounce_iovs, bounce_iovcnt);
1158 :
1159 78 : data_block_size = ctx->block_size - ctx->md_size;
1160 :
1161 78 : if (!_dif_sgl_is_valid(&src_sgl, data_block_size * num_blocks)) {
1162 0 : SPDK_ERRLOG("Size of iovec arrays are not valid.\n");
1163 0 : return -EINVAL;
1164 : }
1165 :
1166 78 : if (!_dif_sgl_is_valid_block_aligned(&dst_sgl, num_blocks, ctx->block_size)) {
1167 0 : SPDK_ERRLOG("Size of bounce_iovs arrays are not valid or misaligned with block_size.\n");
1168 0 : return -EINVAL;
1169 : }
1170 :
1171 78 : if (_dif_is_disabled(ctx->dif_type)) {
1172 0 : return 0;
1173 : }
1174 :
1175 78 : if (_dif_sgl_is_bytes_multiple(&src_sgl, data_block_size)) {
1176 48 : dif_generate_copy(&src_sgl, &dst_sgl, num_blocks, ctx);
1177 : } else {
1178 30 : dif_generate_copy_split(&src_sgl, &dst_sgl, num_blocks, ctx);
1179 : }
1180 :
1181 78 : return 0;
1182 : }
1183 :
1184 : static int
1185 48 : dif_verify_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
1186 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
1187 : struct spdk_dif_error *err_blk)
1188 : {
1189 48 : uint32_t offset_blocks = 0, data_block_size;
1190 48 : uint8_t *src, *dst;
1191 : int rc;
1192 : uint64_t guard;
1193 :
1194 48 : data_block_size = ctx->block_size - ctx->md_size;
1195 :
1196 348 : while (offset_blocks < num_blocks) {
1197 324 : _dif_sgl_get_buf(src_sgl, &src, NULL);
1198 324 : _dif_sgl_get_buf(dst_sgl, &dst, NULL);
1199 :
1200 324 : guard = 0;
1201 324 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1202 210 : guard = _dif_generate_guard_copy(ctx->guard_seed, dst, src, data_block_size,
1203 210 : ctx->dif_pi_format);
1204 210 : guard = _dif_generate_guard(guard, src + data_block_size,
1205 210 : ctx->guard_interval - data_block_size, ctx->dif_pi_format);
1206 : } else {
1207 114 : memcpy(dst, src, data_block_size);
1208 : }
1209 :
1210 324 : rc = _dif_verify(src + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
1211 324 : if (rc != 0) {
1212 24 : return rc;
1213 : }
1214 :
1215 300 : _dif_sgl_advance(src_sgl, ctx->block_size);
1216 300 : _dif_sgl_advance(dst_sgl, data_block_size);
1217 300 : offset_blocks++;
1218 : }
1219 :
1220 24 : return 0;
1221 : }
1222 :
1223 : static int
1224 39 : _dif_verify_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
1225 : uint32_t offset_blocks, const struct spdk_dif_ctx *ctx,
1226 : struct spdk_dif_error *err_blk)
1227 : {
1228 39 : uint32_t offset_in_block, dst_len, data_block_size;
1229 39 : uint8_t *src, *dst;
1230 39 : uint64_t guard = 0;
1231 :
1232 39 : _dif_sgl_get_buf(src_sgl, &src, NULL);
1233 :
1234 39 : data_block_size = ctx->block_size - ctx->md_size;
1235 :
1236 39 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1237 39 : guard = ctx->guard_seed;
1238 : }
1239 39 : offset_in_block = 0;
1240 :
1241 120 : while (offset_in_block < data_block_size) {
1242 : /* Compute CRC over split logical block data and copy
1243 : * data to bounce buffer.
1244 : */
1245 81 : _dif_sgl_get_buf(dst_sgl, &dst, &dst_len);
1246 81 : dst_len = spdk_min(dst_len, data_block_size - offset_in_block);
1247 :
1248 81 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1249 81 : guard = _dif_generate_guard_copy(guard, dst, src + offset_in_block,
1250 81 : dst_len, ctx->dif_pi_format);
1251 : } else {
1252 0 : memcpy(dst, src + offset_in_block, dst_len);
1253 : }
1254 :
1255 81 : _dif_sgl_advance(dst_sgl, dst_len);
1256 81 : offset_in_block += dst_len;
1257 : }
1258 :
1259 39 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1260 39 : guard = _dif_generate_guard(guard, src + data_block_size,
1261 39 : ctx->guard_interval - data_block_size, ctx->dif_pi_format);
1262 : }
1263 :
1264 39 : _dif_sgl_advance(src_sgl, ctx->block_size);
1265 :
1266 39 : return _dif_verify(src + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
1267 : }
1268 :
1269 : static int
1270 30 : dif_verify_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
1271 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
1272 : struct spdk_dif_error *err_blk)
1273 : {
1274 : uint32_t offset_blocks;
1275 : int rc;
1276 :
1277 45 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
1278 39 : rc = _dif_verify_copy_split(src_sgl, dst_sgl, offset_blocks, ctx, err_blk);
1279 39 : if (rc != 0) {
1280 24 : return rc;
1281 : }
1282 : }
1283 :
1284 6 : return 0;
1285 : }
1286 :
1287 : int
1288 78 : spdk_dif_verify_copy(struct iovec *iovs, int iovcnt, struct iovec *bounce_iovs,
1289 : int bounce_iovcnt, uint32_t num_blocks,
1290 : const struct spdk_dif_ctx *ctx,
1291 : struct spdk_dif_error *err_blk)
1292 : {
1293 78 : struct _dif_sgl src_sgl, dst_sgl;
1294 : uint32_t data_block_size;
1295 :
1296 78 : _dif_sgl_init(&src_sgl, bounce_iovs, bounce_iovcnt);
1297 78 : _dif_sgl_init(&dst_sgl, iovs, iovcnt);
1298 :
1299 78 : data_block_size = ctx->block_size - ctx->md_size;
1300 :
1301 78 : if (!_dif_sgl_is_valid(&dst_sgl, data_block_size * num_blocks)) {
1302 0 : SPDK_ERRLOG("Size of iovec arrays are not valid\n");
1303 0 : return -EINVAL;
1304 : }
1305 :
1306 78 : if (!_dif_sgl_is_valid_block_aligned(&src_sgl, num_blocks, ctx->block_size)) {
1307 0 : SPDK_ERRLOG("Size of bounce_iovs arrays are not valid or misaligned with block_size.\n");
1308 0 : return -EINVAL;
1309 : }
1310 :
1311 78 : if (_dif_is_disabled(ctx->dif_type)) {
1312 0 : return 0;
1313 : }
1314 :
1315 78 : if (_dif_sgl_is_bytes_multiple(&dst_sgl, data_block_size)) {
1316 48 : return dif_verify_copy(&src_sgl, &dst_sgl, num_blocks, ctx, err_blk);
1317 : } else {
1318 30 : return dif_verify_copy_split(&src_sgl, &dst_sgl, num_blocks, ctx, err_blk);
1319 : }
1320 : }
1321 :
1322 : static void
1323 240 : _bit_flip(uint8_t *buf, uint32_t flip_bit)
1324 : {
1325 : uint8_t byte;
1326 :
1327 240 : byte = *buf;
1328 240 : byte ^= 1 << flip_bit;
1329 240 : *buf = byte;
1330 240 : }
1331 :
1332 : static int
1333 240 : _dif_inject_error(struct _dif_sgl *sgl,
1334 : uint32_t block_size, uint32_t num_blocks,
1335 : uint32_t inject_offset_blocks,
1336 : uint32_t inject_offset_bytes,
1337 : uint32_t inject_offset_bits)
1338 : {
1339 240 : uint32_t offset_in_block, buf_len;
1340 240 : uint8_t *buf;
1341 :
1342 240 : _dif_sgl_advance(sgl, block_size * inject_offset_blocks);
1343 :
1344 240 : offset_in_block = 0;
1345 :
1346 322 : while (offset_in_block < block_size) {
1347 322 : _dif_sgl_get_buf(sgl, &buf, &buf_len);
1348 322 : buf_len = spdk_min(buf_len, block_size - offset_in_block);
1349 :
1350 322 : if (inject_offset_bytes >= offset_in_block &&
1351 322 : inject_offset_bytes < offset_in_block + buf_len) {
1352 240 : buf += inject_offset_bytes - offset_in_block;
1353 240 : _bit_flip(buf, inject_offset_bits);
1354 240 : return 0;
1355 : }
1356 :
1357 82 : _dif_sgl_advance(sgl, buf_len);
1358 82 : offset_in_block += buf_len;
1359 : }
1360 :
1361 0 : return -1;
1362 : }
1363 :
1364 : static int
1365 240 : dif_inject_error(struct _dif_sgl *sgl, uint32_t block_size, uint32_t num_blocks,
1366 : uint32_t start_inject_bytes, uint32_t inject_range_bytes,
1367 : uint32_t *inject_offset)
1368 : {
1369 : uint32_t inject_offset_blocks, inject_offset_bytes, inject_offset_bits;
1370 : uint32_t offset_blocks;
1371 : int rc;
1372 :
1373 240 : srand(time(0));
1374 :
1375 240 : inject_offset_blocks = rand() % num_blocks;
1376 240 : inject_offset_bytes = start_inject_bytes + (rand() % inject_range_bytes);
1377 240 : inject_offset_bits = rand() % 8;
1378 :
1379 528 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
1380 528 : if (offset_blocks == inject_offset_blocks) {
1381 240 : rc = _dif_inject_error(sgl, block_size, num_blocks,
1382 : inject_offset_blocks,
1383 : inject_offset_bytes,
1384 : inject_offset_bits);
1385 240 : if (rc == 0) {
1386 240 : *inject_offset = inject_offset_blocks;
1387 : }
1388 240 : return rc;
1389 : }
1390 : }
1391 :
1392 0 : return -1;
1393 : }
1394 :
1395 : int
1396 192 : spdk_dif_inject_error(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
1397 : const struct spdk_dif_ctx *ctx, uint32_t inject_flags,
1398 : uint32_t *inject_offset)
1399 : {
1400 192 : struct _dif_sgl sgl;
1401 : int rc;
1402 :
1403 192 : _dif_sgl_init(&sgl, iovs, iovcnt);
1404 :
1405 192 : if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
1406 0 : SPDK_ERRLOG("Size of iovec array is not valid.\n");
1407 0 : return -EINVAL;
1408 : }
1409 :
1410 192 : if (inject_flags & SPDK_DIF_REFTAG_ERROR) {
1411 96 : rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
1412 48 : ctx->guard_interval + _dif_reftag_offset(ctx->dif_pi_format),
1413 48 : _dif_reftag_size(ctx->dif_pi_format),
1414 : inject_offset);
1415 48 : if (rc != 0) {
1416 0 : SPDK_ERRLOG("Failed to inject error to Reference Tag.\n");
1417 0 : return rc;
1418 : }
1419 : }
1420 :
1421 192 : if (inject_flags & SPDK_DIF_APPTAG_ERROR) {
1422 96 : rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
1423 48 : ctx->guard_interval + _dif_apptag_offset(ctx->dif_pi_format),
1424 48 : _dif_apptag_size(),
1425 : inject_offset);
1426 48 : if (rc != 0) {
1427 0 : SPDK_ERRLOG("Failed to inject error to Application Tag.\n");
1428 0 : return rc;
1429 : }
1430 : }
1431 192 : if (inject_flags & SPDK_DIF_GUARD_ERROR) {
1432 48 : rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
1433 : ctx->guard_interval,
1434 48 : _dif_guard_size(ctx->dif_pi_format),
1435 : inject_offset);
1436 48 : if (rc != 0) {
1437 0 : SPDK_ERRLOG("Failed to inject error to Guard.\n");
1438 0 : return rc;
1439 : }
1440 : }
1441 :
1442 192 : if (inject_flags & SPDK_DIF_DATA_ERROR) {
1443 : /* If the DIF information is contained within the last 8/16 bytes of
1444 : * metadata (depending on the PI format), then the CRC covers all metadata
1445 : * bytes up to but excluding the last 8/16 bytes. But error injection does not
1446 : * cover these metadata because classification is not determined yet.
1447 : *
1448 : * Note: Error injection to data block is expected to be detected as
1449 : * guard error.
1450 : */
1451 48 : rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
1452 : 0,
1453 48 : ctx->block_size - ctx->md_size,
1454 : inject_offset);
1455 48 : if (rc != 0) {
1456 0 : SPDK_ERRLOG("Failed to inject error to data block.\n");
1457 0 : return rc;
1458 : }
1459 : }
1460 :
1461 192 : return 0;
1462 : }
1463 :
1464 : static void
1465 54 : dix_generate(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1466 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
1467 : {
1468 54 : uint32_t offset_blocks = 0;
1469 54 : uint8_t *data_buf, *md_buf;
1470 : uint64_t guard;
1471 :
1472 558 : while (offset_blocks < num_blocks) {
1473 504 : _dif_sgl_get_buf(data_sgl, &data_buf, NULL);
1474 504 : _dif_sgl_get_buf(md_sgl, &md_buf, NULL);
1475 :
1476 504 : guard = 0;
1477 504 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1478 390 : guard = _dif_generate_guard(ctx->guard_seed, data_buf, ctx->block_size,
1479 390 : ctx->dif_pi_format);
1480 390 : guard = _dif_generate_guard(guard, md_buf, ctx->guard_interval,
1481 390 : ctx->dif_pi_format);
1482 : }
1483 :
1484 504 : _dif_generate(md_buf + ctx->guard_interval, guard, offset_blocks, ctx);
1485 :
1486 504 : _dif_sgl_advance(data_sgl, ctx->block_size);
1487 504 : _dif_sgl_advance(md_sgl, ctx->md_size);
1488 504 : offset_blocks++;
1489 : }
1490 54 : }
1491 :
1492 : static void
1493 75 : _dix_generate_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1494 : uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
1495 : {
1496 75 : uint32_t offset_in_block, data_buf_len;
1497 75 : uint8_t *data_buf, *md_buf;
1498 75 : uint64_t guard = 0;
1499 :
1500 75 : _dif_sgl_get_buf(md_sgl, &md_buf, NULL);
1501 :
1502 75 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1503 75 : guard = ctx->guard_seed;
1504 : }
1505 75 : offset_in_block = 0;
1506 :
1507 231 : while (offset_in_block < ctx->block_size) {
1508 156 : _dif_sgl_get_buf(data_sgl, &data_buf, &data_buf_len);
1509 156 : data_buf_len = spdk_min(data_buf_len, ctx->block_size - offset_in_block);
1510 :
1511 156 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1512 156 : guard = _dif_generate_guard(guard, data_buf, data_buf_len,
1513 156 : ctx->dif_pi_format);
1514 : }
1515 :
1516 156 : _dif_sgl_advance(data_sgl, data_buf_len);
1517 156 : offset_in_block += data_buf_len;
1518 : }
1519 :
1520 75 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1521 75 : guard = _dif_generate_guard(guard, md_buf, ctx->guard_interval,
1522 75 : ctx->dif_pi_format);
1523 : }
1524 :
1525 75 : _dif_sgl_advance(md_sgl, ctx->md_size);
1526 :
1527 75 : _dif_generate(md_buf + ctx->guard_interval, guard, offset_blocks, ctx);
1528 75 : }
1529 :
1530 : static void
1531 33 : dix_generate_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1532 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
1533 : {
1534 : uint32_t offset_blocks;
1535 :
1536 108 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
1537 75 : _dix_generate_split(data_sgl, md_sgl, offset_blocks, ctx);
1538 : }
1539 33 : }
1540 :
1541 : int
1542 87 : spdk_dix_generate(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
1543 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
1544 : {
1545 87 : struct _dif_sgl data_sgl, md_sgl;
1546 :
1547 87 : _dif_sgl_init(&data_sgl, iovs, iovcnt);
1548 87 : _dif_sgl_init(&md_sgl, md_iov, 1);
1549 :
1550 87 : if (!_dif_sgl_is_valid(&data_sgl, ctx->block_size * num_blocks) ||
1551 87 : !_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
1552 0 : SPDK_ERRLOG("Size of iovec array is not valid.\n");
1553 0 : return -EINVAL;
1554 : }
1555 :
1556 87 : if (_dif_is_disabled(ctx->dif_type)) {
1557 0 : return 0;
1558 : }
1559 :
1560 87 : if (_dif_sgl_is_bytes_multiple(&data_sgl, ctx->block_size)) {
1561 54 : dix_generate(&data_sgl, &md_sgl, num_blocks, ctx);
1562 : } else {
1563 33 : dix_generate_split(&data_sgl, &md_sgl, num_blocks, ctx);
1564 : }
1565 :
1566 87 : return 0;
1567 : }
1568 :
1569 : static int
1570 54 : dix_verify(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1571 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
1572 : struct spdk_dif_error *err_blk)
1573 : {
1574 54 : uint32_t offset_blocks = 0;
1575 54 : uint8_t *data_buf, *md_buf;
1576 : uint64_t guard;
1577 : int rc;
1578 :
1579 414 : while (offset_blocks < num_blocks) {
1580 384 : _dif_sgl_get_buf(data_sgl, &data_buf, NULL);
1581 384 : _dif_sgl_get_buf(md_sgl, &md_buf, NULL);
1582 :
1583 384 : guard = 0;
1584 384 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1585 270 : guard = _dif_generate_guard(ctx->guard_seed, data_buf, ctx->block_size,
1586 270 : ctx->dif_pi_format);
1587 270 : guard = _dif_generate_guard(guard, md_buf, ctx->guard_interval,
1588 270 : ctx->dif_pi_format);
1589 : }
1590 :
1591 384 : rc = _dif_verify(md_buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
1592 384 : if (rc != 0) {
1593 24 : return rc;
1594 : }
1595 :
1596 360 : _dif_sgl_advance(data_sgl, ctx->block_size);
1597 360 : _dif_sgl_advance(md_sgl, ctx->md_size);
1598 360 : offset_blocks++;
1599 : }
1600 :
1601 30 : return 0;
1602 : }
1603 :
1604 : static int
1605 51 : _dix_verify_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1606 : uint32_t offset_blocks, const struct spdk_dif_ctx *ctx,
1607 : struct spdk_dif_error *err_blk)
1608 : {
1609 51 : uint32_t offset_in_block, data_buf_len;
1610 51 : uint8_t *data_buf, *md_buf;
1611 51 : uint64_t guard = 0;
1612 :
1613 51 : _dif_sgl_get_buf(md_sgl, &md_buf, NULL);
1614 :
1615 51 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1616 51 : guard = ctx->guard_seed;
1617 : }
1618 51 : offset_in_block = 0;
1619 :
1620 159 : while (offset_in_block < ctx->block_size) {
1621 108 : _dif_sgl_get_buf(data_sgl, &data_buf, &data_buf_len);
1622 108 : data_buf_len = spdk_min(data_buf_len, ctx->block_size - offset_in_block);
1623 :
1624 108 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1625 108 : guard = _dif_generate_guard(guard, data_buf, data_buf_len,
1626 108 : ctx->dif_pi_format);
1627 : }
1628 :
1629 108 : _dif_sgl_advance(data_sgl, data_buf_len);
1630 108 : offset_in_block += data_buf_len;
1631 : }
1632 :
1633 51 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1634 51 : guard = _dif_generate_guard(guard, md_buf, ctx->guard_interval,
1635 51 : ctx->dif_pi_format);
1636 : }
1637 :
1638 51 : _dif_sgl_advance(md_sgl, ctx->md_size);
1639 :
1640 51 : return _dif_verify(md_buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
1641 : }
1642 :
1643 : static int
1644 33 : dix_verify_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1645 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
1646 : struct spdk_dif_error *err_blk)
1647 : {
1648 : uint32_t offset_blocks;
1649 : int rc;
1650 :
1651 60 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
1652 51 : rc = _dix_verify_split(data_sgl, md_sgl, offset_blocks, ctx, err_blk);
1653 51 : if (rc != 0) {
1654 24 : return rc;
1655 : }
1656 : }
1657 :
1658 9 : return 0;
1659 : }
1660 :
1661 : int
1662 87 : spdk_dix_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
1663 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
1664 : struct spdk_dif_error *err_blk)
1665 : {
1666 87 : struct _dif_sgl data_sgl, md_sgl;
1667 :
1668 87 : if (md_iov->iov_base == NULL) {
1669 0 : SPDK_ERRLOG("Metadata buffer is NULL.\n");
1670 0 : return -EINVAL;
1671 : }
1672 :
1673 87 : _dif_sgl_init(&data_sgl, iovs, iovcnt);
1674 87 : _dif_sgl_init(&md_sgl, md_iov, 1);
1675 :
1676 87 : if (!_dif_sgl_is_valid(&data_sgl, ctx->block_size * num_blocks) ||
1677 87 : !_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
1678 0 : SPDK_ERRLOG("Size of iovec array is not valid.\n");
1679 0 : return -EINVAL;
1680 : }
1681 :
1682 87 : if (_dif_is_disabled(ctx->dif_type)) {
1683 0 : return 0;
1684 : }
1685 :
1686 87 : if (_dif_sgl_is_bytes_multiple(&data_sgl, ctx->block_size)) {
1687 54 : return dix_verify(&data_sgl, &md_sgl, num_blocks, ctx, err_blk);
1688 : } else {
1689 33 : return dix_verify_split(&data_sgl, &md_sgl, num_blocks, ctx, err_blk);
1690 : }
1691 : }
1692 :
1693 : int
1694 48 : spdk_dix_inject_error(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
1695 : uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
1696 : uint32_t inject_flags, uint32_t *inject_offset)
1697 : {
1698 48 : struct _dif_sgl data_sgl, md_sgl;
1699 : int rc;
1700 :
1701 48 : _dif_sgl_init(&data_sgl, iovs, iovcnt);
1702 48 : _dif_sgl_init(&md_sgl, md_iov, 1);
1703 :
1704 48 : if (!_dif_sgl_is_valid(&data_sgl, ctx->block_size * num_blocks) ||
1705 48 : !_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
1706 0 : SPDK_ERRLOG("Size of iovec array is not valid.\n");
1707 0 : return -EINVAL;
1708 : }
1709 :
1710 48 : if (inject_flags & SPDK_DIF_REFTAG_ERROR) {
1711 24 : rc = dif_inject_error(&md_sgl, ctx->md_size, num_blocks,
1712 12 : ctx->guard_interval + _dif_reftag_offset(ctx->dif_pi_format),
1713 12 : _dif_reftag_size(ctx->dif_pi_format),
1714 : inject_offset);
1715 12 : if (rc != 0) {
1716 0 : SPDK_ERRLOG("Failed to inject error to Reference Tag.\n");
1717 0 : return rc;
1718 : }
1719 : }
1720 :
1721 48 : if (inject_flags & SPDK_DIF_APPTAG_ERROR) {
1722 24 : rc = dif_inject_error(&md_sgl, ctx->md_size, num_blocks,
1723 12 : ctx->guard_interval + _dif_apptag_offset(ctx->dif_pi_format),
1724 12 : _dif_apptag_size(),
1725 : inject_offset);
1726 12 : if (rc != 0) {
1727 0 : SPDK_ERRLOG("Failed to inject error to Application Tag.\n");
1728 0 : return rc;
1729 : }
1730 : }
1731 :
1732 48 : if (inject_flags & SPDK_DIF_GUARD_ERROR) {
1733 12 : rc = dif_inject_error(&md_sgl, ctx->md_size, num_blocks,
1734 : ctx->guard_interval,
1735 12 : _dif_guard_size(ctx->dif_pi_format),
1736 : inject_offset);
1737 12 : if (rc != 0) {
1738 0 : SPDK_ERRLOG("Failed to inject error to Guard.\n");
1739 0 : return rc;
1740 : }
1741 : }
1742 :
1743 48 : if (inject_flags & SPDK_DIF_DATA_ERROR) {
1744 : /* Note: Error injection to data block is expected to be detected
1745 : * as guard error.
1746 : */
1747 12 : rc = dif_inject_error(&data_sgl, ctx->block_size, num_blocks,
1748 : 0,
1749 : ctx->block_size,
1750 : inject_offset);
1751 12 : if (rc != 0) {
1752 0 : SPDK_ERRLOG("Failed to inject error to Guard.\n");
1753 0 : return rc;
1754 : }
1755 : }
1756 :
1757 48 : return 0;
1758 : }
1759 :
1760 : static uint32_t
1761 369 : _to_next_boundary(uint32_t offset, uint32_t boundary)
1762 : {
1763 369 : return boundary - (offset % boundary);
1764 : }
1765 :
1766 : static uint32_t
1767 237 : _to_size_with_md(uint32_t size, uint32_t data_block_size, uint32_t block_size)
1768 : {
1769 237 : return (size / data_block_size) * block_size + (size % data_block_size);
1770 : }
1771 :
1772 : int
1773 38 : spdk_dif_set_md_interleave_iovs(struct iovec *iovs, int iovcnt,
1774 : struct iovec *buf_iovs, int buf_iovcnt,
1775 : uint32_t data_offset, uint32_t data_len,
1776 : uint32_t *_mapped_len,
1777 : const struct spdk_dif_ctx *ctx)
1778 : {
1779 : uint32_t data_block_size, data_unalign, buf_len, buf_offset, len;
1780 38 : struct _dif_sgl dif_sgl;
1781 38 : struct _dif_sgl buf_sgl;
1782 :
1783 38 : if (iovs == NULL || iovcnt == 0 || buf_iovs == NULL || buf_iovcnt == 0) {
1784 0 : return -EINVAL;
1785 : }
1786 :
1787 38 : data_block_size = ctx->block_size - ctx->md_size;
1788 :
1789 38 : data_unalign = ctx->data_offset % data_block_size;
1790 :
1791 38 : buf_len = _to_size_with_md(data_unalign + data_offset + data_len, data_block_size,
1792 : ctx->block_size);
1793 38 : buf_len -= data_unalign;
1794 :
1795 38 : _dif_sgl_init(&dif_sgl, iovs, iovcnt);
1796 38 : _dif_sgl_init(&buf_sgl, buf_iovs, buf_iovcnt);
1797 :
1798 38 : if (!_dif_sgl_is_valid(&buf_sgl, buf_len)) {
1799 1 : SPDK_ERRLOG("Buffer overflow will occur.\n");
1800 1 : return -ERANGE;
1801 : }
1802 :
1803 37 : buf_offset = _to_size_with_md(data_unalign + data_offset, data_block_size, ctx->block_size);
1804 37 : buf_offset -= data_unalign;
1805 :
1806 37 : _dif_sgl_advance(&buf_sgl, buf_offset);
1807 :
1808 121 : while (data_len != 0) {
1809 104 : len = spdk_min(data_len, _to_next_boundary(ctx->data_offset + data_offset, data_block_size));
1810 104 : if (!_dif_sgl_append_split(&dif_sgl, &buf_sgl, len)) {
1811 20 : break;
1812 : }
1813 84 : _dif_sgl_advance(&buf_sgl, ctx->md_size);
1814 84 : data_offset += len;
1815 84 : data_len -= len;
1816 : }
1817 :
1818 37 : if (_mapped_len != NULL) {
1819 37 : *_mapped_len = dif_sgl.total_size;
1820 : }
1821 :
1822 37 : return iovcnt - dif_sgl.iovcnt;
1823 : }
1824 :
1825 : static int
1826 67 : _dif_sgl_setup_stream(struct _dif_sgl *sgl, uint32_t *_buf_offset, uint32_t *_buf_len,
1827 : uint32_t data_offset, uint32_t data_len,
1828 : const struct spdk_dif_ctx *ctx)
1829 : {
1830 : uint32_t data_block_size, data_unalign, buf_len, buf_offset;
1831 :
1832 67 : data_block_size = ctx->block_size - ctx->md_size;
1833 :
1834 67 : data_unalign = ctx->data_offset % data_block_size;
1835 :
1836 : /* If the last data block is complete, DIF of the data block is
1837 : * inserted or verified in this turn.
1838 : */
1839 67 : buf_len = _to_size_with_md(data_unalign + data_offset + data_len, data_block_size,
1840 : ctx->block_size);
1841 67 : buf_len -= data_unalign;
1842 :
1843 67 : if (!_dif_sgl_is_valid(sgl, buf_len)) {
1844 3 : return -ERANGE;
1845 : }
1846 :
1847 64 : buf_offset = _to_size_with_md(data_unalign + data_offset, data_block_size, ctx->block_size);
1848 64 : buf_offset -= data_unalign;
1849 :
1850 64 : _dif_sgl_advance(sgl, buf_offset);
1851 64 : buf_len -= buf_offset;
1852 :
1853 64 : buf_offset += data_unalign;
1854 :
1855 64 : *_buf_offset = buf_offset;
1856 64 : *_buf_len = buf_len;
1857 :
1858 64 : return 0;
1859 : }
1860 :
1861 : int
1862 49 : spdk_dif_generate_stream(struct iovec *iovs, int iovcnt,
1863 : uint32_t data_offset, uint32_t data_len,
1864 : struct spdk_dif_ctx *ctx)
1865 : {
1866 49 : uint32_t buf_len = 0, buf_offset = 0;
1867 : uint32_t len, offset_in_block, offset_blocks;
1868 49 : uint64_t guard = 0;
1869 49 : struct _dif_sgl sgl;
1870 : int rc;
1871 :
1872 49 : if (iovs == NULL || iovcnt == 0) {
1873 0 : return -EINVAL;
1874 : }
1875 :
1876 49 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1877 49 : guard = ctx->last_guard;
1878 : }
1879 :
1880 49 : _dif_sgl_init(&sgl, iovs, iovcnt);
1881 :
1882 49 : rc = _dif_sgl_setup_stream(&sgl, &buf_offset, &buf_len, data_offset, data_len, ctx);
1883 49 : if (rc != 0) {
1884 3 : return rc;
1885 : }
1886 :
1887 122 : while (buf_len != 0) {
1888 76 : len = spdk_min(buf_len, _to_next_boundary(buf_offset, ctx->block_size));
1889 76 : offset_in_block = buf_offset % ctx->block_size;
1890 76 : offset_blocks = buf_offset / ctx->block_size;
1891 :
1892 76 : guard = _dif_generate_split(&sgl, offset_in_block, len, guard, offset_blocks, ctx);
1893 :
1894 76 : buf_len -= len;
1895 76 : buf_offset += len;
1896 : }
1897 :
1898 46 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1899 46 : ctx->last_guard = guard;
1900 : }
1901 :
1902 46 : return 0;
1903 : }
1904 :
1905 : int
1906 9 : spdk_dif_verify_stream(struct iovec *iovs, int iovcnt,
1907 : uint32_t data_offset, uint32_t data_len,
1908 : struct spdk_dif_ctx *ctx,
1909 : struct spdk_dif_error *err_blk)
1910 : {
1911 9 : uint32_t buf_len = 0, buf_offset = 0;
1912 : uint32_t len, offset_in_block, offset_blocks;
1913 9 : uint64_t guard = 0;
1914 9 : struct _dif_sgl sgl;
1915 9 : int rc = 0;
1916 :
1917 9 : if (iovs == NULL || iovcnt == 0) {
1918 0 : return -EINVAL;
1919 : }
1920 :
1921 9 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1922 9 : guard = ctx->last_guard;
1923 : }
1924 :
1925 9 : _dif_sgl_init(&sgl, iovs, iovcnt);
1926 :
1927 9 : rc = _dif_sgl_setup_stream(&sgl, &buf_offset, &buf_len, data_offset, data_len, ctx);
1928 9 : if (rc != 0) {
1929 0 : return rc;
1930 : }
1931 :
1932 27 : while (buf_len != 0) {
1933 18 : len = spdk_min(buf_len, _to_next_boundary(buf_offset, ctx->block_size));
1934 18 : offset_in_block = buf_offset % ctx->block_size;
1935 18 : offset_blocks = buf_offset / ctx->block_size;
1936 :
1937 18 : rc = _dif_verify_split(&sgl, offset_in_block, len, &guard, offset_blocks,
1938 : ctx, err_blk);
1939 18 : if (rc != 0) {
1940 0 : goto error;
1941 : }
1942 :
1943 18 : buf_len -= len;
1944 18 : buf_offset += len;
1945 : }
1946 :
1947 9 : if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1948 9 : ctx->last_guard = guard;
1949 : }
1950 9 : error:
1951 9 : return rc;
1952 : }
1953 :
1954 : int
1955 9 : spdk_dif_update_crc32c_stream(struct iovec *iovs, int iovcnt,
1956 : uint32_t data_offset, uint32_t data_len,
1957 : uint32_t *_crc32c, const struct spdk_dif_ctx *ctx)
1958 : {
1959 9 : uint32_t buf_len = 0, buf_offset = 0, len, offset_in_block;
1960 : uint32_t crc32c;
1961 9 : struct _dif_sgl sgl;
1962 : int rc;
1963 :
1964 9 : if (iovs == NULL || iovcnt == 0) {
1965 0 : return -EINVAL;
1966 : }
1967 :
1968 9 : crc32c = *_crc32c;
1969 9 : _dif_sgl_init(&sgl, iovs, iovcnt);
1970 :
1971 9 : rc = _dif_sgl_setup_stream(&sgl, &buf_offset, &buf_len, data_offset, data_len, ctx);
1972 9 : if (rc != 0) {
1973 0 : return rc;
1974 : }
1975 :
1976 27 : while (buf_len != 0) {
1977 18 : len = spdk_min(buf_len, _to_next_boundary(buf_offset, ctx->block_size));
1978 18 : offset_in_block = buf_offset % ctx->block_size;
1979 :
1980 18 : crc32c = _dif_update_crc32c_split(&sgl, offset_in_block, len, crc32c, ctx);
1981 :
1982 18 : buf_len -= len;
1983 18 : buf_offset += len;
1984 : }
1985 :
1986 9 : *_crc32c = crc32c;
1987 :
1988 9 : return 0;
1989 : }
1990 :
1991 : void
1992 10 : spdk_dif_get_range_with_md(uint32_t data_offset, uint32_t data_len,
1993 : uint32_t *_buf_offset, uint32_t *_buf_len,
1994 : const struct spdk_dif_ctx *ctx)
1995 : {
1996 : uint32_t data_block_size, data_unalign, buf_offset, buf_len;
1997 :
1998 10 : if (!ctx->md_interleave) {
1999 0 : buf_offset = data_offset;
2000 0 : buf_len = data_len;
2001 : } else {
2002 10 : data_block_size = ctx->block_size - ctx->md_size;
2003 :
2004 10 : data_unalign = data_offset % data_block_size;
2005 :
2006 10 : buf_offset = _to_size_with_md(data_offset, data_block_size, ctx->block_size);
2007 10 : buf_len = _to_size_with_md(data_unalign + data_len, data_block_size, ctx->block_size) -
2008 : data_unalign;
2009 : }
2010 :
2011 10 : if (_buf_offset != NULL) {
2012 10 : *_buf_offset = buf_offset;
2013 : }
2014 :
2015 10 : if (_buf_len != NULL) {
2016 10 : *_buf_len = buf_len;
2017 : }
2018 10 : }
2019 :
2020 : uint32_t
2021 11 : spdk_dif_get_length_with_md(uint32_t data_len, const struct spdk_dif_ctx *ctx)
2022 : {
2023 : uint32_t data_block_size;
2024 :
2025 11 : if (!ctx->md_interleave) {
2026 0 : return data_len;
2027 : } else {
2028 11 : data_block_size = ctx->block_size - ctx->md_size;
2029 :
2030 11 : return _to_size_with_md(data_len, data_block_size, ctx->block_size);
2031 : }
2032 : }
2033 :
2034 : static int
2035 72 : _dif_remap_ref_tag(struct _dif_sgl *sgl, uint32_t offset_blocks,
2036 : const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
2037 : {
2038 72 : uint32_t offset, buf_len;
2039 72 : uint64_t expected = 0, _actual, remapped;
2040 72 : uint8_t *buf;
2041 72 : struct _dif_sgl tmp_sgl;
2042 72 : struct spdk_dif dif;
2043 :
2044 : /* Fast forward to DIF field. */
2045 72 : _dif_sgl_advance(sgl, ctx->guard_interval);
2046 72 : _dif_sgl_copy(&tmp_sgl, sgl);
2047 :
2048 : /* Copy the split DIF field to the temporary DIF buffer */
2049 72 : offset = 0;
2050 162 : while (offset < _dif_size(ctx->dif_pi_format)) {
2051 90 : _dif_sgl_get_buf(sgl, &buf, &buf_len);
2052 90 : buf_len = spdk_min(buf_len, _dif_size(ctx->dif_pi_format) - offset);
2053 :
2054 90 : memcpy((uint8_t *)&dif + offset, buf, buf_len);
2055 :
2056 90 : _dif_sgl_advance(sgl, buf_len);
2057 90 : offset += buf_len;
2058 : }
2059 :
2060 72 : switch (ctx->dif_type) {
2061 72 : case SPDK_DIF_TYPE1:
2062 : case SPDK_DIF_TYPE2:
2063 : /* If Type 1 or 2 is used, then all DIF checks are disabled when
2064 : * the Application Tag is 0xFFFF.
2065 : */
2066 72 : if (_dif_apptag_ignore(&dif, ctx->dif_pi_format)) {
2067 0 : goto end;
2068 : }
2069 72 : break;
2070 0 : case SPDK_DIF_TYPE3:
2071 : /* If Type 3 is used, then all DIF checks are disabled when the
2072 : * Application Tag is 0xFFFF and the Reference Tag is 0xFFFFFFFF
2073 : * or 0xFFFFFFFFFFFFFFFF depending on the PI format.
2074 : */
2075 0 : if (_dif_apptag_ignore(&dif, ctx->dif_pi_format) &&
2076 0 : _dif_reftag_ignore(&dif, ctx->dif_pi_format)) {
2077 0 : goto end;
2078 : }
2079 0 : break;
2080 0 : default:
2081 0 : break;
2082 : }
2083 :
2084 : /* For type 1 and 2, the Reference Tag is incremented for each
2085 : * subsequent logical block. For type 3, the Reference Tag
2086 : * remains the same as the initial Reference Tag.
2087 : */
2088 72 : if (ctx->dif_type != SPDK_DIF_TYPE3) {
2089 72 : expected = ctx->init_ref_tag + ctx->ref_tag_offset + offset_blocks;
2090 72 : remapped = ctx->remapped_init_ref_tag + ctx->ref_tag_offset + offset_blocks;
2091 : } else {
2092 0 : remapped = ctx->remapped_init_ref_tag;
2093 : }
2094 :
2095 : /* Verify the stored Reference Tag. */
2096 72 : switch (ctx->dif_type) {
2097 72 : case SPDK_DIF_TYPE1:
2098 : case SPDK_DIF_TYPE2:
2099 : /* Compare the DIF Reference Tag field to the passed Reference Tag.
2100 : * The passed Reference Tag will be the least significant 4 bytes
2101 : * or 8 bytes (depending on the PI format)
2102 : * of the LBA when Type 1 is used, and application specific value
2103 : * if Type 2 is used.
2104 : */
2105 72 : if (!_dif_reftag_match(&dif, expected, ctx->dif_pi_format)) {
2106 0 : _actual = _dif_get_reftag(&dif, ctx->dif_pi_format);
2107 0 : _dif_error_set(err_blk, SPDK_DIF_REFTAG_ERROR, expected,
2108 : _actual, offset_blocks);
2109 0 : SPDK_ERRLOG("Failed to compare Ref Tag: LBA=%" PRIu64 "," \
2110 : " Expected=%lx, Actual=%lx\n",
2111 : expected, expected, _actual);
2112 0 : return -1;
2113 : }
2114 72 : break;
2115 0 : case SPDK_DIF_TYPE3:
2116 : /* For type 3, the computed Reference Tag remains unchanged.
2117 : * Hence ignore the Reference Tag field.
2118 : */
2119 0 : break;
2120 0 : default:
2121 0 : break;
2122 : }
2123 :
2124 : /* Update the stored Reference Tag to the remapped one. */
2125 72 : _dif_set_reftag(&dif, remapped, ctx->dif_pi_format);
2126 :
2127 72 : offset = 0;
2128 162 : while (offset < _dif_size(ctx->dif_pi_format)) {
2129 90 : _dif_sgl_get_buf(&tmp_sgl, &buf, &buf_len);
2130 90 : buf_len = spdk_min(buf_len, _dif_size(ctx->dif_pi_format) - offset);
2131 :
2132 90 : memcpy(buf, (uint8_t *)&dif + offset, buf_len);
2133 :
2134 90 : _dif_sgl_advance(&tmp_sgl, buf_len);
2135 90 : offset += buf_len;
2136 : }
2137 :
2138 72 : end:
2139 72 : _dif_sgl_advance(sgl, ctx->block_size - ctx->guard_interval - _dif_size(ctx->dif_pi_format));
2140 :
2141 72 : return 0;
2142 : }
2143 :
2144 : int
2145 12 : spdk_dif_remap_ref_tag(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
2146 : const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
2147 : {
2148 12 : struct _dif_sgl sgl;
2149 : uint32_t offset_blocks;
2150 : int rc;
2151 :
2152 12 : _dif_sgl_init(&sgl, iovs, iovcnt);
2153 :
2154 12 : if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
2155 0 : SPDK_ERRLOG("Size of iovec array is not valid.\n");
2156 0 : return -EINVAL;
2157 : }
2158 :
2159 12 : if (_dif_is_disabled(ctx->dif_type)) {
2160 0 : return 0;
2161 : }
2162 :
2163 12 : if (!(ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK)) {
2164 0 : return 0;
2165 : }
2166 :
2167 84 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
2168 72 : rc = _dif_remap_ref_tag(&sgl, offset_blocks, ctx, err_blk);
2169 72 : if (rc != 0) {
2170 0 : return rc;
2171 : }
2172 : }
2173 :
2174 12 : return 0;
2175 : }
2176 :
2177 : static int
2178 72 : _dix_remap_ref_tag(struct _dif_sgl *md_sgl, uint32_t offset_blocks,
2179 : const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
2180 : {
2181 72 : uint64_t expected = 0, _actual, remapped;
2182 72 : uint8_t *md_buf;
2183 : struct spdk_dif *dif;
2184 :
2185 72 : _dif_sgl_get_buf(md_sgl, &md_buf, NULL);
2186 :
2187 72 : dif = (struct spdk_dif *)(md_buf + ctx->guard_interval);
2188 :
2189 72 : switch (ctx->dif_type) {
2190 72 : case SPDK_DIF_TYPE1:
2191 : case SPDK_DIF_TYPE2:
2192 : /* If Type 1 or 2 is used, then all DIF checks are disabled when
2193 : * the Application Tag is 0xFFFF.
2194 : */
2195 72 : if (_dif_apptag_ignore(dif, ctx->dif_pi_format)) {
2196 0 : goto end;
2197 : }
2198 72 : break;
2199 0 : case SPDK_DIF_TYPE3:
2200 : /* If Type 3 is used, then all DIF checks are disabled when the
2201 : * Application Tag is 0xFFFF and the Reference Tag is 0xFFFFFFFF
2202 : * or 0xFFFFFFFFFFFFFFFF depending on the PI format.
2203 : */
2204 0 : if (_dif_apptag_ignore(dif, ctx->dif_pi_format) &&
2205 0 : _dif_reftag_ignore(dif, ctx->dif_pi_format)) {
2206 0 : goto end;
2207 : }
2208 0 : break;
2209 0 : default:
2210 0 : break;
2211 : }
2212 :
2213 : /* For type 1 and 2, the Reference Tag is incremented for each
2214 : * subsequent logical block. For type 3, the Reference Tag
2215 : * remains the same as the initialReference Tag.
2216 : */
2217 72 : if (ctx->dif_type != SPDK_DIF_TYPE3) {
2218 72 : expected = ctx->init_ref_tag + ctx->ref_tag_offset + offset_blocks;
2219 72 : remapped = ctx->remapped_init_ref_tag + ctx->ref_tag_offset + offset_blocks;
2220 : } else {
2221 0 : remapped = ctx->remapped_init_ref_tag;
2222 : }
2223 :
2224 : /* Verify the stored Reference Tag. */
2225 72 : switch (ctx->dif_type) {
2226 72 : case SPDK_DIF_TYPE1:
2227 : case SPDK_DIF_TYPE2:
2228 : /* Compare the DIF Reference Tag field to the passed Reference Tag.
2229 : * The passed Reference Tag will be the least significant 4 bytes
2230 : * or 8 bytes (depending on the PI format)
2231 : * of the LBA when Type 1 is used, and application specific value
2232 : * if Type 2 is used.
2233 : */
2234 72 : if (!_dif_reftag_match(dif, expected, ctx->dif_pi_format)) {
2235 0 : _actual = _dif_get_reftag(dif, ctx->dif_pi_format);
2236 0 : _dif_error_set(err_blk, SPDK_DIF_REFTAG_ERROR, expected,
2237 : _actual, offset_blocks);
2238 0 : SPDK_ERRLOG("Failed to compare Ref Tag: LBA=%" PRIu64 "," \
2239 : " Expected=%lx, Actual=%lx\n",
2240 : expected, expected, _actual);
2241 0 : return -1;
2242 : }
2243 72 : break;
2244 0 : case SPDK_DIF_TYPE3:
2245 : /* For type 3, the computed Reference Tag remains unchanged.
2246 : * Hence ignore the Reference Tag field.
2247 : */
2248 0 : break;
2249 0 : default:
2250 0 : break;
2251 : }
2252 :
2253 : /* Update the stored Reference Tag to the remapped one. */
2254 72 : _dif_set_reftag(dif, remapped, ctx->dif_pi_format);
2255 :
2256 72 : end:
2257 72 : _dif_sgl_advance(md_sgl, ctx->md_size);
2258 :
2259 72 : return 0;
2260 : }
2261 :
2262 : int
2263 9 : spdk_dix_remap_ref_tag(struct iovec *md_iov, uint32_t num_blocks,
2264 : const struct spdk_dif_ctx *ctx,
2265 : struct spdk_dif_error *err_blk)
2266 : {
2267 9 : struct _dif_sgl md_sgl;
2268 : uint32_t offset_blocks;
2269 : int rc;
2270 :
2271 9 : _dif_sgl_init(&md_sgl, md_iov, 1);
2272 :
2273 9 : if (!_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
2274 0 : SPDK_ERRLOG("Size of metadata iovec array is not valid.\n");
2275 0 : return -EINVAL;
2276 : }
2277 :
2278 9 : if (_dif_is_disabled(ctx->dif_type)) {
2279 0 : return 0;
2280 : }
2281 :
2282 9 : if (!(ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK)) {
2283 0 : return 0;
2284 : }
2285 :
2286 81 : for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
2287 72 : rc = _dix_remap_ref_tag(&md_sgl, offset_blocks, ctx, err_blk);
2288 72 : if (rc != 0) {
2289 0 : return rc;
2290 : }
2291 : }
2292 :
2293 9 : return 0;
2294 : }
|