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