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