summaryrefslogtreecommitdiffstats
path: root/src/spdk/test/nvme/deallocated_value/deallocated_value.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/test/nvme/deallocated_value/deallocated_value.c')
-rw-r--r--src/spdk/test/nvme/deallocated_value/deallocated_value.c447
1 files changed, 447 insertions, 0 deletions
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 000000000..91600e83e
--- /dev/null
+++ b/src/spdk/test/nvme/deallocated_value/deallocated_value.c
@@ -0,0 +1,447 @@
+/*-
+ * 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_zmalloc(0x1000, max_block_size, NULL, SPDK_ENV_LCORE_ID_ANY,
+ SPDK_MALLOC_DMA);
+ 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_zmalloc(0x1000, max_block_size, NULL, SPDK_ENV_LCORE_ID_ANY,
+ SPDK_MALLOC_DMA);
+ 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 - 1);
+ 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 && context->write_buf[i]) {
+ spdk_free(context->write_buf[i]);
+ } else {
+ break;
+ }
+ if (context->read_buf && context->read_buf[i]) {
+ spdk_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;
+}