diff --git a/include/spdk/dif.h b/include/spdk/dif.h index 613fc6fca..eb1d7351b 100644 --- a/include/spdk/dif.h +++ b/include/spdk/dif.h @@ -87,6 +87,21 @@ struct spdk_dif_ctx { uint16_t apptag_mask; }; +/** DIF error information */ +struct spdk_dif_error { + /** Error type */ + uint8_t err_type; + + /** Expected value */ + uint32_t expected; + + /** Actual value */ + uint32_t actual; + + /** Offset the error occurred at, block based */ + uint32_t err_offset; +}; + /** * Initialize DIF context. * @@ -128,11 +143,12 @@ int spdk_dif_generate(struct iovec *iovs, int iovcnt, uint32_t num_blocks, * \param iovcnt Number of elements in the iovec array. * \param num_blocks Number of blocks of the 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_dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks, - const struct spdk_dif_ctx *ctx); + const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk); /** * Inject bit flip error to extended LBA payload. @@ -141,9 +157,13 @@ int spdk_dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks, * \param iovcnt Number of elements in the iovec array. * \param num_blocks Number of blocks of the payload. * \param ctx DIF context. + * \param inject_flags Flags 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_dif_inject_error(struct iovec *iovs, int iovcnt, uint32_t num_blocks, - const struct spdk_dif_ctx *ctx, uint32_t inject_flags); + 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 15dcc3c39..6bfba6b31 100644 --- a/lib/util/dif.c +++ b/lib/util/dif.c @@ -348,9 +348,21 @@ spdk_dif_generate(struct iovec *iovs, int iovcnt, uint32_t num_blocks, return 0; } +static void +_dif_error_set(struct spdk_dif_error *err_blk, uint8_t err_type, + uint32_t expected, uint32_t actual, uint32_t err_offset) +{ + if (err_blk) { + err_blk->err_type = err_type; + err_blk->expected = expected; + err_blk->actual = actual; + err_blk->err_offset = err_offset; + } +} + static int _dif_verify(void *_dif, uint16_t guard, uint32_t offset_blocks, - const struct spdk_dif_ctx *ctx) + const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk) { struct spdk_dif *dif = _dif; uint16_t _guard; @@ -395,6 +407,8 @@ _dif_verify(void *_dif, uint16_t guard, uint32_t offset_blocks, */ _guard = from_be16(&dif->guard); if (_guard != guard) { + _dif_error_set(err_blk, SPDK_DIF_GUARD_ERROR, _guard, guard, + offset_blocks); SPDK_ERRLOG("Failed to compare Guard: LBA=%" PRIu32 "," \ " Expected=%x, Actual=%x\n", ref_tag, _guard, guard); @@ -408,6 +422,8 @@ _dif_verify(void *_dif, uint16_t guard, uint32_t offset_blocks, */ _app_tag = from_be16(&dif->app_tag); if ((_app_tag & ctx->apptag_mask) != ctx->app_tag) { + _dif_error_set(err_blk, SPDK_DIF_APPTAG_ERROR, ctx->app_tag, + (_app_tag & ctx->apptag_mask), offset_blocks); SPDK_ERRLOG("Failed to compare App Tag: LBA=%" PRIu32 "," \ " Expected=%x, Actual=%x\n", ref_tag, ctx->app_tag, (_app_tag & ctx->apptag_mask)); @@ -426,6 +442,8 @@ _dif_verify(void *_dif, uint16_t guard, uint32_t offset_blocks, */ _ref_tag = from_be32(&dif->ref_tag); if (_ref_tag != ref_tag) { + _dif_error_set(err_blk, SPDK_DIF_REFTAG_ERROR, ref_tag, + _ref_tag, offset_blocks); SPDK_ERRLOG("Failed to compare Ref Tag: LBA=%" PRIu32 "," \ " Expected=%x, Actual=%x\n", ref_tag, ref_tag, _ref_tag); @@ -447,7 +465,7 @@ _dif_verify(void *_dif, uint16_t guard, uint32_t offset_blocks, static int dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks, - const struct spdk_dif_ctx *ctx) + const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk) { struct _iov_iter iter; uint32_t offset_blocks; @@ -465,7 +483,7 @@ dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks, guard = spdk_crc16_t10dif(0, buf, ctx->guard_interval); } - rc = _dif_verify(buf + ctx->guard_interval, guard, offset_blocks, ctx); + rc = _dif_verify(buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk); if (rc != 0) { return rc; } @@ -479,7 +497,7 @@ dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks, static int _dif_verify_split(struct _iov_iter *iter, uint32_t offset_blocks, - const struct spdk_dif_ctx *ctx) + const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk) { uint32_t offset_in_block, offset_in_dif, buf_len; void *buf; @@ -514,12 +532,12 @@ _dif_verify_split(struct _iov_iter *iter, uint32_t offset_blocks, offset_in_block += buf_len; } - return _dif_verify(&dif, guard, offset_blocks, ctx); + return _dif_verify(&dif, guard, offset_blocks, ctx, err_blk); } static int dif_verify_split(struct iovec *iovs, int iovcnt, uint32_t num_blocks, - const struct spdk_dif_ctx *ctx) + const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk) { struct _iov_iter iter; uint32_t offset_blocks; @@ -529,7 +547,7 @@ dif_verify_split(struct iovec *iovs, int iovcnt, uint32_t num_blocks, _iov_iter_init(&iter, iovs, iovcnt); while (offset_blocks < num_blocks && _iov_iter_cont(&iter)) { - rc = _dif_verify_split(&iter, offset_blocks, ctx); + rc = _dif_verify_split(&iter, offset_blocks, ctx, err_blk); if (rc != 0) { return rc; } @@ -541,7 +559,7 @@ dif_verify_split(struct iovec *iovs, int iovcnt, uint32_t num_blocks, int spdk_dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks, - const struct spdk_dif_ctx *ctx) + const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk) { if (!_are_iovs_valid(iovs, iovcnt, ctx->block_size * num_blocks)) { SPDK_ERRLOG("Size of iovec array is not valid.\n"); @@ -553,9 +571,9 @@ spdk_dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks, } if (_are_iovs_bytes_multiple(iovs, iovcnt, ctx->block_size)) { - return dif_verify(iovs, iovcnt, num_blocks, ctx); + return dif_verify(iovs, iovcnt, num_blocks, ctx, err_blk); } else { - return dif_verify_split(iovs, iovcnt, num_blocks, ctx); + return dif_verify_split(iovs, iovcnt, num_blocks, ctx, err_blk); } } @@ -607,10 +625,12 @@ _dif_inject_error(struct iovec *iovs, int iovcnt, static int dif_inject_error(struct iovec *iovs, int iovcnt, uint32_t block_size, uint32_t num_blocks, - uint32_t start_inject_bytes, uint32_t inject_range_bytes) + uint32_t start_inject_bytes, uint32_t inject_range_bytes, + uint32_t *inject_offset) { uint32_t inject_offset_blocks, inject_offset_bytes, inject_offset_bits; uint32_t offset_blocks; + int rc; srand(time(0)); @@ -620,10 +640,14 @@ dif_inject_error(struct iovec *iovs, int iovcnt, for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) { if (offset_blocks == inject_offset_blocks) { - return _dif_inject_error(iovs, iovcnt, block_size, num_blocks, - inject_offset_blocks, - inject_offset_bytes, - inject_offset_bits); + rc = _dif_inject_error(iovs, iovcnt, block_size, num_blocks, + inject_offset_blocks, + inject_offset_bytes, + inject_offset_bits); + if (rc == 0) { + *inject_offset = inject_offset_blocks; + } + return rc; } } @@ -634,7 +658,8 @@ dif_inject_error(struct iovec *iovs, int iovcnt, int spdk_dif_inject_error(struct iovec *iovs, int iovcnt, uint32_t num_blocks, - const struct spdk_dif_ctx *ctx, uint32_t inject_flags) + const struct spdk_dif_ctx *ctx, uint32_t inject_flags, + uint32_t *inject_offset) { int rc; @@ -646,7 +671,8 @@ spdk_dif_inject_error(struct iovec *iovs, int iovcnt, uint32_t num_blocks, if (inject_flags & SPDK_DIF_REFTAG_ERROR) { rc = dif_inject_error(iovs, iovcnt, ctx->block_size, num_blocks, ctx->guard_interval + offsetof(struct spdk_dif, ref_tag), - _member_size(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; @@ -656,7 +682,8 @@ spdk_dif_inject_error(struct iovec *iovs, int iovcnt, uint32_t num_blocks, if (inject_flags & SPDK_DIF_APPTAG_ERROR) { rc = dif_inject_error(iovs, iovcnt, ctx->block_size, num_blocks, ctx->guard_interval + offsetof(struct spdk_dif, app_tag), - _member_size(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; @@ -665,7 +692,8 @@ spdk_dif_inject_error(struct iovec *iovs, int iovcnt, uint32_t num_blocks, if (inject_flags & SPDK_DIF_GUARD_ERROR) { rc = dif_inject_error(iovs, iovcnt, ctx->block_size, num_blocks, ctx->guard_interval, - _member_size(struct spdk_dif, guard)); + _member_size(struct spdk_dif, guard), + inject_offset); if (rc != 0) { SPDK_ERRLOG("Failed to inject error to Guard.\n"); return rc; @@ -677,10 +705,14 @@ spdk_dif_inject_error(struct iovec *iovs, int iovcnt, uint32_t num_blocks, * metadata, then the CRC covers all metadata bytes up to but excluding * the last 8 bytes. But error injection does not cover these metadata * because classification is not determined yet. + * + * 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 - ctx->md_size); + ctx->block_size - ctx->md_size, + inject_offset); if (rc != 0) { SPDK_ERRLOG("Failed to inject error to data block.\n"); return rc; diff --git a/test/unit/lib/util/dif.c/dif_ut.c b/test/unit/lib/util/dif.c/dif_ut.c index 515c9b704..d5cbd2641 100644 --- a/test/unit/lib/util/dif.c/dif_ut.c +++ b/test/unit/lib/util/dif.c/dif_ut.c @@ -162,7 +162,7 @@ _dif_generate_and_verify(struct iovec *iov, ctx.apptag_mask = apptag_mask; ctx.app_tag = e_app_tag; - rc = _dif_verify(iov->iov_base + guard_interval, guard, 0, &ctx); + rc = _dif_verify(iov->iov_base + guard_interval, guard, 0, &ctx, NULL); CU_ASSERT((expect_pass && rc == 0) || (!expect_pass && rc != 0)); rc = ut_data_pattern_verify(iov, 1, block_size, md_size, 1); @@ -293,7 +293,7 @@ dif_generate_and_verify(struct iovec *iovs, int iovcnt, rc = spdk_dif_generate(iovs, iovcnt, num_blocks, &ctx); CU_ASSERT(rc == 0); - rc = spdk_dif_verify(iovs, iovcnt, num_blocks, &ctx); + rc = spdk_dif_verify(iovs, iovcnt, num_blocks, &ctx, NULL); CU_ASSERT(rc == 0); rc = ut_data_pattern_verify(iovs, iovcnt, block_size, md_size, num_blocks); @@ -573,7 +573,8 @@ _dif_inject_error_and_verify(struct iovec *iovs, int iovcnt, uint32_t inject_flags, bool dif_loc) { struct spdk_dif_ctx ctx = {}; - uint32_t dif_flags; + 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; @@ -588,15 +589,21 @@ _dif_inject_error_and_verify(struct iovec *iovs, int iovcnt, rc = spdk_dif_generate(iovs, iovcnt, num_blocks, &ctx); CU_ASSERT(rc == 0); - rc = spdk_dif_inject_error(iovs, iovcnt, num_blocks, &ctx, inject_flags); + rc = spdk_dif_inject_error(iovs, iovcnt, num_blocks, &ctx, inject_flags, &inject_offset); CU_ASSERT(rc == 0); - rc = spdk_dif_verify(iovs, iovcnt, num_blocks, &ctx); + rc = spdk_dif_verify(iovs, iovcnt, 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); rc = ut_data_pattern_verify(iovs, iovcnt, block_size, md_size, num_blocks); - CU_ASSERT((rc == 0 && !(inject_flags & SPDK_DIF_DATA_ERROR)) || - (rc != 0 && (inject_flags & SPDK_DIF_DATA_ERROR))); + CU_ASSERT((rc == 0 && (inject_flags != SPDK_DIF_DATA_ERROR)) || + (rc != 0 && (inject_flags == SPDK_DIF_DATA_ERROR))); } static void