From 450ee009442908136cd7c3ce062379e9e1529b98 Mon Sep 17 00:00:00 2001 From: Changpeng Liu Date: Wed, 10 Jan 2018 20:25:19 +0800 Subject: [PATCH] examples/perf: add application verification for e2e data protection tests For end-to-end data protection tests with extended lba format, when Protection Information Action bit was cleared to 0, the perf application can do some comparison based on the flags we used for writes. For the unexptected protection information value, print a notice for users. Change-Id: I9851ce6338030f9b57180cd5e46368e0c94bc212 Signed-off-by: Changpeng Liu Reviewed-on: https://review.gerrithub.io/394158 Reviewed-by: Daniel Verkamp Reviewed-by: Jim Harris Reviewed-by: Shuhei Matsumoto Tested-by: SPDK Automated Test System --- examples/nvme/perf/perf.c | 90 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/examples/nvme/perf/perf.c b/examples/nvme/perf/perf.c index ec24b8769..4378deab5 100644 --- a/examples/nvme/perf/perf.c +++ b/examples/nvme/perf/perf.c @@ -137,6 +137,8 @@ struct perf_task { uint64_t submit_tsc; uint16_t appmask; uint16_t apptag; + uint64_t lba; + bool is_read; #if HAVE_LIBAIO struct iocb iocb; #endif @@ -539,6 +541,72 @@ task_extended_lba_setup_pi(struct ns_entry *entry, struct perf_task *task, uint6 } } +static void +task_extended_lba_pi_verify(struct ns_entry *entry, struct perf_task *task, + uint64_t lba, uint32_t lba_count) +{ + struct spdk_nvme_protection_info *pi; + uint32_t i, md_size, sector_size, pi_offset, ref_tag; + uint16_t crc16, guard, app_tag; + const struct spdk_nvme_ns_data *nsdata; + + if (spdk_nvme_ns_get_pi_type(entry->u.nvme.ns) == + SPDK_NVME_FMT_NVM_PROTECTION_DISABLE) { + return; + } + + sector_size = spdk_nvme_ns_get_sector_size(entry->u.nvme.ns); + md_size = spdk_nvme_ns_get_md_size(entry->u.nvme.ns); + nsdata = spdk_nvme_ns_get_data(entry->u.nvme.ns); + + /* PI locates at the first 8 bytes of metadata, + * doesn't support now + */ + if (nsdata->dps.md_start) { + return; + } + + for (i = 0; i < lba_count; i++) { + pi_offset = ((sector_size + md_size) * (i + 1)) - 8; + pi = (struct spdk_nvme_protection_info *)(task->buf + pi_offset); + + if (entry->io_flags & SPDK_NVME_IO_FLAGS_PRCHK_GUARD) { + /* CRC buffer should not include last 8 bytes of PI */ + crc16 = spdk_crc16_t10dif(task->buf + (sector_size + md_size) * i, + sector_size + md_size - 8); + to_be16(&guard, crc16); + if (pi->guard != guard) { + fprintf(stdout, "Get Guard Error LBA 0x%16.16"PRIx64"," + " Preferred 0x%04x but returned with 0x%04x," + " may read the LBA without write it first\n", + lba + i, guard, pi->guard); + } + + } + if (entry->io_flags & SPDK_NVME_IO_FLAGS_PRCHK_APPTAG) { + /* Previously we used the number of lbas as + * application tag for writes + */ + to_be16(&app_tag, lba_count); + if (pi->app_tag != app_tag) { + fprintf(stdout, "Get Application Tag Error LBA 0x%16.16"PRIx64"," + " Preferred 0x%04x but returned with 0x%04x," + " may read the LBA without write it first\n", + lba + i, app_tag, pi->app_tag); + } + } + if (entry->io_flags & SPDK_NVME_IO_FLAGS_PRCHK_REFTAG) { + to_be32(&ref_tag, (uint32_t)lba + i); + if (pi->ref_tag != ref_tag) { + fprintf(stdout, "Get Reference Tag Error LBA 0x%16.16"PRIx64"," + " Preferred 0x%08x but returned with 0x%08x," + " may read the LBA without write it first\n", + lba + i, ref_tag, pi->ref_tag); + } + } + } +} + static void io_complete(void *ctx, const struct spdk_nvme_cpl *completion); static __thread unsigned int seed = 0; @@ -560,7 +628,9 @@ submit_single_io(struct perf_task *task) } } + task->is_read = false; task->submit_tsc = spdk_get_ticks(); + task->lba = offset_in_ios * entry->io_size_blocks; if ((g_rw_percentage == 100) || (g_rw_percentage != 0 && ((rand_r(&seed) % 100) < g_rw_percentage))) { @@ -571,12 +641,13 @@ submit_single_io(struct perf_task *task) } else #endif { - task_extended_lba_setup_pi(entry, task, offset_in_ios * entry->io_size_blocks, + task_extended_lba_setup_pi(entry, task, task->lba, entry->io_size_blocks, false); + task->is_read = true; rc = spdk_nvme_ns_cmd_read_with_md(entry->u.nvme.ns, ns_ctx->u.nvme.qpair, task->buf, NULL, - offset_in_ios * entry->io_size_blocks, + task->lba, entry->io_size_blocks, io_complete, task, entry->io_flags, task->appmask, task->apptag); @@ -589,12 +660,12 @@ submit_single_io(struct perf_task *task) } else #endif { - task_extended_lba_setup_pi(entry, task, offset_in_ios * entry->io_size_blocks, + task_extended_lba_setup_pi(entry, task, task->lba, entry->io_size_blocks, true); rc = spdk_nvme_ns_cmd_write_with_md(entry->u.nvme.ns, ns_ctx->u.nvme.qpair, task->buf, NULL, - offset_in_ios * entry->io_size_blocks, + task->lba, entry->io_size_blocks, io_complete, task, entry->io_flags, task->appmask, task->apptag); @@ -613,8 +684,10 @@ task_complete(struct perf_task *task) { struct ns_worker_ctx *ns_ctx; uint64_t tsc_diff; + struct ns_entry *entry; ns_ctx = task->ns_ctx; + entry = ns_ctx->entry; ns_ctx->current_queue_depth--; ns_ctx->io_completed++; tsc_diff = spdk_get_ticks() - task->submit_tsc; @@ -629,6 +702,15 @@ task_complete(struct perf_task *task) spdk_histogram_data_tally(ns_ctx->histogram, tsc_diff); } + /* add application level verification for end-to-end data protection */ + if (entry->type == ENTRY_TYPE_NVME_NS) { + if (spdk_nvme_ns_supports_extended_lba(entry->u.nvme.ns) && + task->is_read && !g_metacfg_pract_flag) { + task_extended_lba_pi_verify(entry, task, task->lba, + entry->io_size_blocks); + } + } + /* * is_draining indicates when time has expired for the test run * and we are just waiting for the previously submitted I/O