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