diff --git a/include/spdk/dif.h b/include/spdk/dif.h index 06d279396..21c924a3a 100644 --- a/include/spdk/dif.h +++ b/include/spdk/dif.h @@ -163,6 +163,20 @@ int spdk_dif_generate(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, struct spdk_dif_error *err_blk); +/** + * Calculate CRC-32C checksum for extended LBA payload. + * + * \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 crc32c Initial and updated CRC-32C value. + * \param ctx DIF context. + * + * \return 0 on success and negated errno otherwise. + */ +int spdk_dif_update_crc32c(struct iovec *iovs, int iovcnt, uint32_t num_blocks, + uint32_t *crc32c, const struct spdk_dif_ctx *ctx); + /** * Copy data and generate DIF for extended LBA payload. * diff --git a/lib/util/dif.c b/lib/util/dif.c index 2f9385432..72d2416e1 100644 --- a/lib/util/dif.c +++ b/lib/util/dif.c @@ -33,6 +33,7 @@ #include "spdk/dif.h" #include "spdk/crc16.h" +#include "spdk/crc32.h" #include "spdk/endian.h" #include "spdk/log.h" #include "spdk/util.h" @@ -597,6 +598,90 @@ spdk_dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks, } } +static uint32_t +dif_update_crc32c(struct _dif_sgl *sgl, uint32_t num_blocks, + uint32_t crc32c, const struct spdk_dif_ctx *ctx) +{ + uint32_t offset_blocks; + void *buf; + + for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) { + _dif_sgl_get_buf(sgl, &buf, NULL); + + crc32c = spdk_crc32c_update(buf, ctx->block_size - ctx->md_size, crc32c); + + _dif_sgl_advance(sgl, ctx->block_size); + } + + return crc32c; +} + +static uint32_t +_dif_update_crc32c_split(struct _dif_sgl *sgl, uint32_t crc32c, + const struct spdk_dif_ctx *ctx) +{ + uint32_t data_block_size, offset_in_block, buf_len; + void *buf; + + data_block_size = ctx->block_size - ctx->md_size; + offset_in_block = 0; + + while (offset_in_block < ctx->block_size) { + _dif_sgl_get_buf(sgl, &buf, &buf_len); + + if (offset_in_block < data_block_size) { + buf_len = spdk_min(buf_len, data_block_size - offset_in_block); + crc32c = spdk_crc32c_update(buf, buf_len, crc32c); + } else { + buf_len = spdk_min(buf_len, ctx->block_size - offset_in_block); + } + + _dif_sgl_advance(sgl, buf_len); + offset_in_block += buf_len; + } + + return crc32c; +} + +static uint32_t +dif_update_crc32c_split(struct _dif_sgl *sgl, uint32_t num_blocks, + uint32_t crc32c, const struct spdk_dif_ctx *ctx) +{ + uint32_t offset_blocks; + + for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) { + crc32c = _dif_update_crc32c_split(sgl, crc32c, ctx); + } + + return crc32c; +} + +int +spdk_dif_update_crc32c(struct iovec *iovs, int iovcnt, uint32_t num_blocks, + uint32_t *_crc32c, const struct spdk_dif_ctx *ctx) +{ + struct _dif_sgl sgl; + + if (_crc32c == NULL) { + return -EINVAL; + } + + _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_sgl_is_bytes_multiple(&sgl, ctx->block_size)) { + *_crc32c = dif_update_crc32c(&sgl, num_blocks, *_crc32c, ctx); + } else { + *_crc32c = dif_update_crc32c_split(&sgl, num_blocks, *_crc32c, ctx); + } + + return 0; +} + static void dif_generate_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl, uint32_t num_blocks, const struct spdk_dif_ctx *ctx) diff --git a/test/unit/lib/util/dif.c/dif_ut.c b/test/unit/lib/util/dif.c/dif_ut.c index b7532e0f5..77d6b2dcf 100644 --- a/test/unit/lib/util/dif.c/dif_ut.c +++ b/test/unit/lib/util/dif.c/dif_ut.c @@ -1677,6 +1677,99 @@ dif_generate_stream_test(void) _iov_free_buf(&iov); } +#define UT_CRC32C_XOR 0xffffffffUL + +static void +update_crc32c_test(void) +{ + struct spdk_dif_ctx ctx = {}; + struct iovec iovs[7]; + uint32_t crc32c1, crc32c2, crc32c3, crc32c4; + uint32_t dif_flags; + int i, rc; + + dif_flags = SPDK_DIF_FLAGS_GUARD_CHECK | SPDK_DIF_FLAGS_APPTAG_CHECK | + SPDK_DIF_FLAGS_REFTAG_CHECK; + + rc = spdk_dif_ctx_init(&ctx, 512 + 8, 8, true, false, SPDK_DIF_TYPE1, + dif_flags, 0, 0, 0, 0); + CU_ASSERT(rc == 0); + + /* data[0][255:0] */ + _iov_alloc_buf(&iovs[0], 256); + + /* data[0][511:256], md[0][0] */ + _iov_alloc_buf(&iovs[1], 256 + 1); + + /* md[0][4:1] */ + _iov_alloc_buf(&iovs[2], 4); + + /* md[0][7:5], data[1][122:0] */ + _iov_alloc_buf(&iovs[3], 3 + 123); + + /* data[1][511:123], md[1][5:0] */ + _iov_alloc_buf(&iovs[4], 399 + 6); + + /* md[1][7:6], data[2][511:0], md[2][7:0], data[3][431:0] */ + _iov_alloc_buf(&iovs[5], 2 + 512 + 8 + 432); + + /* data[3][511:432], md[3][7:0] */ + _iov_alloc_buf(&iovs[6], 80 + 8); + + rc = ut_data_pattern_generate(iovs, 7, 512 + 8, 8, 4); + CU_ASSERT(rc == 0); + + crc32c1 = UT_CRC32C_XOR; + + rc = spdk_dif_update_crc32c(iovs, 7, 4, &crc32c1, &ctx); + CU_ASSERT(rc == 0); + + /* Test if DIF doesn't affect CRC for split case. */ + rc = spdk_dif_generate(iovs, 7, 4, &ctx); + CU_ASSERT(rc == 0); + + crc32c2 = UT_CRC32C_XOR; + + rc = spdk_dif_update_crc32c(iovs, 7, 4, &crc32c2, &ctx); + CU_ASSERT(rc == 0); + + CU_ASSERT(crc32c1 == crc32c2); + + for (i = 0; i < 7; i++) { + _iov_free_buf(&iovs[i]); + } + + /* Test if CRC is same regardless of splitting. */ + for (i = 0; i < 4; i++) { + _iov_alloc_buf(&iovs[i], 512 + 8); + } + + rc = ut_data_pattern_generate(iovs, 4, 512 + 8, 8, 4); + CU_ASSERT(rc == 0); + + crc32c3 = UT_CRC32C_XOR; + + rc = spdk_dif_update_crc32c(iovs, 4, 4, &crc32c3, &ctx); + CU_ASSERT(rc == 0); + + CU_ASSERT(crc32c1 == crc32c3); + + /* Test if DIF doesn't affect CRC for non-split case. */ + rc = spdk_dif_generate(iovs, 4, 4, &ctx); + CU_ASSERT(rc == 0); + + crc32c4 = UT_CRC32C_XOR; + + rc = spdk_dif_update_crc32c(iovs, 4, 4, &crc32c4, &ctx); + CU_ASSERT(rc == 0); + + CU_ASSERT(crc32c1 == crc32c4); + + for (i = 0; i < 4; i++) { + _iov_free_buf(&iovs[i]); + } +} + int main(int argc, char **argv) { @@ -1764,7 +1857,8 @@ main(int argc, char **argv) CU_add_test(suite, "set_md_interleave_iovs_test", set_md_interleave_iovs_test) == NULL || CU_add_test(suite, "set_md_interleave_iovs_split_test", set_md_interleave_iovs_split_test) == NULL || - CU_add_test(suite, "dif_generate_stream_test", dif_generate_stream_test) == NULL + CU_add_test(suite, "dif_generate_stream_test", dif_generate_stream_test) == NULL || + CU_add_test(suite, "update_crc32c_test", update_crc32c_test) == NULL ) { CU_cleanup_registry(); return CU_get_error();