1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2015 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 8 #include "spdk/log.h" 9 #include "spdk/nvme.h" 10 #include "spdk/env.h" 11 #include "spdk/string.h" 12 13 #define MAX_DEVS 64 14 15 struct dev { 16 struct spdk_nvme_ctrlr *ctrlr; 17 /* Expected changed NS ID state before AER */ 18 bool ns_test_active; 19 struct spdk_nvme_health_information_page *health_page; 20 uint32_t orig_temp_threshold; 21 bool reset_temp_active; 22 char name[SPDK_NVMF_TRADDR_MAX_LEN + 1]; 23 }; 24 25 static void get_feature_test(struct dev *dev); 26 27 static struct dev g_devs[MAX_DEVS]; 28 static int g_num_devs = 0; 29 30 #define foreach_dev(iter) \ 31 for (iter = g_devs; iter - g_devs < g_num_devs; iter++) 32 #define AER_PRINTF(format, ...) printf("%s" format, g_parent_process ? "" : "[Child] ", \ 33 ##__VA_ARGS__) 34 #define AER_FPRINTF(f, format, ...) fprintf(f, "%s" format, g_parent_process ? \ 35 "" : "[Child] ", ##__VA_ARGS__) 36 37 static int g_outstanding_commands = 0; 38 static int g_aer_done = 0; 39 static int g_temperature_done = 0; 40 static int g_failed = 0; 41 static struct spdk_nvme_transport_id g_trid; 42 static char *g_touch_file; 43 44 /* Enable AER temperature test */ 45 static int g_enable_temp_test = 0; 46 /* Expected changed NS ID */ 47 static uint32_t g_expected_ns_test = 0; 48 /* For multi-process test */ 49 static int g_multi_process_test = 0; 50 static bool g_parent_process = true; 51 static const char *g_sem_init_name = "/init"; 52 static const char *g_sem_child_name = "/child"; 53 static sem_t *g_sem_init_id; 54 static sem_t *g_sem_child_id; 55 56 static void 57 set_temp_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl) 58 { 59 struct dev *dev = cb_arg; 60 61 g_outstanding_commands--; 62 63 if (spdk_nvme_cpl_is_error(cpl)) { 64 AER_PRINTF("%s: set feature (temp threshold) failed\n", dev->name); 65 g_failed = 1; 66 return; 67 } 68 69 /* Admin command completions are synchronized by the NVMe driver, 70 * so we don't need to do any special locking here. */ 71 g_temperature_done++; 72 } 73 74 static int 75 set_temp_threshold(struct dev *dev, uint32_t temp) 76 { 77 struct spdk_nvme_cmd cmd = {}; 78 int rc; 79 80 cmd.opc = SPDK_NVME_OPC_SET_FEATURES; 81 cmd.cdw10_bits.set_features.fid = SPDK_NVME_FEAT_TEMPERATURE_THRESHOLD; 82 cmd.cdw11_bits.feat_temp_threshold.bits.tmpth = temp; 83 84 rc = spdk_nvme_ctrlr_cmd_admin_raw(dev->ctrlr, &cmd, NULL, 0, set_temp_completion, dev); 85 if (rc == 0) { 86 g_outstanding_commands++; 87 } else { 88 AER_FPRINTF(stderr, "Submitting Admin cmd failed with rc: %d\n", rc); 89 } 90 91 return rc; 92 } 93 94 static void 95 get_temp_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl) 96 { 97 struct dev *dev = cb_arg; 98 99 g_outstanding_commands--; 100 101 if (spdk_nvme_cpl_is_error(cpl)) { 102 AER_PRINTF("%s: get feature (temp threshold) failed\n", dev->name); 103 g_failed = 1; 104 return; 105 } 106 107 dev->orig_temp_threshold = cpl->cdw0; 108 AER_PRINTF("%s: original temperature threshold: %u Kelvin (%d Celsius)\n", 109 dev->name, dev->orig_temp_threshold, dev->orig_temp_threshold - 273); 110 111 g_temperature_done++; 112 } 113 114 static int 115 get_temp_threshold(struct dev *dev) 116 { 117 struct spdk_nvme_cmd cmd = {}; 118 int rc; 119 120 cmd.opc = SPDK_NVME_OPC_GET_FEATURES; 121 cmd.cdw10_bits.get_features.fid = SPDK_NVME_FEAT_TEMPERATURE_THRESHOLD; 122 123 rc = spdk_nvme_ctrlr_cmd_admin_raw(dev->ctrlr, &cmd, NULL, 0, get_temp_completion, dev); 124 if (rc == 0) { 125 g_outstanding_commands++; 126 } 127 128 return rc; 129 } 130 131 static void 132 print_health_page(struct dev *dev, struct spdk_nvme_health_information_page *hip) 133 { 134 AER_PRINTF("%s: Current Temperature: %u Kelvin (%d Celsius)\n", 135 dev->name, hip->temperature, hip->temperature - 273); 136 } 137 138 static void 139 get_health_log_page_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl) 140 { 141 struct dev *dev = cb_arg; 142 143 g_outstanding_commands--; 144 145 if (spdk_nvme_cpl_is_error(cpl)) { 146 AER_PRINTF("%s: get log page failed\n", dev->name); 147 g_failed = 1; 148 return; 149 } 150 151 print_health_page(dev, dev->health_page); 152 g_aer_done++; 153 } 154 155 static int 156 get_health_log_page(struct dev *dev) 157 { 158 int rc; 159 160 rc = spdk_nvme_ctrlr_cmd_get_log_page(dev->ctrlr, SPDK_NVME_LOG_HEALTH_INFORMATION, 161 SPDK_NVME_GLOBAL_NS_TAG, dev->health_page, 162 sizeof(*dev->health_page), 0, 163 get_health_log_page_completion, dev); 164 165 if (rc == 0) { 166 g_outstanding_commands++; 167 } 168 169 return rc; 170 } 171 172 static void 173 get_ns_state_test(struct dev *dev, uint32_t nsid) 174 { 175 bool new_ns_state; 176 177 new_ns_state = spdk_nvme_ctrlr_is_active_ns(dev->ctrlr, nsid); 178 if (new_ns_state == dev->ns_test_active) { 179 g_failed = 1; 180 } 181 } 182 183 static void 184 cleanup(void) 185 { 186 struct dev *dev; 187 188 foreach_dev(dev) { 189 if (dev->health_page) { 190 spdk_free(dev->health_page); 191 } 192 } 193 } 194 195 static void 196 aer_cb(void *arg, const struct spdk_nvme_cpl *cpl) 197 { 198 struct dev *dev = arg; 199 uint32_t log_page_id; 200 uint32_t aen_event_info; 201 uint32_t aen_event_type; 202 union spdk_nvme_async_event_completion aen_cpl; 203 204 aen_cpl.raw = cpl->cdw0; 205 aen_event_info = aen_cpl.bits.async_event_info; 206 aen_event_type = aen_cpl.bits.async_event_type; 207 log_page_id = aen_cpl.bits.log_page_identifier; 208 209 if (spdk_nvme_cpl_is_error(cpl)) { 210 AER_FPRINTF(stderr, "%s: AER failed\n", dev->name); 211 g_failed = 1; 212 return; 213 } 214 215 /* If we are already resetting the temp, no need to print more AENs */ 216 if (dev->reset_temp_active) { 217 return; 218 } 219 220 AER_PRINTF("%s: aer_cb for log page %d, aen_event_type: 0x%02x, aen_event_info: 0x%02x\n", 221 dev->name, log_page_id, aen_event_type, aen_event_info); 222 /* Temp Test: Verify proper EventType, Event Info and Log Page. 223 * NOTE: QEMU NVMe controllers return Spare Below Threshold Status event info 224 * instead of Temperate Threshold even info which is why it's used in the check 225 * below. 226 */ 227 if ((log_page_id == SPDK_NVME_LOG_HEALTH_INFORMATION) && \ 228 (aen_event_type == SPDK_NVME_ASYNC_EVENT_TYPE_SMART) && \ 229 ((aen_event_info == SPDK_NVME_ASYNC_EVENT_TEMPERATURE_THRESHOLD) || \ 230 (aen_event_info == SPDK_NVME_ASYNC_EVENT_SPARE_BELOW_THRESHOLD))) { 231 /* Set the temperature threshold back to the original value to stop triggering */ 232 if (g_parent_process) { 233 AER_PRINTF("aer_cb - Resetting Temp Threshold for device: %s\n", dev->name); 234 if (set_temp_threshold(dev, dev->orig_temp_threshold)) { 235 g_failed = 1; 236 } 237 dev->reset_temp_active = true; 238 } 239 get_health_log_page(dev); 240 } else if (log_page_id == SPDK_NVME_LOG_CHANGED_NS_LIST) { 241 AER_PRINTF("aer_cb - Changed Namespace\n"); 242 get_ns_state_test(dev, g_expected_ns_test); 243 g_aer_done++; 244 } else { 245 AER_PRINTF("aer_cb - Unknown Log Page\n"); 246 } 247 } 248 249 static void 250 usage(const char *program_name) 251 { 252 AER_PRINTF("%s [options]", program_name); 253 AER_PRINTF("\n"); 254 AER_PRINTF("options:\n"); 255 AER_PRINTF(" -g use single file descriptor for DPDK memory segments]\n"); 256 AER_PRINTF(" -T enable temperature tests\n"); 257 AER_PRINTF(" -n expected Namespace attribute notice ID\n"); 258 AER_PRINTF(" -t <file> touch specified file when ready to receive AER\n"); 259 AER_PRINTF(" -r trid remote NVMe over Fabrics target address\n"); 260 AER_PRINTF(" Format: 'key:value [key:value] ...'\n"); 261 AER_PRINTF(" Keys:\n"); 262 AER_PRINTF(" trtype Transport type (e.g. RDMA)\n"); 263 AER_PRINTF(" adrfam Address family (e.g. IPv4, IPv6)\n"); 264 AER_PRINTF(" traddr Transport address (e.g. 192.168.100.8)\n"); 265 AER_PRINTF(" trsvcid Transport service identifier (e.g. 4420)\n"); 266 AER_PRINTF(" subnqn Subsystem NQN (default: %s)\n", SPDK_NVMF_DISCOVERY_NQN); 267 AER_PRINTF(" Example: -r 'trtype:RDMA adrfam:IPv4 traddr:192.168.100.8 trsvcid:4420'\n"); 268 269 spdk_log_usage(stdout, "-L"); 270 271 AER_PRINTF(" -i <id> shared memory group ID\n"); 272 AER_PRINTF(" -m Multi-Process AER Test (only with Temp Test)\n"); 273 AER_PRINTF(" -H show this usage\n"); 274 } 275 276 static int 277 parse_args(int argc, char **argv, struct spdk_env_opts *env_opts) 278 { 279 int op, rc; 280 long int val; 281 282 spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE); 283 snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN); 284 285 while ((op = getopt(argc, argv, "gi:mn:r:t:HL:T")) != -1) { 286 switch (op) { 287 case 'n': 288 val = spdk_strtol(optarg, 10); 289 if (val < 0) { 290 AER_FPRINTF(stderr, "Invalid NS attribute notice ID\n"); 291 return val; 292 } 293 g_expected_ns_test = (uint32_t)val; 294 break; 295 case 'g': 296 env_opts->hugepage_single_segments = true; 297 break; 298 case 'r': 299 if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) { 300 AER_FPRINTF(stderr, "Error parsing transport address\n"); 301 return 1; 302 } 303 break; 304 case 't': 305 g_touch_file = optarg; 306 break; 307 case 'L': 308 rc = spdk_log_set_flag(optarg); 309 if (rc < 0) { 310 AER_FPRINTF(stderr, "unknown flag\n"); 311 usage(argv[0]); 312 exit(EXIT_FAILURE); 313 } 314 #ifdef DEBUG 315 spdk_log_set_print_level(SPDK_LOG_DEBUG); 316 #endif 317 break; 318 case 'T': 319 g_enable_temp_test = 1; 320 break; 321 case 'H': 322 usage(argv[0]); 323 exit(EXIT_SUCCESS); 324 case 'i': 325 env_opts->shm_id = spdk_strtol(optarg, 10); 326 if (env_opts->shm_id < 0) { 327 AER_FPRINTF(stderr, "Invalid shared memory ID\n"); 328 return env_opts->shm_id; 329 } 330 break; 331 case 'm': 332 g_multi_process_test = 1; 333 break; 334 default: 335 usage(argv[0]); 336 return 1; 337 } 338 } 339 340 return 0; 341 } 342 343 static bool 344 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 345 struct spdk_nvme_ctrlr_opts *opts) 346 { 347 AER_PRINTF("Attaching to %s\n", trid->traddr); 348 349 return true; 350 } 351 352 static void 353 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 354 struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 355 { 356 struct dev *dev; 357 358 /* add to dev list */ 359 dev = &g_devs[g_num_devs++]; 360 361 dev->ctrlr = ctrlr; 362 363 snprintf(dev->name, sizeof(dev->name), "%s", 364 trid->traddr); 365 366 AER_PRINTF("Attached to %s\n", dev->name); 367 368 dev->health_page = spdk_zmalloc(sizeof(*dev->health_page), 4096, NULL, SPDK_ENV_LCORE_ID_ANY, 369 SPDK_MALLOC_DMA); 370 if (dev->health_page == NULL) { 371 AER_PRINTF("Allocation error (health page)\n"); 372 g_failed = 1; 373 } 374 } 375 376 static void 377 get_feature_test_cb(void *cb_arg, const struct spdk_nvme_cpl *cpl) 378 { 379 struct dev *dev = cb_arg; 380 381 g_outstanding_commands--; 382 383 if (spdk_nvme_cpl_is_error(cpl)) { 384 AER_PRINTF("%s: get number of queues failed\n", dev->name); 385 g_failed = 1; 386 return; 387 } 388 389 if (g_aer_done < g_num_devs) { 390 /* 391 * Resubmit Get Features command to continue filling admin queue 392 * while the test is running. 393 */ 394 get_feature_test(dev); 395 } 396 } 397 398 static void 399 get_feature_test(struct dev *dev) 400 { 401 struct spdk_nvme_cmd cmd; 402 403 memset(&cmd, 0, sizeof(cmd)); 404 cmd.opc = SPDK_NVME_OPC_GET_FEATURES; 405 cmd.cdw10_bits.get_features.fid = SPDK_NVME_FEAT_NUMBER_OF_QUEUES; 406 if (spdk_nvme_ctrlr_cmd_admin_raw(dev->ctrlr, &cmd, NULL, 0, 407 get_feature_test_cb, dev) != 0) { 408 AER_PRINTF("Failed to send Get Features command for dev=%p\n", dev); 409 g_failed = 1; 410 return; 411 } 412 413 g_outstanding_commands++; 414 } 415 416 static int 417 spdk_aer_temperature_test(void) 418 { 419 struct dev *dev; 420 421 AER_PRINTF("Getting orig temperature thresholds of all controllers\n"); 422 foreach_dev(dev) { 423 /* Get the original temperature threshold */ 424 get_temp_threshold(dev); 425 dev->reset_temp_active = false; 426 } 427 428 while (!g_failed && g_temperature_done < g_num_devs) { 429 foreach_dev(dev) { 430 spdk_nvme_ctrlr_process_admin_completions(dev->ctrlr); 431 } 432 } 433 434 if (g_failed) { 435 return g_failed; 436 } 437 g_temperature_done = 0; 438 g_aer_done = 0; 439 440 /* Send admin commands to test admin queue wraparound while waiting for the AER */ 441 foreach_dev(dev) { 442 get_feature_test(dev); 443 } 444 445 if (g_failed) { 446 return g_failed; 447 } 448 449 /* Only single process needs to set and verify lower threshold */ 450 if (g_parent_process) { 451 /* Wait until child has init'd and ready for test to continue */ 452 if (g_multi_process_test) { 453 sem_wait(g_sem_child_id); 454 } 455 AER_PRINTF("Setting all controllers temperature threshold low to trigger AER\n"); 456 foreach_dev(dev) { 457 /* Set the temperature threshold to a low value */ 458 set_temp_threshold(dev, 200); 459 } 460 461 AER_PRINTF("Waiting for all controllers temperature threshold to be set lower\n"); 462 while (!g_failed && (g_temperature_done < g_num_devs)) { 463 foreach_dev(dev) { 464 spdk_nvme_ctrlr_process_admin_completions(dev->ctrlr); 465 } 466 } 467 468 if (g_failed) { 469 return g_failed; 470 } 471 } 472 473 AER_PRINTF("Waiting for all controllers to trigger AER and reset threshold\n"); 474 /* Let parent know init is done and it's okay to continue */ 475 if (!g_parent_process) { 476 sem_post(g_sem_child_id); 477 } 478 /* Waiting for AEN to be occur here. Each device will increment g_aer_done on an AEN */ 479 while (!g_failed && (g_aer_done < g_num_devs)) { 480 foreach_dev(dev) { 481 if (spdk_nvme_ctrlr_process_admin_completions(dev->ctrlr) < 0) { 482 g_failed = 1; 483 } 484 } 485 } 486 487 if (g_failed) { 488 return g_failed; 489 } 490 491 return 0; 492 } 493 494 static int 495 spdk_aer_changed_ns_test(void) 496 { 497 struct dev *dev; 498 499 g_aer_done = 0; 500 501 AER_PRINTF("Starting namespace attribute notice tests for all controllers...\n"); 502 503 foreach_dev(dev) { 504 get_feature_test(dev); 505 dev->ns_test_active = spdk_nvme_ctrlr_is_active_ns(dev->ctrlr, g_expected_ns_test); 506 } 507 508 if (g_failed) { 509 return g_failed; 510 } 511 512 while (!g_failed && (g_aer_done < g_num_devs)) { 513 foreach_dev(dev) { 514 spdk_nvme_ctrlr_process_admin_completions(dev->ctrlr); 515 } 516 } 517 518 if (g_failed) { 519 return g_failed; 520 } 521 522 return 0; 523 } 524 525 static int 526 setup_multi_process(void) 527 { 528 pid_t pid; 529 int rc = 0; 530 531 /* If AEN test was killed, remove named semaphore to start again */ 532 rc = sem_unlink(g_sem_init_name); 533 if (rc < 0 && errno != ENOENT) { 534 AER_FPRINTF(stderr, "Init semaphore removal failure: %s", spdk_strerror(errno)); 535 return rc; 536 } 537 rc = sem_unlink(g_sem_child_name); 538 if (rc < 0 && errno != ENOENT) { 539 AER_FPRINTF(stderr, "Child semaphore removal failure: %s", spdk_strerror(errno)); 540 return rc; 541 } 542 pid = fork(); 543 if (pid == -1) { 544 perror("Failed to fork\n"); 545 return -1; 546 } else if (pid == 0) { 547 AER_PRINTF("Child process pid: %d\n", getpid()); 548 g_parent_process = false; 549 g_sem_init_id = sem_open(g_sem_init_name, O_CREAT, 0600, 0); 550 g_sem_child_id = sem_open(g_sem_child_name, O_CREAT, 0600, 0); 551 if ((g_sem_init_id == SEM_FAILED) || (g_sem_child_id == SEM_FAILED)) { 552 AER_FPRINTF(stderr, "Sem Open failed for child: %s\n", 553 spdk_strerror(errno)); 554 return -1; 555 } 556 } 557 /* Parent process */ 558 else { 559 g_parent_process = true; 560 g_sem_init_id = sem_open(g_sem_init_name, O_CREAT, 0600, 0); 561 g_sem_child_id = sem_open(g_sem_child_name, O_CREAT, 0600, 0); 562 if ((g_sem_init_id == SEM_FAILED) || (g_sem_child_id == SEM_FAILED)) { 563 AER_FPRINTF(stderr, "Sem Open failed for parent: %s\n", 564 spdk_strerror(errno)); 565 return -1; 566 } 567 } 568 return 0; 569 } 570 571 int 572 main(int argc, char **argv) 573 { 574 struct dev *dev; 575 struct spdk_env_opts opts; 576 int rc; 577 struct spdk_nvme_detach_ctx *detach_ctx = NULL; 578 579 opts.opts_size = sizeof(opts); 580 spdk_env_opts_init(&opts); 581 rc = parse_args(argc, argv, &opts); 582 if (rc != 0) { 583 return rc; 584 } 585 586 if (g_multi_process_test) { 587 /* Multi-Process test only available with Temp Test */ 588 if (!g_enable_temp_test) { 589 AER_FPRINTF(stderr, "Multi Process only available with Temp Test (-T)\n"); 590 return 1; 591 } 592 if (opts.shm_id < 0) { 593 AER_FPRINTF(stderr, "Multi Process requires shared memory id (-i <id>)\n"); 594 return 1; 595 } 596 rc = setup_multi_process(); 597 if (rc != 0) { 598 AER_FPRINTF(stderr, "Multi Process test failed to setup\n"); 599 return rc; 600 } 601 } else { 602 /* Only one process in test, set it to the parent process */ 603 g_parent_process = true; 604 } 605 opts.name = "aer"; 606 if (g_parent_process) { 607 opts.core_mask = "0x1"; 608 } else { 609 opts.core_mask = "0x2"; 610 } 611 612 /* 613 * For multi-process test, parent (primary) and child (secondary) processes 614 * will execute all following code but DPDK setup is serialized 615 */ 616 if (!g_parent_process) { 617 if (sem_wait(g_sem_init_id) < 0) { 618 AER_FPRINTF(stderr, "sem_wait failed for child process\n"); 619 return (-1); 620 } 621 } 622 if (spdk_env_init(&opts) < 0) { 623 AER_FPRINTF(stderr, "Unable to initialize SPDK env\n"); 624 return 1; 625 } 626 627 AER_PRINTF("Asynchronous Event Request test\n"); 628 629 if (spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL) != 0) { 630 AER_FPRINTF(stderr, "spdk_nvme_probe() failed\n"); 631 return 1; 632 } 633 634 if (g_num_devs == 0) { 635 AER_FPRINTF(stderr, "No controllers found - exiting\n"); 636 g_failed = 1; 637 } 638 if (g_failed) { 639 goto done; 640 } 641 642 if (g_parent_process && g_enable_temp_test) { 643 AER_PRINTF("Reset controller to setup AER completions for this process\n"); 644 foreach_dev(dev) { 645 if (spdk_nvme_ctrlr_reset(dev->ctrlr) != 0) { 646 AER_FPRINTF(stderr, "nvme reset failed.\n"); 647 return -1; 648 } 649 } 650 } 651 if (g_parent_process && g_multi_process_test) { 652 /* Primary can release child/secondary for init now */ 653 sem_post(g_sem_init_id); 654 } 655 656 AER_PRINTF("Registering asynchronous event callbacks...\n"); 657 foreach_dev(dev) { 658 spdk_nvme_ctrlr_register_aer_callback(dev->ctrlr, aer_cb, dev); 659 } 660 661 if (g_touch_file) { 662 int fd; 663 664 fd = open(g_touch_file, O_CREAT | O_EXCL | O_RDWR, S_IFREG); 665 if (fd == -1) { 666 AER_FPRINTF(stderr, "Could not touch %s (%s).\n", g_touch_file, 667 strerror(errno)); 668 g_failed = true; 669 goto done; 670 } 671 close(fd); 672 } 673 674 /* AER temperature test */ 675 if (g_enable_temp_test) { 676 if (spdk_aer_temperature_test()) { 677 goto done; 678 } 679 } 680 681 /* AER changed namespace list test */ 682 if (g_expected_ns_test) { 683 if (spdk_aer_changed_ns_test()) { 684 goto done; 685 } 686 } 687 688 AER_PRINTF("Cleaning up...\n"); 689 690 while (g_outstanding_commands) { 691 foreach_dev(dev) { 692 spdk_nvme_ctrlr_process_admin_completions(dev->ctrlr); 693 } 694 } 695 696 /* Only one process cleans up at a time - let child go first */ 697 if (g_multi_process_test && g_parent_process) { 698 /* Parent waits for child to clean up before executing clean up process */ 699 sem_wait(g_sem_child_id); 700 } 701 /* unregister AER callback so we don't fail on aborted AERs when we close out qpairs. */ 702 foreach_dev(dev) { 703 spdk_nvme_ctrlr_register_aer_callback(dev->ctrlr, NULL, NULL); 704 } 705 706 foreach_dev(dev) { 707 spdk_nvme_ctrlr_process_admin_completions(dev->ctrlr); 708 } 709 710 foreach_dev(dev) { 711 spdk_nvme_detach_async(dev->ctrlr, &detach_ctx); 712 } 713 714 if (detach_ctx) { 715 spdk_nvme_detach_poll(detach_ctx); 716 } 717 718 /* Release semaphore to allow parent to cleanup */ 719 if (!g_parent_process) { 720 sem_post(g_sem_child_id); 721 sem_wait(g_sem_init_id); 722 } 723 done: 724 cleanup(); 725 726 /* Wait for child process to finish and verify it finished correctly before detaching 727 * resources */ 728 if (g_multi_process_test && g_parent_process) { 729 int status; 730 sem_post(g_sem_init_id); 731 wait(&status); 732 if (WIFEXITED(status)) { 733 /* Child ended normally */ 734 if (WEXITSTATUS(status) != 0) { 735 AER_FPRINTF(stderr, "Child Failed with status: %d.\n", 736 (int8_t)(WEXITSTATUS(status))); 737 g_failed = true; 738 } 739 } 740 if (sem_close(g_sem_init_id) != 0) { 741 perror("sem_close Failed for init\n"); 742 g_failed = true; 743 } 744 if (sem_close(g_sem_child_id) != 0) { 745 perror("sem_close Failed for child\n"); 746 g_failed = true; 747 } 748 749 if (sem_unlink(g_sem_init_name) != 0) { 750 perror("sem_unlink Failed for init\n"); 751 g_failed = true; 752 } 753 if (sem_unlink(g_sem_child_name) != 0) { 754 perror("sem_unlink Failed for child\n"); 755 g_failed = true; 756 } 757 } 758 return g_failed; 759 } 760