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