1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2017 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 8 #include "env_internal.h" 9 10 #include "spdk/version.h" 11 #include "spdk/env_dpdk.h" 12 #include "spdk/log.h" 13 #include "spdk/config.h" 14 15 #include <openssl/ssl.h> 16 #include <openssl/err.h> 17 18 #include <rte_config.h> 19 #include <rte_eal.h> 20 #include <rte_errno.h> 21 #include <rte_vfio.h> 22 23 #define SPDK_ENV_DPDK_DEFAULT_NAME "spdk" 24 #define SPDK_ENV_DPDK_DEFAULT_SHM_ID -1 25 #define SPDK_ENV_DPDK_DEFAULT_MEM_SIZE -1 26 #define SPDK_ENV_DPDK_DEFAULT_MAIN_CORE -1 27 #define SPDK_ENV_DPDK_DEFAULT_MEM_CHANNEL -1 28 #define SPDK_ENV_DPDK_DEFAULT_CORE_MASK "0x1" 29 #define SPDK_ENV_DPDK_DEFAULT_BASE_VIRTADDR 0x200000000000 30 31 #define DPDK_ALLOW_PARAM "--allow" 32 #define DPDK_BLOCK_PARAM "--block" 33 #define DPDK_MAIN_CORE_PARAM "--main-lcore" 34 35 static char **g_eal_cmdline; 36 static int g_eal_cmdline_argcount; 37 static bool g_external_init = true; 38 39 static char * 40 _sprintf_alloc(const char *format, ...) 41 { 42 va_list args; 43 va_list args_copy; 44 char *buf; 45 size_t bufsize; 46 int rc; 47 48 va_start(args, format); 49 50 /* Try with a small buffer first. */ 51 bufsize = 32; 52 53 /* Limit maximum buffer size to something reasonable so we don't loop forever. */ 54 while (bufsize <= 1024 * 1024) { 55 buf = malloc(bufsize); 56 if (buf == NULL) { 57 va_end(args); 58 return NULL; 59 } 60 61 va_copy(args_copy, args); 62 rc = vsnprintf(buf, bufsize, format, args_copy); 63 va_end(args_copy); 64 65 /* 66 * If vsnprintf() returned a count within our current buffer size, we are done. 67 * The count does not include the \0 terminator, so rc == bufsize is not OK. 68 */ 69 if (rc >= 0 && (size_t)rc < bufsize) { 70 va_end(args); 71 return buf; 72 } 73 74 /* 75 * vsnprintf() should return the required space, but some libc versions do not 76 * implement this correctly, so just double the buffer size and try again. 77 * 78 * We don't need the data in buf, so rather than realloc(), use free() and malloc() 79 * again to avoid a copy. 80 */ 81 free(buf); 82 bufsize *= 2; 83 } 84 85 va_end(args); 86 return NULL; 87 } 88 89 void 90 spdk_env_opts_init(struct spdk_env_opts *opts) 91 { 92 if (!opts) { 93 return; 94 } 95 96 memset(opts, 0, sizeof(*opts)); 97 98 opts->name = SPDK_ENV_DPDK_DEFAULT_NAME; 99 opts->core_mask = SPDK_ENV_DPDK_DEFAULT_CORE_MASK; 100 opts->shm_id = SPDK_ENV_DPDK_DEFAULT_SHM_ID; 101 opts->mem_size = SPDK_ENV_DPDK_DEFAULT_MEM_SIZE; 102 opts->main_core = SPDK_ENV_DPDK_DEFAULT_MAIN_CORE; 103 opts->mem_channel = SPDK_ENV_DPDK_DEFAULT_MEM_CHANNEL; 104 opts->base_virtaddr = SPDK_ENV_DPDK_DEFAULT_BASE_VIRTADDR; 105 106 #define SET_FIELD(field, value) \ 107 if (offsetof(struct spdk_env_opts, field) + sizeof(opts->field) <= opts->opts_size) { \ 108 opts->field = value; \ 109 } 110 111 #undef SET_FIELD 112 } 113 114 static void 115 free_args(char **args, int argcount) 116 { 117 int i; 118 119 if (args == NULL) { 120 return; 121 } 122 123 for (i = 0; i < argcount; i++) { 124 free(args[i]); 125 } 126 127 if (argcount) { 128 free(args); 129 } 130 } 131 132 static char ** 133 push_arg(char *args[], int *argcount, char *arg) 134 { 135 char **tmp; 136 137 if (arg == NULL) { 138 SPDK_ERRLOG("%s: NULL arg supplied\n", __func__); 139 free_args(args, *argcount); 140 return NULL; 141 } 142 143 tmp = realloc(args, sizeof(char *) * (*argcount + 1)); 144 if (tmp == NULL) { 145 free(arg); 146 free_args(args, *argcount); 147 return NULL; 148 } 149 150 tmp[*argcount] = arg; 151 (*argcount)++; 152 153 return tmp; 154 } 155 156 #if defined(__linux__) && defined(__x86_64__) 157 158 /* TODO: Can likely get this value from rlimits in the future */ 159 #define SPDK_IOMMU_VA_REQUIRED_WIDTH 48 160 #define VTD_CAP_MGAW_SHIFT 16 161 #define VTD_CAP_MGAW_MASK (0x3F << VTD_CAP_MGAW_SHIFT) 162 #define RD_AMD_CAP_VASIZE_SHIFT 15 163 #define RD_AMD_CAP_VASIZE_MASK (0x7F << RD_AMD_CAP_VASIZE_SHIFT) 164 165 static int 166 get_iommu_width(void) 167 { 168 int width = 0; 169 glob_t glob_results = {}; 170 171 /* Break * and / into separate strings to appease check_format.sh comment style check. */ 172 glob("/sys/devices/virtual/iommu/dmar*" "/intel-iommu/cap", 0, NULL, &glob_results); 173 glob("/sys/class/iommu/ivhd*" "/amd-iommu/cap", GLOB_APPEND, NULL, &glob_results); 174 175 for (size_t i = 0; i < glob_results.gl_pathc; i++) { 176 const char *filename = glob_results.gl_pathv[0]; 177 FILE *file = fopen(filename, "r"); 178 uint64_t cap_reg = 0; 179 180 if (file == NULL) { 181 continue; 182 } 183 184 if (fscanf(file, "%" PRIx64, &cap_reg) == 1) { 185 if (strstr(filename, "intel-iommu") != NULL) { 186 /* We have an Intel IOMMU */ 187 int mgaw = ((cap_reg & VTD_CAP_MGAW_MASK) >> VTD_CAP_MGAW_SHIFT) + 1; 188 189 if (width == 0 || (mgaw > 0 && mgaw < width)) { 190 width = mgaw; 191 } 192 } else if (strstr(filename, "amd-iommu") != NULL) { 193 /* We have an AMD IOMMU */ 194 int mgaw = ((cap_reg & RD_AMD_CAP_VASIZE_MASK) >> RD_AMD_CAP_VASIZE_SHIFT) + 1; 195 196 if (width == 0 || (mgaw > 0 && mgaw < width)) { 197 width = mgaw; 198 } 199 } 200 } 201 202 fclose(file); 203 } 204 205 globfree(&glob_results); 206 return width; 207 } 208 209 #endif 210 211 static int 212 build_eal_cmdline(const struct spdk_env_opts *opts) 213 { 214 int argcount = 0; 215 char **args; 216 bool no_huge; 217 218 args = NULL; 219 no_huge = opts->no_huge || (opts->env_context && strstr(opts->env_context, "--no-huge") != NULL); 220 221 /* set the program name */ 222 args = push_arg(args, &argcount, _sprintf_alloc("%s", opts->name)); 223 if (args == NULL) { 224 return -1; 225 } 226 227 /* disable shared configuration files when in single process mode. This allows for cleaner shutdown */ 228 if (opts->shm_id < 0) { 229 args = push_arg(args, &argcount, _sprintf_alloc("%s", "--no-shconf")); 230 if (args == NULL) { 231 return -1; 232 } 233 } 234 235 /* Either lcore_map or core_mask must be set. If both, or none specified, fail */ 236 if ((opts->core_mask == NULL) == (opts->lcore_map == NULL)) { 237 if (opts->core_mask && opts->lcore_map) { 238 fprintf(stderr, 239 "Both, lcore map and core mask are provided, while only one can be set\n"); 240 } else { 241 fprintf(stderr, "Core mask or lcore map must be specified\n"); 242 } 243 free_args(args, argcount); 244 return -1; 245 } 246 247 if (opts->lcore_map) { 248 /* If lcore list is set, generate --lcores parameter */ 249 args = push_arg(args, &argcount, _sprintf_alloc("--lcores=%s", opts->lcore_map)); 250 } else if (opts->core_mask[0] == '-') { 251 /* 252 * Set the coremask: 253 * 254 * - if it starts with '-', we presume it's literal EAL arguments such 255 * as --lcores. 256 * 257 * - if it starts with '[', we presume it's a core list to use with the 258 * -l option. 259 * 260 * - otherwise, it's a CPU mask of the form "0xff.." as expected by the 261 * -c option. 262 */ 263 args = push_arg(args, &argcount, _sprintf_alloc("%s", opts->core_mask)); 264 } else if (opts->core_mask[0] == '[') { 265 char *l_arg = _sprintf_alloc("-l %s", opts->core_mask + 1); 266 267 if (l_arg != NULL) { 268 int len = strlen(l_arg); 269 270 if (l_arg[len - 1] == ']') { 271 l_arg[len - 1] = '\0'; 272 } 273 } 274 args = push_arg(args, &argcount, l_arg); 275 } else { 276 args = push_arg(args, &argcount, _sprintf_alloc("-c %s", opts->core_mask)); 277 } 278 279 if (args == NULL) { 280 return -1; 281 } 282 283 /* set the memory channel number */ 284 if (opts->mem_channel > 0) { 285 args = push_arg(args, &argcount, _sprintf_alloc("-n %d", opts->mem_channel)); 286 if (args == NULL) { 287 return -1; 288 } 289 } 290 291 /* set the memory size */ 292 if (opts->mem_size >= 0) { 293 args = push_arg(args, &argcount, _sprintf_alloc("-m %d", opts->mem_size)); 294 if (args == NULL) { 295 return -1; 296 } 297 } 298 299 /* set no huge pages */ 300 if (opts->no_huge) { 301 mem_disable_huge_pages(); 302 } 303 304 /* set the main core */ 305 if (opts->main_core > 0) { 306 args = push_arg(args, &argcount, _sprintf_alloc("%s=%d", 307 DPDK_MAIN_CORE_PARAM, opts->main_core)); 308 if (args == NULL) { 309 return -1; 310 } 311 } 312 313 /* set no pci if enabled */ 314 if (opts->no_pci) { 315 args = push_arg(args, &argcount, _sprintf_alloc("--no-pci")); 316 if (args == NULL) { 317 return -1; 318 } 319 } 320 321 if (no_huge) { 322 if (opts->hugepage_single_segments || opts->unlink_hugepage || opts->hugedir) { 323 fprintf(stderr, "--no-huge invalid with other hugepage options\n"); 324 free_args(args, argcount); 325 return -1; 326 } 327 328 if (opts->mem_size < 0) { 329 fprintf(stderr, 330 "Disabling hugepages requires specifying how much memory " 331 "will be allocated using -s parameter\n"); 332 free_args(args, argcount); 333 return -1; 334 } 335 336 /* iova-mode=pa is incompatible with no_huge */ 337 if (opts->iova_mode && 338 (strcmp(opts->iova_mode, "pa") == 0)) { 339 fprintf(stderr, "iova-mode=pa is incompatible with specified " 340 "no-huge parameter\n"); 341 free_args(args, argcount); 342 return -1; 343 } 344 345 args = push_arg(args, &argcount, _sprintf_alloc("--no-huge")); 346 args = push_arg(args, &argcount, _sprintf_alloc("--iova-mode=va")); 347 348 } else { 349 /* create just one hugetlbfs file */ 350 if (opts->hugepage_single_segments) { 351 args = push_arg(args, &argcount, _sprintf_alloc("--single-file-segments")); 352 if (args == NULL) { 353 return -1; 354 } 355 } 356 357 /* unlink hugepages after initialization */ 358 /* Note: Automatically unlink hugepage when shm_id < 0, since it means we're not using 359 * multi-process so we don't need the hugepage links anymore. But we need to make sure 360 * we don't specify --huge-unlink implicitly if --single-file-segments was specified since 361 * DPDK doesn't support that. 362 */ 363 if (opts->unlink_hugepage || 364 (opts->shm_id < 0 && !opts->hugepage_single_segments)) { 365 args = push_arg(args, &argcount, _sprintf_alloc("--huge-unlink")); 366 if (args == NULL) { 367 return -1; 368 } 369 } 370 371 /* use a specific hugetlbfs mount */ 372 if (opts->hugedir) { 373 args = push_arg(args, &argcount, _sprintf_alloc("--huge-dir=%s", opts->hugedir)); 374 if (args == NULL) { 375 return -1; 376 } 377 } 378 } 379 380 if (opts->num_pci_addr) { 381 size_t i; 382 char bdf[32]; 383 struct spdk_pci_addr *pci_addr = 384 opts->pci_blocked ? opts->pci_blocked : opts->pci_allowed; 385 386 for (i = 0; i < opts->num_pci_addr; i++) { 387 spdk_pci_addr_fmt(bdf, 32, &pci_addr[i]); 388 args = push_arg(args, &argcount, _sprintf_alloc("%s=%s", 389 (opts->pci_blocked ? DPDK_BLOCK_PARAM : DPDK_ALLOW_PARAM), 390 bdf)); 391 if (args == NULL) { 392 return -1; 393 } 394 } 395 } 396 397 /* Disable DPDK telemetry information by default, can be modified with env_context. 398 * Prevents creation of dpdk_telemetry socket and additional pthread for it. 399 */ 400 args = push_arg(args, &argcount, _sprintf_alloc("--no-telemetry")); 401 if (args == NULL) { 402 return -1; 403 } 404 405 /* Lower default EAL loglevel to RTE_LOG_NOTICE - normal, but significant messages. 406 * This can be overridden by specifying the same option in opts->env_context 407 */ 408 args = push_arg(args, &argcount, strdup("--log-level=lib.eal:6")); 409 if (args == NULL) { 410 return -1; 411 } 412 413 /* Lower default CRYPTO loglevel to RTE_LOG_WARNING to avoid a ton of init msgs. 414 * This can be overridden by specifying the same option in opts->env_context 415 */ 416 args = push_arg(args, &argcount, strdup("--log-level=lib.cryptodev:5")); 417 if (args == NULL) { 418 return -1; 419 } 420 421 /* Lower default POWER loglevel to RTE_LOG_WARNING to avoid a ton of init msgs. 422 * This can be overridden by specifying the same option in opts->env_context 423 */ 424 args = push_arg(args, &argcount, strdup("--log-level=lib.power:5")); 425 if (args == NULL) { 426 return -1; 427 } 428 429 /* `user1` log type is used by rte_vhost, which prints an INFO log for each received 430 * vhost user message. We don't want that. The same log type is also used by a couple 431 * of other DPDK libs, but none of which we make use right now. If necessary, this can 432 * be overridden via opts->env_context. 433 */ 434 args = push_arg(args, &argcount, strdup("--log-level=user1:6")); 435 if (args == NULL) { 436 return -1; 437 } 438 439 #ifdef __linux__ 440 441 if (opts->iova_mode) { 442 /* iova-mode=pa is incompatible with no_huge */ 443 args = push_arg(args, &argcount, _sprintf_alloc("--iova-mode=%s", opts->iova_mode)); 444 if (args == NULL) { 445 return -1; 446 } 447 } else { 448 /* When using vfio with enable_unsafe_noiommu_mode=Y, we need iova-mode=pa, 449 * but DPDK guesses it should be iova-mode=va. Add a check and force 450 * iova-mode=pa here. */ 451 if (!no_huge && rte_vfio_noiommu_is_enabled()) { 452 args = push_arg(args, &argcount, _sprintf_alloc("--iova-mode=pa")); 453 if (args == NULL) { 454 return -1; 455 } 456 } 457 458 #if defined(__x86_64__) 459 /* DPDK by default guesses that it should be using iova-mode=va so that it can 460 * support running as an unprivileged user. However, some systems (especially 461 * virtual machines) don't have an IOMMU capable of handling the full virtual 462 * address space and DPDK doesn't currently catch that. Add a check in SPDK 463 * and force iova-mode=pa here. */ 464 if (!no_huge && get_iommu_width() < SPDK_IOMMU_VA_REQUIRED_WIDTH) { 465 args = push_arg(args, &argcount, _sprintf_alloc("--iova-mode=pa")); 466 if (args == NULL) { 467 return -1; 468 } 469 } 470 #elif defined(__PPC64__) 471 /* On Linux + PowerPC, DPDK doesn't support VA mode at all. Unfortunately, it doesn't correctly 472 * auto-detect at the moment, so we'll just force it here. */ 473 args = push_arg(args, &argcount, _sprintf_alloc("--iova-mode=pa")); 474 if (args == NULL) { 475 return -1; 476 } 477 #endif 478 } 479 480 481 /* Set the base virtual address - it must be an address that is not in the 482 * ASAN shadow region, otherwise ASAN-enabled builds will ignore the 483 * mmap hint. 484 * 485 * Ref: https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm 486 */ 487 args = push_arg(args, &argcount, _sprintf_alloc("--base-virtaddr=0x%" PRIx64, opts->base_virtaddr)); 488 if (args == NULL) { 489 return -1; 490 } 491 492 /* --match-allocation prevents DPDK from merging or splitting system memory allocations under the hood. 493 * This is critical for RDMA when attempting to use an rte_mempool based buffer pool. If DPDK merges two 494 * physically or IOVA contiguous memory regions, then when we go to allocate a buffer pool, it can split 495 * the memory for a buffer over two allocations meaning the buffer will be split over a memory region. 496 */ 497 498 /* --no-huge is incompatible with --match-allocations 499 * Ref: https://doc.dpdk.org/guides/prog_guide/env_abstraction_layer.html#hugepage-allocation-matching 500 */ 501 if (!no_huge && 502 (!opts->env_context || strstr(opts->env_context, "--legacy-mem") == NULL)) { 503 args = push_arg(args, &argcount, _sprintf_alloc("%s", "--match-allocations")); 504 if (args == NULL) { 505 return -1; 506 } 507 } 508 509 if (opts->shm_id < 0) { 510 args = push_arg(args, &argcount, _sprintf_alloc("--file-prefix=spdk_pid%d", 511 getpid())); 512 if (args == NULL) { 513 return -1; 514 } 515 } else { 516 args = push_arg(args, &argcount, _sprintf_alloc("--file-prefix=spdk%d", 517 opts->shm_id)); 518 if (args == NULL) { 519 return -1; 520 } 521 522 /* set the process type */ 523 args = push_arg(args, &argcount, _sprintf_alloc("--proc-type=auto")); 524 if (args == NULL) { 525 return -1; 526 } 527 } 528 529 /* --vfio-vf-token used for VF initialized by vfio_pci driver. */ 530 if (opts->vf_token) { 531 args = push_arg(args, &argcount, _sprintf_alloc("--vfio-vf-token=%s", 532 opts->vf_token)); 533 if (args == NULL) { 534 return -1; 535 } 536 } 537 #endif 538 539 if (opts->env_context) { 540 char *ptr = strdup(opts->env_context); 541 char *tok = strtok(ptr, " \t"); 542 543 /* DPDK expects each argument as a separate string in the argv 544 * array, so we need to tokenize here in case the caller 545 * passed multiple arguments in the env_context string. 546 */ 547 while (tok != NULL) { 548 args = push_arg(args, &argcount, strdup(tok)); 549 tok = strtok(NULL, " \t"); 550 } 551 552 free(ptr); 553 } 554 555 g_eal_cmdline = args; 556 g_eal_cmdline_argcount = argcount; 557 return argcount; 558 } 559 560 int 561 spdk_env_dpdk_post_init(bool legacy_mem) 562 { 563 int rc; 564 565 rc = pci_env_init(); 566 if (rc < 0) { 567 SPDK_ERRLOG("pci_env_init() failed\n"); 568 return rc; 569 } 570 571 rc = mem_map_init(legacy_mem); 572 if (rc < 0) { 573 SPDK_ERRLOG("Failed to allocate mem_map\n"); 574 return rc; 575 } 576 577 rc = vtophys_init(); 578 if (rc < 0) { 579 SPDK_ERRLOG("Failed to initialize vtophys\n"); 580 return rc; 581 } 582 583 return 0; 584 } 585 586 void 587 spdk_env_dpdk_post_fini(void) 588 { 589 pci_env_fini(); 590 591 free_args(g_eal_cmdline, g_eal_cmdline_argcount); 592 g_eal_cmdline = NULL; 593 g_eal_cmdline_argcount = 0; 594 } 595 596 static void 597 env_copy_opts(struct spdk_env_opts *opts, const struct spdk_env_opts *opts_user, 598 size_t user_opts_size) 599 { 600 opts->opts_size = sizeof(*opts); 601 spdk_env_opts_init(opts); 602 memcpy(opts, opts_user, offsetof(struct spdk_env_opts, opts_size)); 603 604 #define SET_FIELD(field) \ 605 if (offsetof(struct spdk_env_opts, field) + sizeof(opts->field) <= user_opts_size) { \ 606 opts->field = opts_user->field; \ 607 } 608 609 #undef SET_FIELD 610 } 611 612 int 613 spdk_env_init(const struct spdk_env_opts *opts_user) 614 { 615 struct spdk_env_opts opts_local = {}; 616 struct spdk_env_opts *opts = &opts_local; 617 char **dpdk_args = NULL; 618 char *args_print = NULL, *args_tmp = NULL; 619 OPENSSL_INIT_SETTINGS *settings; 620 int i, rc; 621 int orig_optind; 622 bool legacy_mem; 623 size_t min_opts_size, user_opts_size; 624 625 /* If SPDK env has been initialized before, then only pci env requires 626 * reinitialization. 627 */ 628 if (g_external_init == false) { 629 if (opts_user != NULL) { 630 fprintf(stderr, "Invalid arguments to reinitialize SPDK env\n"); 631 return -EINVAL; 632 } 633 634 printf("Starting %s / %s reinitialization...\n", SPDK_VERSION_STRING, rte_version()); 635 pci_env_reinit(); 636 637 return 0; 638 } 639 640 if (opts_user == NULL) { 641 fprintf(stderr, "NULL arguments to initialize DPDK\n"); 642 return -EINVAL; 643 } 644 645 min_opts_size = offsetof(struct spdk_env_opts, opts_size) + sizeof(opts->opts_size); 646 user_opts_size = opts_user->opts_size; 647 if (user_opts_size < min_opts_size) { 648 fprintf(stderr, "Invalid opts->opts_size %d too small, please set opts_size correctly\n", 649 (int)opts_user->opts_size); 650 user_opts_size = min_opts_size; 651 } 652 653 env_copy_opts(opts, opts_user, user_opts_size); 654 655 settings = OPENSSL_INIT_new(); 656 if (!settings) { 657 fprintf(stderr, "Failed to create openssl settings object\n"); 658 ERR_print_errors_fp(stderr); 659 return -ENOMEM; 660 } 661 662 #if OPENSSL_VERSION_NUMBER >= 0x30000000 /* OPENSSL 3.0.0 */ 663 OPENSSL_INIT_set_config_file_flags(settings, 0); 664 #endif 665 rc = OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, settings); 666 if (rc != 1) { 667 fprintf(stderr, "Failed to initialize OpenSSL\n"); 668 ERR_print_errors_fp(stderr); 669 return -EINVAL; 670 } 671 OPENSSL_INIT_free(settings); 672 673 rc = build_eal_cmdline(opts); 674 if (rc < 0) { 675 SPDK_ERRLOG("Invalid arguments to initialize DPDK\n"); 676 return -EINVAL; 677 } 678 679 SPDK_PRINTF("Starting %s / %s initialization...\n", SPDK_VERSION_STRING, rte_version()); 680 681 args_print = _sprintf_alloc("[ DPDK EAL parameters: "); 682 if (args_print == NULL) { 683 return -ENOMEM; 684 } 685 for (i = 0; i < g_eal_cmdline_argcount; i++) { 686 args_tmp = args_print; 687 args_print = _sprintf_alloc("%s%s ", args_tmp, g_eal_cmdline[i]); 688 if (args_print == NULL) { 689 free(args_tmp); 690 return -ENOMEM; 691 } 692 free(args_tmp); 693 } 694 SPDK_PRINTF("%s]\n", args_print); 695 free(args_print); 696 697 /* DPDK rearranges the array we pass to it, so make a copy 698 * before passing so we can still free the individual strings 699 * correctly. 700 */ 701 dpdk_args = calloc(g_eal_cmdline_argcount, sizeof(char *)); 702 if (dpdk_args == NULL) { 703 SPDK_ERRLOG("Failed to allocate dpdk_args\n"); 704 return -ENOMEM; 705 } 706 memcpy(dpdk_args, g_eal_cmdline, sizeof(char *) * g_eal_cmdline_argcount); 707 708 fflush(stdout); 709 orig_optind = optind; 710 optind = 1; 711 rc = rte_eal_init(g_eal_cmdline_argcount, dpdk_args); 712 optind = orig_optind; 713 714 free(dpdk_args); 715 716 if (rc < 0) { 717 if (rte_errno == EALREADY) { 718 SPDK_ERRLOG("DPDK already initialized\n"); 719 } else { 720 SPDK_ERRLOG("Failed to initialize DPDK\n"); 721 } 722 return -rte_errno; 723 } 724 725 legacy_mem = false; 726 if (opts->env_context && strstr(opts->env_context, "--legacy-mem") != NULL) { 727 legacy_mem = true; 728 } 729 730 rc = spdk_env_dpdk_post_init(legacy_mem); 731 if (rc == 0) { 732 g_external_init = false; 733 } 734 735 return rc; 736 } 737 738 /* We use priority 101 which is the highest priority level available 739 * to applications (the toolchains reserve 1 to 100 for internal usage). 740 * This ensures this destructor runs last, after any other destructors 741 * that might still need the environment up and running. 742 */ 743 __attribute__((destructor(101))) static void 744 dpdk_cleanup(void) 745 { 746 /* Only call rte_eal_cleanup if the SPDK env library called rte_eal_init. */ 747 if (!g_external_init) { 748 rte_eal_cleanup(); 749 } 750 } 751 752 void 753 spdk_env_fini(void) 754 { 755 spdk_env_dpdk_post_fini(); 756 } 757 758 bool 759 spdk_env_dpdk_external_init(void) 760 { 761 return g_external_init; 762 } 763