xref: /spdk/lib/event/app.c (revision 07d28d02f73bbcd7732a5421bcaebfb067b46ca0)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2016 Intel Corporation. All rights reserved.
3  *   Copyright (c) 2019 Mellanox Technologies LTD. All rights reserved.
4  *   Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5  */
6 
7 #include "spdk/stdinc.h"
8 #include "spdk/version.h"
9 
10 #include "spdk_internal/event.h"
11 
12 #include "spdk/assert.h"
13 #include "spdk/env.h"
14 #include "spdk/init.h"
15 #include "spdk/log.h"
16 #include "spdk/thread.h"
17 #include "spdk/trace.h"
18 #include "spdk/string.h"
19 #include "spdk/scheduler.h"
20 #include "spdk/rpc.h"
21 #include "spdk/util.h"
22 
23 #define SPDK_APP_DEFAULT_LOG_LEVEL		SPDK_LOG_NOTICE
24 #define SPDK_APP_DEFAULT_LOG_PRINT_LEVEL	SPDK_LOG_INFO
25 #define SPDK_APP_DEFAULT_NUM_TRACE_ENTRIES	SPDK_DEFAULT_NUM_TRACE_ENTRIES
26 
27 #define SPDK_APP_DPDK_DEFAULT_MEM_SIZE		-1
28 #define SPDK_APP_DPDK_DEFAULT_MAIN_CORE		-1
29 #define SPDK_APP_DPDK_DEFAULT_MEM_CHANNEL	-1
30 #define SPDK_APP_DPDK_DEFAULT_CORE_MASK		"0x1"
31 #define SPDK_APP_DPDK_DEFAULT_BASE_VIRTADDR	0x200000000000
32 #define SPDK_APP_DEFAULT_CORE_LIMIT		0x140000000 /* 5 GiB */
33 
34 /* For core counts <= 63, the message memory pool size is set to
35  * SPDK_DEFAULT_MSG_MEMPOOL_SIZE.
36  * For core counts > 63, the message memory pool size is dependend on
37  * number of cores. Per core, it is calculated as SPDK_MSG_MEMPOOL_CACHE_SIZE
38  * multiplied by factor of 4 to have space for multiple spdk threads running
39  * on single core (e.g  iscsi + nvmf + vhost ). */
40 #define SPDK_APP_PER_CORE_MSG_MEMPOOL_SIZE	(4 * SPDK_MSG_MEMPOOL_CACHE_SIZE)
41 
42 #define MAX_CPU_CORES				128
43 
44 struct spdk_app {
45 	const char			*json_config_file;
46 	bool				json_config_ignore_errors;
47 	bool				stopped;
48 	const char			*rpc_addr;
49 	const char			**rpc_allowlist;
50 	int				shm_id;
51 	spdk_app_shutdown_cb		shutdown_cb;
52 	int				rc;
53 };
54 
55 static struct spdk_app g_spdk_app;
56 static spdk_msg_fn g_start_fn = NULL;
57 static void *g_start_arg = NULL;
58 static bool g_delay_subsystem_init = false;
59 static bool g_shutdown_sig_received = false;
60 static char *g_executable_name;
61 static struct spdk_app_opts g_default_opts;
62 static bool g_disable_cpumask_locks = false;
63 
64 static int g_core_locks[MAX_CPU_CORES];
65 
66 int
67 spdk_app_get_shm_id(void)
68 {
69 	return g_spdk_app.shm_id;
70 }
71 
72 /* append one empty option to indicate the end of the array */
73 static const struct option g_cmdline_options[] = {
74 #define CONFIG_FILE_OPT_IDX	'c'
75 	{"config",			required_argument,	NULL, CONFIG_FILE_OPT_IDX},
76 #define LIMIT_COREDUMP_OPT_IDX 'd'
77 	{"limit-coredump",		no_argument,		NULL, LIMIT_COREDUMP_OPT_IDX},
78 #define TPOINT_GROUP_OPT_IDX 'e'
79 	{"tpoint-group",		required_argument,	NULL, TPOINT_GROUP_OPT_IDX},
80 #define SINGLE_FILE_SEGMENTS_OPT_IDX 'g'
81 	{"single-file-segments",	no_argument,		NULL, SINGLE_FILE_SEGMENTS_OPT_IDX},
82 #define HELP_OPT_IDX		'h'
83 	{"help",			no_argument,		NULL, HELP_OPT_IDX},
84 #define SHM_ID_OPT_IDX		'i'
85 	{"shm-id",			required_argument,	NULL, SHM_ID_OPT_IDX},
86 #define CPUMASK_OPT_IDX		'm'
87 	{"cpumask",			required_argument,	NULL, CPUMASK_OPT_IDX},
88 #define MEM_CHANNELS_OPT_IDX	'n'
89 	{"mem-channels",		required_argument,	NULL, MEM_CHANNELS_OPT_IDX},
90 #define MAIN_CORE_OPT_IDX	'p'
91 	{"main-core",			required_argument,	NULL, MAIN_CORE_OPT_IDX},
92 	{"master-core",			required_argument,	NULL, MAIN_CORE_OPT_IDX}, /* deprecated */
93 #define RPC_SOCKET_OPT_IDX	'r'
94 	{"rpc-socket",			required_argument,	NULL, RPC_SOCKET_OPT_IDX},
95 #define MEM_SIZE_OPT_IDX	's'
96 	{"mem-size",			required_argument,	NULL, MEM_SIZE_OPT_IDX},
97 #define NO_PCI_OPT_IDX		'u'
98 	{"no-pci",			no_argument,		NULL, NO_PCI_OPT_IDX},
99 #define VERSION_OPT_IDX		'v'
100 	{"version",			no_argument,		NULL, VERSION_OPT_IDX},
101 #define PCI_BLOCKED_OPT_IDX	'B'
102 	{"pci-blocked",			required_argument,	NULL, PCI_BLOCKED_OPT_IDX},
103 	{"pci-blacklist",		required_argument,	NULL, PCI_BLOCKED_OPT_IDX}, /* deprecated */
104 #define LOGFLAG_OPT_IDX		'L'
105 	{"logflag",			required_argument,	NULL, LOGFLAG_OPT_IDX},
106 #define HUGE_UNLINK_OPT_IDX	'R'
107 	{"huge-unlink",			no_argument,		NULL, HUGE_UNLINK_OPT_IDX},
108 #define PCI_ALLOWED_OPT_IDX	'A'
109 	{"pci-allowed",			required_argument,	NULL, PCI_ALLOWED_OPT_IDX},
110 #define PCI_WHITELIST_OPT_IDX	'W'
111 	{"pci-whitelist",		required_argument,	NULL, PCI_WHITELIST_OPT_IDX}, /* deprecated */
112 #define SILENCE_NOTICELOG_OPT_IDX 257
113 	{"silence-noticelog",		no_argument,		NULL, SILENCE_NOTICELOG_OPT_IDX},
114 #define WAIT_FOR_RPC_OPT_IDX	258
115 	{"wait-for-rpc",		no_argument,		NULL, WAIT_FOR_RPC_OPT_IDX},
116 #define HUGE_DIR_OPT_IDX	259
117 	{"huge-dir",			required_argument,	NULL, HUGE_DIR_OPT_IDX},
118 #define NUM_TRACE_ENTRIES_OPT_IDX	260
119 	{"num-trace-entries",		required_argument,	NULL, NUM_TRACE_ENTRIES_OPT_IDX},
120 #define MAX_REACTOR_DELAY_OPT_IDX	261
121 	{"max-delay",			required_argument,	NULL, MAX_REACTOR_DELAY_OPT_IDX},
122 #define JSON_CONFIG_OPT_IDX		262
123 	{"json",			required_argument,	NULL, JSON_CONFIG_OPT_IDX},
124 #define JSON_CONFIG_IGNORE_INIT_ERRORS_IDX	263
125 	{"json-ignore-init-errors",	no_argument,		NULL, JSON_CONFIG_IGNORE_INIT_ERRORS_IDX},
126 #define IOVA_MODE_OPT_IDX	264
127 	{"iova-mode",			required_argument,	NULL, IOVA_MODE_OPT_IDX},
128 #define BASE_VIRTADDR_OPT_IDX	265
129 	{"base-virtaddr",		required_argument,	NULL, BASE_VIRTADDR_OPT_IDX},
130 #define ENV_CONTEXT_OPT_IDX	266
131 	{"env-context",			required_argument,	NULL, ENV_CONTEXT_OPT_IDX},
132 #define DISABLE_CPUMASK_LOCKS_OPT_IDX	267
133 	{"disable-cpumask-locks",	no_argument,		NULL, DISABLE_CPUMASK_LOCKS_OPT_IDX},
134 #define RPCS_ALLOWED_OPT_IDX	268
135 	{"rpcs-allowed",		required_argument,	NULL, RPCS_ALLOWED_OPT_IDX},
136 #define ENV_VF_TOKEN_OPT_IDX 269
137 	{"vfio-vf-token",		required_argument,	NULL, ENV_VF_TOKEN_OPT_IDX},
138 #define MSG_MEMPOOL_SIZE_OPT_IDX 270
139 	{"msg-mempool-size",		required_argument,	NULL, MSG_MEMPOOL_SIZE_OPT_IDX},
140 #define LCORES_OPT_IDX	271
141 	{"lcores",			required_argument,	NULL, LCORES_OPT_IDX},
142 };
143 
144 static void
145 app_start_shutdown(void *ctx)
146 {
147 	if (g_spdk_app.shutdown_cb) {
148 		g_spdk_app.shutdown_cb();
149 		g_spdk_app.shutdown_cb = NULL;
150 	} else {
151 		spdk_app_stop(0);
152 	}
153 }
154 
155 void
156 spdk_app_start_shutdown(void)
157 {
158 	spdk_thread_send_critical_msg(spdk_thread_get_app_thread(), app_start_shutdown);
159 }
160 
161 static void
162 __shutdown_signal(int signo)
163 {
164 	if (!g_shutdown_sig_received) {
165 		g_shutdown_sig_received = true;
166 		spdk_app_start_shutdown();
167 	}
168 }
169 
170 static int
171 app_opts_validate(const char *app_opts)
172 {
173 	int i = 0, j;
174 
175 	for (i = 0; app_opts[i] != '\0'; i++) {
176 		/* ignore getopt control characters */
177 		if (app_opts[i] == ':' || app_opts[i] == '+' || app_opts[i] == '-') {
178 			continue;
179 		}
180 
181 		for (j = 0; SPDK_APP_GETOPT_STRING[j] != '\0'; j++) {
182 			if (app_opts[i] == SPDK_APP_GETOPT_STRING[j]) {
183 				return app_opts[i];
184 			}
185 		}
186 	}
187 	return 0;
188 }
189 
190 static void
191 calculate_mempool_size(struct spdk_app_opts *opts,
192 		       struct spdk_app_opts *opts_user)
193 {
194 	uint32_t core_count = spdk_env_get_core_count();
195 
196 	if (!opts_user->msg_mempool_size) {
197 		/* The user didn't specify msg_mempool_size, so let's calculate it.
198 		   Set the default (SPDK_DEFAULT_MSG_MEMPOOL_SIZE) if less than
199 		   64 cores, and use 4k per core otherwise */
200 		opts->msg_mempool_size = spdk_max(SPDK_DEFAULT_MSG_MEMPOOL_SIZE,
201 						  core_count * SPDK_APP_PER_CORE_MSG_MEMPOOL_SIZE);
202 	} else {
203 		opts->msg_mempool_size = opts_user->msg_mempool_size;
204 	}
205 }
206 
207 void
208 spdk_app_opts_init(struct spdk_app_opts *opts, size_t opts_size)
209 {
210 	if (!opts) {
211 		SPDK_ERRLOG("opts should not be NULL\n");
212 		return;
213 	}
214 
215 	if (!opts_size) {
216 		SPDK_ERRLOG("opts_size should not be zero value\n");
217 		return;
218 	}
219 
220 	memset(opts, 0, opts_size);
221 	opts->opts_size = opts_size;
222 
223 #define SET_FIELD(field, value) \
224 	if (offsetof(struct spdk_app_opts, field) + sizeof(opts->field) <= opts_size) { \
225 		opts->field = value; \
226 	} \
227 
228 	SET_FIELD(enable_coredump, true);
229 	SET_FIELD(shm_id, -1);
230 	SET_FIELD(mem_size, SPDK_APP_DPDK_DEFAULT_MEM_SIZE);
231 	SET_FIELD(main_core, SPDK_APP_DPDK_DEFAULT_MAIN_CORE);
232 	SET_FIELD(mem_channel, SPDK_APP_DPDK_DEFAULT_MEM_CHANNEL);
233 	SET_FIELD(base_virtaddr, SPDK_APP_DPDK_DEFAULT_BASE_VIRTADDR);
234 	SET_FIELD(print_level, SPDK_APP_DEFAULT_LOG_PRINT_LEVEL);
235 	SET_FIELD(rpc_addr, SPDK_DEFAULT_RPC_ADDR);
236 	SET_FIELD(num_entries, SPDK_APP_DEFAULT_NUM_TRACE_ENTRIES);
237 	SET_FIELD(delay_subsystem_init, false);
238 	SET_FIELD(disable_signal_handlers, false);
239 	/* Don't set msg_mempool_size here, it is set or calculated later */
240 	SET_FIELD(rpc_allowlist, NULL);
241 #undef SET_FIELD
242 }
243 
244 static int
245 app_setup_signal_handlers(struct spdk_app_opts *opts)
246 {
247 	struct sigaction	sigact;
248 	sigset_t		sigmask;
249 	int			rc;
250 
251 	sigemptyset(&sigmask);
252 	memset(&sigact, 0, sizeof(sigact));
253 	sigemptyset(&sigact.sa_mask);
254 
255 	sigact.sa_handler = SIG_IGN;
256 	rc = sigaction(SIGPIPE, &sigact, NULL);
257 	if (rc < 0) {
258 		SPDK_ERRLOG("sigaction(SIGPIPE) failed\n");
259 		return rc;
260 	}
261 
262 	/* Install the same handler for SIGINT and SIGTERM */
263 	g_shutdown_sig_received = false;
264 	sigact.sa_handler = __shutdown_signal;
265 	rc = sigaction(SIGINT, &sigact, NULL);
266 	if (rc < 0) {
267 		SPDK_ERRLOG("sigaction(SIGINT) failed\n");
268 		return rc;
269 	}
270 	sigaddset(&sigmask, SIGINT);
271 
272 	rc = sigaction(SIGTERM, &sigact, NULL);
273 	if (rc < 0) {
274 		SPDK_ERRLOG("sigaction(SIGTERM) failed\n");
275 		return rc;
276 	}
277 	sigaddset(&sigmask, SIGTERM);
278 
279 	pthread_sigmask(SIG_UNBLOCK, &sigmask, NULL);
280 
281 	return 0;
282 }
283 
284 static void
285 app_start_application(void)
286 {
287 	assert(spdk_thread_is_app_thread(NULL));
288 
289 	g_start_fn(g_start_arg);
290 }
291 
292 static void
293 app_start_rpc(int rc, void *arg1)
294 {
295 	if (rc) {
296 		spdk_app_stop(rc);
297 		return;
298 	}
299 
300 	spdk_rpc_set_allowlist(g_spdk_app.rpc_allowlist);
301 
302 	rc = spdk_rpc_initialize(g_spdk_app.rpc_addr);
303 	if (rc) {
304 		spdk_app_stop(rc);
305 		return;
306 	}
307 
308 	if (!g_delay_subsystem_init) {
309 		spdk_rpc_set_state(SPDK_RPC_RUNTIME);
310 		app_start_application();
311 	}
312 }
313 
314 static int
315 app_opts_add_pci_addr(struct spdk_app_opts *opts, struct spdk_pci_addr **list, char *bdf)
316 {
317 	struct spdk_pci_addr *tmp = *list;
318 	size_t i = opts->num_pci_addr;
319 
320 	tmp = realloc(tmp, sizeof(*tmp) * (i + 1));
321 	if (tmp == NULL) {
322 		SPDK_ERRLOG("realloc error\n");
323 		return -ENOMEM;
324 	}
325 
326 	*list = tmp;
327 	if (spdk_pci_addr_parse(*list + i, bdf) < 0) {
328 		SPDK_ERRLOG("Invalid address %s\n", bdf);
329 		return -EINVAL;
330 	}
331 
332 	opts->num_pci_addr++;
333 	return 0;
334 }
335 
336 static int
337 app_setup_env(struct spdk_app_opts *opts)
338 {
339 	struct spdk_env_opts env_opts = {};
340 	int rc;
341 
342 	if (opts == NULL) {
343 		rc = spdk_env_init(NULL);
344 		if (rc != 0) {
345 			SPDK_ERRLOG("Unable to reinitialize SPDK env\n");
346 		}
347 
348 		return rc;
349 	}
350 
351 	spdk_env_opts_init(&env_opts);
352 
353 	env_opts.name = opts->name;
354 	env_opts.core_mask = opts->reactor_mask;
355 	env_opts.lcore_map = opts->lcore_map;
356 	env_opts.shm_id = opts->shm_id;
357 	env_opts.mem_channel = opts->mem_channel;
358 	env_opts.main_core = opts->main_core;
359 	env_opts.mem_size = opts->mem_size;
360 	env_opts.hugepage_single_segments = opts->hugepage_single_segments;
361 	env_opts.unlink_hugepage = opts->unlink_hugepage;
362 	env_opts.hugedir = opts->hugedir;
363 	env_opts.no_pci = opts->no_pci;
364 	env_opts.num_pci_addr = opts->num_pci_addr;
365 	env_opts.pci_blocked = opts->pci_blocked;
366 	env_opts.pci_allowed = opts->pci_allowed;
367 	env_opts.base_virtaddr = opts->base_virtaddr;
368 	env_opts.env_context = opts->env_context;
369 	env_opts.iova_mode = opts->iova_mode;
370 	env_opts.vf_token = opts->vf_token;
371 
372 	rc = spdk_env_init(&env_opts);
373 	free(env_opts.pci_blocked);
374 	free(env_opts.pci_allowed);
375 
376 	if (rc < 0) {
377 		SPDK_ERRLOG("Unable to initialize SPDK env\n");
378 	}
379 
380 	return rc;
381 }
382 
383 static int
384 app_setup_trace(struct spdk_app_opts *opts)
385 {
386 	char		shm_name[64];
387 	uint64_t	tpoint_group_mask, tpoint_mask = -1ULL;
388 	char		*end = NULL, *tpoint_group_mask_str, *tpoint_group_str = NULL;
389 	char		*tp_g_str, *tpoint_group, *tpoints;
390 	bool		error_found = false;
391 	uint64_t	group_id;
392 
393 	if (opts->shm_id >= 0) {
394 		snprintf(shm_name, sizeof(shm_name), "/%s_trace.%d", opts->name, opts->shm_id);
395 	} else {
396 		snprintf(shm_name, sizeof(shm_name), "/%s_trace.pid%d", opts->name, (int)getpid());
397 	}
398 
399 	if (spdk_trace_init(shm_name, opts->num_entries) != 0) {
400 		return -1;
401 	}
402 
403 	if (opts->tpoint_group_mask == NULL) {
404 		return 0;
405 	}
406 
407 	tpoint_group_mask_str = strdup(opts->tpoint_group_mask);
408 	if (tpoint_group_mask_str == NULL) {
409 		SPDK_ERRLOG("Unable to get string of tpoint group mask from opts.\n");
410 		return -1;
411 	}
412 	/* Save a pointer to the original value of the tpoint group mask string
413 	 * to free later, because spdk_strsepq() modifies given char*. */
414 	tp_g_str = tpoint_group_mask_str;
415 	while ((tpoint_group_str = spdk_strsepq(&tpoint_group_mask_str, ",")) != NULL) {
416 		if (strchr(tpoint_group_str, ':')) {
417 			/* Get the tpoint group mask */
418 			tpoint_group = spdk_strsepq(&tpoint_group_str, ":");
419 			/* Get the tpoint mask inside that group */
420 			tpoints = spdk_strsepq(&tpoint_group_str, ":");
421 
422 			errno = 0;
423 			tpoint_group_mask = strtoull(tpoint_group, &end, 16);
424 			if (*end != '\0' || errno) {
425 				tpoint_group_mask = spdk_trace_create_tpoint_group_mask(tpoint_group);
426 				if (tpoint_group_mask == 0) {
427 					error_found = true;
428 					break;
429 				}
430 			}
431 			/* Check if tpoint group mask has only one bit set.
432 			 * This is to avoid enabling individual tpoints in
433 			 * more than one tracepoint group at once. */
434 			if (!spdk_u64_is_pow2(tpoint_group_mask)) {
435 				SPDK_ERRLOG("Tpoint group mask: %s contains multiple tpoint groups.\n", tpoint_group);
436 				SPDK_ERRLOG("This is not supported, to prevent from activating tpoints by mistake.\n");
437 				error_found = true;
438 				break;
439 			}
440 
441 			errno = 0;
442 			tpoint_mask = strtoull(tpoints, &end, 16);
443 			if (*end != '\0' || errno) {
444 				error_found = true;
445 				break;
446 			}
447 		} else {
448 			errno = 0;
449 			tpoint_group_mask = strtoull(tpoint_group_str, &end, 16);
450 			if (*end != '\0' || errno) {
451 				tpoint_group_mask = spdk_trace_create_tpoint_group_mask(tpoint_group_str);
452 				if (tpoint_group_mask == 0) {
453 					error_found = true;
454 					break;
455 				}
456 			}
457 			tpoint_mask = -1ULL;
458 		}
459 
460 		for (group_id = 0; group_id < SPDK_TRACE_MAX_GROUP_ID; ++group_id) {
461 			if (tpoint_group_mask & (1 << group_id)) {
462 				spdk_trace_set_tpoints(group_id, tpoint_mask);
463 			}
464 		}
465 	}
466 
467 	if (error_found) {
468 		SPDK_ERRLOG("invalid tpoint mask %s\n", opts->tpoint_group_mask);
469 		free(tp_g_str);
470 		return -1;
471 	} else {
472 		SPDK_NOTICELOG("Tracepoint Group Mask %s specified.\n", opts->tpoint_group_mask);
473 		SPDK_NOTICELOG("Use 'spdk_trace -s %s %s %d' to capture a snapshot of events at runtime.\n",
474 			       opts->name,
475 			       opts->shm_id >= 0 ? "-i" : "-p",
476 			       opts->shm_id >= 0 ? opts->shm_id : getpid());
477 #if defined(__linux__)
478 		SPDK_NOTICELOG("Or copy /dev/shm%s for offline analysis/debug.\n", shm_name);
479 #endif
480 	}
481 	free(tp_g_str);
482 
483 	return 0;
484 }
485 
486 static void
487 bootstrap_fn(void *arg1)
488 {
489 	int rc;
490 
491 	if (g_spdk_app.json_config_file) {
492 		g_delay_subsystem_init = false;
493 		spdk_subsystem_init_from_json_config(g_spdk_app.json_config_file, g_spdk_app.rpc_addr,
494 						     app_start_rpc,
495 						     NULL, !g_spdk_app.json_config_ignore_errors);
496 	} else {
497 		if (!g_delay_subsystem_init) {
498 			spdk_subsystem_init(app_start_rpc, NULL);
499 		} else {
500 			spdk_rpc_set_allowlist(g_spdk_app.rpc_allowlist);
501 
502 			rc = spdk_rpc_initialize(g_spdk_app.rpc_addr);
503 			if (rc) {
504 				spdk_app_stop(rc);
505 				return;
506 			}
507 		}
508 	}
509 }
510 
511 static void
512 app_copy_opts(struct spdk_app_opts *opts, struct spdk_app_opts *opts_user, size_t opts_size)
513 {
514 	spdk_app_opts_init(opts, sizeof(*opts));
515 	opts->opts_size = opts_size;
516 
517 #define SET_FIELD(field) \
518         if (offsetof(struct spdk_app_opts, field) + sizeof(opts->field) <= (opts->opts_size)) { \
519 		opts->field = opts_user->field; \
520 	} \
521 
522 	SET_FIELD(name);
523 	SET_FIELD(json_config_file);
524 	SET_FIELD(json_config_ignore_errors);
525 	SET_FIELD(rpc_addr);
526 	SET_FIELD(reactor_mask);
527 	SET_FIELD(lcore_map);
528 	SET_FIELD(tpoint_group_mask);
529 	SET_FIELD(shm_id);
530 	SET_FIELD(shutdown_cb);
531 	SET_FIELD(enable_coredump);
532 	SET_FIELD(mem_channel);
533 	SET_FIELD(main_core);
534 	SET_FIELD(mem_size);
535 	SET_FIELD(no_pci);
536 	SET_FIELD(hugepage_single_segments);
537 	SET_FIELD(unlink_hugepage);
538 	SET_FIELD(hugedir);
539 	SET_FIELD(print_level);
540 	SET_FIELD(num_pci_addr);
541 	SET_FIELD(pci_blocked);
542 	SET_FIELD(pci_allowed);
543 	SET_FIELD(iova_mode);
544 	SET_FIELD(delay_subsystem_init);
545 	SET_FIELD(num_entries);
546 	SET_FIELD(env_context);
547 	SET_FIELD(log);
548 	SET_FIELD(base_virtaddr);
549 	SET_FIELD(disable_signal_handlers);
550 	SET_FIELD(msg_mempool_size);
551 	SET_FIELD(rpc_allowlist);
552 	SET_FIELD(vf_token);
553 
554 	/* You should not remove this statement, but need to update the assert statement
555 	 * if you add a new field, and also add a corresponding SET_FIELD statement */
556 	SPDK_STATIC_ASSERT(sizeof(struct spdk_app_opts) == 224, "Incorrect size");
557 
558 #undef SET_FIELD
559 }
560 
561 static int
562 unclaim_cpu_cores(uint32_t *failed_core)
563 {
564 	char core_name[40];
565 	uint32_t i;
566 	int rc;
567 
568 	for (i = 0; i < MAX_CPU_CORES; i++) {
569 		if (g_core_locks[i] != -1) {
570 			snprintf(core_name, sizeof(core_name), "/var/tmp/spdk_cpu_lock_%03d", i);
571 			rc = close(g_core_locks[i]);
572 			if (rc) {
573 				SPDK_ERRLOG("Failed to close lock fd for core %d, errno: %d\n", i, errno);
574 				goto error;
575 			}
576 
577 			g_core_locks[i] = -1;
578 			rc = unlink(core_name);
579 			if (rc) {
580 				SPDK_ERRLOG("Failed to unlink lock fd for core %d, errno: %d\n", i, errno);
581 				goto error;
582 			}
583 		}
584 	}
585 
586 	return 0;
587 
588 error:
589 	if (failed_core != NULL) {
590 		/* Set number of core we failed to claim. */
591 		*failed_core = i;
592 	}
593 	return -1;
594 }
595 
596 static int
597 claim_cpu_cores(uint32_t *failed_core)
598 {
599 	char core_name[40];
600 	int core_fd, pid;
601 	int *core_map;
602 	uint32_t core;
603 
604 	struct flock core_lock = {
605 		.l_type = F_WRLCK,
606 		.l_whence = SEEK_SET,
607 		.l_start = 0,
608 		.l_len = 0,
609 	};
610 
611 	SPDK_ENV_FOREACH_CORE(core) {
612 		if (g_core_locks[core] != -1) {
613 			/* If this core is locked already, do not try lock it again. */
614 			continue;
615 		}
616 
617 		snprintf(core_name, sizeof(core_name), "/var/tmp/spdk_cpu_lock_%03d", core);
618 		core_fd = open(core_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
619 		if (core_fd == -1) {
620 			SPDK_ERRLOG("Could not open %s (%s).\n", core_name, spdk_strerror(errno));
621 			/* Return number of core we failed to claim. */
622 			goto error;
623 		}
624 
625 		if (ftruncate(core_fd, sizeof(int)) != 0) {
626 			SPDK_ERRLOG("Could not truncate %s (%s).\n", core_name, spdk_strerror(errno));
627 			close(core_fd);
628 			goto error;
629 		}
630 
631 		core_map = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, core_fd, 0);
632 		if (core_map == MAP_FAILED) {
633 			SPDK_ERRLOG("Could not mmap core %s (%s).\n", core_name, spdk_strerror(errno));
634 			close(core_fd);
635 			goto error;
636 		}
637 
638 		if (fcntl(core_fd, F_SETLK, &core_lock) != 0) {
639 			pid = *core_map;
640 			SPDK_ERRLOG("Cannot create lock on core %" PRIu32 ", probably process %d has claimed it.\n",
641 				    core, pid);
642 			munmap(core_map, sizeof(int));
643 			close(core_fd);
644 			goto error;
645 		}
646 
647 		/* We write the PID to the core lock file so that other processes trying
648 		* to claim the same core will know what process is holding the lock. */
649 		*core_map = (int)getpid();
650 		munmap(core_map, sizeof(int));
651 		g_core_locks[core] = core_fd;
652 		/* Keep core_fd open to maintain the lock. */
653 	}
654 
655 	return 0;
656 
657 error:
658 	if (failed_core != NULL) {
659 		/* Set number of core we failed to claim. */
660 		*failed_core = core;
661 	}
662 	unclaim_cpu_cores(NULL);
663 	return -1;
664 }
665 
666 int
667 spdk_app_start(struct spdk_app_opts *opts_user, spdk_msg_fn start_fn,
668 	       void *arg1)
669 {
670 	int			rc;
671 	char			*tty;
672 	struct spdk_cpuset	tmp_cpumask = {};
673 	static bool		g_env_was_setup = false;
674 	struct spdk_app_opts opts_local = {};
675 	struct spdk_app_opts *opts = &opts_local;
676 	uint32_t i;
677 
678 	if (!opts_user) {
679 		SPDK_ERRLOG("opts_user should not be NULL\n");
680 		return 1;
681 	}
682 
683 	if (!opts_user->opts_size) {
684 		SPDK_ERRLOG("The opts_size in opts_user structure should not be zero value\n");
685 		return 1;
686 	}
687 
688 	if (opts_user->name == NULL) {
689 		SPDK_ERRLOG("spdk_app_opts::name not specified\n");
690 		return 1;
691 	}
692 
693 	app_copy_opts(opts, opts_user, opts_user->opts_size);
694 
695 	if (!start_fn) {
696 		SPDK_ERRLOG("start_fn should not be NULL\n");
697 		return 1;
698 	}
699 
700 	if (!(opts->lcore_map || opts->reactor_mask)) {
701 		/* Set default CPU mask */
702 		opts->reactor_mask = SPDK_APP_DPDK_DEFAULT_CORE_MASK;
703 	}
704 
705 	tty = ttyname(STDERR_FILENO);
706 	if (opts->print_level > SPDK_LOG_WARN &&
707 	    isatty(STDERR_FILENO) &&
708 	    tty &&
709 	    !strncmp(tty, "/dev/tty", strlen("/dev/tty"))) {
710 		printf("Warning: printing stderr to console terminal without -q option specified.\n");
711 		printf("Suggest using --silence-noticelog to disable logging to stderr and\n");
712 		printf("monitor syslog, or redirect stderr to a file.\n");
713 		printf("(Delaying for 10 seconds...)\n");
714 		sleep(10);
715 	}
716 
717 	spdk_log_set_print_level(opts->print_level);
718 
719 #ifndef SPDK_NO_RLIMIT
720 	if (opts->enable_coredump) {
721 		struct rlimit core_limits;
722 
723 		core_limits.rlim_cur = core_limits.rlim_max = SPDK_APP_DEFAULT_CORE_LIMIT;
724 		setrlimit(RLIMIT_CORE, &core_limits);
725 	}
726 #endif
727 
728 	memset(&g_spdk_app, 0, sizeof(g_spdk_app));
729 	g_spdk_app.json_config_file = opts->json_config_file;
730 	g_spdk_app.json_config_ignore_errors = opts->json_config_ignore_errors;
731 	g_spdk_app.rpc_addr = opts->rpc_addr;
732 	g_spdk_app.rpc_allowlist = opts->rpc_allowlist;
733 	g_spdk_app.shm_id = opts->shm_id;
734 	g_spdk_app.shutdown_cb = opts->shutdown_cb;
735 	g_spdk_app.rc = 0;
736 	g_spdk_app.stopped = false;
737 
738 	spdk_log_set_level(SPDK_APP_DEFAULT_LOG_LEVEL);
739 
740 	/* Pass NULL to app_setup_env if SPDK app has been set up, in order to
741 	 * indicate that this is a reinitialization.
742 	 */
743 	if (app_setup_env(g_env_was_setup ? NULL : opts) < 0) {
744 		return 1;
745 	}
746 
747 	/* Calculate mempool size now that the env layer has configured the core count
748 	 * for the application */
749 	calculate_mempool_size(opts, opts_user);
750 
751 	spdk_log_open(opts->log);
752 
753 	/* Initialize each lock to -1 to indicate "empty" status */
754 	for (i = 0; i < MAX_CPU_CORES; i++) {
755 		g_core_locks[i] = -1;
756 	}
757 
758 	if (!g_disable_cpumask_locks) {
759 		if (claim_cpu_cores(NULL)) {
760 			SPDK_ERRLOG("Unable to acquire lock on assigned core mask - exiting.\n");
761 			return 1;
762 		}
763 	} else {
764 		SPDK_NOTICELOG("CPU core locks deactivated.\n");
765 	}
766 
767 	SPDK_NOTICELOG("Total cores available: %d\n", spdk_env_get_core_count());
768 
769 	if ((rc = spdk_reactors_init(opts->msg_mempool_size)) != 0) {
770 		SPDK_ERRLOG("Reactor Initialization failed: rc = %d\n", rc);
771 		return 1;
772 	}
773 
774 	spdk_cpuset_set_cpu(&tmp_cpumask, spdk_env_get_current_core(), true);
775 
776 	/* Now that the reactors have been initialized, we can create the app thread. */
777 	spdk_thread_create("app_thread", &tmp_cpumask);
778 	if (!spdk_thread_get_app_thread()) {
779 		SPDK_ERRLOG("Unable to create an spdk_thread for initialization\n");
780 		return 1;
781 	}
782 
783 	/*
784 	 * Disable and ignore trace setup if setting num_entries
785 	 * to be 0.
786 	 *
787 	 * Note the call to app_setup_trace() is located here
788 	 * ahead of app_setup_signal_handlers().
789 	 * That's because there is not an easy/direct clean
790 	 * way of unwinding alloc'd resources that can occur
791 	 * in app_setup_signal_handlers().
792 	 */
793 	if (opts->num_entries != 0 && app_setup_trace(opts) != 0) {
794 		return 1;
795 	}
796 
797 	if (!opts->disable_signal_handlers && app_setup_signal_handlers(opts) != 0) {
798 		return 1;
799 	}
800 
801 	g_delay_subsystem_init = opts->delay_subsystem_init;
802 	g_start_fn = start_fn;
803 	g_start_arg = arg1;
804 
805 	spdk_thread_send_msg(spdk_thread_get_app_thread(), bootstrap_fn, NULL);
806 
807 	/* This blocks until spdk_app_stop is called */
808 	spdk_reactors_start();
809 
810 	g_env_was_setup = true;
811 
812 	return g_spdk_app.rc;
813 }
814 
815 void
816 spdk_app_fini(void)
817 {
818 	spdk_trace_cleanup();
819 	spdk_reactors_fini();
820 	spdk_env_fini();
821 	spdk_log_close();
822 	unclaim_cpu_cores(NULL);
823 }
824 
825 static void
826 _start_subsystem_fini(void *arg1)
827 {
828 	if (g_scheduling_in_progress) {
829 		spdk_thread_send_msg(spdk_thread_get_app_thread(), _start_subsystem_fini, NULL);
830 		return;
831 	}
832 
833 	spdk_subsystem_fini(spdk_reactors_stop, NULL);
834 }
835 
836 static int
837 log_deprecation_hits(void *ctx, struct spdk_deprecation *dep)
838 {
839 	uint64_t hits = spdk_deprecation_get_hits(dep);
840 
841 	if (hits == 0) {
842 		return 0;
843 	}
844 
845 	SPDK_WARNLOG("%s: deprecation '%s' scheduled for removal in %s hit %" PRIu64 " times\n",
846 		     spdk_deprecation_get_tag(dep), spdk_deprecation_get_description(dep),
847 		     spdk_deprecation_get_remove_release(dep), hits);
848 	return 0;
849 }
850 
851 static void
852 app_stop(void *arg1)
853 {
854 	if (g_spdk_app.rc == 0) {
855 		g_spdk_app.rc = (int)(intptr_t)arg1;
856 	}
857 
858 	if (g_spdk_app.stopped) {
859 		SPDK_NOTICELOG("spdk_app_stop called twice\n");
860 		return;
861 	}
862 
863 	spdk_rpc_finish();
864 	g_spdk_app.stopped = true;
865 	spdk_log_for_each_deprecation(NULL, log_deprecation_hits);
866 	_start_subsystem_fini(NULL);
867 }
868 
869 void
870 spdk_app_stop(int rc)
871 {
872 	if (rc) {
873 		SPDK_WARNLOG("spdk_app_stop'd on non-zero\n");
874 	}
875 
876 	/*
877 	 * We want to run spdk_subsystem_fini() from the same thread where spdk_subsystem_init()
878 	 * was called.
879 	 */
880 	spdk_thread_send_msg(spdk_thread_get_app_thread(), app_stop, (void *)(intptr_t)rc);
881 }
882 
883 static void
884 usage(void (*app_usage)(void))
885 {
886 	printf("%s [options]\n", g_executable_name);
887 	printf("options:\n");
888 	printf(" -c, --config <config>     JSON config file (default %s)\n",
889 	       g_default_opts.json_config_file != NULL ? g_default_opts.json_config_file : "none");
890 	printf("     --json <config>       JSON config file (default %s)\n",
891 	       g_default_opts.json_config_file != NULL ? g_default_opts.json_config_file : "none");
892 	printf("     --json-ignore-init-errors\n");
893 	printf("                           don't exit on invalid config entry\n");
894 	printf(" -d, --limit-coredump      do not set max coredump size to RLIM_INFINITY\n");
895 	printf(" -g, --single-file-segments\n");
896 	printf("                           force creating just one hugetlbfs file\n");
897 	printf(" -h, --help                show this usage\n");
898 	printf(" -i, --shm-id <id>         shared memory ID (optional)\n");
899 	printf(" -m, --cpumask <mask or list>    core mask (like 0xF) or core list of '[]' embraced (like [0,1,10]) for DPDK\n");
900 	printf("     --lcores <list>       lcore to CPU mapping list. The list is in the format:\n");
901 	printf("                           <lcores[@CPUs]>[<,lcores[@CPUs]>...]\n");
902 	printf("                           lcores and cpus list are grouped by '(' and ')', e.g '--lcores \"(5-7)@(10-12)\"'\n");
903 	printf("                           Within the group, '-' is used for range separator,\n");
904 	printf("                           ',' is used for single number separator.\n");
905 	printf("                           '( )' can be omitted for single element group,\n");
906 	printf("                           '@' can be omitted if cpus and lcores have the same value\n");
907 	printf(" -n, --mem-channels <num>  channel number of memory channels used for DPDK\n");
908 	printf(" -p, --main-core <id>      main (primary) core for DPDK\n");
909 	printf(" -r, --rpc-socket <path>   RPC listen address (default %s)\n", SPDK_DEFAULT_RPC_ADDR);
910 	printf(" -s, --mem-size <size>     memory size in MB for DPDK (default: ");
911 #ifndef __linux__
912 	if (g_default_opts.mem_size <= 0) {
913 		printf("all hugepage memory)\n");
914 	} else
915 #endif
916 	{
917 		printf("%dMB)\n", g_default_opts.mem_size >= 0 ? g_default_opts.mem_size : 0);
918 	}
919 	printf("     --disable-cpumask-locks    Disable CPU core lock files.\n");
920 	printf("     --silence-noticelog   disable notice level logging to stderr\n");
921 	printf("     --msg-mempool-size <size>  global message memory pool size in count (default: %d)\n",
922 	       SPDK_DEFAULT_MSG_MEMPOOL_SIZE);
923 	printf(" -u, --no-pci              disable PCI access\n");
924 	printf("     --wait-for-rpc        wait for RPCs to initialize subsystems\n");
925 	printf("     --max-delay <num>     maximum reactor delay (in microseconds)\n");
926 	printf(" -B, --pci-blocked <bdf>   pci addr to block (can be used more than once)\n");
927 	printf(" -A, --pci-allowed <bdf>   pci addr to allow (-B and -A cannot be used at the same time)\n");
928 	printf(" -R, --huge-unlink         unlink huge files after initialization\n");
929 	printf(" -v, --version             print SPDK version\n");
930 	printf("     --huge-dir <path>     use a specific hugetlbfs mount to reserve memory from\n");
931 	printf("     --iova-mode <pa/va>   set IOVA mode ('pa' for IOVA_PA and 'va' for IOVA_VA)\n");
932 	printf("     --base-virtaddr <addr>      the base virtual address for DPDK (default: 0x200000000000)\n");
933 	printf("     --num-trace-entries <num>   number of trace entries for each core, must be power of 2, setting 0 to disable trace (default %d)\n",
934 	       SPDK_APP_DEFAULT_NUM_TRACE_ENTRIES);
935 	printf("                                 Tracepoints vary in size and can use more than one trace entry.\n");
936 	printf("     --rpcs-allowed	   comma-separated list of permitted RPCS\n");
937 	printf("     --env-context         Opaque context for use of the env implementation\n");
938 	printf("     --vfio-vf-token       VF token (UUID) shared between SR-IOV PF and VFs for vfio_pci driver\n");
939 	spdk_log_usage(stdout, "-L");
940 	spdk_trace_mask_usage(stdout, "-e");
941 	if (app_usage) {
942 		app_usage();
943 	}
944 }
945 
946 spdk_app_parse_args_rvals_t
947 spdk_app_parse_args(int argc, char **argv, struct spdk_app_opts *opts,
948 		    const char *app_getopt_str, const struct option *app_long_opts,
949 		    int (*app_parse)(int ch, char *arg),
950 		    void (*app_usage)(void))
951 {
952 	int ch, rc, opt_idx, global_long_opts_len, app_long_opts_len;
953 	struct option *cmdline_options;
954 	char *cmdline_short_opts = NULL;
955 	char *shm_id_str = NULL;
956 	enum spdk_app_parse_args_rvals retval = SPDK_APP_PARSE_ARGS_FAIL;
957 	long int tmp;
958 
959 	memcpy(&g_default_opts, opts, sizeof(g_default_opts));
960 
961 	if (opts->json_config_file && access(opts->json_config_file, R_OK) != 0) {
962 		SPDK_WARNLOG("Can't read JSON configuration file '%s'\n", opts->json_config_file);
963 		opts->json_config_file = NULL;
964 	}
965 
966 	if (app_long_opts == NULL) {
967 		app_long_opts_len = 0;
968 	} else {
969 		for (app_long_opts_len = 0;
970 		     app_long_opts[app_long_opts_len].name != NULL;
971 		     app_long_opts_len++);
972 	}
973 
974 	global_long_opts_len = SPDK_COUNTOF(g_cmdline_options);
975 
976 	cmdline_options = calloc(global_long_opts_len + app_long_opts_len + 1, sizeof(*cmdline_options));
977 	if (!cmdline_options) {
978 		SPDK_ERRLOG("Out of memory\n");
979 		return SPDK_APP_PARSE_ARGS_FAIL;
980 	}
981 
982 	memcpy(&cmdline_options[0], g_cmdline_options, sizeof(g_cmdline_options));
983 	if (app_long_opts) {
984 		memcpy(&cmdline_options[global_long_opts_len], app_long_opts,
985 		       app_long_opts_len * sizeof(*app_long_opts));
986 	}
987 
988 	if (app_getopt_str != NULL) {
989 		ch = app_opts_validate(app_getopt_str);
990 		if (ch) {
991 			SPDK_ERRLOG("Duplicated option '%c' between the generic and application specific spdk opts.\n",
992 				    ch);
993 			goto out;
994 		}
995 	}
996 
997 	cmdline_short_opts = spdk_sprintf_alloc("%s%s", app_getopt_str, SPDK_APP_GETOPT_STRING);
998 	if (!cmdline_short_opts) {
999 		SPDK_ERRLOG("Out of memory\n");
1000 		goto out;
1001 	}
1002 
1003 	g_executable_name = argv[0];
1004 
1005 	while ((ch = getopt_long(argc, argv, cmdline_short_opts, cmdline_options, &opt_idx)) != -1) {
1006 		switch (ch) {
1007 		case CONFIG_FILE_OPT_IDX:
1008 		case JSON_CONFIG_OPT_IDX:
1009 			opts->json_config_file = optarg;
1010 			break;
1011 		case JSON_CONFIG_IGNORE_INIT_ERRORS_IDX:
1012 			opts->json_config_ignore_errors = true;
1013 			break;
1014 		case LIMIT_COREDUMP_OPT_IDX:
1015 			opts->enable_coredump = false;
1016 			break;
1017 		case TPOINT_GROUP_OPT_IDX:
1018 			opts->tpoint_group_mask = optarg;
1019 			break;
1020 		case SINGLE_FILE_SEGMENTS_OPT_IDX:
1021 			opts->hugepage_single_segments = true;
1022 			break;
1023 		case HELP_OPT_IDX:
1024 			usage(app_usage);
1025 			retval = SPDK_APP_PARSE_ARGS_HELP;
1026 			goto out;
1027 		case SHM_ID_OPT_IDX:
1028 			shm_id_str = optarg;
1029 			/* a negative shm-id disables shared configuration file */
1030 			if (optarg[0] == '-') {
1031 				shm_id_str++;
1032 			}
1033 			/* check if the positive value of provided shm_id can be parsed as
1034 			 * an integer
1035 			 */
1036 			opts->shm_id = spdk_strtol(shm_id_str, 0);
1037 			if (opts->shm_id < 0) {
1038 				SPDK_ERRLOG("Invalid shared memory ID %s\n", optarg);
1039 				goto out;
1040 			}
1041 			if (optarg[0] == '-') {
1042 				opts->shm_id = -opts->shm_id;
1043 			}
1044 			break;
1045 		case CPUMASK_OPT_IDX:
1046 			if (opts->lcore_map) {
1047 				SPDK_ERRLOG("lcore map and core mask can't be set simultaneously\n");
1048 				goto out;
1049 			}
1050 			opts->reactor_mask = optarg;
1051 			break;
1052 		case LCORES_OPT_IDX:
1053 			if (opts->reactor_mask) {
1054 				SPDK_ERRLOG("lcore map and core mask can't be set simultaneously\n");
1055 				goto out;
1056 			}
1057 			opts->lcore_map = optarg;
1058 			break;
1059 		case DISABLE_CPUMASK_LOCKS_OPT_IDX:
1060 			g_disable_cpumask_locks = true;
1061 			break;
1062 		case MEM_CHANNELS_OPT_IDX:
1063 			opts->mem_channel = spdk_strtol(optarg, 0);
1064 			if (opts->mem_channel < 0) {
1065 				SPDK_ERRLOG("Invalid memory channel %s\n", optarg);
1066 				goto out;
1067 			}
1068 			break;
1069 		case MAIN_CORE_OPT_IDX:
1070 			opts->main_core = spdk_strtol(optarg, 0);
1071 			if (opts->main_core < 0) {
1072 				SPDK_ERRLOG("Invalid main core %s\n", optarg);
1073 				goto out;
1074 			}
1075 			break;
1076 		case SILENCE_NOTICELOG_OPT_IDX:
1077 			opts->print_level = SPDK_LOG_WARN;
1078 			break;
1079 		case RPC_SOCKET_OPT_IDX:
1080 			opts->rpc_addr = optarg;
1081 			break;
1082 		case MEM_SIZE_OPT_IDX: {
1083 			uint64_t mem_size_mb;
1084 			bool mem_size_has_prefix;
1085 
1086 			rc = spdk_parse_capacity(optarg, &mem_size_mb, &mem_size_has_prefix);
1087 			if (rc != 0) {
1088 				SPDK_ERRLOG("invalid memory pool size `-s %s`\n", optarg);
1089 				usage(app_usage);
1090 				goto out;
1091 			}
1092 
1093 			if (mem_size_has_prefix) {
1094 				/* the mem size is in MB by default, so if a prefix was
1095 				 * specified, we need to manually convert to MB.
1096 				 */
1097 				mem_size_mb /= 1024 * 1024;
1098 			}
1099 
1100 			if (mem_size_mb > INT_MAX) {
1101 				SPDK_ERRLOG("invalid memory pool size `-s %s`\n", optarg);
1102 				usage(app_usage);
1103 				goto out;
1104 			}
1105 
1106 			opts->mem_size = (int) mem_size_mb;
1107 			break;
1108 		}
1109 		case MSG_MEMPOOL_SIZE_OPT_IDX:
1110 			tmp = spdk_strtol(optarg, 10);
1111 			if (tmp <= 0) {
1112 				SPDK_ERRLOG("Invalid message memory pool size %s\n", optarg);
1113 				goto out;
1114 			}
1115 
1116 			opts->msg_mempool_size = (size_t)tmp;
1117 			break;
1118 
1119 		case NO_PCI_OPT_IDX:
1120 			opts->no_pci = true;
1121 			break;
1122 		case WAIT_FOR_RPC_OPT_IDX:
1123 			opts->delay_subsystem_init = true;
1124 			break;
1125 		case PCI_BLOCKED_OPT_IDX:
1126 			if (opts->pci_allowed) {
1127 				free(opts->pci_allowed);
1128 				opts->pci_allowed = NULL;
1129 				SPDK_ERRLOG("-B and -A cannot be used at the same time\n");
1130 				usage(app_usage);
1131 				goto out;
1132 			}
1133 
1134 			rc = app_opts_add_pci_addr(opts, &opts->pci_blocked, optarg);
1135 			if (rc != 0) {
1136 				free(opts->pci_blocked);
1137 				opts->pci_blocked = NULL;
1138 				goto out;
1139 			}
1140 			break;
1141 		case LOGFLAG_OPT_IDX:
1142 			rc = spdk_log_set_flag(optarg);
1143 			if (rc < 0) {
1144 				SPDK_ERRLOG("unknown flag\n");
1145 				usage(app_usage);
1146 				goto out;
1147 			}
1148 #ifdef DEBUG
1149 			opts->print_level = SPDK_LOG_DEBUG;
1150 #endif
1151 			break;
1152 		case HUGE_UNLINK_OPT_IDX:
1153 			opts->unlink_hugepage = true;
1154 			break;
1155 		case PCI_WHITELIST_OPT_IDX:
1156 			SPDK_WARNLOG("-W/--pci-whitelist is deprecated.  Use -A/--pci-allowed.\n");
1157 		/* fallthrough */
1158 		case PCI_ALLOWED_OPT_IDX:
1159 			if (opts->pci_blocked) {
1160 				free(opts->pci_blocked);
1161 				opts->pci_blocked = NULL;
1162 				SPDK_ERRLOG("-B and -W cannot be used at the same time\n");
1163 				usage(app_usage);
1164 				goto out;
1165 			}
1166 
1167 			rc = app_opts_add_pci_addr(opts, &opts->pci_allowed, optarg);
1168 			if (rc != 0) {
1169 				free(opts->pci_allowed);
1170 				opts->pci_allowed = NULL;
1171 				goto out;
1172 			}
1173 			break;
1174 		case BASE_VIRTADDR_OPT_IDX:
1175 			tmp = spdk_strtoll(optarg, 0);
1176 			if (tmp <= 0) {
1177 				SPDK_ERRLOG("Invalid base-virtaddr %s\n", optarg);
1178 				usage(app_usage);
1179 				goto out;
1180 			}
1181 			opts->base_virtaddr = (uint64_t)tmp;
1182 			break;
1183 		case HUGE_DIR_OPT_IDX:
1184 			opts->hugedir = optarg;
1185 			break;
1186 		case IOVA_MODE_OPT_IDX:
1187 			opts->iova_mode = optarg;
1188 			break;
1189 		case NUM_TRACE_ENTRIES_OPT_IDX:
1190 			tmp = spdk_strtoll(optarg, 0);
1191 			if (tmp < 0) {
1192 				SPDK_ERRLOG("Invalid num-trace-entries %s\n", optarg);
1193 				usage(app_usage);
1194 				goto out;
1195 			}
1196 			opts->num_entries = (uint64_t)tmp;
1197 			if (opts->num_entries > 0 && opts->num_entries & (opts->num_entries - 1)) {
1198 				SPDK_ERRLOG("num-trace-entries must be power of 2\n");
1199 				usage(app_usage);
1200 				goto out;
1201 			}
1202 			break;
1203 		case MAX_REACTOR_DELAY_OPT_IDX:
1204 			SPDK_ERRLOG("Deprecation warning: The maximum allowed latency parameter is no longer supported.\n");
1205 			break;
1206 		case ENV_CONTEXT_OPT_IDX:
1207 			opts->env_context = optarg;
1208 			break;
1209 		case RPCS_ALLOWED_OPT_IDX:
1210 			opts->rpc_allowlist = (const char **)spdk_strarray_from_string(optarg, ",");
1211 			if (opts->rpc_allowlist == NULL) {
1212 				SPDK_ERRLOG("Invalid --rpcs-allowed argument\n");
1213 				usage(app_usage);
1214 				goto out;
1215 			}
1216 			break;
1217 		case ENV_VF_TOKEN_OPT_IDX:
1218 			opts->vf_token = optarg;
1219 			break;
1220 		case VERSION_OPT_IDX:
1221 			printf(SPDK_VERSION_STRING"\n");
1222 			retval = SPDK_APP_PARSE_ARGS_HELP;
1223 			goto out;
1224 		case '?':
1225 			/*
1226 			 * In the event getopt() above detects an option
1227 			 * in argv that is NOT in the getopt_str,
1228 			 * getopt() will return a '?' indicating failure.
1229 			 */
1230 			usage(app_usage);
1231 			goto out;
1232 		default:
1233 			rc = app_parse(ch, optarg);
1234 			if (rc) {
1235 				SPDK_ERRLOG("Parsing application specific arguments failed: %d\n", rc);
1236 				goto out;
1237 			}
1238 		}
1239 	}
1240 
1241 	if (opts->json_config_file && opts->delay_subsystem_init) {
1242 		SPDK_ERRLOG("JSON configuration file can't be used together with --wait-for-rpc.\n");
1243 		goto out;
1244 	}
1245 
1246 	retval = SPDK_APP_PARSE_ARGS_SUCCESS;
1247 out:
1248 	if (retval != SPDK_APP_PARSE_ARGS_SUCCESS) {
1249 		free(opts->pci_blocked);
1250 		opts->pci_blocked = NULL;
1251 		free(opts->pci_allowed);
1252 		opts->pci_allowed = NULL;
1253 		spdk_strarray_free((char **)opts->rpc_allowlist);
1254 		opts->rpc_allowlist = NULL;
1255 	}
1256 	free(cmdline_short_opts);
1257 	free(cmdline_options);
1258 	return retval;
1259 }
1260 
1261 void
1262 spdk_app_usage(void)
1263 {
1264 	if (g_executable_name == NULL) {
1265 		SPDK_ERRLOG("%s not valid before calling spdk_app_parse_args()\n", __func__);
1266 		return;
1267 	}
1268 
1269 	usage(NULL);
1270 }
1271 
1272 static void
1273 rpc_framework_start_init_cpl(int rc, void *arg1)
1274 {
1275 	struct spdk_jsonrpc_request *request = arg1;
1276 
1277 	assert(spdk_thread_is_app_thread(NULL));
1278 
1279 	if (rc) {
1280 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
1281 						 "framework_initialization failed");
1282 		return;
1283 	}
1284 
1285 	spdk_rpc_set_state(SPDK_RPC_RUNTIME);
1286 	app_start_application();
1287 
1288 	spdk_jsonrpc_send_bool_response(request, true);
1289 }
1290 
1291 static void
1292 rpc_framework_start_init(struct spdk_jsonrpc_request *request,
1293 			 const struct spdk_json_val *params)
1294 {
1295 	if (params != NULL) {
1296 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
1297 						 "framework_start_init requires no parameters");
1298 		return;
1299 	}
1300 
1301 	spdk_subsystem_init(rpc_framework_start_init_cpl, request);
1302 }
1303 SPDK_RPC_REGISTER("framework_start_init", rpc_framework_start_init, SPDK_RPC_STARTUP)
1304 
1305 struct subsystem_init_poller_ctx {
1306 	struct spdk_poller *init_poller;
1307 	struct spdk_jsonrpc_request *request;
1308 };
1309 
1310 static int
1311 rpc_subsystem_init_poller_ctx(void *ctx)
1312 {
1313 	struct subsystem_init_poller_ctx *poller_ctx = ctx;
1314 
1315 	if (spdk_rpc_get_state() == SPDK_RPC_RUNTIME) {
1316 		spdk_jsonrpc_send_bool_response(poller_ctx->request, true);
1317 		spdk_poller_unregister(&poller_ctx->init_poller);
1318 		free(poller_ctx);
1319 	}
1320 
1321 	return SPDK_POLLER_BUSY;
1322 }
1323 
1324 static void
1325 rpc_framework_wait_init(struct spdk_jsonrpc_request *request,
1326 			const struct spdk_json_val *params)
1327 {
1328 	struct subsystem_init_poller_ctx *ctx;
1329 
1330 	if (spdk_rpc_get_state() == SPDK_RPC_RUNTIME) {
1331 		spdk_jsonrpc_send_bool_response(request, true);
1332 	} else {
1333 		ctx = malloc(sizeof(struct subsystem_init_poller_ctx));
1334 		if (ctx == NULL) {
1335 			spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
1336 							 "Unable to allocate memory for the request context\n");
1337 			return;
1338 		}
1339 		ctx->request = request;
1340 		ctx->init_poller = SPDK_POLLER_REGISTER(rpc_subsystem_init_poller_ctx, ctx, 0);
1341 	}
1342 }
1343 SPDK_RPC_REGISTER("framework_wait_init", rpc_framework_wait_init,
1344 		  SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
1345 
1346 static void
1347 rpc_framework_disable_cpumask_locks(struct spdk_jsonrpc_request *request,
1348 				    const struct spdk_json_val *params)
1349 {
1350 	char msg[128];
1351 	int rc;
1352 	uint32_t failed_core;
1353 
1354 	if (params != NULL) {
1355 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
1356 						 "framework_disable_cpumask_locks"
1357 						 "requires no arguments");
1358 		return;
1359 	}
1360 
1361 	rc = unclaim_cpu_cores(&failed_core);
1362 	if (rc) {
1363 		snprintf(msg, sizeof(msg), "Failed to unclaim CPU core: %" PRIu32, failed_core);
1364 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, msg);
1365 		return;
1366 	}
1367 
1368 	spdk_jsonrpc_send_bool_response(request, true);
1369 }
1370 SPDK_RPC_REGISTER("framework_disable_cpumask_locks", rpc_framework_disable_cpumask_locks,
1371 		  SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
1372 
1373 static void
1374 rpc_framework_enable_cpumask_locks(struct spdk_jsonrpc_request *request,
1375 				   const struct spdk_json_val *params)
1376 {
1377 	char msg[128];
1378 	int rc;
1379 	uint32_t failed_core;
1380 
1381 	if (params != NULL) {
1382 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
1383 						 "framework_enable_cpumask_locks"
1384 						 "requires no arguments");
1385 		return;
1386 	}
1387 
1388 	rc = claim_cpu_cores(&failed_core);
1389 	if (rc) {
1390 		snprintf(msg, sizeof(msg), "Failed to claim CPU core: %" PRIu32, failed_core);
1391 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, msg);
1392 		return;
1393 	}
1394 
1395 	spdk_jsonrpc_send_bool_response(request, true);
1396 }
1397 SPDK_RPC_REGISTER("framework_enable_cpumask_locks", rpc_framework_enable_cpumask_locks,
1398 		  SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME)
1399