xref: /spdk/lib/util/dif.c (revision c4d9daeb7bf491bc0eb6e8d417b75d44773cb009)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/dif.h"
35 #include "spdk/crc16.h"
36 #include "spdk/endian.h"
37 #include "spdk/log.h"
38 #include "spdk/util.h"
39 
40 /* Context to iterate or create a iovec array.
41  * Each sgl is either iterated or created at a time.
42  */
43 struct _dif_sgl {
44 	/* Current iovec in the iteration or iteration */
45 	struct iovec *iov;
46 
47 	/* Remaining count of iovecs in the iteration or iteration. */
48 	int iovcnt;
49 
50 	/* Current offset in the iovec */
51 	uint32_t iov_offset;
52 
53 	/* Size of the created iovec array in bytes */
54 	uint32_t total_size;
55 };
56 
57 static inline void
58 _dif_sgl_init(struct _dif_sgl *s, struct iovec *iovs, int iovcnt)
59 {
60 	s->iov = iovs;
61 	s->iovcnt = iovcnt;
62 	s->iov_offset = 0;
63 	s->total_size = 0;
64 }
65 
66 static inline void
67 _dif_sgl_advance(struct _dif_sgl *s, uint32_t step)
68 {
69 	s->iov_offset += step;
70 	if (s->iov_offset == s->iov->iov_len) {
71 		s->iov++;
72 		assert(s->iovcnt > 0);
73 		s->iovcnt--;
74 		s->iov_offset = 0;
75 	}
76 }
77 
78 static inline void
79 _dif_sgl_get_buf(struct _dif_sgl *s, void **_buf, uint32_t *_buf_len)
80 {
81 	if (_buf != NULL) {
82 		*_buf = s->iov->iov_base + s->iov_offset;
83 	}
84 	if (_buf_len != NULL) {
85 		*_buf_len = s->iov->iov_len - s->iov_offset;
86 	}
87 }
88 
89 static void
90 _dif_sgl_fast_forward(struct _dif_sgl *s, uint32_t offset)
91 {
92 	s->iov_offset += offset;
93 	while (s->iovcnt != 0) {
94 		if (s->iov_offset < s->iov->iov_len) {
95 			break;
96 		}
97 
98 		s->iov_offset -= s->iov->iov_len;
99 		s->iov++;
100 		s->iovcnt--;
101 	}
102 }
103 
104 static inline bool
105 _dif_sgl_append(struct _dif_sgl *s, uint8_t *data, uint32_t data_len)
106 {
107 	assert(s->iovcnt > 0);
108 	s->iov->iov_base = data;
109 	s->iov->iov_len = data_len;
110 	s->total_size += data_len;
111 	s->iov++;
112 	s->iovcnt--;
113 
114 	if (s->iovcnt > 0) {
115 		return true;
116 	} else {
117 		return false;
118 	}
119 }
120 
121 /* This function must be used before starting iteration. */
122 static bool
123 _dif_sgl_is_bytes_multiple(struct _dif_sgl *s, uint32_t bytes)
124 {
125 	int i;
126 
127 	for (i = 0; i < s->iovcnt; i++) {
128 		if (s->iov[i].iov_len % bytes) {
129 			return false;
130 		}
131 	}
132 
133 	return true;
134 }
135 
136 /* This function must be used before starting iteration. */
137 static bool
138 _dif_sgl_is_valid(struct _dif_sgl *s, uint32_t bytes)
139 {
140 	uint64_t total = 0;
141 	int i;
142 
143 	for (i = 0; i < s->iovcnt; i++) {
144 		total += s->iov[i].iov_len;
145 	}
146 
147 	return total >= bytes;
148 }
149 
150 static bool
151 _dif_type_is_valid(enum spdk_dif_type dif_type, uint32_t dif_flags)
152 {
153 	switch (dif_type) {
154 	case SPDK_DIF_TYPE1:
155 	case SPDK_DIF_TYPE2:
156 	case SPDK_DIF_DISABLE:
157 		break;
158 	case SPDK_DIF_TYPE3:
159 		if (dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK) {
160 			SPDK_ERRLOG("Reference Tag should not be checked for Type 3\n");
161 			return false;
162 		}
163 		break;
164 	default:
165 		SPDK_ERRLOG("Unknown DIF Type: %d\n", dif_type);
166 		return false;
167 	}
168 
169 	return true;
170 }
171 
172 static bool
173 _dif_is_disabled(enum spdk_dif_type dif_type)
174 {
175 	if (dif_type == SPDK_DIF_DISABLE) {
176 		return true;
177 	} else {
178 		return false;
179 	}
180 }
181 
182 
183 static uint32_t
184 _get_guard_interval(uint32_t block_size, uint32_t md_size, bool dif_loc, bool md_interleave)
185 {
186 	if (!dif_loc) {
187 		/* For metadata formats with more than 8 bytes, if the DIF is
188 		 * contained in the last 8 bytes of metadata, then the CRC
189 		 * covers all metadata up to but excluding these last 8 bytes.
190 		 */
191 		if (md_interleave) {
192 			return block_size - sizeof(struct spdk_dif);
193 		} else {
194 			return md_size - sizeof(struct spdk_dif);
195 		}
196 	} else {
197 		/* For metadata formats with more than 8 bytes, if the DIF is
198 		 * contained in the first 8 bytes of metadata, then the CRC
199 		 * does not cover any metadata.
200 		 */
201 		if (md_interleave) {
202 			return block_size - md_size;
203 		} else {
204 			return 0;
205 		}
206 	}
207 }
208 
209 int
210 spdk_dif_ctx_init(struct spdk_dif_ctx *ctx, uint32_t block_size, uint32_t md_size,
211 		  bool md_interleave, bool dif_loc, enum spdk_dif_type dif_type, uint32_t dif_flags,
212 		  uint32_t init_ref_tag, uint16_t apptag_mask, uint16_t app_tag,
213 		  uint16_t guard_seed)
214 {
215 	if (md_size < sizeof(struct spdk_dif)) {
216 		SPDK_ERRLOG("Metadata size is smaller than DIF size.\n");
217 		return -EINVAL;
218 	}
219 
220 	if (md_interleave) {
221 		if (block_size < md_size) {
222 			SPDK_ERRLOG("Block size is smaller than DIF size.\n");
223 			return -EINVAL;
224 		}
225 	} else {
226 		if (block_size == 0 || (block_size % 512) != 0) {
227 			SPDK_ERRLOG("Zero block size is not allowed\n");
228 			return -EINVAL;
229 		}
230 	}
231 
232 	if (!_dif_type_is_valid(dif_type, dif_flags)) {
233 		SPDK_ERRLOG("DIF type is invalid.\n");
234 		return -EINVAL;
235 	}
236 
237 	ctx->block_size = block_size;
238 	ctx->md_size = md_size;
239 	ctx->guard_interval = _get_guard_interval(block_size, md_size, dif_loc, md_interleave);
240 	ctx->dif_type = dif_type;
241 	ctx->dif_flags = dif_flags;
242 	ctx->init_ref_tag = init_ref_tag;
243 	ctx->apptag_mask = apptag_mask;
244 	ctx->app_tag = app_tag;
245 	ctx->guard_seed = guard_seed;
246 
247 	return 0;
248 }
249 
250 static void
251 _dif_generate(void *_dif, uint16_t guard, uint32_t offset_blocks,
252 	      const struct spdk_dif_ctx *ctx)
253 {
254 	struct spdk_dif *dif = _dif;
255 	uint32_t ref_tag;
256 
257 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
258 		to_be16(&dif->guard, guard);
259 	}
260 
261 	if (ctx->dif_flags & SPDK_DIF_FLAGS_APPTAG_CHECK) {
262 		to_be16(&dif->app_tag, ctx->app_tag);
263 	}
264 
265 	if (ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK) {
266 		/* For type 1 and 2, the reference tag is incremented for each
267 		 * subsequent logical block. For type 3, the reference tag
268 		 * remains the same as the initial reference tag.
269 		 */
270 		if (ctx->dif_type != SPDK_DIF_TYPE3) {
271 			ref_tag = ctx->init_ref_tag + offset_blocks;
272 		} else {
273 			ref_tag = ctx->init_ref_tag;
274 		}
275 
276 		to_be32(&dif->ref_tag, ref_tag);
277 	}
278 }
279 
280 static void
281 dif_generate(struct _dif_sgl *sgl, uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
282 {
283 	uint32_t offset_blocks = 0;
284 	void *buf;
285 	uint16_t guard = 0;
286 
287 	while (offset_blocks < num_blocks) {
288 		_dif_sgl_get_buf(sgl, &buf, NULL);
289 
290 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
291 			guard = spdk_crc16_t10dif(ctx->guard_seed, buf, ctx->guard_interval);
292 		}
293 
294 		_dif_generate(buf + ctx->guard_interval, guard, offset_blocks, ctx);
295 
296 		_dif_sgl_advance(sgl, ctx->block_size);
297 		offset_blocks++;
298 	}
299 }
300 
301 static void
302 _dif_generate_split(struct _dif_sgl *sgl, uint32_t offset_blocks,
303 		    const struct spdk_dif_ctx *ctx)
304 {
305 	uint32_t offset_in_block, offset_in_dif, buf_len;
306 	void *buf;
307 	uint16_t guard = 0;
308 	struct spdk_dif dif = {};
309 
310 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
311 		guard = ctx->guard_seed;
312 	}
313 	offset_in_block = 0;
314 
315 	while (offset_in_block < ctx->block_size) {
316 		_dif_sgl_get_buf(sgl, &buf, &buf_len);
317 
318 		if (offset_in_block < ctx->guard_interval) {
319 			buf_len = spdk_min(buf_len, ctx->guard_interval - offset_in_block);
320 
321 			if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
322 				/* Compute CRC over split logical block data. */
323 				guard = spdk_crc16_t10dif(guard, buf, buf_len);
324 			}
325 
326 			if (offset_in_block + buf_len == ctx->guard_interval) {
327 				/* If a whole logical block data is parsed, generate DIF
328 				 * and save it to the temporary DIF area.
329 				 */
330 				_dif_generate(&dif, guard, offset_blocks, ctx);
331 			}
332 		} else if (offset_in_block < ctx->guard_interval + sizeof(struct spdk_dif)) {
333 			/* Copy generated DIF to the split DIF field. */
334 			offset_in_dif = offset_in_block - ctx->guard_interval;
335 			buf_len = spdk_min(buf_len, sizeof(struct spdk_dif) - offset_in_dif);
336 
337 			memcpy(buf, ((uint8_t *)&dif) + offset_in_dif, buf_len);
338 		} else {
339 			/* Skip metadata field after DIF field. */
340 			buf_len = spdk_min(buf_len, ctx->block_size - offset_in_block);
341 		}
342 
343 		_dif_sgl_advance(sgl, buf_len);
344 		offset_in_block += buf_len;
345 	}
346 }
347 
348 static void
349 dif_generate_split(struct _dif_sgl *sgl, uint32_t num_blocks,
350 		   const struct spdk_dif_ctx *ctx)
351 {
352 	uint32_t offset_blocks;
353 
354 	for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
355 		_dif_generate_split(sgl, offset_blocks, ctx);
356 	}
357 }
358 
359 int
360 spdk_dif_generate(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
361 		  const struct spdk_dif_ctx *ctx)
362 {
363 	struct _dif_sgl sgl;
364 
365 	_dif_sgl_init(&sgl, iovs, iovcnt);
366 
367 	if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
368 		SPDK_ERRLOG("Size of iovec array is not valid.\n");
369 		return -EINVAL;
370 	}
371 
372 	if (_dif_is_disabled(ctx->dif_type)) {
373 		return 0;
374 	}
375 
376 	if (_dif_sgl_is_bytes_multiple(&sgl, ctx->block_size)) {
377 		dif_generate(&sgl, num_blocks, ctx);
378 	} else {
379 		dif_generate_split(&sgl, num_blocks, ctx);
380 	}
381 
382 	return 0;
383 }
384 
385 static void
386 _dif_error_set(struct spdk_dif_error *err_blk, uint8_t err_type,
387 	       uint32_t expected, uint32_t actual, uint32_t err_offset)
388 {
389 	if (err_blk) {
390 		err_blk->err_type = err_type;
391 		err_blk->expected = expected;
392 		err_blk->actual = actual;
393 		err_blk->err_offset = err_offset;
394 	}
395 }
396 
397 static int
398 _dif_verify(void *_dif, uint16_t guard, uint32_t offset_blocks,
399 	    const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
400 {
401 	struct spdk_dif *dif = _dif;
402 	uint16_t _guard;
403 	uint16_t _app_tag;
404 	uint32_t ref_tag, _ref_tag;
405 
406 	switch (ctx->dif_type) {
407 	case SPDK_DIF_TYPE1:
408 	case SPDK_DIF_TYPE2:
409 		/* If Type 1 or 2 is used, then all DIF checks are disabled when
410 		 * the Application Tag is 0xFFFF.
411 		 */
412 		if (dif->app_tag == 0xFFFF) {
413 			return 0;
414 		}
415 		break;
416 	case SPDK_DIF_TYPE3:
417 		/* If Type 3 is used, then all DIF checks are disabled when the
418 		 * Application Tag is 0xFFFF and the Reference Tag is 0xFFFFFFFF.
419 		 */
420 		if (dif->app_tag == 0xFFFF && dif->ref_tag == 0xFFFFFFFF) {
421 			return 0;
422 		}
423 		break;
424 	default:
425 		break;
426 	}
427 
428 	/* For type 1 and 2, the reference tag is incremented for each
429 	 * subsequent logical block. For type 3, the reference tag
430 	 * remains the same as the initial reference tag.
431 	 */
432 	if (ctx->dif_type != SPDK_DIF_TYPE3) {
433 		ref_tag = ctx->init_ref_tag + offset_blocks;
434 	} else {
435 		ref_tag = ctx->init_ref_tag;
436 	}
437 
438 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
439 		/* Compare the DIF Guard field to the CRC computed over the logical
440 		 * block data.
441 		 */
442 		_guard = from_be16(&dif->guard);
443 		if (_guard != guard) {
444 			_dif_error_set(err_blk, SPDK_DIF_GUARD_ERROR, _guard, guard,
445 				       offset_blocks);
446 			SPDK_ERRLOG("Failed to compare Guard: LBA=%" PRIu32 "," \
447 				    "  Expected=%x, Actual=%x\n",
448 				    ref_tag, _guard, guard);
449 			return -1;
450 		}
451 	}
452 
453 	if (ctx->dif_flags & SPDK_DIF_FLAGS_APPTAG_CHECK) {
454 		/* Compare unmasked bits in the DIF Application Tag field to the
455 		 * passed Application Tag.
456 		 */
457 		_app_tag = from_be16(&dif->app_tag);
458 		if ((_app_tag & ctx->apptag_mask) != ctx->app_tag) {
459 			_dif_error_set(err_blk, SPDK_DIF_APPTAG_ERROR, ctx->app_tag,
460 				       (_app_tag & ctx->apptag_mask), offset_blocks);
461 			SPDK_ERRLOG("Failed to compare App Tag: LBA=%" PRIu32 "," \
462 				    "  Expected=%x, Actual=%x\n",
463 				    ref_tag, ctx->app_tag, (_app_tag & ctx->apptag_mask));
464 			return -1;
465 		}
466 	}
467 
468 	if (ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK) {
469 		switch (ctx->dif_type) {
470 		case SPDK_DIF_TYPE1:
471 		case SPDK_DIF_TYPE2:
472 			/* Compare the DIF Reference Tag field to the passed Reference Tag.
473 			 * The passed Reference Tag will be the least significant 4 bytes
474 			 * of the LBA when Type 1 is used, and application specific value
475 			 * if Type 2 is used,
476 			 */
477 			_ref_tag = from_be32(&dif->ref_tag);
478 			if (_ref_tag != ref_tag) {
479 				_dif_error_set(err_blk, SPDK_DIF_REFTAG_ERROR, ref_tag,
480 					       _ref_tag, offset_blocks);
481 				SPDK_ERRLOG("Failed to compare Ref Tag: LBA=%" PRIu32 "," \
482 					    " Expected=%x, Actual=%x\n",
483 					    ref_tag, ref_tag, _ref_tag);
484 				return -1;
485 			}
486 			break;
487 		case SPDK_DIF_TYPE3:
488 			/* For Type 3, computed Reference Tag remains unchanged.
489 			 * Hence ignore the Reference Tag field.
490 			 */
491 			break;
492 		default:
493 			break;
494 		}
495 	}
496 
497 	return 0;
498 }
499 
500 static int
501 dif_verify(struct _dif_sgl *sgl, uint32_t num_blocks,
502 	   const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
503 {
504 	uint32_t offset_blocks = 0;
505 	int rc;
506 	void *buf;
507 	uint16_t guard = 0;
508 
509 	while (offset_blocks < num_blocks) {
510 		_dif_sgl_get_buf(sgl, &buf, NULL);
511 
512 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
513 			guard = spdk_crc16_t10dif(ctx->guard_seed, buf, ctx->guard_interval);
514 		}
515 
516 		rc = _dif_verify(buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
517 		if (rc != 0) {
518 			return rc;
519 		}
520 
521 		_dif_sgl_advance(sgl, ctx->block_size);
522 		offset_blocks++;
523 	}
524 
525 	return 0;
526 }
527 
528 static int
529 _dif_verify_split(struct _dif_sgl *sgl, uint32_t offset_blocks,
530 		  const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
531 {
532 	uint32_t offset_in_block, offset_in_dif, buf_len;
533 	void *buf;
534 	uint16_t guard = 0;
535 	struct spdk_dif dif = {};
536 
537 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
538 		guard = ctx->guard_seed;
539 	}
540 	offset_in_block = 0;
541 
542 	while (offset_in_block < ctx->block_size) {
543 		_dif_sgl_get_buf(sgl, &buf, &buf_len);
544 
545 		if (offset_in_block < ctx->guard_interval) {
546 			buf_len = spdk_min(buf_len, ctx->guard_interval - offset_in_block);
547 
548 			if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
549 				/* Compute CRC over split logical block data. */
550 				guard = spdk_crc16_t10dif(guard, buf, buf_len);
551 			}
552 		} else if (offset_in_block < ctx->guard_interval + sizeof(struct spdk_dif)) {
553 			/* Copy the split DIF field to the temporary DIF buffer. */
554 			offset_in_dif = offset_in_block - ctx->guard_interval;
555 			buf_len = spdk_min(buf_len, sizeof(struct spdk_dif) - offset_in_dif);
556 
557 			memcpy((uint8_t *)&dif + offset_in_dif, buf, buf_len);
558 		} else {
559 			/* Skip metadata field after DIF field. */
560 			buf_len = spdk_min(buf_len, ctx->block_size - offset_in_block);
561 		}
562 
563 		_dif_sgl_advance(sgl, buf_len);
564 		offset_in_block += buf_len;
565 	}
566 
567 	return _dif_verify(&dif, guard, offset_blocks, ctx, err_blk);
568 }
569 
570 static int
571 dif_verify_split(struct _dif_sgl *sgl, uint32_t num_blocks,
572 		 const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
573 {
574 	uint32_t offset_blocks;
575 	int rc;
576 
577 	for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
578 		rc = _dif_verify_split(sgl, offset_blocks, ctx, err_blk);
579 		if (rc != 0) {
580 			return rc;
581 		}
582 	}
583 
584 	return 0;
585 }
586 
587 int
588 spdk_dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
589 		const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
590 {
591 	struct _dif_sgl sgl;
592 
593 	_dif_sgl_init(&sgl, iovs, iovcnt);
594 
595 	if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
596 		SPDK_ERRLOG("Size of iovec array is not valid.\n");
597 		return -EINVAL;
598 	}
599 
600 	if (_dif_is_disabled(ctx->dif_type)) {
601 		return 0;
602 	}
603 
604 	if (_dif_sgl_is_bytes_multiple(&sgl, ctx->block_size)) {
605 		return dif_verify(&sgl, num_blocks, ctx, err_blk);
606 	} else {
607 		return dif_verify_split(&sgl, num_blocks, ctx, err_blk);
608 	}
609 }
610 
611 static void
612 dif_generate_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
613 		  uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
614 {
615 	uint32_t offset_blocks = 0, data_block_size;
616 	void *src, *dst;
617 	uint16_t guard;
618 
619 	data_block_size = ctx->block_size - ctx->md_size;
620 
621 	while (offset_blocks < num_blocks) {
622 		_dif_sgl_get_buf(src_sgl, &src, NULL);
623 		_dif_sgl_get_buf(dst_sgl, &dst, NULL);
624 
625 		guard = 0;
626 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
627 			guard = spdk_crc16_t10dif_copy(ctx->guard_seed, dst, src, data_block_size);
628 			guard = spdk_crc16_t10dif(guard, dst + data_block_size,
629 						  ctx->guard_interval - data_block_size);
630 		} else {
631 			memcpy(dst, src, data_block_size);
632 		}
633 
634 		_dif_generate(dst + ctx->guard_interval, guard, offset_blocks, ctx);
635 
636 		_dif_sgl_advance(src_sgl, data_block_size);
637 		_dif_sgl_advance(dst_sgl, ctx->block_size);
638 		offset_blocks++;
639 	}
640 }
641 
642 static void
643 _dif_generate_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
644 			 uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
645 {
646 	uint32_t offset_in_block, src_len, data_block_size;
647 	uint16_t guard = 0;
648 	void *src, *dst;
649 
650 	_dif_sgl_get_buf(dst_sgl, &dst, NULL);
651 
652 	data_block_size = ctx->block_size - ctx->md_size;
653 
654 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
655 		guard = ctx->guard_seed;
656 	}
657 	offset_in_block = 0;
658 
659 	while (offset_in_block < data_block_size) {
660 		/* Compute CRC over split logical block data and copy
661 		 * data to bounce buffer.
662 		 */
663 		_dif_sgl_get_buf(src_sgl, &src, &src_len);
664 		src_len = spdk_min(src_len, data_block_size - offset_in_block);
665 
666 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
667 			guard = spdk_crc16_t10dif_copy(guard, dst + offset_in_block,
668 						       src, src_len);
669 		} else {
670 			memcpy(dst + offset_in_block, src, src_len);
671 		}
672 
673 		_dif_sgl_advance(src_sgl, src_len);
674 		offset_in_block += src_len;
675 	}
676 
677 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
678 		guard = spdk_crc16_t10dif(guard, dst + data_block_size,
679 					  ctx->guard_interval - data_block_size);
680 	}
681 
682 	_dif_sgl_advance(dst_sgl, ctx->block_size);
683 
684 	_dif_generate(dst + ctx->guard_interval, guard, offset_blocks, ctx);
685 }
686 
687 static void
688 dif_generate_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
689 			uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
690 {
691 	uint32_t offset_blocks;
692 
693 	for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
694 		_dif_generate_copy_split(src_sgl, dst_sgl, offset_blocks, ctx);
695 	}
696 }
697 
698 int
699 spdk_dif_generate_copy(struct iovec *iovs, int iovcnt, struct iovec *bounce_iov,
700 		       uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
701 {
702 	struct _dif_sgl src_sgl, dst_sgl;
703 	uint32_t data_block_size;
704 
705 	_dif_sgl_init(&src_sgl, iovs, iovcnt);
706 	_dif_sgl_init(&dst_sgl, bounce_iov, 1);
707 
708 	data_block_size = ctx->block_size - ctx->md_size;
709 
710 	if (!_dif_sgl_is_valid(&src_sgl, data_block_size * num_blocks) ||
711 	    !_dif_sgl_is_valid(&dst_sgl, ctx->block_size * num_blocks)) {
712 		SPDK_ERRLOG("Size of iovec arrays are not valid.\n");
713 		return -EINVAL;
714 	}
715 
716 	if (_dif_is_disabled(ctx->dif_type)) {
717 		return 0;
718 	}
719 
720 	if (_dif_sgl_is_bytes_multiple(&src_sgl, data_block_size)) {
721 		dif_generate_copy(&src_sgl, &dst_sgl, num_blocks, ctx);
722 	} else {
723 		dif_generate_copy_split(&src_sgl, &dst_sgl, num_blocks, ctx);
724 	}
725 
726 	return 0;
727 }
728 
729 static int
730 dif_verify_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
731 		uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
732 		struct spdk_dif_error *err_blk)
733 {
734 	uint32_t offset_blocks = 0, data_block_size;
735 	void *src, *dst;
736 	int rc;
737 	uint16_t guard;
738 
739 	data_block_size = ctx->block_size - ctx->md_size;
740 
741 	while (offset_blocks < num_blocks) {
742 		_dif_sgl_get_buf(src_sgl, &src, NULL);
743 		_dif_sgl_get_buf(dst_sgl, &dst, NULL);
744 
745 		guard = 0;
746 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
747 			guard = spdk_crc16_t10dif_copy(ctx->guard_seed, dst, src, data_block_size);
748 			guard = spdk_crc16_t10dif(guard, src + data_block_size,
749 						  ctx->guard_interval - data_block_size);
750 		} else {
751 			memcpy(dst, src, data_block_size);
752 		}
753 
754 		rc = _dif_verify(src + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
755 		if (rc != 0) {
756 			return rc;
757 		}
758 
759 		_dif_sgl_advance(src_sgl, ctx->block_size);
760 		_dif_sgl_advance(dst_sgl, data_block_size);
761 		offset_blocks++;
762 	}
763 
764 	return 0;
765 }
766 
767 static int
768 _dif_verify_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
769 		       uint32_t offset_blocks, const struct spdk_dif_ctx *ctx,
770 		       struct spdk_dif_error *err_blk)
771 {
772 	uint32_t offset_in_block, dst_len, data_block_size;
773 	uint16_t guard = 0;
774 	void *src, *dst;
775 
776 	_dif_sgl_get_buf(src_sgl, &src, NULL);
777 
778 	data_block_size = ctx->block_size - ctx->md_size;
779 
780 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
781 		guard = ctx->guard_seed;
782 	}
783 	offset_in_block = 0;
784 
785 	while (offset_in_block < data_block_size) {
786 		/* Compute CRC over split logical block data and copy
787 		 * data to bounce buffer.
788 		 */
789 		_dif_sgl_get_buf(dst_sgl, &dst, &dst_len);
790 		dst_len = spdk_min(dst_len, data_block_size - offset_in_block);
791 
792 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
793 			guard = spdk_crc16_t10dif_copy(guard, dst,
794 						       src + offset_in_block, dst_len);
795 		} else {
796 			memcpy(dst, src + offset_in_block, dst_len);
797 		}
798 
799 		_dif_sgl_advance(dst_sgl, dst_len);
800 		offset_in_block += dst_len;
801 	}
802 
803 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
804 		guard = spdk_crc16_t10dif(guard, src + data_block_size,
805 					  ctx->guard_interval - data_block_size);
806 	}
807 
808 	_dif_sgl_advance(src_sgl, ctx->block_size);
809 
810 	return _dif_verify(src + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
811 }
812 
813 static int
814 dif_verify_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
815 		      uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
816 		      struct spdk_dif_error *err_blk)
817 {
818 	uint32_t offset_blocks;
819 	int rc;
820 
821 	for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
822 		rc = _dif_verify_copy_split(src_sgl, dst_sgl, offset_blocks, ctx, err_blk);
823 		if (rc != 0) {
824 			return rc;
825 		}
826 	}
827 
828 	return 0;
829 }
830 
831 int
832 spdk_dif_verify_copy(struct iovec *iovs, int iovcnt, struct iovec *bounce_iov,
833 		     uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
834 		     struct spdk_dif_error *err_blk)
835 {
836 	struct _dif_sgl src_sgl, dst_sgl;
837 	uint32_t data_block_size;
838 
839 	_dif_sgl_init(&src_sgl, bounce_iov, 1);
840 	_dif_sgl_init(&dst_sgl, iovs, iovcnt);
841 
842 	data_block_size = ctx->block_size - ctx->md_size;
843 
844 	if (!_dif_sgl_is_valid(&dst_sgl, data_block_size * num_blocks) ||
845 	    !_dif_sgl_is_valid(&src_sgl, ctx->block_size * num_blocks)) {
846 		SPDK_ERRLOG("Size of iovec arrays are not valid\n");
847 		return -EINVAL;
848 	}
849 
850 	if (_dif_is_disabled(ctx->dif_type)) {
851 		return 0;
852 	}
853 
854 	if (_dif_sgl_is_bytes_multiple(&dst_sgl, data_block_size)) {
855 		return dif_verify_copy(&src_sgl, &dst_sgl, num_blocks, ctx, err_blk);
856 	} else {
857 		return dif_verify_copy_split(&src_sgl, &dst_sgl, num_blocks, ctx, err_blk);
858 	}
859 }
860 
861 static void
862 _bit_flip(uint8_t *buf, uint32_t flip_bit)
863 {
864 	uint8_t byte;
865 
866 	byte = *buf;
867 	byte ^= 1 << flip_bit;
868 	*buf = byte;
869 }
870 
871 static int
872 _dif_inject_error(struct _dif_sgl *sgl,
873 		  uint32_t block_size, uint32_t num_blocks,
874 		  uint32_t inject_offset_blocks,
875 		  uint32_t inject_offset_bytes,
876 		  uint32_t inject_offset_bits)
877 {
878 	uint32_t offset_in_block, buf_len;
879 	void *buf;
880 
881 	_dif_sgl_fast_forward(sgl, block_size * inject_offset_blocks);
882 
883 	offset_in_block = 0;
884 
885 	while (offset_in_block < block_size) {
886 		_dif_sgl_get_buf(sgl, &buf, &buf_len);
887 		buf_len = spdk_min(buf_len, block_size - offset_in_block);
888 
889 		if (inject_offset_bytes >= offset_in_block &&
890 		    inject_offset_bytes < offset_in_block + buf_len) {
891 			buf += inject_offset_bytes - offset_in_block;
892 			_bit_flip(buf, inject_offset_bits);
893 			return 0;
894 		}
895 
896 		_dif_sgl_advance(sgl, buf_len);
897 		offset_in_block += buf_len;
898 	}
899 
900 	return -1;
901 }
902 
903 static int
904 dif_inject_error(struct _dif_sgl *sgl, uint32_t block_size, uint32_t num_blocks,
905 		 uint32_t start_inject_bytes, uint32_t inject_range_bytes,
906 		 uint32_t *inject_offset)
907 {
908 	uint32_t inject_offset_blocks, inject_offset_bytes, inject_offset_bits;
909 	uint32_t offset_blocks;
910 	int rc;
911 
912 	srand(time(0));
913 
914 	inject_offset_blocks = rand() % num_blocks;
915 	inject_offset_bytes = start_inject_bytes + (rand() % inject_range_bytes);
916 	inject_offset_bits = rand() % 8;
917 
918 	for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
919 		if (offset_blocks == inject_offset_blocks) {
920 			rc = _dif_inject_error(sgl, block_size, num_blocks,
921 					       inject_offset_blocks,
922 					       inject_offset_bytes,
923 					       inject_offset_bits);
924 			if (rc == 0) {
925 				*inject_offset = inject_offset_blocks;
926 			}
927 			return rc;
928 		}
929 	}
930 
931 	return -1;
932 }
933 
934 #define _member_size(type, member)	sizeof(((type *)0)->member)
935 
936 int
937 spdk_dif_inject_error(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
938 		      const struct spdk_dif_ctx *ctx, uint32_t inject_flags,
939 		      uint32_t *inject_offset)
940 {
941 	struct _dif_sgl sgl;
942 	int rc;
943 
944 	_dif_sgl_init(&sgl, iovs, iovcnt);
945 
946 	if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
947 		SPDK_ERRLOG("Size of iovec array is not valid.\n");
948 		return -EINVAL;
949 	}
950 
951 	if (inject_flags & SPDK_DIF_REFTAG_ERROR) {
952 		rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
953 				      ctx->guard_interval + offsetof(struct spdk_dif, ref_tag),
954 				      _member_size(struct spdk_dif, ref_tag),
955 				      inject_offset);
956 		if (rc != 0) {
957 			SPDK_ERRLOG("Failed to inject error to Reference Tag.\n");
958 			return rc;
959 		}
960 	}
961 
962 	if (inject_flags & SPDK_DIF_APPTAG_ERROR) {
963 		rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
964 				      ctx->guard_interval + offsetof(struct spdk_dif, app_tag),
965 				      _member_size(struct spdk_dif, app_tag),
966 				      inject_offset);
967 		if (rc != 0) {
968 			SPDK_ERRLOG("Failed to inject error to Application Tag.\n");
969 			return rc;
970 		}
971 	}
972 	if (inject_flags & SPDK_DIF_GUARD_ERROR) {
973 		rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
974 				      ctx->guard_interval,
975 				      _member_size(struct spdk_dif, guard),
976 				      inject_offset);
977 		if (rc != 0) {
978 			SPDK_ERRLOG("Failed to inject error to Guard.\n");
979 			return rc;
980 		}
981 	}
982 
983 	if (inject_flags & SPDK_DIF_DATA_ERROR) {
984 		/* If the DIF information is contained within the last 8 bytes of
985 		 * metadata, then the CRC covers all metadata bytes up to but excluding
986 		 * the last 8 bytes. But error injection does not cover these metadata
987 		 * because classification is not determined yet.
988 		 *
989 		 * Note: Error injection to data block is expected to be detected as
990 		 * guard error.
991 		 */
992 		rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
993 				      0,
994 				      ctx->block_size - ctx->md_size,
995 				      inject_offset);
996 		if (rc != 0) {
997 			SPDK_ERRLOG("Failed to inject error to data block.\n");
998 			return rc;
999 		}
1000 	}
1001 
1002 	return 0;
1003 }
1004 
1005 static void
1006 dix_generate(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1007 	     uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
1008 {
1009 	uint32_t offset_blocks = 0;
1010 	uint16_t guard;
1011 	void *data_buf, *md_buf;
1012 
1013 	while (offset_blocks < num_blocks) {
1014 		_dif_sgl_get_buf(data_sgl, &data_buf, NULL);
1015 		_dif_sgl_get_buf(md_sgl, &md_buf, NULL);
1016 
1017 		guard = 0;
1018 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1019 			guard = spdk_crc16_t10dif(ctx->guard_seed, data_buf, ctx->block_size);
1020 			guard = spdk_crc16_t10dif(guard, md_buf, ctx->guard_interval);
1021 		}
1022 
1023 		_dif_generate(md_buf + ctx->guard_interval, guard, offset_blocks, ctx);
1024 
1025 		_dif_sgl_advance(data_sgl, ctx->block_size);
1026 		_dif_sgl_advance(md_sgl, ctx->md_size);
1027 		offset_blocks++;
1028 	}
1029 }
1030 
1031 static void
1032 _dix_generate_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1033 		    uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
1034 {
1035 	uint32_t offset_in_block, data_buf_len;
1036 	uint16_t guard = 0;
1037 	void *data_buf, *md_buf;
1038 
1039 	_dif_sgl_get_buf(md_sgl, &md_buf, NULL);
1040 
1041 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1042 		guard = ctx->guard_seed;
1043 	}
1044 	offset_in_block = 0;
1045 
1046 	while (offset_in_block < ctx->block_size) {
1047 		_dif_sgl_get_buf(data_sgl, &data_buf, &data_buf_len);
1048 		data_buf_len = spdk_min(data_buf_len, ctx->block_size - offset_in_block);
1049 
1050 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1051 			guard = spdk_crc16_t10dif(guard, data_buf, data_buf_len);
1052 		}
1053 
1054 		_dif_sgl_advance(data_sgl, data_buf_len);
1055 		offset_in_block += data_buf_len;
1056 	}
1057 
1058 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1059 		guard = spdk_crc16_t10dif(guard, md_buf, ctx->guard_interval);
1060 	}
1061 
1062 	_dif_sgl_advance(md_sgl, ctx->md_size);
1063 
1064 	_dif_generate(md_buf + ctx->guard_interval, guard, offset_blocks, ctx);
1065 }
1066 
1067 static void
1068 dix_generate_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1069 		   uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
1070 {
1071 	uint32_t offset_blocks;
1072 
1073 	for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
1074 		_dix_generate_split(data_sgl, md_sgl, offset_blocks, ctx);
1075 	}
1076 }
1077 
1078 int
1079 spdk_dix_generate(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
1080 		  uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
1081 {
1082 	struct _dif_sgl data_sgl, md_sgl;
1083 
1084 	_dif_sgl_init(&data_sgl, iovs, iovcnt);
1085 	_dif_sgl_init(&md_sgl, md_iov, 1);
1086 
1087 	if (!_dif_sgl_is_valid(&data_sgl, ctx->block_size * num_blocks) ||
1088 	    !_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
1089 		SPDK_ERRLOG("Size of iovec array is not valid.\n");
1090 		return -EINVAL;
1091 	}
1092 
1093 	if (_dif_is_disabled(ctx->dif_type)) {
1094 		return 0;
1095 	}
1096 
1097 	if (_dif_sgl_is_bytes_multiple(&data_sgl, ctx->block_size)) {
1098 		dix_generate(&data_sgl, &md_sgl, num_blocks, ctx);
1099 	} else {
1100 		dix_generate_split(&data_sgl, &md_sgl, num_blocks, ctx);
1101 	}
1102 
1103 	return 0;
1104 }
1105 
1106 static int
1107 dix_verify(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1108 	   uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
1109 	   struct spdk_dif_error *err_blk)
1110 {
1111 	uint32_t offset_blocks = 0;
1112 	uint16_t guard;
1113 	void *data_buf, *md_buf;
1114 	int rc;
1115 
1116 	while (offset_blocks < num_blocks) {
1117 		_dif_sgl_get_buf(data_sgl, &data_buf, NULL);
1118 		_dif_sgl_get_buf(md_sgl, &md_buf, NULL);
1119 
1120 		guard = 0;
1121 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1122 			guard = spdk_crc16_t10dif(ctx->guard_seed, data_buf, ctx->block_size);
1123 			guard = spdk_crc16_t10dif(guard, md_buf, ctx->guard_interval);
1124 		}
1125 
1126 		rc = _dif_verify(md_buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
1127 		if (rc != 0) {
1128 			return rc;
1129 		}
1130 
1131 		_dif_sgl_advance(data_sgl, ctx->block_size);
1132 		_dif_sgl_advance(md_sgl, ctx->md_size);
1133 		offset_blocks++;
1134 	}
1135 
1136 	return 0;
1137 }
1138 
1139 static int
1140 _dix_verify_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1141 		  uint32_t offset_blocks, const struct spdk_dif_ctx *ctx,
1142 		  struct spdk_dif_error *err_blk)
1143 {
1144 	uint32_t offset_in_block, data_buf_len;
1145 	uint16_t guard = 0;
1146 	void *data_buf, *md_buf;
1147 
1148 	_dif_sgl_get_buf(md_sgl, &md_buf, NULL);
1149 
1150 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1151 		guard = ctx->guard_seed;
1152 	}
1153 	offset_in_block = 0;
1154 
1155 	while (offset_in_block < ctx->block_size) {
1156 		_dif_sgl_get_buf(data_sgl, &data_buf, &data_buf_len);
1157 		data_buf_len = spdk_min(data_buf_len, ctx->block_size - offset_in_block);
1158 
1159 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1160 			guard = spdk_crc16_t10dif(guard, data_buf, data_buf_len);
1161 		}
1162 
1163 		_dif_sgl_advance(data_sgl, data_buf_len);
1164 		offset_in_block += data_buf_len;
1165 	}
1166 
1167 	if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1168 		guard = spdk_crc16_t10dif(guard, md_buf, ctx->guard_interval);
1169 	}
1170 
1171 	_dif_sgl_advance(md_sgl, ctx->md_size);
1172 
1173 	return _dif_verify(md_buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
1174 }
1175 
1176 static int
1177 dix_verify_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
1178 		 uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
1179 		 struct spdk_dif_error *err_blk)
1180 {
1181 	uint32_t offset_blocks;
1182 	int rc;
1183 
1184 	for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
1185 		rc = _dix_verify_split(data_sgl, md_sgl, offset_blocks, ctx, err_blk);
1186 		if (rc != 0) {
1187 			return rc;
1188 		}
1189 	}
1190 
1191 	return 0;
1192 }
1193 
1194 int
1195 spdk_dix_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
1196 		uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
1197 		struct spdk_dif_error *err_blk)
1198 {
1199 	struct _dif_sgl data_sgl, md_sgl;
1200 
1201 	_dif_sgl_init(&data_sgl, iovs, iovcnt);
1202 	_dif_sgl_init(&md_sgl, md_iov, 1);
1203 
1204 	if (!_dif_sgl_is_valid(&data_sgl, ctx->block_size * num_blocks) ||
1205 	    !_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
1206 		SPDK_ERRLOG("Size of iovec array is not valid.\n");
1207 		return -EINVAL;
1208 	}
1209 
1210 	if (_dif_is_disabled(ctx->dif_type)) {
1211 		return 0;
1212 	}
1213 
1214 	if (_dif_sgl_is_bytes_multiple(&data_sgl, ctx->block_size)) {
1215 		return dix_verify(&data_sgl, &md_sgl, num_blocks, ctx, err_blk);
1216 	} else {
1217 		return dix_verify_split(&data_sgl, &md_sgl, num_blocks, ctx, err_blk);
1218 	}
1219 }
1220 
1221 int
1222 spdk_dix_inject_error(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
1223 		      uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
1224 		      uint32_t inject_flags, uint32_t *inject_offset)
1225 {
1226 	struct _dif_sgl data_sgl, md_sgl;
1227 	int rc;
1228 
1229 	_dif_sgl_init(&data_sgl, iovs, iovcnt);
1230 	_dif_sgl_init(&md_sgl, md_iov, 1);
1231 
1232 	if (!_dif_sgl_is_valid(&data_sgl, ctx->block_size * num_blocks) ||
1233 	    !_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
1234 		SPDK_ERRLOG("Size of iovec array is not valid.\n");
1235 		return -EINVAL;
1236 	}
1237 
1238 	if (inject_flags & SPDK_DIF_REFTAG_ERROR) {
1239 		rc = dif_inject_error(&md_sgl, ctx->md_size, num_blocks,
1240 				      ctx->guard_interval + offsetof(struct spdk_dif, ref_tag),
1241 				      _member_size(struct spdk_dif, ref_tag),
1242 				      inject_offset);
1243 		if (rc != 0) {
1244 			SPDK_ERRLOG("Failed to inject error to Reference Tag.\n");
1245 			return rc;
1246 		}
1247 	}
1248 
1249 	if (inject_flags & SPDK_DIF_APPTAG_ERROR) {
1250 		rc = dif_inject_error(&md_sgl, ctx->md_size, num_blocks,
1251 				      ctx->guard_interval + offsetof(struct spdk_dif, app_tag),
1252 				      _member_size(struct spdk_dif, app_tag),
1253 				      inject_offset);
1254 		if (rc != 0) {
1255 			SPDK_ERRLOG("Failed to inject error to Application Tag.\n");
1256 			return rc;
1257 		}
1258 	}
1259 
1260 	if (inject_flags & SPDK_DIF_GUARD_ERROR) {
1261 		rc = dif_inject_error(&md_sgl, ctx->md_size, num_blocks,
1262 				      ctx->guard_interval,
1263 				      _member_size(struct spdk_dif, guard),
1264 				      inject_offset);
1265 		if (rc != 0) {
1266 			SPDK_ERRLOG("Failed to inject error to Guard.\n");
1267 			return rc;
1268 		}
1269 	}
1270 
1271 	if (inject_flags & SPDK_DIF_DATA_ERROR) {
1272 		/* Note: Error injection to data block is expected to be detected
1273 		 * as guard error.
1274 		 */
1275 		rc = dif_inject_error(&data_sgl, ctx->block_size, num_blocks,
1276 				      0,
1277 				      ctx->block_size,
1278 				      inject_offset);
1279 		if (rc != 0) {
1280 			SPDK_ERRLOG("Failed to inject error to Guard.\n");
1281 			return rc;
1282 		}
1283 	}
1284 
1285 	return 0;
1286 }
1287 
1288 static int
1289 dif_set_md_interleave_iovs(struct iovec *iovs, int iovcnt,
1290 			   uint8_t *buf, uint32_t buf_len,
1291 			   uint32_t data_offset, uint32_t data_len,
1292 			   uint32_t *_mapped_len,
1293 			   const struct spdk_dif_ctx *ctx)
1294 {
1295 	uint32_t data_block_size, head_unalign;
1296 	uint32_t num_blocks, offset_blocks;
1297 	struct _dif_sgl sgl;
1298 
1299 	data_block_size = ctx->block_size - ctx->md_size;
1300 
1301 	num_blocks = data_len / data_block_size;
1302 
1303 	if (buf_len < num_blocks * ctx->block_size) {
1304 		SPDK_ERRLOG("Buffer overflow will occur. Buffer size is %" PRIu32 " but"
1305 			    " necessary size is %" PRIu32 "\n",
1306 			    buf_len, num_blocks * ctx->block_size);
1307 		return -ERANGE;
1308 	}
1309 
1310 	offset_blocks = data_offset / data_block_size;
1311 	head_unalign = data_offset % data_block_size;
1312 
1313 	_dif_sgl_init(&sgl, iovs, iovcnt);
1314 	buf += offset_blocks * ctx->block_size;
1315 
1316 	while (offset_blocks < num_blocks) {
1317 		buf += head_unalign;
1318 
1319 		if (!_dif_sgl_append(&sgl, buf, data_block_size - head_unalign)) {
1320 			break;
1321 		}
1322 
1323 		buf += ctx->block_size - head_unalign;
1324 		offset_blocks++;
1325 
1326 		head_unalign = 0;
1327 	}
1328 
1329 	if (_mapped_len != NULL) {
1330 		*_mapped_len = sgl.total_size;
1331 	}
1332 
1333 	return iovcnt - sgl.iovcnt;
1334 }
1335 
1336 static int
1337 dif_set_md_interleave_iovs_split(struct iovec *iovs, int iovcnt,
1338 				 struct iovec *buf_iovs, int buf_iovcnt,
1339 				 uint32_t data_offset, uint32_t data_len,
1340 				 uint32_t *_mapped_len,
1341 				 const struct spdk_dif_ctx *ctx)
1342 {
1343 	uint32_t data_block_size, head_unalign;
1344 	uint32_t num_blocks, offset_blocks;
1345 	struct _dif_sgl dif_sgl;
1346 	struct _dif_sgl buf_sgl;
1347 	uint8_t *buf;
1348 	uint32_t buf_len, remaining;
1349 
1350 	data_block_size = ctx->block_size - ctx->md_size;
1351 	num_blocks = data_len / data_block_size;
1352 
1353 	_dif_sgl_init(&dif_sgl, iovs, iovcnt);
1354 	_dif_sgl_init(&buf_sgl, buf_iovs, buf_iovcnt);
1355 
1356 	if (!_dif_sgl_is_valid(&buf_sgl, num_blocks * ctx->block_size)) {
1357 		SPDK_ERRLOG("Buffer overflow will occur.\n");
1358 		return -ERANGE;
1359 	}
1360 
1361 	offset_blocks = data_offset / data_block_size;
1362 	head_unalign = data_offset % data_block_size;
1363 
1364 	_dif_sgl_fast_forward(&buf_sgl, offset_blocks * ctx->block_size);
1365 
1366 	while (offset_blocks < num_blocks) {
1367 		_dif_sgl_fast_forward(&buf_sgl, head_unalign);
1368 
1369 		remaining = data_block_size - head_unalign;
1370 		while (remaining != 0) {
1371 			_dif_sgl_get_buf(&buf_sgl, (void *)&buf, &buf_len);
1372 			buf_len = spdk_min(buf_len, remaining);
1373 
1374 			if (!_dif_sgl_append(&dif_sgl, buf, buf_len)) {
1375 				goto end;
1376 			}
1377 			_dif_sgl_advance(&buf_sgl, buf_len);
1378 			remaining -= buf_len;
1379 		}
1380 		_dif_sgl_fast_forward(&buf_sgl, ctx->md_size);
1381 		offset_blocks++;
1382 
1383 		head_unalign = 0;
1384 	}
1385 
1386 end:
1387 	if (_mapped_len != NULL) {
1388 		*_mapped_len = dif_sgl.total_size;
1389 	}
1390 
1391 	return iovcnt - dif_sgl.iovcnt;
1392 }
1393 
1394 int
1395 spdk_dif_set_md_interleave_iovs(struct iovec *iovs, int iovcnt,
1396 				struct iovec *buf_iovs, int buf_iovcnt,
1397 				uint32_t data_offset, uint32_t data_len,
1398 				uint32_t *_mapped_len,
1399 				const struct spdk_dif_ctx *ctx)
1400 {
1401 	uint32_t data_block_size;
1402 
1403 	if (iovs == NULL || iovcnt == 0 || buf_iovs == NULL || buf_iovcnt == 0) {
1404 		return -EINVAL;
1405 	}
1406 
1407 	data_block_size = ctx->block_size - ctx->md_size;
1408 
1409 	if ((data_len % data_block_size) != 0) {
1410 		SPDK_ERRLOG("Data length must be a multiple of data block size\n");
1411 		return -EINVAL;
1412 	}
1413 
1414 	if (data_offset >= data_len) {
1415 		SPDK_ERRLOG("Data offset must be smaller than data length\n");
1416 		return -ERANGE;
1417 	}
1418 
1419 	if (buf_iovcnt == 1) {
1420 		return dif_set_md_interleave_iovs(iovs, iovcnt,
1421 						  buf_iovs[0].iov_base, buf_iovs[0].iov_len,
1422 						  data_offset, data_len, _mapped_len, ctx);
1423 	} else {
1424 		return dif_set_md_interleave_iovs_split(iovs, iovcnt, buf_iovs, buf_iovcnt,
1425 							data_offset, data_len, _mapped_len, ctx);
1426 	}
1427 }
1428 
1429 static int
1430 dif_generate_stream(uint8_t *buf, uint32_t buf_len,
1431 		    uint32_t offset, uint32_t read_len,
1432 		    const struct spdk_dif_ctx *ctx)
1433 {
1434 	uint32_t data_block_size, offset_blocks, num_blocks, i;
1435 	uint16_t guard = 0;
1436 
1437 	data_block_size = ctx->block_size - ctx->md_size;
1438 
1439 	offset_blocks = offset / data_block_size;
1440 	read_len += offset % data_block_size;
1441 
1442 	offset = offset_blocks * ctx->block_size;
1443 	num_blocks = read_len / data_block_size;
1444 
1445 	if (offset + num_blocks * ctx->block_size > buf_len) {
1446 		return -ERANGE;
1447 	}
1448 
1449 	buf += offset;
1450 
1451 	for (i = 0; i < num_blocks; i++) {
1452 		if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
1453 			guard = spdk_crc16_t10dif(ctx->guard_seed, buf, ctx->guard_interval);
1454 		}
1455 
1456 		_dif_generate(buf + ctx->guard_interval, guard, offset_blocks + i, ctx);
1457 
1458 		buf += ctx->block_size;
1459 	}
1460 
1461 	return 0;
1462 }
1463 
1464 static int
1465 dif_generate_stream_split(struct iovec *iovs, int iovcnt,
1466 			  uint32_t offset, uint32_t read_len,
1467 			  const struct spdk_dif_ctx *ctx)
1468 {
1469 	uint32_t data_block_size, offset_blocks, num_blocks, i;
1470 	struct _dif_sgl sgl;
1471 
1472 	data_block_size = ctx->block_size - ctx->md_size;
1473 
1474 	offset_blocks = offset / data_block_size;
1475 	read_len += offset % data_block_size;
1476 
1477 	offset = offset_blocks * ctx->block_size;
1478 	num_blocks = read_len / data_block_size;
1479 
1480 	_dif_sgl_init(&sgl, iovs, iovcnt);
1481 
1482 	if (!_dif_sgl_is_valid(&sgl, offset + num_blocks * ctx->block_size)) {
1483 		return -ERANGE;
1484 	}
1485 
1486 	_dif_sgl_fast_forward(&sgl, offset);
1487 
1488 	for (i = 0; i < num_blocks; i++) {
1489 		_dif_generate_split(&sgl, offset_blocks + i, ctx);
1490 	}
1491 
1492 	return 0;
1493 }
1494 
1495 int
1496 spdk_dif_generate_stream(struct iovec *iovs, int iovcnt,
1497 			 uint32_t offset, uint32_t read_len,
1498 			 const struct spdk_dif_ctx *ctx)
1499 {
1500 	if (iovs == NULL || iovcnt == 0) {
1501 		return -EINVAL;
1502 	}
1503 
1504 	if (iovcnt == 1) {
1505 		return dif_generate_stream(iovs[0].iov_base, iovs[0].iov_len,
1506 					   offset, read_len, ctx);
1507 	} else {
1508 		return dif_generate_stream_split(iovs, iovcnt, offset, read_len, ctx);
1509 	}
1510 }
1511