1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "spdk/stdinc.h" 35 36 #include "spdk/nvme.h" 37 #include "spdk/env.h" 38 #include "spdk/string.h" 39 40 struct ctrlr_entry { 41 struct spdk_nvme_ctrlr *ctrlr; 42 TAILQ_ENTRY(ctrlr_entry) link; 43 char name[1024]; 44 }; 45 46 struct ns_entry { 47 struct spdk_nvme_ctrlr *ctrlr; 48 struct spdk_nvme_ns *ns; 49 TAILQ_ENTRY(ns_entry) link; 50 struct spdk_nvme_qpair *qpair; 51 }; 52 53 static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers); 54 static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces); 55 static int g_startup_time = 0; 56 57 static bool 58 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 59 struct spdk_nvme_ctrlr_opts *opts) 60 { 61 printf("Attaching to %s\n", trid->traddr); 62 63 return true; 64 } 65 66 static void 67 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 68 struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 69 { 70 71 struct ctrlr_entry *entry; 72 const struct spdk_nvme_ctrlr_data *cdata; 73 74 entry = malloc(sizeof(struct ctrlr_entry)); 75 if (entry == NULL) { 76 perror("ctrlr_entry malloc"); 77 exit(1); 78 } 79 80 printf("Attached to %s\n", trid->traddr); 81 82 /* 83 * spdk_nvme_ctrlr is the logical abstraction in SPDK for an NVMe 84 * controller. During initialization, the IDENTIFY data for the 85 * controller is read using an NVMe admin command, and that data 86 * can be retrieved using spdk_nvme_ctrlr_get_data() to get 87 * detailed information on the controller. Refer to the NVMe 88 * specification for more details on IDENTIFY for NVMe controllers. 89 */ 90 cdata = spdk_nvme_ctrlr_get_data(ctrlr); 91 92 snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn); 93 94 entry->ctrlr = ctrlr; 95 TAILQ_INSERT_TAIL(&g_controllers, entry, link); 96 } 97 98 static void 99 cleanup(void) 100 { 101 struct ns_entry *ns_entry, *tmp_ns_entry; 102 struct ctrlr_entry *ctrlr_entry, *tmp_ctrlr_entry; 103 struct spdk_nvme_detach_ctx *detach_ctx = NULL; 104 105 TAILQ_FOREACH_SAFE(ns_entry, &g_namespaces, link, tmp_ns_entry) { 106 TAILQ_REMOVE(&g_namespaces, ns_entry, link); 107 free(ns_entry); 108 } 109 110 TAILQ_FOREACH_SAFE(ctrlr_entry, &g_controllers, link, tmp_ctrlr_entry) { 111 TAILQ_REMOVE(&g_controllers, ctrlr_entry, link); 112 spdk_nvme_detach_async(ctrlr_entry->ctrlr, &detach_ctx); 113 free(ctrlr_entry); 114 } 115 116 while (detach_ctx && spdk_nvme_detach_poll_async(detach_ctx) == -EAGAIN) { 117 ; 118 } 119 } 120 121 static void 122 usage(const char *program_name) 123 { 124 printf("%s [options]", program_name); 125 printf("\n"); 126 printf("options:\n"); 127 printf(" -t The maximum time needed for startup. The unit is us. The value should be bigger than 0.\n"); 128 } 129 130 static int 131 parse_args(int argc, char **argv) 132 { 133 int op; 134 135 while ((op = getopt(argc, argv, "t:")) != -1) { 136 switch (op) { 137 case 't': 138 g_startup_time = spdk_strtol(optarg, 10); 139 if (g_startup_time < 0) { 140 fprintf(stderr, "Invalid nvme startup time\n"); 141 return g_startup_time; 142 } 143 break; 144 default: 145 usage(argv[0]); 146 return 1; 147 } 148 } 149 150 return 0; 151 } 152 153 int main(int argc, char **argv) 154 { 155 int rc; 156 struct spdk_env_opts opts; 157 uint64_t start_tsc, end_tsc, tsc_diff; 158 float time_used_in_usec; 159 160 rc = parse_args(argc, argv); 161 if (rc != 0) { 162 return rc; 163 } 164 165 if (g_startup_time == 0) { 166 usage(argv[0]); 167 return 1; 168 } 169 170 start_tsc = spdk_get_ticks(); 171 /* 172 * SPDK relies on an abstraction around the local environment 173 * named env that handles memory allocation and PCI device operations. 174 * This library must be initialized first. 175 * 176 */ 177 spdk_env_opts_init(&opts); 178 opts.name = "startup"; 179 opts.shm_id = 0; 180 if (spdk_env_init(&opts) < 0) { 181 fprintf(stderr, "Unable to initialize SPDK env\n"); 182 return 1; 183 } 184 185 printf("Initializing NVMe Controllers\n"); 186 187 188 /* 189 * Start the SPDK NVMe enumeration process. probe_cb will be called 190 * for each NVMe controller found, giving our application a choice on 191 * whether to attach to each controller. attach_cb will then be 192 * called for each controller after the SPDK NVMe driver has completed 193 * initializing the controller we chose to attach. 194 */ 195 rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL); 196 if (rc != 0) { 197 fprintf(stderr, "spdk_nvme_probe() failed\n"); 198 cleanup(); 199 return 1; 200 } 201 202 if (TAILQ_EMPTY(&g_controllers)) { 203 fprintf(stderr, "no NVMe controllers found\n"); 204 return 0; 205 } 206 207 end_tsc = spdk_get_ticks(); 208 tsc_diff = end_tsc - start_tsc; 209 time_used_in_usec = ((float)tsc_diff) * 1000 * 1000 / spdk_get_ticks_hz(); 210 printf("Initialization complete.\n"); 211 printf("Time used:%-16.3f(us).\n", time_used_in_usec); 212 if (time_used_in_usec > g_startup_time) { 213 fprintf(stderr, "Too long time for initialization.\n"); 214 cleanup(); 215 return 1; 216 } 217 cleanup(); 218 return 0; 219 } 220