From f0e00ef2871c0b915893a71b475513ff68b6d757 Mon Sep 17 00:00:00 2001 From: Shuhei Matsumoto Date: Tue, 18 Dec 2018 10:51:22 +0900 Subject: [PATCH] dif: Inject bit-flip error for extended LBA payload This patch adds APIs to inject bit flip error into any field of the extended LBA payload. Change-Id: I3ca601999e55ea6228bb525ac8c0744c7df32398 Signed-off-by: Shuhei Matsumoto Reviewed-on: https://review.gerrithub.io/434292 Tested-by: SPDK CI Jenkins Chandler-Test-Pool: SPDK Automated Test System Reviewed-by: Changpeng Liu Reviewed-by: Jim Harris --- include/spdk/dif.h | 23 +++++ lib/util/dif.c | 154 +++++++++++++++++++++++++++ test/unit/lib/util/dif.c/dif_ut.c | 166 +++++++++++++++++++++++++++++- 3 files changed, 342 insertions(+), 1 deletion(-) diff --git a/include/spdk/dif.h b/include/spdk/dif.h index 0391ef4c4..49ec4462c 100644 --- a/include/spdk/dif.h +++ b/include/spdk/dif.h @@ -41,6 +41,11 @@ #define SPDK_DIF_APPTAG_CHECK (1U << 27) #define SPDK_DIF_GUARD_CHECK (1U << 28) +#define SPDK_DIF_REFTAG_ERROR 0x1 +#define SPDK_DIF_APPTAG_ERROR 0x2 +#define SPDK_DIF_GUARD_ERROR 0x4 +#define SPDK_DIF_DATA_ERROR 0x8 + enum spdk_dif_type { SPDK_DIF_TYPE1 = 1, SPDK_DIF_TYPE2, @@ -100,4 +105,22 @@ int spdk_dif_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, uint16_t apptag_mask, uint16_t app_tag); + +/** + * Inject bit flip error to extended LBA payload. + * + * \param iovs iovec array describing the extended LBA payload. + * \param iovcnt Number of elements in the iovec array. + * \param block_size Block size in a block. + * \param md_size Metadata size in a block. + * \param num_blocks Number of blocks of the payload. + * \param dif_loc DIF location. If true, DIF is set in the last 8 bytes of metadata. + * If false, DIF is set in the first 8 bytes of metadata. + * \param inject_flags Flag to specify the action of error injection. + * + * \return 0 on success and negated errno otherwise including no metadata. + */ +int spdk_dif_inject_error(struct iovec *iovs, int iovcnt, + uint32_t block_size, uint32_t md_size, uint32_t num_blocks, + bool dif_loc, uint32_t inject_flags); #endif /* SPDK_DIF_H */ diff --git a/lib/util/dif.c b/lib/util/dif.c index f86b5d608..36b0f334e 100644 --- a/lib/util/dif.c +++ b/lib/util/dif.c @@ -85,6 +85,21 @@ _iov_iter_get_buf(struct _iov_iter *i, void **_buf, uint32_t *_buf_len) } } +static void +_iov_iter_fast_forward(struct _iov_iter *i, uint32_t offset) +{ + i->iov_offset = offset; + while (i->iovcnt != 0) { + if (i->iov_offset < i->iov->iov_len) { + break; + } + + i->iov_offset -= i->iov->iov_len; + i->iov++; + i->iovcnt--; + } +} + static bool _are_iovs_bytes_multiple(struct iovec *iovs, int iovcnt, uint32_t bytes) { @@ -559,3 +574,142 @@ spdk_dif_verify(struct iovec *iovs, int iovcnt, dif_type, dif_flags, init_ref_tag, apptag_mask, app_tag); } } + +static void +_bit_flip(uint8_t *buf, uint32_t flip_bit) +{ + uint8_t byte; + + byte = *buf; + byte ^= 1 << flip_bit; + *buf = byte; +} + +static int +_dif_inject_error(struct iovec *iovs, int iovcnt, + uint32_t block_size, uint32_t num_blocks, + uint32_t inject_offset_blocks, + uint32_t inject_offset_bytes, + uint32_t inject_offset_bits) +{ + struct _iov_iter iter; + uint32_t offset_in_block, buf_len; + void *buf; + + _iov_iter_init(&iter, iovs, iovcnt); + + _iov_iter_fast_forward(&iter, block_size * inject_offset_blocks); + + offset_in_block = 0; + + while (offset_in_block < block_size && _iov_iter_cont(&iter)) { + _iov_iter_get_buf(&iter, &buf, &buf_len); + buf_len = spdk_min(buf_len, block_size - offset_in_block); + + if (inject_offset_bytes >= offset_in_block && + inject_offset_bytes < offset_in_block + buf_len) { + buf += inject_offset_bytes - offset_in_block; + _bit_flip(buf, inject_offset_bits); + return 0; + } + + _iov_iter_advance(&iter, buf_len); + offset_in_block += buf_len; + } + + return -1; +} + +static int +dif_inject_error(struct iovec *iovs, int iovcnt, + uint32_t block_size, uint32_t num_blocks, + uint32_t start_inject_bytes, uint32_t inject_range_bytes) +{ + uint32_t inject_offset_blocks, inject_offset_bytes, inject_offset_bits; + uint32_t offset_blocks; + + srand(time(0)); + + inject_offset_blocks = rand() % num_blocks; + inject_offset_bytes = start_inject_bytes + (rand() % inject_range_bytes); + inject_offset_bits = rand() % sizeof(uint8_t); + + for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) { + if (offset_blocks == inject_offset_blocks) { + return _dif_inject_error(iovs, iovcnt, block_size, num_blocks, + inject_offset_blocks, + inject_offset_bytes, + inject_offset_bits); + } + } + + return -1; +} + +#define _member_size(type, member) sizeof(((type *)0)->member) + +int +spdk_dif_inject_error(struct iovec *iovs, int iovcnt, + uint32_t block_size, uint32_t md_size, uint32_t num_blocks, + bool dif_loc, uint32_t inject_flags) +{ + uint32_t guard_interval; + int rc; + + if (md_size == 0) { + return -EINVAL; + } + + if (!_are_iovs_valid(iovs, iovcnt, block_size * num_blocks)) { + SPDK_ERRLOG("Size of iovec array is not valid.\n"); + return -EINVAL; + } + + guard_interval = _get_dif_guard_interval(block_size, md_size, dif_loc); + + if (inject_flags & SPDK_DIF_REFTAG_ERROR) { + rc = dif_inject_error(iovs, iovcnt, block_size, num_blocks, + guard_interval + offsetof(struct spdk_dif, ref_tag), + _member_size(struct spdk_dif, ref_tag)); + if (rc != 0) { + SPDK_ERRLOG("Failed to inject error to Reference Tag.\n"); + return rc; + } + } + + if (inject_flags & SPDK_DIF_APPTAG_ERROR) { + rc = dif_inject_error(iovs, iovcnt, block_size, num_blocks, + guard_interval + offsetof(struct spdk_dif, app_tag), + _member_size(struct spdk_dif, app_tag)); + if (rc != 0) { + SPDK_ERRLOG("Failed to inject error to Application Tag.\n"); + return rc; + } + } + if (inject_flags & SPDK_DIF_GUARD_ERROR) { + rc = dif_inject_error(iovs, iovcnt, block_size, num_blocks, + guard_interval, + _member_size(struct spdk_dif, guard)); + if (rc != 0) { + SPDK_ERRLOG("Failed to inject error to Guard.\n"); + return rc; + } + } + + if (inject_flags & SPDK_DIF_DATA_ERROR) { + /* If the DIF information is contained within the last 8 bytes of + * metadata, then the CRC covers all metadata bytes up to but excluding + * the last 8 bytes. But error injection does not cover these metadata + * because classification is not determined yet. + */ + rc = dif_inject_error(iovs, iovcnt, block_size, num_blocks, + 0, + block_size - md_size); + if (rc != 0) { + SPDK_ERRLOG("Failed to inject error to data block.\n"); + 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 ef52e1ff0..5d8bea4c6 100644 --- a/test/unit/lib/util/dif.c/dif_ut.c +++ b/test/unit/lib/util/dif.c/dif_ut.c @@ -545,6 +545,158 @@ dif_sec_4096_md_128_prchk_7_multi_iovs_complex_splits_test(void) } } +static void +_dif_inject_error_and_verify(struct iovec *iovs, int iovcnt, + uint32_t block_size, uint32_t md_size, uint32_t num_blocks, + uint32_t inject_flags, bool dif_loc) +{ + uint32_t dif_flags; + int rc; + + dif_flags = SPDK_DIF_GUARD_CHECK | SPDK_DIF_APPTAG_CHECK | SPDK_DIF_REFTAG_CHECK; + + rc = ut_data_pattern_generate(iovs, iovcnt, block_size, md_size, num_blocks); + CU_ASSERT(rc == 0); + + rc = spdk_dif_generate(iovs, iovcnt, block_size, md_size, num_blocks, + dif_loc, SPDK_DIF_TYPE1, dif_flags, 88, 0x88); + CU_ASSERT(rc == 0); + + rc = spdk_dif_inject_error(iovs, iovcnt, block_size, md_size, num_blocks, + dif_loc, inject_flags); + CU_ASSERT(rc == 0); + + rc = spdk_dif_verify(iovs, iovcnt, block_size, md_size, num_blocks, + dif_loc, SPDK_DIF_TYPE1, dif_flags, 88, 0xFFFF, 0x88); + CU_ASSERT(rc != 0); + + rc = ut_data_pattern_verify(iovs, iovcnt, block_size, md_size, num_blocks); + CU_ASSERT((rc == 0 && !(inject_flags & SPDK_DIF_DATA_ERROR)) || + (rc != 0 && (inject_flags & SPDK_DIF_DATA_ERROR))); +} + +static void +dif_inject_error_and_verify(struct iovec *iovs, int iovcnt, + uint32_t block_size, uint32_t md_size, uint32_t num_blocks, + uint32_t inject_flags) +{ + /* The case that DIF is contained in the first 8 bytes of metadata. */ + _dif_inject_error_and_verify(iovs, iovcnt, block_size, md_size, num_blocks, + inject_flags, false); + + /* The case that DIF is contained in the last 8 bytes of metadata. */ + _dif_inject_error_and_verify(iovs, iovcnt, block_size, md_size, num_blocks, + inject_flags, true); +} + +static void +dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_test(void) +{ + struct iovec iovs[4]; + int i, num_blocks; + + num_blocks = 0; + + for (i = 0; i < 4; i++) { + _iov_alloc_buf(&iovs[i], (4096 + 128) * (i + 1)); + num_blocks += i + 1; + } + + dif_inject_error_and_verify(iovs, 4, 4096 + 128, 128, num_blocks, SPDK_DIF_GUARD_ERROR); + dif_inject_error_and_verify(iovs, 4, 4096 + 128, 128, num_blocks, SPDK_DIF_APPTAG_ERROR); + dif_inject_error_and_verify(iovs, 4, 4096 + 128, 128, num_blocks, SPDK_DIF_REFTAG_ERROR); + dif_inject_error_and_verify(iovs, 4, 4096 + 128, 128, num_blocks, SPDK_DIF_DATA_ERROR); + + for (i = 0; i < 4; i++) { + _iov_free_buf(&iovs[i]); + } +} + +static void +dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_data_and_md_test(void) +{ + struct iovec iovs[2]; + + _iov_alloc_buf(&iovs[0], 4096); + _iov_alloc_buf(&iovs[1], 128); + + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_GUARD_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_APPTAG_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_REFTAG_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_DATA_ERROR); + + _iov_free_buf(&iovs[0]); + _iov_free_buf(&iovs[1]); +} + +static void +dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_data_test(void) +{ + struct iovec iovs[2]; + + _iov_alloc_buf(&iovs[0], 2048); + _iov_alloc_buf(&iovs[1], 2048 + 128); + + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_GUARD_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_APPTAG_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_REFTAG_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_DATA_ERROR); + + _iov_free_buf(&iovs[0]); + _iov_free_buf(&iovs[1]); +} + +static void +dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_guard_test(void) +{ + struct iovec iovs[2]; + + _iov_alloc_buf(&iovs[0], 4096 + 1); + _iov_alloc_buf(&iovs[1], 127); + + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_GUARD_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_APPTAG_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_REFTAG_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_DATA_ERROR); + + _iov_free_buf(&iovs[0]); + _iov_free_buf(&iovs[1]); +} + +static void +dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_apptag_test(void) +{ + struct iovec iovs[2]; + + _iov_alloc_buf(&iovs[0], 4096 + 3); + _iov_alloc_buf(&iovs[1], 125); + + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_GUARD_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_APPTAG_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_REFTAG_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_DATA_ERROR); + + _iov_free_buf(&iovs[0]); + _iov_free_buf(&iovs[1]); +} + +static void +dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_reftag_test(void) +{ + struct iovec iovs[2]; + + _iov_alloc_buf(&iovs[0], 4096 + 6); + _iov_alloc_buf(&iovs[1], 122); + + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_GUARD_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_APPTAG_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_REFTAG_ERROR); + dif_inject_error_and_verify(iovs, 2, 4096 + 128, 128, 1, SPDK_DIF_DATA_ERROR); + + _iov_free_buf(&iovs[0]); + _iov_free_buf(&iovs[1]); +} + int main(int argc, char **argv) { @@ -584,7 +736,19 @@ main(int argc, char **argv) CU_add_test(suite, "dif_sec_512_md_8_prchk_7_multi_iovs_complex_splits_test", dif_sec_512_md_8_prchk_7_multi_iovs_complex_splits_test) == NULL || CU_add_test(suite, "dif_sec_4096_md_128_prchk_7_multi_iovs_complex_splits_test", - dif_sec_4096_md_128_prchk_7_multi_iovs_complex_splits_test) == NULL + dif_sec_4096_md_128_prchk_7_multi_iovs_complex_splits_test) == NULL || + CU_add_test(suite, "dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_test", + dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_test) == NULL || + CU_add_test(suite, "dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_data_and_md_test", + dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_data_and_md_test) == NULL || + CU_add_test(suite, "dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_data_test", + dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_data_test) == NULL || + CU_add_test(suite, "dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_guard_test", + dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_guard_test) == NULL || + CU_add_test(suite, "dif_sec_4096_md_128_inject_1_2_4_8__multi_iovs_split_apptag_test", + dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_apptag_test) == NULL || + CU_add_test(suite, "dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_reftag_test", + dif_sec_4096_md_128_inject_1_2_4_8_multi_iovs_split_reftag_test) == NULL ) { CU_cleanup_registry(); return CU_get_error();