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