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