diff --git a/include/spdk/dif.h b/include/spdk/dif.h index d64fd4625..684afad4c 100644 --- a/include/spdk/dif.h +++ b/include/spdk/dif.h @@ -221,9 +221,29 @@ int spdk_dix_generate(struct iovec *iovs, int iovcnt, struct iovec *md_iov, * \param md_iov A contiguous buffer for metadata. * \param num_blocks Number of blocks of the separate metadata payload. * \param ctx DIF context. + * \param err_blk Error information of the block in which DIF error is found. * * \return 0 on success and negated errno otherwise. */ int spdk_dix_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov, - uint32_t num_blocks, const struct spdk_dif_ctx *ctx); + uint32_t num_blocks, const struct spdk_dif_ctx *ctx, + struct spdk_dif_error *err_blk); + +/** + * Inject bit flip error to separate metadata payload. + * + * \param iovs iovec array describing the extended LBA payload. + * \param iovcnt Number of elements in the iovec array. + * \param md_iov A contiguous buffer for metadata. + * \param num_blocks Number of blocks of the payload. + * \param ctx DIF context. + * \param inject_flags Flag to specify the action of error injection. + * \param inject_offset Offset, in blocks, to which error is injected. + * If multiple error is injected, only the last injection is stored. + * + * \return 0 on success and negated errno otherwise including no metadata. + */ +int spdk_dix_inject_error(struct iovec *iovs, int iovcnt, struct iovec *md_iov, + uint32_t num_blocks, const struct spdk_dif_ctx *ctx, + uint32_t inject_flags, uint32_t *inject_offset); #endif /* SPDK_DIF_H */ diff --git a/lib/util/dif.c b/lib/util/dif.c index 099981ce5..ef9004f25 100644 --- a/lib/util/dif.c +++ b/lib/util/dif.c @@ -1114,7 +1114,8 @@ spdk_dix_generate(struct iovec *iovs, int iovcnt, struct iovec *md_iov, static int dix_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov, - uint32_t num_blocks, const struct spdk_dif_ctx *ctx) + uint32_t num_blocks, const struct spdk_dif_ctx *ctx, + struct spdk_dif_error *err_blk) { struct _iov_iter data_iter, md_iter; uint32_t offset_blocks; @@ -1138,7 +1139,7 @@ dix_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov, guard = spdk_crc16_t10dif(guard, md_buf, ctx->guard_interval); } - rc = _dif_verify(md_buf + ctx->guard_interval, guard, offset_blocks, ctx, NULL); + rc = _dif_verify(md_buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk); if (rc != 0) { return rc; } @@ -1153,7 +1154,8 @@ dix_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov, static int _dix_verify_split(struct _iov_iter *data_iter, struct _iov_iter *md_iter, - uint32_t offset_blocks, const struct spdk_dif_ctx *ctx) + uint32_t offset_blocks, const struct spdk_dif_ctx *ctx, + struct spdk_dif_error *err_blk) { uint32_t offset_in_block, data_buf_len; uint16_t guard; @@ -1182,12 +1184,13 @@ _dix_verify_split(struct _iov_iter *data_iter, struct _iov_iter *md_iter, _iov_iter_advance(md_iter, ctx->md_size); - return _dif_verify(md_buf + ctx->guard_interval, guard, offset_blocks, ctx, NULL); + return _dif_verify(md_buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk); } static int dix_verify_split(struct iovec *iovs, int iovcnt, struct iovec *md_iov, - uint32_t num_blocks, const struct spdk_dif_ctx *ctx) + uint32_t num_blocks, const struct spdk_dif_ctx *ctx, + struct spdk_dif_error *err_blk) { struct _iov_iter data_iter, md_iter; uint32_t offset_blocks; @@ -1199,7 +1202,7 @@ dix_verify_split(struct iovec *iovs, int iovcnt, struct iovec *md_iov, while (offset_blocks < num_blocks && _iov_iter_cont(&data_iter) && _iov_iter_cont(&md_iter)) { - rc = _dix_verify_split(&data_iter, &md_iter, offset_blocks, ctx); + rc = _dix_verify_split(&data_iter, &md_iter, offset_blocks, ctx, err_blk); if (rc != 0) { return rc; } @@ -1211,7 +1214,8 @@ dix_verify_split(struct iovec *iovs, int iovcnt, struct iovec *md_iov, int spdk_dix_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov, - uint32_t num_blocks, const struct spdk_dif_ctx *ctx) + uint32_t num_blocks, const struct spdk_dif_ctx *ctx, + struct spdk_dif_error *err_blk) { if (!_are_iovs_valid(iovs, iovcnt, ctx->block_size * num_blocks) || !_are_iovs_valid(md_iov, 1, ctx->md_size * num_blocks)) { @@ -1224,8 +1228,71 @@ spdk_dix_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov, } if (_are_iovs_bytes_multiple(iovs, iovcnt, ctx->block_size)) { - return dix_verify(iovs, iovcnt, md_iov, num_blocks, ctx); + return dix_verify(iovs, iovcnt, md_iov, num_blocks, ctx, err_blk); } else { - return dix_verify_split(iovs, iovcnt, md_iov, num_blocks, ctx); + return dix_verify_split(iovs, iovcnt, md_iov, num_blocks, ctx, err_blk); } } + +int +spdk_dix_inject_error(struct iovec *iovs, int iovcnt, struct iovec *md_iov, + uint32_t num_blocks, const struct spdk_dif_ctx *ctx, + uint32_t inject_flags, uint32_t *inject_offset) +{ + int rc; + + if (!_are_iovs_valid(iovs, iovcnt, ctx->block_size * num_blocks) || + !_are_iovs_valid(md_iov, 1, ctx->md_size * num_blocks)) { + SPDK_ERRLOG("Size of iovec array is not valid.\n"); + return -EINVAL; + } + + if (inject_flags & SPDK_DIF_REFTAG_ERROR) { + rc = dif_inject_error(md_iov, 1, ctx->md_size, num_blocks, + ctx->guard_interval + offsetof(struct spdk_dif, ref_tag), + _member_size(struct spdk_dif, ref_tag), + inject_offset); + if (rc != 0) { + SPDK_ERRLOG("Failed to inject error to Reference Tag.\n"); + return rc; + } + } + + if (inject_flags & SPDK_DIF_APPTAG_ERROR) { + rc = dif_inject_error(md_iov, 1, ctx->md_size, num_blocks, + ctx->guard_interval + offsetof(struct spdk_dif, app_tag), + _member_size(struct spdk_dif, app_tag), + inject_offset); + if (rc != 0) { + SPDK_ERRLOG("Failed to inject error to Application Tag.\n"); + return rc; + } + } + + if (inject_flags & SPDK_DIF_GUARD_ERROR) { + rc = dif_inject_error(md_iov, 1, ctx->md_size, num_blocks, + ctx->guard_interval, + _member_size(struct spdk_dif, guard), + inject_offset); + if (rc != 0) { + SPDK_ERRLOG("Failed to inject error to Guard.\n"); + return rc; + } + } + + if (inject_flags & SPDK_DIF_DATA_ERROR) { + /* Note: Error injection to data block is expected to be detected + * as guard error. + */ + rc = dif_inject_error(iovs, iovcnt, ctx->block_size, num_blocks, + 0, + ctx->block_size, + inject_offset); + if (rc != 0) { + SPDK_ERRLOG("Failed to inject error to Guard.\n"); + return rc; + } + } + + return 0; +} diff --git a/test/unit/lib/util/dif.c/dif_ut.c b/test/unit/lib/util/dif.c/dif_ut.c index 90399583f..4acfde2e2 100644 --- a/test/unit/lib/util/dif.c/dif_ut.c +++ b/test/unit/lib/util/dif.c/dif_ut.c @@ -1036,7 +1036,7 @@ dix_generate_and_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov, rc = spdk_dix_generate(iovs, iovcnt, md_iov, num_blocks, &ctx); CU_ASSERT(rc == 0); - rc = spdk_dix_verify(iovs, iovcnt, md_iov, num_blocks, &ctx); + rc = spdk_dix_verify(iovs, iovcnt, md_iov, num_blocks, &ctx, NULL); CU_ASSERT(rc == 0); rc = ut_data_pattern_verify(iovs, iovcnt, block_size, 0, num_blocks); @@ -1176,6 +1176,106 @@ dix_sec_512_md_8_prchk_7_multi_iovs_complex_splits(void) _iov_free_buf(&md_iov); } +static void +_dix_inject_error_and_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov, + uint32_t block_size, uint32_t md_size, uint32_t num_blocks, + uint32_t inject_flags, bool dif_loc) +{ + struct spdk_dif_ctx ctx = {}; + struct spdk_dif_error err_blk = {}; + uint32_t inject_offset = 0, dif_flags; + int rc; + + dif_flags = SPDK_DIF_GUARD_CHECK | SPDK_DIF_APPTAG_CHECK | SPDK_DIF_REFTAG_CHECK; + + rc = ut_data_pattern_generate(iovs, iovcnt, block_size, 0, num_blocks); + CU_ASSERT(rc == 0); + + rc = spdk_dif_ctx_init(&ctx, block_size, md_size, false, dif_loc, SPDK_DIF_TYPE1, dif_flags, + 88, 0xFFFF, 0x88); + CU_ASSERT(rc == 0); + + rc = spdk_dix_generate(iovs, iovcnt, md_iov, num_blocks, &ctx); + CU_ASSERT(rc == 0); + + rc = spdk_dix_inject_error(iovs, iovcnt, md_iov, num_blocks, &ctx, inject_flags, &inject_offset); + CU_ASSERT(rc == 0); + + rc = spdk_dix_verify(iovs, iovcnt, md_iov, num_blocks, &ctx, &err_blk); + CU_ASSERT(rc != 0); + + if (inject_flags == SPDK_DIF_DATA_ERROR) { + CU_ASSERT(SPDK_DIF_GUARD_ERROR == err_blk.err_type); + } else { + CU_ASSERT(inject_flags == err_blk.err_type); + } + CU_ASSERT(inject_offset == err_blk.err_offset); +} + +static void +dix_inject_error_and_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov, + uint32_t block_size, uint32_t md_size, uint32_t num_blocks, + uint32_t inject_flags) +{ + /* The case that DIF is contained in the first 8 bytes of metadata. */ + _dix_inject_error_and_verify(iovs, iovcnt, md_iov, block_size, md_size, num_blocks, + inject_flags, false); + + /* The case that DIF is contained in the last 8 bytes of metadata. */ + _dix_inject_error_and_verify(iovs, iovcnt, md_iov, block_size, md_size, num_blocks, + inject_flags, true); +} + +static void +dix_sec_4096_md_128_inject_1_2_4_8_multi_iovs_test(void) +{ + struct iovec iovs[4], md_iov; + int i, num_blocks; + + num_blocks = 0; + + for (i = 0; i < 4; i++) { + _iov_alloc_buf(&iovs[i], 4096 * (i + 1)); + num_blocks += i + 1; + } + + _iov_alloc_buf(&md_iov, 128 * num_blocks); + + dix_inject_error_and_verify(iovs, 4, &md_iov, 4096, 128, num_blocks, SPDK_DIF_GUARD_ERROR); + dix_inject_error_and_verify(iovs, 4, &md_iov, 4096, 128, num_blocks, SPDK_DIF_APPTAG_ERROR); + dix_inject_error_and_verify(iovs, 4, &md_iov, 4096, 128, num_blocks, SPDK_DIF_REFTAG_ERROR); + dix_inject_error_and_verify(iovs, 4, &md_iov, 4096, 128, num_blocks, SPDK_DIF_DATA_ERROR); + + for (i = 0; i < 4; i++) { + _iov_free_buf(&iovs[i]); + } + _iov_free_buf(&md_iov); +} + +static void +dix_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_test(void) +{ + struct iovec iovs[4], md_iov; + int i; + + _iov_alloc_buf(&iovs[0], 2048); + _iov_alloc_buf(&iovs[1], 2048); + _iov_alloc_buf(&iovs[2], 1); + _iov_alloc_buf(&iovs[3], 4095); + + _iov_alloc_buf(&md_iov, 128 * 2); + + dix_inject_error_and_verify(iovs, 4, &md_iov, 4096, 128, 2, SPDK_DIF_GUARD_ERROR); + dix_inject_error_and_verify(iovs, 4, &md_iov, 4096, 128, 2, SPDK_DIF_APPTAG_ERROR); + dix_inject_error_and_verify(iovs, 4, &md_iov, 4096, 128, 2, SPDK_DIF_REFTAG_ERROR); + dix_inject_error_and_verify(iovs, 4, &md_iov, 4096, 128, 2, SPDK_DIF_DATA_ERROR); + + for (i = 0; i < 4; i++) { + _iov_free_buf(&iovs[i]); + } + _iov_free_buf(&md_iov); +} + int main(int argc, char **argv) { @@ -1254,7 +1354,11 @@ main(int argc, char **argv) CU_add_test(suite, "dix_sec_512_md_8_prchk_7_multi_iovs_split_data", dix_sec_512_md_8_prchk_7_multi_iovs_split_data) == NULL || CU_add_test(suite, "dix_sec_512_md_8_prchk_7_multi_iovs_complex_splits", - dix_sec_512_md_8_prchk_7_multi_iovs_complex_splits) == NULL + dix_sec_512_md_8_prchk_7_multi_iovs_complex_splits) == NULL || + CU_add_test(suite, "dix_sec_4096_md_128_inject_1_2_4_8_multi_iovs_test", + dix_sec_4096_md_128_inject_1_2_4_8_multi_iovs_test) == NULL || + CU_add_test(suite, "dix_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_test", + dix_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_test) == NULL ) { CU_cleanup_registry(); return CU_get_error();