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