1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2019 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 8 #include "spdk/nvme.h" 9 #include "spdk/env.h" 10 #include "spdk/string.h" 11 12 struct ctrlr_entry { 13 struct spdk_nvme_ctrlr *ctrlr; 14 TAILQ_ENTRY(ctrlr_entry) link; 15 char name[1024]; 16 }; 17 18 struct ns_entry { 19 struct spdk_nvme_ctrlr *ctrlr; 20 struct spdk_nvme_ns *ns; 21 TAILQ_ENTRY(ns_entry) link; 22 struct spdk_nvme_qpair *qpair; 23 }; 24 25 static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers); 26 static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces); 27 static int g_startup_time = 0; 28 29 static bool 30 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 31 struct spdk_nvme_ctrlr_opts *opts) 32 { 33 printf("Attaching to %s\n", trid->traddr); 34 35 return true; 36 } 37 38 static void 39 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 40 struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 41 { 42 43 struct ctrlr_entry *entry; 44 const struct spdk_nvme_ctrlr_data *cdata; 45 46 entry = malloc(sizeof(struct ctrlr_entry)); 47 if (entry == NULL) { 48 perror("ctrlr_entry malloc"); 49 exit(1); 50 } 51 52 printf("Attached to %s\n", trid->traddr); 53 54 /* 55 * spdk_nvme_ctrlr is the logical abstraction in SPDK for an NVMe 56 * controller. During initialization, the IDENTIFY data for the 57 * controller is read using an NVMe admin command, and that data 58 * can be retrieved using spdk_nvme_ctrlr_get_data() to get 59 * detailed information on the controller. Refer to the NVMe 60 * specification for more details on IDENTIFY for NVMe controllers. 61 */ 62 cdata = spdk_nvme_ctrlr_get_data(ctrlr); 63 64 snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn); 65 66 entry->ctrlr = ctrlr; 67 TAILQ_INSERT_TAIL(&g_controllers, entry, link); 68 } 69 70 static void 71 cleanup(void) 72 { 73 struct ns_entry *ns_entry, *tmp_ns_entry; 74 struct ctrlr_entry *ctrlr_entry, *tmp_ctrlr_entry; 75 struct spdk_nvme_detach_ctx *detach_ctx = NULL; 76 77 TAILQ_FOREACH_SAFE(ns_entry, &g_namespaces, link, tmp_ns_entry) { 78 TAILQ_REMOVE(&g_namespaces, ns_entry, link); 79 free(ns_entry); 80 } 81 82 TAILQ_FOREACH_SAFE(ctrlr_entry, &g_controllers, link, tmp_ctrlr_entry) { 83 TAILQ_REMOVE(&g_controllers, ctrlr_entry, link); 84 spdk_nvme_detach_async(ctrlr_entry->ctrlr, &detach_ctx); 85 free(ctrlr_entry); 86 } 87 88 if (detach_ctx) { 89 spdk_nvme_detach_poll(detach_ctx); 90 } 91 } 92 93 static void 94 usage(const char *program_name) 95 { 96 printf("%s [options]", program_name); 97 printf("\n"); 98 printf("options:\n"); 99 printf(" -t The maximum time needed for startup. The unit is us. The value should be bigger than 0.\n"); 100 } 101 102 static int 103 parse_args(int argc, char **argv) 104 { 105 int op; 106 107 while ((op = getopt(argc, argv, "t:")) != -1) { 108 switch (op) { 109 case 't': 110 g_startup_time = spdk_strtol(optarg, 10); 111 if (g_startup_time < 0) { 112 fprintf(stderr, "Invalid nvme startup time\n"); 113 return g_startup_time; 114 } 115 break; 116 default: 117 usage(argv[0]); 118 return 1; 119 } 120 } 121 122 return 0; 123 } 124 125 int 126 main(int argc, char **argv) 127 { 128 int rc; 129 struct spdk_env_opts opts; 130 uint64_t start_tsc, end_tsc, tsc_diff; 131 float time_used_in_usec; 132 133 rc = parse_args(argc, argv); 134 if (rc != 0) { 135 return rc; 136 } 137 138 if (g_startup_time == 0) { 139 usage(argv[0]); 140 return 1; 141 } 142 143 start_tsc = spdk_get_ticks(); 144 /* 145 * SPDK relies on an abstraction around the local environment 146 * named env that handles memory allocation and PCI device operations. 147 * This library must be initialized first. 148 * 149 */ 150 opts.opts_size = sizeof(opts); 151 spdk_env_opts_init(&opts); 152 opts.name = "startup"; 153 opts.shm_id = 0; 154 if (spdk_env_init(&opts) < 0) { 155 fprintf(stderr, "Unable to initialize SPDK env\n"); 156 return 1; 157 } 158 159 printf("Initializing NVMe Controllers\n"); 160 161 162 /* 163 * Start the SPDK NVMe enumeration process. probe_cb will be called 164 * for each NVMe controller found, giving our application a choice on 165 * whether to attach to each controller. attach_cb will then be 166 * called for each controller after the SPDK NVMe driver has completed 167 * initializing the controller we chose to attach. 168 */ 169 rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL); 170 if (rc != 0) { 171 fprintf(stderr, "spdk_nvme_probe() failed\n"); 172 cleanup(); 173 return 1; 174 } 175 176 if (TAILQ_EMPTY(&g_controllers)) { 177 fprintf(stderr, "no NVMe controllers found\n"); 178 return 0; 179 } 180 181 end_tsc = spdk_get_ticks(); 182 tsc_diff = end_tsc - start_tsc; 183 time_used_in_usec = ((float)tsc_diff) * 1000 * 1000 / spdk_get_ticks_hz(); 184 printf("Initialization complete.\n"); 185 printf("Time used:%-16.3f(us).\n", time_used_in_usec); 186 if (time_used_in_usec > g_startup_time) { 187 fprintf(stderr, "Too long time for initialization.\n"); 188 cleanup(); 189 return 1; 190 } 191 cleanup(); 192 return 0; 193 } 194