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/event.h" 37 #include "spdk/nvme.h" 38 #include "spdk/string.h" 39 #include "spdk/thread.h" 40 41 static char g_path[256]; 42 static struct spdk_poller *g_poller; 43 /* default sleep time in ms */ 44 static uint32_t g_sleep_time = 1000; 45 46 struct ctrlr_entry { 47 struct spdk_nvme_ctrlr *ctrlr; 48 TAILQ_ENTRY(ctrlr_entry) link; 49 }; 50 51 static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers); 52 53 static void 54 cleanup(void) 55 { 56 struct ctrlr_entry *ctrlr_entry, *tmp; 57 struct spdk_nvme_detach_ctx *detach_ctx = NULL; 58 59 TAILQ_FOREACH_SAFE(ctrlr_entry, &g_controllers, link, tmp) { 60 TAILQ_REMOVE(&g_controllers, ctrlr_entry, link); 61 spdk_nvme_detach_async(ctrlr_entry->ctrlr, &detach_ctx); 62 free(ctrlr_entry); 63 } 64 65 if (detach_ctx) { 66 spdk_nvme_detach_poll(detach_ctx); 67 } 68 } 69 70 static void 71 usage(char *executable_name) 72 { 73 printf("%s [options]\n", executable_name); 74 printf("options:\n"); 75 printf(" -i shared memory ID [required]\n"); 76 printf(" -m mask core mask for DPDK\n"); 77 printf(" -n channel number of memory channels used for DPDK\n"); 78 printf(" -p core main (primary) core for DPDK\n"); 79 printf(" -s size memory size in MB for DPDK\n"); 80 printf(" -t msec sleep time (ms) between checking for admin completions\n"); 81 printf(" -H show this usage\n"); 82 } 83 84 static bool 85 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 86 struct spdk_nvme_ctrlr_opts *opts) 87 { 88 /* 89 * Set the io_queue_size to UINT16_MAX to initialize 90 * the controller with the possible largest queue size. 91 */ 92 opts->io_queue_size = UINT16_MAX; 93 return true; 94 } 95 96 static void 97 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 98 struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 99 { 100 struct ctrlr_entry *entry; 101 102 entry = malloc(sizeof(struct ctrlr_entry)); 103 if (entry == NULL) { 104 fprintf(stderr, "Malloc error\n"); 105 exit(1); 106 } 107 108 entry->ctrlr = ctrlr; 109 TAILQ_INSERT_TAIL(&g_controllers, entry, link); 110 } 111 112 static int 113 stub_sleep(void *arg) 114 { 115 struct ctrlr_entry *ctrlr_entry, *tmp; 116 117 usleep(g_sleep_time * 1000); 118 TAILQ_FOREACH_SAFE(ctrlr_entry, &g_controllers, link, tmp) { 119 spdk_nvme_ctrlr_process_admin_completions(ctrlr_entry->ctrlr); 120 } 121 return 0; 122 } 123 124 static void 125 stub_start(void *arg1) 126 { 127 int shm_id = (intptr_t)arg1; 128 129 spdk_unaffinitize_thread(); 130 131 if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL) != 0) { 132 fprintf(stderr, "spdk_nvme_probe() failed\n"); 133 exit(1); 134 } 135 136 snprintf(g_path, sizeof(g_path), "/var/run/spdk_stub%d", shm_id); 137 if (mknod(g_path, S_IFREG, 0) != 0) { 138 fprintf(stderr, "could not create sentinel file %s\n", g_path); 139 exit(1); 140 } 141 142 g_poller = SPDK_POLLER_REGISTER(stub_sleep, NULL, 0); 143 } 144 145 static void 146 stub_shutdown(void) 147 { 148 spdk_poller_unregister(&g_poller); 149 unlink(g_path); 150 spdk_app_stop(0); 151 } 152 153 int 154 main(int argc, char **argv) 155 { 156 int ch; 157 struct spdk_app_opts opts = {}; 158 long int val; 159 160 /* default value in opts structure */ 161 spdk_app_opts_init(&opts, sizeof(opts)); 162 163 opts.name = "stub"; 164 opts.rpc_addr = NULL; 165 166 while ((ch = getopt(argc, argv, "i:m:n:p:s:t:H")) != -1) { 167 if (ch == 'm') { 168 opts.reactor_mask = optarg; 169 } else if (ch == '?' || ch == 'H') { 170 usage(argv[0]); 171 exit(EXIT_SUCCESS); 172 } else { 173 val = spdk_strtol(optarg, 10); 174 if (val < 0) { 175 fprintf(stderr, "Converting a string to integer failed\n"); 176 exit(1); 177 } 178 switch (ch) { 179 case 'i': 180 opts.shm_id = val; 181 break; 182 case 'n': 183 opts.mem_channel = val; 184 break; 185 case 'p': 186 opts.main_core = val; 187 break; 188 case 's': 189 opts.mem_size = val; 190 break; 191 case 't': 192 g_sleep_time = val; 193 break; 194 default: 195 usage(argv[0]); 196 exit(EXIT_FAILURE); 197 } 198 } 199 } 200 201 if (opts.shm_id < 0) { 202 fprintf(stderr, "%s: -i shared memory ID must be specified\n", argv[0]); 203 usage(argv[0]); 204 exit(1); 205 } 206 207 opts.shutdown_cb = stub_shutdown; 208 209 ch = spdk_app_start(&opts, stub_start, (void *)(intptr_t)opts.shm_id); 210 211 cleanup(); 212 spdk_app_fini(); 213 214 return ch; 215 } 216