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 <shuhei.matsumoto.xt@hitachi.com>
Reviewed-on: https://review.gerrithub.io/434292
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Chandler-Test-Pool: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Shuhei Matsumoto 2018-12-18 10:51:22 +09:00 committed by Jim Harris
parent 0a3cdcf3c3
commit f0e00ef287
3 changed files with 342 additions and 1 deletions

View File

@ -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 */

View File

@ -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;
}

View File

@ -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();