1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "spdk/stdinc.h" 35 36 #include "spdk_internal/event.h" 37 38 #include "spdk/env.h" 39 #include "spdk/log.h" 40 #include "spdk/conf.h" 41 #include "spdk/trace.h" 42 43 #define SPDK_APP_DEFAULT_LOG_PRIORITY SPDK_LOG_INFO 44 45 #define SPDK_APP_DPDK_DEFAULT_MEM_SIZE -1 46 #define SPDK_APP_DPDK_DEFAULT_MASTER_CORE -1 47 #define SPDK_APP_DPDK_DEFAULT_MEM_CHANNEL -1 48 #define SPDK_APP_DPDK_DEFAULT_CORE_MASK "0x1" 49 50 struct spdk_app { 51 struct spdk_conf *config; 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 struct spdk_event *g_shutdown_event = NULL; 59 60 int 61 spdk_app_get_shm_id(void) 62 { 63 return g_spdk_app.shm_id; 64 } 65 66 /* Global section */ 67 #define GLOBAL_CONFIG_TMPL \ 68 "# Configuration file\n" \ 69 "#\n" \ 70 "# Please write all parameters using ASCII.\n" \ 71 "# The parameter must be quoted if it includes whitespace.\n" \ 72 "#\n" \ 73 "# Configuration syntax:\n" \ 74 "# Spaces at head of line are deleted, other spaces are as separator\n" \ 75 "# Lines starting with '#' are comments and not evaluated.\n" \ 76 "# Lines ending with '\\' are concatenated with the next line.\n" \ 77 "# Bracketed keys are section keys grouping the following value keys.\n" \ 78 "# Number of section key is used as a tag number.\n" \ 79 "# Ex. [TargetNode1] = TargetNode section key with tag number 1\n" \ 80 "[Global]\n" \ 81 " Comment \"Global section\"\n" \ 82 "\n" \ 83 " # Users can restrict work items to only run on certain cores by\n" \ 84 " # specifying a ReactorMask. Default is to allow work items to run\n" \ 85 " # on all cores. Core 0 must be set in the mask if one is specified.\n" \ 86 " # Default: 0xFFFF (cores 0-15)\n" \ 87 " ReactorMask \"0x%" PRIX64 "\"\n" \ 88 "\n" \ 89 " # Tracepoint group mask for spdk trace buffers\n" \ 90 " # Default: 0x0 (all tracepoint groups disabled)\n" \ 91 " # Set to 0xFFFFFFFFFFFFFFFF to enable all tracepoint groups.\n" \ 92 " TpointGroupMask \"0x%" PRIX64 "\"\n" \ 93 "\n" \ 94 95 static void 96 spdk_app_config_dump_global_section(FILE *fp) 97 { 98 if (NULL == fp) 99 return; 100 101 fprintf(fp, GLOBAL_CONFIG_TMPL, 102 spdk_app_get_core_mask(), spdk_trace_get_tpoint_group_mask()); 103 } 104 105 int 106 spdk_app_get_running_config(char **config_str, char *name) 107 { 108 FILE *fp = NULL; 109 int fd = -1; 110 long length = 0, ret = 0; 111 char vbuf[BUFSIZ]; 112 char config_template[64]; 113 114 snprintf(config_template, sizeof(config_template), "/tmp/%s.XXXXXX", name); 115 /* Create temporary file to hold config */ 116 fd = mkstemp(config_template); 117 if (fd == -1) { 118 SPDK_ERRLOG("mkstemp failed\n"); 119 return -1; 120 } 121 fp = fdopen(fd, "wb+"); 122 if (NULL == fp) { 123 SPDK_ERRLOG("error opening tmpfile fd = %d\n", fd); 124 return -1; 125 } 126 127 /* Buffered IO */ 128 setvbuf(fp, vbuf, _IOFBF, BUFSIZ); 129 130 spdk_app_config_dump_global_section(fp); 131 spdk_subsystem_config(fp); 132 133 length = ftell(fp); 134 135 *config_str = malloc(length + 1); 136 if (!*config_str) { 137 perror("config_str"); 138 fclose(fp); 139 return -1; 140 } 141 fseek(fp, 0, SEEK_SET); 142 ret = fread(*config_str, sizeof(char), length, fp); 143 if (ret < length) 144 SPDK_ERRLOG("short read\n"); 145 fclose(fp); 146 (*config_str)[length] = '\0'; 147 148 return 0; 149 } 150 151 void 152 spdk_app_start_shutdown(void) 153 { 154 if (g_shutdown_event != NULL) { 155 spdk_event_call(g_shutdown_event); 156 g_shutdown_event = NULL; 157 } else { 158 spdk_app_stop(0); 159 } 160 } 161 162 static void 163 __shutdown_signal(int signo) 164 { 165 spdk_app_start_shutdown(); 166 } 167 168 static void 169 __shutdown_event_cb(void *arg1, void *arg2) 170 { 171 g_spdk_app.shutdown_cb(); 172 } 173 174 void 175 spdk_app_opts_init(struct spdk_app_opts *opts) 176 { 177 if (!opts) 178 return; 179 180 memset(opts, 0, sizeof(*opts)); 181 182 opts->enable_coredump = true; 183 opts->shm_id = -1; 184 opts->mem_size = SPDK_APP_DPDK_DEFAULT_MEM_SIZE; 185 opts->master_core = SPDK_APP_DPDK_DEFAULT_MASTER_CORE; 186 opts->mem_channel = SPDK_APP_DPDK_DEFAULT_MEM_CHANNEL; 187 opts->reactor_mask = NULL; 188 opts->max_delay_us = 0; 189 } 190 191 static int 192 spdk_app_setup_signal_handlers(struct spdk_app_opts *opts) 193 { 194 struct sigaction sigact; 195 sigset_t sigmask; 196 int rc; 197 198 /* Set up custom shutdown handling if the user requested it. */ 199 if (opts->shutdown_cb != NULL) { 200 g_shutdown_event = spdk_event_allocate(spdk_env_get_current_core(), 201 __shutdown_event_cb, 202 NULL, NULL); 203 } 204 205 sigemptyset(&sigmask); 206 memset(&sigact, 0, sizeof(sigact)); 207 sigemptyset(&sigact.sa_mask); 208 209 sigact.sa_handler = SIG_IGN; 210 rc = sigaction(SIGPIPE, &sigact, NULL); 211 if (rc < 0) { 212 SPDK_ERRLOG("sigaction(SIGPIPE) failed\n"); 213 return rc; 214 } 215 216 /* Install the same handler for SIGINT and SIGTERM */ 217 sigact.sa_handler = __shutdown_signal; 218 219 rc = sigaction(SIGINT, &sigact, NULL); 220 if (rc < 0) { 221 SPDK_ERRLOG("sigaction(SIGINT) failed\n"); 222 return rc; 223 } 224 sigaddset(&sigmask, SIGINT); 225 226 rc = sigaction(SIGTERM, &sigact, NULL); 227 if (rc < 0) { 228 SPDK_ERRLOG("sigaction(SIGTERM) failed\n"); 229 return rc; 230 } 231 sigaddset(&sigmask, SIGTERM); 232 233 if (opts->usr1_handler != NULL) { 234 sigact.sa_handler = opts->usr1_handler; 235 rc = sigaction(SIGUSR1, &sigact, NULL); 236 if (rc < 0) { 237 SPDK_ERRLOG("sigaction(SIGUSR1) failed\n"); 238 return rc; 239 } 240 sigaddset(&sigmask, SIGUSR1); 241 } 242 243 pthread_sigmask(SIG_UNBLOCK, &sigmask, NULL); 244 245 return 0; 246 } 247 248 int 249 spdk_app_start(struct spdk_app_opts *opts, spdk_event_fn start_fn, 250 void *arg1, void *arg2) 251 { 252 struct spdk_conf *config; 253 struct spdk_conf_section *sp; 254 char shm_name[64]; 255 int rc; 256 uint64_t tpoint_group_mask; 257 char *end; 258 struct spdk_env_opts env_opts = {}; 259 struct spdk_event *app_start_event; 260 261 if (!opts) { 262 SPDK_ERRLOG("opts should not be NULL\n"); 263 exit(EXIT_FAILURE); 264 } 265 266 if (opts->enable_coredump) { 267 struct rlimit core_limits; 268 269 core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY; 270 setrlimit(RLIMIT_CORE, &core_limits); 271 } 272 273 config = spdk_conf_allocate(); 274 assert(config != NULL); 275 if (opts->config_file) { 276 rc = spdk_conf_read(config, opts->config_file); 277 if (rc != 0) { 278 SPDK_ERRLOG("Could not read config file %s\n", opts->config_file); 279 spdk_conf_free(config); 280 exit(EXIT_FAILURE); 281 } 282 if (spdk_conf_first_section(config) == NULL) { 283 SPDK_ERRLOG("Invalid config file %s\n", opts->config_file); 284 spdk_conf_free(config); 285 exit(EXIT_FAILURE); 286 } 287 } 288 spdk_conf_set_as_default(config); 289 290 if (opts->shm_id == -1) { 291 sp = spdk_conf_find_section(config, "Global"); 292 if (sp != NULL) { 293 opts->shm_id = spdk_conf_section_get_intval(sp, "SharedMemoryID"); 294 } 295 } 296 297 memset(&g_spdk_app, 0, sizeof(g_spdk_app)); 298 g_spdk_app.config = config; 299 g_spdk_app.shm_id = opts->shm_id; 300 g_spdk_app.shutdown_cb = opts->shutdown_cb; 301 302 spdk_log_set_level(SPDK_APP_DEFAULT_LOG_PRIORITY); 303 spdk_log_open(); 304 305 sp = spdk_conf_find_section(g_spdk_app.config, "Global"); 306 if (opts->reactor_mask == NULL) { 307 if (sp && spdk_conf_section_get_val(sp, "ReactorMask")) { 308 opts->reactor_mask = spdk_conf_section_get_val(sp, "ReactorMask"); 309 } else { 310 opts->reactor_mask = SPDK_APP_DPDK_DEFAULT_CORE_MASK; 311 } 312 } 313 314 if (!opts->no_pci && sp) { 315 opts->no_pci = spdk_conf_section_get_boolval(sp, "NoPci", false); 316 } 317 318 spdk_env_opts_init(&env_opts); 319 320 env_opts.name = opts->name; 321 env_opts.core_mask = opts->reactor_mask; 322 env_opts.shm_id = opts->shm_id; 323 env_opts.mem_channel = opts->mem_channel; 324 env_opts.master_core = opts->master_core; 325 env_opts.mem_size = opts->mem_size; 326 env_opts.no_pci = opts->no_pci; 327 328 spdk_env_init(&env_opts); 329 330 printf("Total cores available: %d\n", spdk_env_get_core_count()); 331 332 /* 333 * If mask not specified on command line or in configuration file, 334 * reactor_mask will be 0x1 which will enable core 0 to run one 335 * reactor. 336 */ 337 if (spdk_reactors_init(opts->max_delay_us)) { 338 SPDK_ERRLOG("Invalid reactor mask.\n"); 339 spdk_conf_free(g_spdk_app.config); 340 exit(EXIT_FAILURE); 341 } 342 343 if (spdk_app_setup_signal_handlers(opts)) { 344 spdk_conf_free(g_spdk_app.config); 345 exit(EXIT_FAILURE); 346 } 347 348 if (opts->shm_id >= 0) { 349 snprintf(shm_name, sizeof(shm_name), "/%s_trace.%d", opts->name, opts->shm_id); 350 } else { 351 snprintf(shm_name, sizeof(shm_name), "/%s_trace.pid%d", opts->name, (int)getpid()); 352 } 353 354 spdk_trace_init(shm_name); 355 356 if (opts->tpoint_group_mask == NULL) { 357 sp = spdk_conf_find_section(g_spdk_app.config, "Global"); 358 if (sp != NULL) { 359 opts->tpoint_group_mask = spdk_conf_section_get_val(sp, "TpointGroupMask"); 360 } 361 } 362 363 if (opts->tpoint_group_mask != NULL) { 364 errno = 0; 365 tpoint_group_mask = strtoull(opts->tpoint_group_mask, &end, 16); 366 if (*end != '\0' || errno) { 367 SPDK_ERRLOG("invalid tpoint mask %s\n", opts->tpoint_group_mask); 368 } else { 369 SPDK_NOTICELOG("Tracepoint Group Mask %s specified.\n", opts->tpoint_group_mask); 370 SPDK_NOTICELOG("Use 'spdk_trace -s %s %s %d' to capture a snapshot of events at runtime.\n", 371 opts->name, 372 opts->shm_id >= 0 ? "-i" : "-p", 373 opts->shm_id >= 0 ? opts->shm_id : getpid()); 374 spdk_trace_set_tpoint_group_mask(tpoint_group_mask); 375 } 376 } 377 378 g_spdk_app.rc = 0; 379 app_start_event = spdk_event_allocate(spdk_env_get_current_core(), start_fn, 380 arg1, arg2); 381 382 spdk_subsystem_init(app_start_event); 383 384 /* This blocks until spdk_app_stop is called */ 385 spdk_reactors_start(); 386 387 return g_spdk_app.rc; 388 } 389 390 void 391 spdk_app_fini(void) 392 { 393 spdk_subsystem_fini(); 394 spdk_trace_cleanup(); 395 spdk_reactors_fini(); 396 spdk_conf_free(g_spdk_app.config); 397 spdk_log_close(); 398 } 399 400 void 401 spdk_app_stop(int rc) 402 { 403 spdk_reactors_stop(); 404 g_spdk_app.rc = rc; 405 } 406