From 1750a0859b183dba55cd09f0dbd763c958037d7f Mon Sep 17 00:00:00 2001 From: Shuhei Matsumoto Date: Thu, 13 Jun 2019 09:48:58 +0900 Subject: [PATCH] dif: Process unaligned end of data buffer in spdk_dif_generate_stream() NVMe/TCP target may split a whole data payload into multiple H2C or C2H PDUs with any alignment. Hence to insert or strip DIF correctly to the split H2C or C2H PDUs, we have to bring the interim guard value of the last partial data block of the current H2C or C2H PDU to the first partial data block of the next H2C or C2H PDU. So we add last_guard to struct spdk_dif_ctx and use it in spdk_dif_generate_stream(). API spdk_dif_generate_stream() is not changed and UT code should pass without any change. Signed-off-by: Shuhei Matsumoto Change-Id: I12636c5ac7f619483402538faff4339a16c0e6b0 Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/457545 Tested-by: SPDK CI Jenkins Reviewed-by: Darek Stojaczyk Reviewed-by: Changpeng Liu --- include/spdk/dif.h | 19 ++++++++++++++++--- lib/util/dif.c | 41 +++++++++++++++++++++++++++++------------ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/include/spdk/dif.h b/include/spdk/dif.h index 317a4056e..983e2cbda 100644 --- a/include/spdk/dif.h +++ b/include/spdk/dif.h @@ -98,6 +98,13 @@ struct spdk_dif_ctx { /* Offset to initial reference tag */ uint32_t ref_tag_offset; + /** Guard value of the last data block. + * + * Interim guard value is set if the last data block is partial, or + * seed value is set otherwise. + */ + uint16_t last_guard; + /* Seed value for guard computation */ uint16_t guard_seed; }; @@ -315,15 +322,21 @@ int spdk_dif_set_md_interleave_iovs(struct iovec *iovs, int iovcnt, /** * Generate and insert DIF into metadata space for newly read data block. * + * When the extended LBA payload is splitted into multiple data segments, + * start of each data segment is passed through the DIF context. data_offset + * and data_len is within a data segment. + * * \param iovs iovec array describing the extended LBA payload. * \param iovcnt Number of elements in the iovec array. - * \param data_offset Offset to the newly read data in the extended LBA payload. - * \param data_len Length of the newly read data in the extended LBA payload. + * \param data_offset Offset to the newly read data in the current data segment of + * the extended LBA payload. + * \param data_len Length of the newly read data in the current data segment of + * the extended LBA payload. * \param ctx DIF context. * * \return 0 on success and negated errno otherwise. */ int spdk_dif_generate_stream(struct iovec *iovs, int iovcnt, uint32_t data_offset, uint32_t data_len, - const struct spdk_dif_ctx *ctx); + struct spdk_dif_ctx *ctx); #endif /* SPDK_DIF_H */ diff --git a/lib/util/dif.c b/lib/util/dif.c index 484113b59..87f1435da 100644 --- a/lib/util/dif.c +++ b/lib/util/dif.c @@ -258,6 +258,7 @@ spdk_dif_ctx_init(struct spdk_dif_ctx *ctx, uint32_t block_size, uint32_t md_siz ctx->app_tag = app_tag; ctx->data_offset = data_offset; ctx->ref_tag_offset = data_offset / data_block_size; + ctx->last_guard = guard_seed; ctx->guard_seed = guard_seed; return 0; @@ -1465,9 +1466,10 @@ spdk_dif_set_md_interleave_iovs(struct iovec *iovs, int iovcnt, int spdk_dif_generate_stream(struct iovec *iovs, int iovcnt, uint32_t data_offset, uint32_t data_len, - const struct spdk_dif_ctx *ctx) + struct spdk_dif_ctx *ctx) { - uint32_t data_block_size, offset_blocks, num_blocks, i; + uint32_t data_block_size, buf_len, buf_offset; + uint32_t len, offset_in_block, offset_blocks; uint16_t guard = 0; struct _dif_sgl sgl; @@ -1478,25 +1480,40 @@ spdk_dif_generate_stream(struct iovec *iovs, int iovcnt, data_block_size = ctx->block_size - ctx->md_size; if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) { - guard = ctx->guard_seed; + guard = ctx->last_guard; } - offset_blocks = data_offset / data_block_size; - data_len += data_offset % data_block_size; - - data_offset = offset_blocks * ctx->block_size; - num_blocks = data_len / data_block_size; + /* If the last data block is complete, DIF of the data block is + * inserted in this function. + */ + buf_len = ((data_offset + data_len) / data_block_size) * ctx->block_size + + ((data_offset + data_len) % data_block_size); _dif_sgl_init(&sgl, iovs, iovcnt); - if (!_dif_sgl_is_valid(&sgl, data_offset + num_blocks * ctx->block_size)) { + if (!_dif_sgl_is_valid(&sgl, buf_len)) { return -ERANGE; } - _dif_sgl_advance(&sgl, data_offset); + buf_offset = (data_offset / data_block_size) * ctx->block_size + + (data_offset % data_block_size); - for (i = 0; i < num_blocks; i++) { - _dif_generate_split(&sgl, 0, ctx->block_size, guard, offset_blocks + i, ctx); + _dif_sgl_advance(&sgl, buf_offset); + buf_len -= buf_offset; + + while (buf_len != 0) { + len = spdk_min(buf_len, _to_next_boundary(buf_offset, ctx->block_size)); + offset_in_block = buf_offset % ctx->block_size; + offset_blocks = buf_offset / ctx->block_size; + + guard = _dif_generate_split(&sgl, offset_in_block, len, guard, offset_blocks, ctx); + + buf_len -= len; + buf_offset += len; + } + + if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) { + ctx->last_guard = guard; } return 0;