dif: Process unaligned end of data segment in spdk_dif_set_md_interleave_iovs()

For NVMe/TCP target, data segments which correspond to H2C or C2H PDU
will have any alignment. spdk_dif_set_md_interleave_iovs() have allowed
reading data to have any alignment but had required data segment to
be a multiple of block size.

In other words, spdk_dif_set_md_interleave_iovs() had required that both
ctx->data_offset and (data_offset + data_len) must be a multiple of the
data block size.

This patch refines the algorithm to remove the latter requirement.

The update implies that spdk_dif_set_md_interleave_iovs support any
data buffer whose size is less than a single data block.

The update doesn't change parameters of spdk_dif_set_md_interleave_iovs
and existing UT should be passed.

This patch adds additional UT code to test these updates.

Change-Id: I88c7d2a80a8d92b54863b6ad1c3a9d2761a6195d
Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/457542
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
This commit is contained in:
Shuhei Matsumoto 2019-06-12 17:26:08 +09:00 committed by Changpeng Liu
parent 2bb4fb4c8c
commit 8b24fc4a31
3 changed files with 84 additions and 18 deletions

View File

@ -288,12 +288,17 @@ int spdk_dix_inject_error(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
* This function removes the necessity of data copy in the SPDK application
* during DIF insertion and strip.
*
* 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 set by this function.
* \param iovcnt Number of elements in the iovec array.
* \param buf_iovs SGL for the buffer to create extended LBA payload.
* \param buf_iovcnt Size of the SGL for the buffer to create extended LBA payload.
* \param data_offset Offset to store the next incoming data.
* \param data_len Expected length of the newly read data in the extended LBA payload.
* \param data_offset Offset to store the next incoming data in the current data segment.
* \param data_len Expected length of the newly read data in the current data segment of
* the extended LBA payload.
* \param mapped_len Output parameter that will contain data length mapped by
* the iovec array.
* \param ctx DIF context.

View File

@ -1385,6 +1385,12 @@ spdk_dix_inject_error(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
return 0;
}
static uint32_t
_to_next_boundary(uint32_t offset, uint32_t boundary)
{
return boundary - (offset % boundary);
}
int
spdk_dif_set_md_interleave_iovs(struct iovec *iovs, int iovcnt,
struct iovec *buf_iovs, int buf_iovcnt,
@ -1392,8 +1398,8 @@ spdk_dif_set_md_interleave_iovs(struct iovec *iovs, int iovcnt,
uint32_t *_mapped_len,
const struct spdk_dif_ctx *ctx)
{
uint32_t data_block_size, head_unalign;
uint32_t num_blocks, offset_blocks;
uint32_t data_block_size, head_unalign, tail_unalign;
uint32_t num_blocks, offset_blocks, len;
struct _dif_sgl dif_sgl;
struct _dif_sgl buf_sgl;
@ -1403,17 +1409,13 @@ spdk_dif_set_md_interleave_iovs(struct iovec *iovs, int iovcnt,
data_block_size = ctx->block_size - ctx->md_size;
if (((data_offset + data_len) % data_block_size) != 0) {
SPDK_ERRLOG("Data offset + length must be a multiple of data block size\n");
return -EINVAL;
}
num_blocks = (data_offset + data_len) / data_block_size;
tail_unalign = (data_offset + data_len) % data_block_size;
_dif_sgl_init(&dif_sgl, iovs, iovcnt);
_dif_sgl_init(&buf_sgl, buf_iovs, buf_iovcnt);
if (!_dif_sgl_is_valid(&buf_sgl, num_blocks * ctx->block_size)) {
if (!_dif_sgl_is_valid(&buf_sgl, num_blocks * ctx->block_size + tail_unalign)) {
SPDK_ERRLOG("Buffer overflow will occur.\n");
return -ERANGE;
}
@ -1421,19 +1423,17 @@ spdk_dif_set_md_interleave_iovs(struct iovec *iovs, int iovcnt,
offset_blocks = data_offset / data_block_size;
head_unalign = data_offset % data_block_size;
_dif_sgl_advance(&buf_sgl, offset_blocks * ctx->block_size);
_dif_sgl_advance(&buf_sgl, offset_blocks * ctx->block_size + head_unalign);
while (offset_blocks < num_blocks) {
_dif_sgl_advance(&buf_sgl, head_unalign);
while (data_len != 0) {
len = spdk_min(data_len, _to_next_boundary(data_offset, data_block_size));
if (!_dif_sgl_append_split(&dif_sgl, &buf_sgl,
data_block_size - head_unalign)) {
if (!_dif_sgl_append_split(&dif_sgl, &buf_sgl, len)) {
break;
}
_dif_sgl_advance(&buf_sgl, ctx->md_size);
offset_blocks++;
head_unalign = 0;
data_offset += len;
data_len -= len;
}
if (_mapped_len != NULL) {

View File

@ -1719,6 +1719,65 @@ dif_generate_stream_test(void)
_iov_free_buf(&iov);
}
static void
set_md_interleave_iovs_alignment_test(void)
{
struct iovec iovs[3], dif_iovs[5];
uint32_t mapped_len = 0;
int rc;
struct spdk_dif_ctx ctx;
rc = spdk_dif_ctx_init(&ctx, 512 + 8, 8, true, false, SPDK_DIF_TYPE1,
0, 0, 0, 0, 0, 0);
CU_ASSERT(rc == 0);
/* The case that buffer size is smaller than necessary. */
_iov_set_buf(&iovs[0], (uint8_t *)0xDEADBEEF, 1024);
_iov_set_buf(&iovs[1], (uint8_t *)0xFEEDBEEF, 1024);
_iov_set_buf(&iovs[2], (uint8_t *)0xC0FFEE, 24);
rc = spdk_dif_set_md_interleave_iovs(dif_iovs, 5, iovs, 3, 0, 2048, &mapped_len, &ctx);
CU_ASSERT(rc == -ERANGE);
/* The folllowing are the normal cases. */
_iov_set_buf(&iovs[2], (uint8_t *)0xC0FFEE, 32);
/* data length is less than a data block size. */
rc = spdk_dif_set_md_interleave_iovs(dif_iovs, 5, iovs, 3, 0, 500, &mapped_len, &ctx);
CU_ASSERT(rc == 1);
CU_ASSERT(mapped_len == 500);
CU_ASSERT(_iov_check(&dif_iovs[0], (void *)0xDEADBEEF, 500) == true);
/* Pass enough number of iovecs */
rc = spdk_dif_set_md_interleave_iovs(dif_iovs, 5, iovs, 3, 500, 1000, &mapped_len, &ctx);
CU_ASSERT(rc == 4);
CU_ASSERT(mapped_len == 1000);
CU_ASSERT(_iov_check(&dif_iovs[0], (void *)(0xDEADBEEF + 500), 12) == true);
CU_ASSERT(_iov_check(&dif_iovs[1], (void *)(0xDEADBEEF + 520), 504) == true);
CU_ASSERT(_iov_check(&dif_iovs[2], (void *)0xFEEDBEEF, 8) == true);
CU_ASSERT(_iov_check(&dif_iovs[3], (void *)(0xFEEDBEEF + 16), 476) == true);
/* Pass iovecs smaller than necessary */
rc = spdk_dif_set_md_interleave_iovs(dif_iovs, 3, iovs, 3, 500, 1000, &mapped_len, &ctx);
CU_ASSERT(rc == 3);
CU_ASSERT(mapped_len == 524);
CU_ASSERT(_iov_check(&dif_iovs[0], (void *)(0xDEADBEEF + 500), 12) == true);
CU_ASSERT(_iov_check(&dif_iovs[1], (void *)(0xDEADBEEF + 520), 504) == true);
CU_ASSERT(_iov_check(&dif_iovs[2], (void *)0xFEEDBEEF, 8) == true);
rc = spdk_dif_set_md_interleave_iovs(dif_iovs, 5, iovs, 3, 1500, 500, &mapped_len, &ctx);
CU_ASSERT(rc == 2);
CU_ASSERT(mapped_len == 500);
CU_ASSERT(_iov_check(&dif_iovs[0], (void *)(0xFEEDBEEF + 492), 36) == true);
CU_ASSERT(_iov_check(&dif_iovs[1], (void *)(0xFEEDBEEF + 536), 464) == true);
rc = spdk_dif_set_md_interleave_iovs(dif_iovs, 5, iovs, 3, 2000, 48, &mapped_len, &ctx);
CU_ASSERT(rc == 2);
CU_ASSERT(mapped_len == 48);
CU_ASSERT(_iov_check(&dif_iovs[0], (void *)0xFEEDBEEF + 1000, 24) == true);
CU_ASSERT(_iov_check(&dif_iovs[1], (void *)0xC0FFEE, 24) == true);
}
#define UT_CRC32C_XOR 0xffffffffUL
static void
@ -1900,6 +1959,8 @@ main(int argc, char **argv)
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, "set_md_interleave_iovs_alignment_test",
set_md_interleave_iovs_alignment_test) == NULL ||
CU_add_test(suite, "update_crc32c_test", update_crc32c_test) == NULL
) {
CU_cleanup_registry();