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