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