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