dif: Add spdk_dif_remap_ref_tag to remap ref. tag for extended LBA payload
When using stacked virtual bdev (e.g. split virtual bdev), block address space will be remapped during I/O processing and so reference tag will have to be remapped accordingly. The use case is explained in detail as follows: - Format a single NVMe SSD with DIF enabled. - Create a NVMe bdev on the NVMe SSD with DIF enabled. - Create four split vbdevs on the NVMe bdev. - Add the split vbdevs to a NVMe-oF target. - Application is aware of block address space of the split vbdevs. - Application submits read/write I/O to the NVMe-oF target. Case 1: - Configure NVMe-oF target to DIF pass-through. Case 2: - Configure NVMe-oF target to DIF insert/strip For the case 1, - Application inserts DIF for write I/O and verifies DIF for read I/O. - The split vbdevs remaps reference tags of DIF both for read and write I/O because application expects reference tags are based on the block address space of split vbdevs. - The NVMe bdev processs read/write I/Os without remapping reference tags because reference tags are already based on the block address space of the NVMe bdev. For the case 2, - NVMe-oF target inserts DIF for write I/O, and verifies and strips DIF or read I/O. - The split vbdevs remaps reference tags of DIF both for read and write I/O because NVMe-oF target expects reference tags are based on the block address space of split vbdevs. - The NVMe bdev processs read/write I/Os without remapping reference tags because reference tags are already based on the block address space of the NVMe bdev. This patch adds two APIs, spdk_dif_ctx_set_remapped_init_ref_tag and spdk_dif_remap_ref_tag to satisfy the use case. UT code is added together in this patch. Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Change-Id: Ib3101129225b334d2f578eab75197790b1818770 Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/461103 Reviewed-by: Changpeng Liu <changpeng.liu@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
parent
1b4c99a2ba
commit
f4a62a3993
@ -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 */
|
||||
|
153
lib/util/dif.c
153
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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user