diff --git a/include/spdk/dif.h b/include/spdk/dif.h index 578af0c62..ab8d43ef0 100644 --- a/include/spdk/dif.h +++ b/include/spdk/dif.h @@ -110,6 +110,9 @@ struct spdk_dif_ctx { /* Seed value for guard computation */ uint16_t guard_seed; + + /* Remapped initial reference tag. */ + uint32_t remapped_init_ref_tag; }; /** DIF error information */ @@ -161,6 +164,16 @@ int spdk_dif_ctx_init(struct spdk_dif_ctx *ctx, uint32_t block_size, uint32_t md */ void spdk_dif_ctx_set_data_offset(struct spdk_dif_ctx *ctx, uint32_t data_offset); +/** + * Set remapped initial reference tag of DIF context. + * + * \param ctx DIF context. + * \param remapped_init_ref_tag Remapped initial reference tag. For type 1, this is the + * starting block address. + */ +void spdk_dif_ctx_set_remapped_init_ref_tag(struct spdk_dif_ctx *ctx, + uint32_t remapped_init_ref_tag); + /** * Generate DIF for extended LBA payload. * @@ -404,4 +417,23 @@ void spdk_dif_get_range_with_md(uint32_t data_offset, uint32_t data_len, * \return Extended LBA based data length. */ uint32_t spdk_dif_get_length_with_md(uint32_t data_len, const struct spdk_dif_ctx *ctx); + +/** + * Remap reference tag for extended LBA payload. + * + * When using stacked virtual bdev (e.g. split virtual bdev), block address space for I/O + * will be remapped during I/O processing and so reference tag will have to be remapped + * accordingly. This patch is for that case. + * + * \param iovs iovec array describing the extended LBA payload. + * \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_remap_ref_tag(struct iovec *iovs, int iovcnt, uint32_t num_blocks, + const struct spdk_dif_ctx *dif_ctx, + struct spdk_dif_error *err_blk); #endif /* SPDK_DIF_H */ diff --git a/lib/util/dif.c b/lib/util/dif.c index 71e469da6..ba7236986 100644 --- a/lib/util/dif.c +++ b/lib/util/dif.c @@ -157,6 +157,12 @@ _dif_sgl_is_valid(struct _dif_sgl *s, uint32_t bytes) return total >= bytes; } +static void +_dif_sgl_copy(struct _dif_sgl *to, struct _dif_sgl *from) +{ + memcpy(to, from, sizeof(struct _dif_sgl)); +} + static bool _dif_type_is_valid(enum spdk_dif_type dif_type, uint32_t dif_flags) { @@ -261,6 +267,7 @@ spdk_dif_ctx_init(struct spdk_dif_ctx *ctx, uint32_t block_size, uint32_t md_siz ctx->ref_tag_offset = data_offset / data_block_size; ctx->last_guard = guard_seed; ctx->guard_seed = guard_seed; + ctx->remapped_init_ref_tag = 0; return 0; } @@ -280,6 +287,13 @@ spdk_dif_ctx_set_data_offset(struct spdk_dif_ctx *ctx, uint32_t data_offset) ctx->ref_tag_offset = data_offset / data_block_size; } +void +spdk_dif_ctx_set_remapped_init_ref_tag(struct spdk_dif_ctx *ctx, + uint32_t remapped_init_ref_tag) +{ + ctx->remapped_init_ref_tag = remapped_init_ref_tag; +} + static void _dif_generate(void *_dif, uint16_t guard, uint32_t offset_blocks, const struct spdk_dif_ctx *ctx) @@ -1728,3 +1742,142 @@ spdk_dif_get_length_with_md(uint32_t data_len, const struct spdk_dif_ctx *ctx) return _to_size_with_md(data_len, data_block_size, ctx->block_size); } } + +static int +_dif_remap_ref_tag(struct _dif_sgl *sgl, uint32_t offset_blocks, + const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk) +{ + uint32_t offset, buf_len, expected = 0, _actual, remapped; + void *buf; + struct _dif_sgl tmp_sgl; + struct spdk_dif dif; + + /* Fast forward to DIF field. */ + _dif_sgl_advance(sgl, ctx->guard_interval); + _dif_sgl_copy(&tmp_sgl, sgl); + + /* Copy the split DIF field to the temporary DIF buffer */ + offset = 0; + while (offset < sizeof(struct spdk_dif)) { + _dif_sgl_get_buf(sgl, &buf, &buf_len); + buf_len = spdk_min(buf_len, sizeof(struct spdk_dif) - offset); + + memcpy((uint8_t *)&dif + offset, buf, buf_len); + + _dif_sgl_advance(sgl, buf_len); + offset += buf_len; + } + + switch (ctx->dif_type) { + case SPDK_DIF_TYPE1: + case SPDK_DIF_TYPE2: + /* If Type 1 or 2 is used, then all DIF checks are disabled when + * the Application Tag is 0xFFFF. + */ + if (dif.app_tag == 0xFFFF) { + goto end; + } + break; + case SPDK_DIF_TYPE3: + /* If Type 3 is used, then all DIF checks are disabled when the + * Application Tag is 0xFFFF and the Reference Tag is 0xFFFFFFFF. + */ + if (dif.app_tag == 0xFFFF && dif.ref_tag == 0xFFFFFFFF) { + goto end; + } + break; + default: + break; + } + + /* For type 1 and 2, the Reference Tag is incremented for each + * subsequent logical block. For type 3, the Reference Tag + * remains the same as the initial Reference Tag. + */ + if (ctx->dif_type != SPDK_DIF_TYPE3) { + expected = ctx->init_ref_tag + ctx->ref_tag_offset + offset_blocks; + remapped = ctx->remapped_init_ref_tag + ctx->ref_tag_offset + offset_blocks; + } else { + remapped = ctx->remapped_init_ref_tag; + } + + /* Verify the stored Reference Tag. */ + switch (ctx->dif_type) { + case SPDK_DIF_TYPE1: + case SPDK_DIF_TYPE2: + /* Compare the DIF Reference Tag field to the computed Reference Tag. + * The computed Reference Tag will be the least significant 4 bytes + * of the LBA when Type 1 is used, and application specific value + * if Type 2 is used. + */ + _actual = from_be32(&dif.ref_tag); + if (_actual != expected) { + _dif_error_set(err_blk, SPDK_DIF_REFTAG_ERROR, expected, + _actual, offset_blocks); + SPDK_ERRLOG("Failed to compare Ref Tag: LBA=%" PRIu32 "," \ + " Expected=%x, Actual=%x\n", + expected, expected, _actual); + return -1; + } + break; + case SPDK_DIF_TYPE3: + /* For type 3, the computed Reference Tag remains unchanged. + * Hence ignore the Reference Tag field. + */ + break; + default: + break; + } + + /* Update the stored Reference Tag to the remapped one. */ + to_be32(&dif.ref_tag, remapped); + + offset = 0; + while (offset < sizeof(struct spdk_dif)) { + _dif_sgl_get_buf(&tmp_sgl, &buf, &buf_len); + buf_len = spdk_min(buf_len, sizeof(struct spdk_dif) - offset); + + memcpy(buf, (uint8_t *)&dif + offset, buf_len); + + _dif_sgl_advance(&tmp_sgl, buf_len); + offset += buf_len; + } + +end: + _dif_sgl_advance(sgl, ctx->block_size - ctx->guard_interval - sizeof(struct spdk_dif)); + + return 0; +} + +int +spdk_dif_remap_ref_tag(struct iovec *iovs, int iovcnt, uint32_t num_blocks, + const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk) +{ + struct _dif_sgl sgl; + uint32_t offset_blocks; + int rc; + + _dif_sgl_init(&sgl, iovs, iovcnt); + + if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) { + SPDK_ERRLOG("Size of iovec array is not valid.\n"); + return -EINVAL; + } + + if (_dif_is_disabled(ctx->dif_type)) { + return 0; + } + + if (!(ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK)) { + return 0; + } + + for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) { + rc = _dif_remap_ref_tag(&sgl, offset_blocks, ctx, err_blk); + if (rc != 0) { + 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 3e4042548..535ef7698 100644 --- a/test/unit/lib/util/dif.c/dif_ut.c +++ b/test/unit/lib/util/dif.c/dif_ut.c @@ -2373,6 +2373,123 @@ get_range_with_md_test(void) CU_ASSERT(buf_len == 6144 + 128); } +static void +dif_generate_remap_and_verify(struct iovec *iovs, int iovcnt, + uint32_t block_size, uint32_t md_size, uint32_t num_blocks, + bool dif_loc, enum spdk_dif_type dif_type, uint32_t dif_flags, + uint32_t init_ref_tag, uint32_t remapped_init_ref_tag, + uint16_t apptag_mask, uint16_t app_tag) +{ + struct spdk_dif_ctx ctx = {}; + int rc; + + rc = ut_data_pattern_generate(iovs, iovcnt, block_size, md_size, num_blocks); + CU_ASSERT(rc == 0); + + rc = spdk_dif_ctx_init(&ctx, block_size, md_size, true, dif_loc, dif_type, dif_flags, + init_ref_tag, apptag_mask, app_tag, 0, GUARD_SEED); + CU_ASSERT(rc == 0); + + rc = spdk_dif_generate(iovs, iovcnt, num_blocks, &ctx); + CU_ASSERT(rc == 0); + + spdk_dif_ctx_set_remapped_init_ref_tag(&ctx, remapped_init_ref_tag); + + rc = spdk_dif_remap_ref_tag(iovs, iovcnt, num_blocks, &ctx, NULL); + CU_ASSERT(rc == 0); + + rc = spdk_dif_ctx_init(&ctx, block_size, md_size, true, dif_loc, dif_type, dif_flags, + remapped_init_ref_tag, apptag_mask, app_tag, 0, GUARD_SEED); + CU_ASSERT(rc == 0); + + 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); + CU_ASSERT(rc == 0); +} + +static void +dif_sec_4096_md_128_prchk_7_multi_iovs_remap_test(void) +{ + struct iovec iovs[4]; + int i, num_blocks; + uint32_t dif_flags; + + dif_flags = SPDK_DIF_FLAGS_GUARD_CHECK | SPDK_DIF_FLAGS_APPTAG_CHECK | + SPDK_DIF_FLAGS_REFTAG_CHECK; + + num_blocks = 0; + + for (i = 0; i < 4; i++) { + _iov_alloc_buf(&iovs[i], (512 + 8) * (i + 1)); + num_blocks += i + 1; + } + + dif_generate_remap_and_verify(iovs, 4, 512 + 8, 8, num_blocks, false, SPDK_DIF_TYPE1, + dif_flags, 22, 99, 0xFFFF, 0x22); + + dif_generate_remap_and_verify(iovs, 4, 512 + 8, 8, num_blocks, true, SPDK_DIF_TYPE1, + dif_flags, 22, 99, 0xFFFF, 0x22); + + for (i = 0; i < 4; i++) { + _iov_free_buf(&iovs[i]); + } +} + +static void +dif_sec_4096_md_128_prchk_7_multi_iovs_complex_splits_remap_test(void) +{ + struct iovec iovs[11]; + uint32_t dif_flags; + int i; + + dif_flags = SPDK_DIF_FLAGS_GUARD_CHECK | SPDK_DIF_FLAGS_APPTAG_CHECK | + SPDK_DIF_FLAGS_REFTAG_CHECK; + + /* data[0][1000:0] */ + _iov_alloc_buf(&iovs[0], 1000); + + /* data[0][3095:1000], guard[0][0] */ + _iov_alloc_buf(&iovs[1], 3096 + 1); + + /* guard[0][1], apptag[0][0] */ + _iov_alloc_buf(&iovs[2], 1 + 1); + + /* apptag[0][1], reftag[0][0] */ + _iov_alloc_buf(&iovs[3], 1 + 1); + + /* reftag[0][3:1], ignore[0][59:0] */ + _iov_alloc_buf(&iovs[4], 3 + 60); + + /* ignore[119:60], data[1][3050:0] */ + _iov_alloc_buf(&iovs[5], 60 + 3051); + + /* data[1][4095:3050], guard[1][0] */ + _iov_alloc_buf(&iovs[6], 1045 + 1); + + /* guard[1][1], apptag[1][0] */ + _iov_alloc_buf(&iovs[7], 1 + 1); + + /* apptag[1][1], reftag[1][0] */ + _iov_alloc_buf(&iovs[8], 1 + 1); + + /* reftag[1][3:1], ignore[1][9:0] */ + _iov_alloc_buf(&iovs[9], 3 + 10); + + /* ignore[1][127:9] */ + _iov_alloc_buf(&iovs[10], 118); + + dif_generate_remap_and_verify(iovs, 11, 4096 + 128, 128, 2, false, SPDK_DIF_TYPE1, dif_flags, + 22, 99, 0xFFFF, 0x22); + dif_generate_remap_and_verify(iovs, 11, 4096 + 128, 128, 2, true, SPDK_DIF_TYPE1, dif_flags, + 22, 99, 0xFFFF, 0x22); + + for (i = 0; i < 11; i++) { + _iov_free_buf(&iovs[i]); + } +} + int main(int argc, char **argv) { @@ -2474,7 +2591,11 @@ main(int argc, char **argv) _dif_update_crc32c_split_test) == NULL || CU_add_test(suite, "dif_update_crc32c_stream_multi_segments_test", dif_update_crc32c_stream_multi_segments_test) == NULL || - CU_add_test(suite, "get_range_with_md_test", get_range_with_md_test) == NULL + CU_add_test(suite, "get_range_with_md_test", get_range_with_md_test) == NULL || + CU_add_test(suite, "dif_sec_4096_md_128_prchk_7_multi_iovs_remap_test", + dif_sec_4096_md_128_prchk_7_multi_iovs_remap_test) == NULL || + CU_add_test(suite, "dif_sec_4096_md_128_prchk_7_multi_iovs_complex_splits_remap_test", + dif_sec_4096_md_128_prchk_7_multi_iovs_complex_splits_remap_test) == NULL ) { CU_cleanup_registry(); return CU_get_error();