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