xref: /spdk/test/nvme/aer/aer.c (revision 57fd99b91e71a4baa5543e19ff83958dc99d4dac)
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