/*- * 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" #include "spdk/string.h" struct ctrlr_entry { struct spdk_nvme_ctrlr *ctrlr; struct ctrlr_entry *next; char name[1024]; }; struct ns_entry { struct spdk_nvme_ctrlr *ctrlr; struct spdk_nvme_ns *ns; struct ns_entry *next; struct spdk_nvme_qpair *qpair; }; static struct ctrlr_entry *g_controllers = NULL; static struct ns_entry *g_namespaces = NULL; static int g_startup_time = 0; 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) { struct ctrlr_entry *entry; const struct spdk_nvme_ctrlr_data *cdata; entry = malloc(sizeof(struct ctrlr_entry)); if (entry == NULL) { perror("ctrlr_entry malloc"); exit(1); } printf("Attached to %s\n", trid->traddr); /* * spdk_nvme_ctrlr is the logical abstraction in SPDK for an NVMe * controller. During initialization, the IDENTIFY data for the * controller is read using an NVMe admin command, and that data * can be retrieved using spdk_nvme_ctrlr_get_data() to get * detailed information on the controller. Refer to the NVMe * specification for more details on IDENTIFY for NVMe controllers. */ cdata = spdk_nvme_ctrlr_get_data(ctrlr); snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn); entry->ctrlr = ctrlr; entry->next = g_controllers; g_controllers = entry; } static void cleanup(void) { struct ns_entry *ns_entry = g_namespaces; struct ctrlr_entry *ctrlr_entry = g_controllers; while (ns_entry) { struct ns_entry *next = ns_entry->next; free(ns_entry); ns_entry = next; } while (ctrlr_entry) { struct ctrlr_entry *next = ctrlr_entry->next; spdk_nvme_detach(ctrlr_entry->ctrlr); free(ctrlr_entry); ctrlr_entry = next; } } static void usage(const char *program_name) { printf("%s [options]", program_name); printf("\n"); printf("options:\n"); printf(" -t The maximum time needed for startup. The unit is us. The value should be bigger than 0.\n"); } static int parse_args(int argc, char **argv) { int op; while ((op = getopt(argc, argv, "t:")) != -1) { switch (op) { case 't': g_startup_time = spdk_strtol(optarg, 10); if (g_startup_time < 0) { fprintf(stderr, "Invalid nvme startup time\n"); return g_startup_time; } break; default: usage(argv[0]); return 1; } } return 0; } int main(int argc, char **argv) { int rc; struct spdk_env_opts opts; uint64_t start_tsc, end_tsc, tsc_diff; float time_used_in_usec; rc = parse_args(argc, argv); if (rc != 0) { return rc; } if (g_startup_time == 0) { usage(argv[0]); return 1; } start_tsc = spdk_get_ticks(); /* * SPDK relies on an abstraction around the local environment * named env that handles memory allocation and PCI device operations. * This library must be initialized first. * */ spdk_env_opts_init(&opts); opts.name = "startup"; 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"); /* * Start the SPDK NVMe enumeration process. probe_cb will be called * for each NVMe controller found, giving our application a choice on * whether to attach to each controller. attach_cb will then be * called for each controller after the SPDK NVMe driver has completed * initializing the controller we chose to attach. */ rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL); if (rc != 0) { fprintf(stderr, "spdk_nvme_probe() failed\n"); cleanup(); return 1; } if (g_controllers == NULL) { fprintf(stderr, "no NVMe controllers found\n"); return 0; } end_tsc = spdk_get_ticks(); tsc_diff = end_tsc - start_tsc; time_used_in_usec = ((float)tsc_diff) * 1000 * 1000 / spdk_get_ticks_hz(); printf("Initialization complete.\n"); printf("Time used:%-16.3f(us).\n", time_used_in_usec); if (time_used_in_usec > g_startup_time) { fprintf(stderr, "Too long time for initialization.\n"); cleanup(); return 1; } cleanup(); return 0; }