diff options
Diffstat (limited to 'src/spdk/test/nvme/deallocated_value')
-rw-r--r-- | src/spdk/test/nvme/deallocated_value/.gitignore | 1 | ||||
-rw-r--r-- | src/spdk/test/nvme/deallocated_value/Makefile | 39 | ||||
-rw-r--r-- | src/spdk/test/nvme/deallocated_value/deallocated_value.c | 445 |
3 files changed, 485 insertions, 0 deletions
diff --git a/src/spdk/test/nvme/deallocated_value/.gitignore b/src/spdk/test/nvme/deallocated_value/.gitignore new file mode 100644 index 00000000..8460e82e --- /dev/null +++ b/src/spdk/test/nvme/deallocated_value/.gitignore @@ -0,0 +1 @@ +deallocated_value diff --git a/src/spdk/test/nvme/deallocated_value/Makefile b/src/spdk/test/nvme/deallocated_value/Makefile new file mode 100644 index 00000000..bff11cad --- /dev/null +++ b/src/spdk/test/nvme/deallocated_value/Makefile @@ -0,0 +1,39 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +APP = deallocated_value + +include $(SPDK_ROOT_DIR)/mk/nvme.libtest.mk diff --git a/src/spdk/test/nvme/deallocated_value/deallocated_value.c b/src/spdk/test/nvme/deallocated_value/deallocated_value.c new file mode 100644 index 00000000..e1d1c9f6 --- /dev/null +++ b/src/spdk/test/nvme/deallocated_value/deallocated_value.c @@ -0,0 +1,445 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" + +#include "spdk/nvme.h" +#include "spdk/env.h" + +#define NUM_BLOCKS 100 + +/* + * The purpose of this sample app is to determine the read value of deallocated logical blocks + * from a given NVMe Controller. The NVMe 1.3 spec requires the controller to list this value, + * but controllers adhering to the NVMe 1.2 spec may not report this value. According to the spec, + * "The values read from a deallocated logical block and its metadata (excluding protection information) shall + * be all bytes set to 00h, all bytes set to FFh, or the last data written to the associated logical block". + */ + +struct ns_entry { + struct spdk_nvme_ctrlr *ctrlr; + struct spdk_nvme_ns *ns; + struct ns_entry *next; + struct spdk_nvme_qpair *qpair; +}; + +struct deallocate_context { + struct ns_entry *ns_entry; + char **write_buf; + char **read_buf; + char *zero_buf; + char *FFh_buf; + int writes_completed; + int reads_completed; + int deallocate_completed; + int flush_complete; + int matches_zeroes; + int matches_previous_data; + int matches_FFh; +}; + +static struct ns_entry *g_namespaces = NULL; + +static void cleanup(struct deallocate_context *context); + +static void +fill_random(char *buf, size_t num_bytes) +{ + size_t i; + + srand((unsigned) time(NULL)); + for (i = 0; i < num_bytes; i++) { + buf[i] = rand() % 0x100; + } +} + +static void +register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns) +{ + struct ns_entry *entry; + const struct spdk_nvme_ctrlr_data *cdata; + + cdata = spdk_nvme_ctrlr_get_data(ctrlr); + + if (!spdk_nvme_ns_is_active(ns)) { + printf("Controller %-20.20s (%-20.20s): Skipping inactive NS %u\n", + cdata->mn, cdata->sn, + spdk_nvme_ns_get_id(ns)); + return; + } + + entry = malloc(sizeof(struct ns_entry)); + if (entry == NULL) { + perror("ns_entry malloc"); + exit(1); + } + + entry->ctrlr = ctrlr; + entry->ns = ns; + entry->next = g_namespaces; + g_namespaces = entry; + + printf(" Namespace ID: %d size: %juGB\n", spdk_nvme_ns_get_id(ns), + spdk_nvme_ns_get_size(ns) / 1000000000); +} + +static uint32_t +get_max_block_size(void) +{ + struct ns_entry *ns; + uint32_t max_block_size, temp_block_size; + + ns = g_namespaces; + max_block_size = 0; + + while (ns != NULL) { + temp_block_size = spdk_nvme_ns_get_sector_size(ns->ns); + max_block_size = temp_block_size > max_block_size ? temp_block_size : max_block_size; + ns = ns->next; + } + + return max_block_size; +} + +static void +write_complete(void *arg, const struct spdk_nvme_cpl *completion) +{ + struct deallocate_context *context = arg; + + context->writes_completed++; +} + +static void +read_complete(void *arg, const struct spdk_nvme_cpl *completion) +{ + struct deallocate_context *context = arg; + struct ns_entry *ns_entry = context->ns_entry; + int rc; + + rc = memcmp(context->write_buf[context->reads_completed], + context->read_buf[context->reads_completed], spdk_nvme_ns_get_sector_size(ns_entry->ns)); + if (rc == 0) { + context->matches_previous_data++; + } + + rc = memcmp(context->zero_buf, context->read_buf[context->reads_completed], + spdk_nvme_ns_get_sector_size(ns_entry->ns)); + if (rc == 0) { + context->matches_zeroes++; + } + + rc = memcmp(context->FFh_buf, context->read_buf[context->reads_completed], + spdk_nvme_ns_get_sector_size(ns_entry->ns)); + if (rc == 0) { + context->matches_FFh++; + } + context->reads_completed++; +} + +static void +deallocate_complete(void *arg, const struct spdk_nvme_cpl *completion) +{ + struct deallocate_context *context = arg; + + printf("blocks matching previous data: %d\n", context->matches_previous_data); + printf("blocks matching zeroes: %d\n", context->matches_zeroes); + printf("blocks matching 0xFF: %d\n", context->matches_FFh); + printf("Deallocating Blocks 0 to %d with random data.\n", NUM_BLOCKS - 1); + printf("On next read, read value will match deallocated block read value.\n"); + context->deallocate_completed = 1; + context->reads_completed = 0; + context->matches_previous_data = 0; + context->matches_zeroes = 0; + context->matches_FFh = 0; +} + +static void +flush_complete(void *arg, const struct spdk_nvme_cpl *completion) +{ + struct deallocate_context *context = arg; + + context->flush_complete = 1; +} + +static void +deallocate_test(void) +{ + struct ns_entry *ns_entry; + struct spdk_nvme_ctrlr *ctrlr; + const struct spdk_nvme_ctrlr_data *data; + struct deallocate_context context; + struct spdk_nvme_dsm_range range; + uint32_t max_block_size; + int rc, i; + + memset(&context, 0, sizeof(struct deallocate_context)); + max_block_size = get_max_block_size(); + ns_entry = g_namespaces; + + if (max_block_size > 0) { + context.zero_buf = malloc(max_block_size); + } else { + printf("Unable to determine max block size.\n"); + return; + } + + if (context.zero_buf == NULL) { + printf("could not allocate buffer for test.\n"); + return; + } + + context.FFh_buf = malloc(max_block_size); + if (context.FFh_buf == NULL) { + cleanup(&context); + printf("could not allocate buffer for test.\n"); + return; + } + + context.write_buf = calloc(NUM_BLOCKS, sizeof(char *)); + if (context.write_buf == NULL) { + cleanup(&context); + return; + } + + context.read_buf = calloc(NUM_BLOCKS, sizeof(char *)); + if (context.read_buf == NULL) { + printf("could not allocate buffer for test.\n"); + cleanup(&context); + return; + } + + memset(context.zero_buf, 0x00, max_block_size); + memset(context.FFh_buf, 0xFF, max_block_size); + + for (i = 0; i < NUM_BLOCKS; i++) { + context.write_buf[i] = spdk_dma_zmalloc(0x1000, max_block_size, NULL); + if (context.write_buf[i] == NULL) { + printf("could not allocate buffer for test.\n"); + cleanup(&context); + return; + } + + fill_random(context.write_buf[i], 0x1000); + context.read_buf[i] = spdk_dma_zmalloc(0x1000, max_block_size, NULL); + if (context.read_buf[i] == NULL) { + printf("could not allocate buffer for test.\n"); + cleanup(&context); + return; + } + } + + while (ns_entry != NULL) { + + ns_entry->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_entry->ctrlr, NULL, 0); + if (ns_entry->qpair == NULL) { + printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed.\n"); + return; + } + + ctrlr = spdk_nvme_ns_get_ctrlr(ns_entry->ns); + data = spdk_nvme_ctrlr_get_data(ctrlr); + + printf("\nController %-20.20s (%-20.20s)\n", data->mn, data->sn); + printf("Controller PCI vendor:%u PCI subsystem vendor:%u\n", data->vid, data->ssvid); + printf("Namespace Block Size:%u\n", spdk_nvme_ns_get_sector_size(ns_entry->ns)); + printf("Writing Blocks 0 to %d with random data.\n", NUM_BLOCKS); + printf("On next read, read value will match random data.\n"); + + context.ns_entry = ns_entry; + + for (i = 0; i < NUM_BLOCKS; i++) { + rc = spdk_nvme_ns_cmd_write(ns_entry->ns, ns_entry->qpair, context.write_buf[i], + i, + 1, + write_complete, &context, 0); + if (rc) { + printf("Error in nvme command completion, values may be inaccurate.\n"); + } + } + while (context.writes_completed < NUM_BLOCKS) { + spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); + } + + spdk_nvme_ns_cmd_flush(ns_entry->ns, ns_entry->qpair, flush_complete, &context); + while (!context.flush_complete) { + spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); + } + + for (i = 0; i < NUM_BLOCKS; i++) { + rc = spdk_nvme_ns_cmd_read(ns_entry->ns, ns_entry->qpair, context.read_buf[i], + i, /* LBA start */ + 1, /* number of LBAs */ + read_complete, &context, 0); + if (rc) { + printf("Error in nvme command completion, values may be inaccurate.\n"); + } + + /* block after each read command so that we can match the block to the write buffer. */ + while (context.reads_completed <= i) { + spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); + } + } + + context.flush_complete = 0; + range.length = NUM_BLOCKS; + range.starting_lba = 0; + rc = spdk_nvme_ns_cmd_dataset_management(ns_entry->ns, ns_entry->qpair, + SPDK_NVME_DSM_ATTR_DEALLOCATE, &range, 1, deallocate_complete, &context); + if (rc) { + printf("Error in nvme command completion, values may be inaccurate.\n"); + } + + while (!context.deallocate_completed) { + spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); + } + + for (i = 0; i < NUM_BLOCKS; i++) { + rc = spdk_nvme_ns_cmd_read(ns_entry->ns, ns_entry->qpair, context.read_buf[i], + i, /* LBA start */ + 1, /* number of LBAs */ + read_complete, &context, 0); + if (rc) { + printf("Error in nvme command completion, values may be inaccurate.\n"); + } + while (context.reads_completed <= i) { + spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); + } + } + + printf("blocks matching previous data: %d\n", context.matches_previous_data); + printf("blocks matching zeroes: %d\n", context.matches_zeroes); + printf("blocks matching FFh: %d\n", context.matches_FFh); + + /* reset counters in between each namespace. */ + context.matches_previous_data = 0; + context.matches_zeroes = 0; + context.matches_FFh = 0; + context.writes_completed = 0; + context.reads_completed = 0; + context.deallocate_completed = 0; + + spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); + ns_entry = ns_entry->next; + } + cleanup(&context); +} + +static bool +probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, + struct spdk_nvme_ctrlr_opts *opts) +{ + printf("Attaching to %s\n", trid->traddr); + + return true; +} + +static void +attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, + struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) +{ + int num_ns; + struct spdk_nvme_ns *ns; + + printf("Attached to %s\n", trid->traddr); + /* + * Use only the first namespace from each controller since we are testing controller level functionality. + */ + num_ns = spdk_nvme_ctrlr_get_num_ns(ctrlr); + if (num_ns < 1) { + printf("No valid namespaces in controller\n"); + } else { + ns = spdk_nvme_ctrlr_get_ns(ctrlr, 1); + register_ns(ctrlr, ns); + } +} + +static void +cleanup(struct deallocate_context *context) +{ + struct ns_entry *ns_entry = g_namespaces; + int i; + + while (ns_entry) { + struct ns_entry *next = ns_entry->next; + free(ns_entry); + ns_entry = next; + } + for (i = 0; i < NUM_BLOCKS; i++) { + if (context->write_buf[i]) { + spdk_dma_free(context->write_buf[i]); + } else { + break; + } + if (context->read_buf[i]) { + spdk_dma_free(context->read_buf[i]); + } else { + break; + } + } + + free(context->write_buf); + free(context->read_buf); + free(context->zero_buf); + free(context->FFh_buf); +} + +int main(int argc, char **argv) +{ + int rc; + struct spdk_env_opts opts; + + spdk_env_opts_init(&opts); + opts.name = "deallocate_test"; + opts.shm_id = 0; + if (spdk_env_init(&opts) < 0) { + fprintf(stderr, "Unable to initialize SPDK env\n"); + return 1; + } + + printf("Initializing NVMe Controllers\n"); + + rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL); + if (rc != 0) { + fprintf(stderr, "spdk_nvme_probe() failed\n"); + return 1; + } + + if (g_namespaces == NULL) { + fprintf(stderr, "no NVMe controllers found\n"); + return 1; + } + + printf("Initialization complete.\n"); + deallocate_test(); + return 0; +} |