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 *ptr = strdup(opts->env_context); 551 char *tok = strtok(ptr, " \t"); 552 553 /* DPDK expects each argument as a separate string in the argv 554 * array, so we need to tokenize here in case the caller 555 * passed multiple arguments in the env_context string. 556 */ 557 while (tok != NULL) { 558 args = push_arg(args, &argcount, strdup(tok)); 559 tok = strtok(NULL, " \t"); 560 } 561 562 free(ptr); 563 } 564 565 g_eal_cmdline = args; 566 g_eal_cmdline_argcount = argcount; 567 return argcount; 568 } 569 570 int 571 spdk_env_dpdk_post_init(bool legacy_mem) 572 { 573 int rc; 574 575 rc = pci_env_init(); 576 if (rc < 0) { 577 SPDK_ERRLOG("pci_env_init() failed\n"); 578 return rc; 579 } 580 581 rc = mem_map_init(legacy_mem); 582 if (rc < 0) { 583 SPDK_ERRLOG("Failed to allocate mem_map\n"); 584 return rc; 585 } 586 587 rc = vtophys_init(); 588 if (rc < 0) { 589 SPDK_ERRLOG("Failed to initialize vtophys\n"); 590 return rc; 591 } 592 593 return 0; 594 } 595 596 void 597 spdk_env_dpdk_post_fini(void) 598 { 599 pci_env_fini(); 600 601 free_args(g_eal_cmdline, g_eal_cmdline_argcount); 602 g_eal_cmdline = NULL; 603 g_eal_cmdline_argcount = 0; 604 } 605 606 static void 607 env_copy_opts(struct spdk_env_opts *opts, const struct spdk_env_opts *opts_user, 608 size_t user_opts_size) 609 { 610 opts->opts_size = sizeof(*opts); 611 spdk_env_opts_init(opts); 612 memcpy(opts, opts_user, offsetof(struct spdk_env_opts, opts_size)); 613 614 #define SET_FIELD(field) \ 615 if (offsetof(struct spdk_env_opts, field) + sizeof(opts->field) <= user_opts_size) { \ 616 opts->field = opts_user->field; \ 617 } 618 619 SET_FIELD(enforce_numa); 620 621 #undef SET_FIELD 622 } 623 624 int 625 spdk_env_init(const struct spdk_env_opts *opts_user) 626 { 627 struct spdk_env_opts opts_local = {}; 628 struct spdk_env_opts *opts = &opts_local; 629 char **dpdk_args = NULL; 630 char *args_print = NULL, *args_tmp = NULL; 631 OPENSSL_INIT_SETTINGS *settings; 632 int i, rc; 633 int orig_optind; 634 bool legacy_mem; 635 size_t min_opts_size, user_opts_size; 636 637 /* If SPDK env has been initialized before, then only pci env requires 638 * reinitialization. 639 */ 640 if (g_external_init == false) { 641 if (opts_user != NULL) { 642 fprintf(stderr, "Invalid arguments to reinitialize SPDK env\n"); 643 return -EINVAL; 644 } 645 646 printf("Starting %s / %s reinitialization...\n", SPDK_VERSION_STRING, rte_version()); 647 pci_env_reinit(); 648 649 return 0; 650 } 651 652 if (opts_user == NULL) { 653 fprintf(stderr, "NULL arguments to initialize DPDK\n"); 654 return -EINVAL; 655 } 656 657 min_opts_size = offsetof(struct spdk_env_opts, opts_size) + sizeof(opts->opts_size); 658 user_opts_size = opts_user->opts_size; 659 if (user_opts_size < min_opts_size) { 660 fprintf(stderr, "Invalid opts->opts_size %d too small, please set opts_size correctly\n", 661 (int)opts_user->opts_size); 662 user_opts_size = min_opts_size; 663 } 664 665 env_copy_opts(opts, opts_user, user_opts_size); 666 667 settings = OPENSSL_INIT_new(); 668 if (!settings) { 669 fprintf(stderr, "Failed to create openssl settings object\n"); 670 ERR_print_errors_fp(stderr); 671 return -ENOMEM; 672 } 673 674 #if OPENSSL_VERSION_NUMBER >= 0x30000000 /* OPENSSL 3.0.0 */ 675 OPENSSL_INIT_set_config_file_flags(settings, 0); 676 #endif 677 rc = OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, settings); 678 if (rc != 1) { 679 fprintf(stderr, "Failed to initialize OpenSSL\n"); 680 ERR_print_errors_fp(stderr); 681 return -EINVAL; 682 } 683 OPENSSL_INIT_free(settings); 684 685 rc = build_eal_cmdline(opts); 686 if (rc < 0) { 687 SPDK_ERRLOG("Invalid arguments to initialize DPDK\n"); 688 return -EINVAL; 689 } 690 691 SPDK_PRINTF("Starting %s / %s initialization...\n", SPDK_VERSION_STRING, rte_version()); 692 693 args_print = _sprintf_alloc("[ DPDK EAL parameters: "); 694 if (args_print == NULL) { 695 return -ENOMEM; 696 } 697 for (i = 0; i < g_eal_cmdline_argcount; i++) { 698 args_tmp = args_print; 699 args_print = _sprintf_alloc("%s%s ", args_tmp, g_eal_cmdline[i]); 700 if (args_print == NULL) { 701 free(args_tmp); 702 return -ENOMEM; 703 } 704 free(args_tmp); 705 } 706 SPDK_PRINTF("%s]\n", args_print); 707 free(args_print); 708 709 /* DPDK rearranges the array we pass to it, so make a copy 710 * before passing so we can still free the individual strings 711 * correctly. 712 */ 713 dpdk_args = calloc(g_eal_cmdline_argcount, sizeof(char *)); 714 if (dpdk_args == NULL) { 715 SPDK_ERRLOG("Failed to allocate dpdk_args\n"); 716 return -ENOMEM; 717 } 718 memcpy(dpdk_args, g_eal_cmdline, sizeof(char *) * g_eal_cmdline_argcount); 719 720 fflush(stdout); 721 orig_optind = optind; 722 optind = 1; 723 rc = rte_eal_init(g_eal_cmdline_argcount, dpdk_args); 724 optind = orig_optind; 725 726 free(dpdk_args); 727 728 if (rc < 0) { 729 if (rte_errno == EALREADY) { 730 SPDK_ERRLOG("DPDK already initialized\n"); 731 } else { 732 SPDK_ERRLOG("Failed to initialize DPDK\n"); 733 } 734 return -rte_errno; 735 } 736 737 legacy_mem = false; 738 if (opts->env_context && strstr(opts->env_context, "--legacy-mem") != NULL) { 739 legacy_mem = true; 740 } 741 742 rc = spdk_env_dpdk_post_init(legacy_mem); 743 if (rc == 0) { 744 g_external_init = false; 745 } 746 747 return rc; 748 } 749 750 /* We use priority 101 which is the highest priority level available 751 * to applications (the toolchains reserve 1 to 100 for internal usage). 752 * This ensures this destructor runs last, after any other destructors 753 * that might still need the environment up and running. 754 */ 755 __attribute__((destructor(101))) static void 756 dpdk_cleanup(void) 757 { 758 /* Only call rte_eal_cleanup if the SPDK env library called rte_eal_init. */ 759 if (!g_external_init) { 760 rte_eal_cleanup(); 761 } 762 } 763 764 void 765 spdk_env_fini(void) 766 { 767 spdk_env_dpdk_post_fini(); 768 } 769 770 bool 771 spdk_env_dpdk_external_init(void) 772 { 773 return g_external_init; 774 } 775