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