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