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 "env_internal.h" 37 38 #include "spdk/version.h" 39 40 #include <rte_config.h> 41 #include <rte_eal.h> 42 43 #define SPDK_ENV_DPDK_DEFAULT_NAME "spdk" 44 #define SPDK_ENV_DPDK_DEFAULT_SHM_ID -1 45 #define SPDK_ENV_DPDK_DEFAULT_MEM_SIZE -1 46 #define SPDK_ENV_DPDK_DEFAULT_MASTER_CORE -1 47 #define SPDK_ENV_DPDK_DEFAULT_MEM_CHANNEL -1 48 #define SPDK_ENV_DPDK_DEFAULT_CORE_MASK "0x1" 49 50 static char **eal_cmdline; 51 static int eal_cmdline_argcount; 52 53 static char * 54 _sprintf_alloc(const char *format, ...) 55 { 56 va_list args; 57 va_list args_copy; 58 char *buf; 59 size_t bufsize; 60 int rc; 61 62 va_start(args, format); 63 64 /* Try with a small buffer first. */ 65 bufsize = 32; 66 67 /* Limit maximum buffer size to something reasonable so we don't loop forever. */ 68 while (bufsize <= 1024 * 1024) { 69 buf = malloc(bufsize); 70 if (buf == NULL) { 71 va_end(args); 72 return NULL; 73 } 74 75 va_copy(args_copy, args); 76 rc = vsnprintf(buf, bufsize, format, args_copy); 77 va_end(args_copy); 78 79 /* 80 * If vsnprintf() returned a count within our current buffer size, we are done. 81 * The count does not include the \0 terminator, so rc == bufsize is not OK. 82 */ 83 if (rc >= 0 && (size_t)rc < bufsize) { 84 va_end(args); 85 return buf; 86 } 87 88 /* 89 * vsnprintf() should return the required space, but some libc versions do not 90 * implement this correctly, so just double the buffer size and try again. 91 * 92 * We don't need the data in buf, so rather than realloc(), use free() and malloc() 93 * again to avoid a copy. 94 */ 95 free(buf); 96 bufsize *= 2; 97 } 98 99 va_end(args); 100 return NULL; 101 } 102 103 #if RTE_VERSION >= RTE_VERSION_NUM(18, 05, 0, 0) 104 const char *eal_get_runtime_dir(void); 105 #endif 106 107 static void 108 spdk_env_unlink_shared_files(void) 109 { 110 char buffer[PATH_MAX]; 111 112 #if RTE_VERSION < RTE_VERSION_NUM(18, 05, 0, 0) 113 snprintf(buffer, PATH_MAX, "/var/run/.spdk_pid%d_hugepage_info", getpid()); 114 if (unlink(buffer)) { 115 fprintf(stderr, "Unable to unlink shared memory file: %s. Error code: %d\n", buffer, errno); 116 } 117 #else 118 DIR *dir; 119 struct dirent *d; 120 121 dir = opendir(eal_get_runtime_dir()); 122 if (!dir) { 123 fprintf(stderr, "Failed to open DPDK runtime dir: %s (%d)\n", eal_get_runtime_dir(), errno); 124 return; 125 } 126 127 while ((d = readdir(dir)) != NULL) { 128 if (d->d_type != DT_REG) { 129 continue; 130 } 131 132 snprintf(buffer, PATH_MAX, "%s/%s", eal_get_runtime_dir(), d->d_name); 133 if (unlink(buffer)) { 134 fprintf(stderr, "Unable to unlink shared memory file: %s. Error code: %d\n", buffer, errno); 135 } 136 } 137 138 closedir(dir); 139 #endif 140 } 141 142 void 143 spdk_env_opts_init(struct spdk_env_opts *opts) 144 { 145 if (!opts) { 146 return; 147 } 148 149 memset(opts, 0, sizeof(*opts)); 150 151 opts->name = SPDK_ENV_DPDK_DEFAULT_NAME; 152 opts->core_mask = SPDK_ENV_DPDK_DEFAULT_CORE_MASK; 153 opts->shm_id = SPDK_ENV_DPDK_DEFAULT_SHM_ID; 154 opts->mem_size = SPDK_ENV_DPDK_DEFAULT_MEM_SIZE; 155 opts->master_core = SPDK_ENV_DPDK_DEFAULT_MASTER_CORE; 156 opts->mem_channel = SPDK_ENV_DPDK_DEFAULT_MEM_CHANNEL; 157 } 158 159 static void 160 spdk_free_args(char **args, int argcount) 161 { 162 int i; 163 164 for (i = 0; i < argcount; i++) { 165 free(args[i]); 166 } 167 168 if (argcount) { 169 free(args); 170 } 171 } 172 173 static char ** 174 spdk_push_arg(char *args[], int *argcount, char *arg) 175 { 176 char **tmp; 177 178 if (arg == NULL) { 179 fprintf(stderr, "%s: NULL arg supplied\n", __func__); 180 spdk_free_args(args, *argcount); 181 return NULL; 182 } 183 184 tmp = realloc(args, sizeof(char *) * (*argcount + 1)); 185 if (tmp == NULL) { 186 spdk_free_args(args, *argcount); 187 return NULL; 188 } 189 190 tmp[*argcount] = arg; 191 (*argcount)++; 192 193 return tmp; 194 } 195 196 static void 197 spdk_destruct_eal_cmdline(void) 198 { 199 spdk_free_args(eal_cmdline, eal_cmdline_argcount); 200 } 201 202 203 static int 204 spdk_build_eal_cmdline(const struct spdk_env_opts *opts) 205 { 206 int argcount = 0; 207 char **args; 208 209 args = NULL; 210 211 /* set the program name */ 212 args = spdk_push_arg(args, &argcount, _sprintf_alloc("%s", opts->name)); 213 if (args == NULL) { 214 return -1; 215 } 216 217 /* disable shared configuration files when in single process mode. This allows for cleaner shutdown */ 218 if (opts->shm_id < 0) { 219 args = spdk_push_arg(args, &argcount, _sprintf_alloc("%s", "--no-shconf")); 220 if (args == NULL) { 221 return -1; 222 } 223 } 224 225 /* set the coremask */ 226 /* NOTE: If coremask starts with '[' and ends with ']' it is a core list 227 */ 228 if (opts->core_mask[0] == '[') { 229 char *l_arg = _sprintf_alloc("-l %s", opts->core_mask + 1); 230 int len = strlen(l_arg); 231 if (l_arg[len - 1] == ']') { 232 l_arg[len - 1] = '\0'; 233 } 234 args = spdk_push_arg(args, &argcount, l_arg); 235 } else { 236 args = spdk_push_arg(args, &argcount, _sprintf_alloc("-c %s", opts->core_mask)); 237 } 238 239 if (args == NULL) { 240 return -1; 241 } 242 243 /* set the memory channel number */ 244 if (opts->mem_channel > 0) { 245 args = spdk_push_arg(args, &argcount, _sprintf_alloc("-n %d", opts->mem_channel)); 246 if (args == NULL) { 247 return -1; 248 } 249 } 250 251 /* set the memory size */ 252 if (opts->mem_size >= 0) { 253 args = spdk_push_arg(args, &argcount, _sprintf_alloc("-m %d", opts->mem_size)); 254 if (args == NULL) { 255 return -1; 256 } 257 } 258 259 /* set the master core */ 260 if (opts->master_core > 0) { 261 args = spdk_push_arg(args, &argcount, _sprintf_alloc("--master-lcore=%d", 262 opts->master_core)); 263 if (args == NULL) { 264 return -1; 265 } 266 } 267 268 /* set no pci if enabled */ 269 if (opts->no_pci) { 270 args = spdk_push_arg(args, &argcount, _sprintf_alloc("--no-pci")); 271 if (args == NULL) { 272 return -1; 273 } 274 } 275 276 /* create just one hugetlbfs file */ 277 if (opts->hugepage_single_segments) { 278 args = spdk_push_arg(args, &argcount, _sprintf_alloc("--single-file-segments")); 279 if (args == NULL) { 280 return -1; 281 } 282 } 283 284 /* unlink hugepages after initialization */ 285 if (opts->unlink_hugepage) { 286 args = spdk_push_arg(args, &argcount, _sprintf_alloc("--huge-unlink")); 287 if (args == NULL) { 288 return -1; 289 } 290 } 291 292 #if RTE_VERSION >= RTE_VERSION_NUM(18, 05, 0, 0) && RTE_VERSION < RTE_VERSION_NUM(18, 8, 0, 0) 293 /* SPDK holds off with using the new memory management model just yet */ 294 args = spdk_push_arg(args, &argcount, _sprintf_alloc("--legacy-mem")); 295 if (args == NULL) { 296 return -1; 297 } 298 #endif 299 300 if (opts->num_pci_addr) { 301 size_t i; 302 char bdf[32]; 303 struct spdk_pci_addr *pci_addr = 304 opts->pci_blacklist ? opts->pci_blacklist : opts->pci_whitelist; 305 306 for (i = 0; i < opts->num_pci_addr; i++) { 307 spdk_pci_addr_fmt(bdf, 32, &pci_addr[i]); 308 args = spdk_push_arg(args, &argcount, _sprintf_alloc("%s=%s", 309 (opts->pci_blacklist ? "--pci-blacklist" : "--pci-whitelist"), 310 bdf)); 311 if (args == NULL) { 312 return -1; 313 } 314 } 315 } 316 317 #ifdef __linux__ 318 if (opts->shm_id < 0) { 319 args = spdk_push_arg(args, &argcount, _sprintf_alloc("--file-prefix=spdk_pid%d", 320 getpid())); 321 if (args == NULL) { 322 return -1; 323 } 324 } else { 325 args = spdk_push_arg(args, &argcount, _sprintf_alloc("--file-prefix=spdk%d", 326 opts->shm_id)); 327 if (args == NULL) { 328 return -1; 329 } 330 331 /* Set the base virtual address - it must be an address that is not in the 332 * ASAN shadow region, otherwise ASAN-enabled builds will ignore the 333 * mmap hint. 334 * 335 * Ref: https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm 336 */ 337 args = spdk_push_arg(args, &argcount, _sprintf_alloc("--base-virtaddr=0x200000000000")); 338 if (args == NULL) { 339 return -1; 340 } 341 342 /* set the process type */ 343 args = spdk_push_arg(args, &argcount, _sprintf_alloc("--proc-type=auto")); 344 if (args == NULL) { 345 return -1; 346 } 347 } 348 #endif 349 350 eal_cmdline = args; 351 eal_cmdline_argcount = argcount; 352 if (atexit(spdk_destruct_eal_cmdline) != 0) { 353 fprintf(stderr, "Failed to register cleanup handler\n"); 354 } 355 356 return argcount; 357 } 358 359 int spdk_env_init(const struct spdk_env_opts *opts) 360 { 361 char **dpdk_args = NULL; 362 int i, rc; 363 int orig_optind; 364 365 rc = spdk_build_eal_cmdline(opts); 366 if (rc < 0) { 367 fprintf(stderr, "Invalid arguments to initialize DPDK\n"); 368 return -1; 369 } 370 371 printf("Starting %s / %s initialization...\n", SPDK_VERSION_STRING, rte_version()); 372 printf("[ DPDK EAL parameters: "); 373 for (i = 0; i < eal_cmdline_argcount; i++) { 374 printf("%s ", eal_cmdline[i]); 375 } 376 printf("]\n"); 377 378 /* DPDK rearranges the array we pass to it, so make a copy 379 * before passing so we can still free the individual strings 380 * correctly. 381 */ 382 dpdk_args = calloc(eal_cmdline_argcount, sizeof(char *)); 383 if (dpdk_args == NULL) { 384 fprintf(stderr, "Failed to allocate dpdk_args\n"); 385 return -1; 386 } 387 memcpy(dpdk_args, eal_cmdline, sizeof(char *) * eal_cmdline_argcount); 388 389 fflush(stdout); 390 orig_optind = optind; 391 optind = 1; 392 rc = rte_eal_init(eal_cmdline_argcount, dpdk_args); 393 optind = orig_optind; 394 395 free(dpdk_args); 396 397 if (rc < 0) { 398 fprintf(stderr, "Failed to initialize DPDK\n"); 399 return -1; 400 } 401 402 if (opts->shm_id < 0 && !opts->hugepage_single_segments) { 403 /* 404 * Unlink hugepage and config info files after init. This will ensure they get 405 * deleted on app exit, even if the app crashes and does not exit normally. 406 * Only do this when not in multi-process mode, since for multi-process other 407 * apps will need to open these files. These files are not created for 408 * "single file segments". 409 */ 410 spdk_env_unlink_shared_files(); 411 } 412 413 if (spdk_mem_map_init() < 0) { 414 fprintf(stderr, "Failed to allocate mem_map\n"); 415 return -1; 416 } 417 if (spdk_vtophys_init() < 0) { 418 fprintf(stderr, "Failed to initialize vtophys\n"); 419 return -1; 420 } 421 422 return 0; 423 } 424