1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 8 #include "spdk/init.h" 9 #include "spdk/util.h" 10 #include "spdk/file.h" 11 #include "spdk/log.h" 12 #include "spdk/env.h" 13 #include "spdk/thread.h" 14 #include "spdk/jsonrpc.h" 15 #include "spdk/rpc.h" 16 #include "spdk/string.h" 17 18 #include "spdk_internal/event.h" 19 20 #define SPDK_DEBUG_APP_CFG(...) SPDK_DEBUGLOG(app_config, __VA_ARGS__) 21 22 /* JSON configuration format is as follows 23 * 24 * { 25 * "subsystems" : [ <<== *subsystems JSON array 26 * { <<== *subsystems_it array entry pointer (iterator) 27 * "subsystem": "<< SUBSYSTEM NAME >>", 28 * "config": [ <<== *config JSON array 29 * { <<== *config_it array entry pointer (iterator) 30 * "method": "<< METHOD NAME >>", <<== *method 31 * "params": { << PARAMS >> } <<== *params 32 * }, 33 * << MORE "config" ARRY ENTRIES >> 34 * ] 35 * }, 36 * << MORE "subsystems" ARRAY ENTRIES >> 37 * ] 38 * 39 * << ANYTHING ELSE IS IGNORED IN ROOT OBJECT>> 40 * } 41 * 42 */ 43 44 struct load_json_config_ctx; 45 typedef void (*client_resp_handler)(struct load_json_config_ctx *, 46 struct spdk_jsonrpc_client_response *); 47 48 #define RPC_SOCKET_PATH_MAX SPDK_SIZEOF_MEMBER(struct sockaddr_un, sun_path) 49 50 /* 1s connections timeout */ 51 #define RPC_CLIENT_CONNECT_TIMEOUT_US (1U * 1000U * 1000U) 52 53 /* 54 * Currently there is no timeout in SPDK for any RPC command. This result that 55 * we can't put a hard limit during configuration load as it most likely randomly fail. 56 * So just print WARNLOG every 10s. */ 57 #define RPC_CLIENT_REQUEST_TIMEOUT_US (10U * 1000 * 1000) 58 59 struct load_json_config_ctx { 60 /* Thread used during configuration. */ 61 struct spdk_thread *thread; 62 spdk_subsystem_init_fn cb_fn; 63 void *cb_arg; 64 bool stop_on_error; 65 66 /* Current subsystem */ 67 struct spdk_json_val *subsystems; /* "subsystems" array */ 68 struct spdk_json_val *subsystems_it; /* current subsystem array position in "subsystems" array */ 69 70 struct spdk_json_val *subsystem_name; /* current subsystem name */ 71 72 /* Current "config" entry we are processing */ 73 struct spdk_json_val *config; /* "config" array */ 74 struct spdk_json_val *config_it; /* current config position in "config" array */ 75 76 /* Current request id we are sending. */ 77 uint32_t rpc_request_id; 78 79 /* Whole configuration file read and parsed. */ 80 size_t json_data_size; 81 char *json_data; 82 83 size_t values_cnt; 84 struct spdk_json_val *values; 85 86 char rpc_socket_path_temp[RPC_SOCKET_PATH_MAX + 1]; 87 88 struct spdk_jsonrpc_client *client_conn; 89 struct spdk_poller *client_conn_poller; 90 91 client_resp_handler client_resp_cb; 92 93 /* Timeout for current RPC client action. */ 94 uint64_t timeout; 95 }; 96 97 static void app_json_config_load_subsystem(void *_ctx); 98 99 static void 100 app_json_config_load_done(struct load_json_config_ctx *ctx, int rc) 101 { 102 spdk_poller_unregister(&ctx->client_conn_poller); 103 if (ctx->client_conn != NULL) { 104 spdk_jsonrpc_client_close(ctx->client_conn); 105 } 106 107 spdk_rpc_finish(); 108 109 SPDK_DEBUG_APP_CFG("Config load finished with rc %d\n", rc); 110 ctx->cb_fn(rc, ctx->cb_arg); 111 112 free(ctx->json_data); 113 free(ctx->values); 114 free(ctx); 115 } 116 117 static void 118 rpc_client_set_timeout(struct load_json_config_ctx *ctx, uint64_t timeout_us) 119 { 120 ctx->timeout = spdk_get_ticks() + timeout_us * spdk_get_ticks_hz() / (1000 * 1000); 121 } 122 123 static int 124 rpc_client_check_timeout(struct load_json_config_ctx *ctx) 125 { 126 if (ctx->timeout < spdk_get_ticks()) { 127 SPDK_WARNLOG("RPC client command timeout.\n"); 128 return -ETIMEDOUT; 129 } 130 131 return 0; 132 } 133 134 struct json_write_buf { 135 char data[1024]; 136 unsigned cur_off; 137 }; 138 139 static int 140 json_write_stdout(void *cb_ctx, const void *data, size_t size) 141 { 142 struct json_write_buf *buf = cb_ctx; 143 size_t rc; 144 145 rc = snprintf(buf->data + buf->cur_off, sizeof(buf->data) - buf->cur_off, 146 "%s", (const char *)data); 147 if (rc > 0) { 148 buf->cur_off += rc; 149 } 150 return rc == size ? 0 : -1; 151 } 152 153 static int 154 rpc_client_poller(void *arg) 155 { 156 struct load_json_config_ctx *ctx = arg; 157 struct spdk_jsonrpc_client_response *resp; 158 client_resp_handler cb; 159 int rc; 160 161 assert(spdk_get_thread() == ctx->thread); 162 163 rc = spdk_jsonrpc_client_poll(ctx->client_conn, 0); 164 if (rc == 0) { 165 rc = rpc_client_check_timeout(ctx); 166 if (rc == -ETIMEDOUT) { 167 rpc_client_set_timeout(ctx, RPC_CLIENT_REQUEST_TIMEOUT_US); 168 rc = 0; 169 } 170 } 171 172 if (rc == 0) { 173 /* No response yet */ 174 return SPDK_POLLER_BUSY; 175 } else if (rc < 0) { 176 app_json_config_load_done(ctx, rc); 177 return SPDK_POLLER_BUSY; 178 } 179 180 resp = spdk_jsonrpc_client_get_response(ctx->client_conn); 181 assert(resp); 182 183 if (resp->error) { 184 struct json_write_buf buf = {}; 185 struct spdk_json_write_ctx *w = spdk_json_write_begin(json_write_stdout, 186 &buf, SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE); 187 188 if (w == NULL) { 189 SPDK_ERRLOG("error response: (?)\n"); 190 } else { 191 spdk_json_write_val(w, resp->error); 192 spdk_json_write_end(w); 193 SPDK_ERRLOG("error response: \n%s\n", buf.data); 194 } 195 } 196 197 if (resp->error && ctx->stop_on_error) { 198 spdk_jsonrpc_client_free_response(resp); 199 app_json_config_load_done(ctx, -EINVAL); 200 } else { 201 /* We have response so we must have callback for it. */ 202 cb = ctx->client_resp_cb; 203 assert(cb != NULL); 204 205 /* Mark we are done with this handler. */ 206 ctx->client_resp_cb = NULL; 207 cb(ctx, resp); 208 } 209 210 211 return SPDK_POLLER_BUSY; 212 } 213 214 static int 215 rpc_client_connect_poller(void *_ctx) 216 { 217 struct load_json_config_ctx *ctx = _ctx; 218 int rc; 219 220 rc = spdk_jsonrpc_client_poll(ctx->client_conn, 0); 221 if (rc != -ENOTCONN) { 222 /* We are connected. Start regular poller and issue first request */ 223 spdk_poller_unregister(&ctx->client_conn_poller); 224 ctx->client_conn_poller = SPDK_POLLER_REGISTER(rpc_client_poller, ctx, 100); 225 app_json_config_load_subsystem(ctx); 226 } else { 227 rc = rpc_client_check_timeout(ctx); 228 if (rc) { 229 app_json_config_load_done(ctx, rc); 230 } 231 232 return SPDK_POLLER_IDLE; 233 } 234 235 return SPDK_POLLER_BUSY; 236 } 237 238 static int 239 client_send_request(struct load_json_config_ctx *ctx, struct spdk_jsonrpc_client_request *request, 240 client_resp_handler client_resp_cb) 241 { 242 int rc; 243 244 assert(spdk_get_thread() == ctx->thread); 245 246 ctx->client_resp_cb = client_resp_cb; 247 rpc_client_set_timeout(ctx, RPC_CLIENT_REQUEST_TIMEOUT_US); 248 rc = spdk_jsonrpc_client_send_request(ctx->client_conn, request); 249 250 if (rc) { 251 SPDK_DEBUG_APP_CFG("Sending request to client failed (%d)\n", rc); 252 } 253 254 return rc; 255 } 256 257 static int 258 cap_string(const struct spdk_json_val *val, void *out) 259 { 260 const struct spdk_json_val **vptr = out; 261 262 if (val->type != SPDK_JSON_VAL_STRING) { 263 return -EINVAL; 264 } 265 266 *vptr = val; 267 return 0; 268 } 269 270 static int 271 cap_object(const struct spdk_json_val *val, void *out) 272 { 273 const struct spdk_json_val **vptr = out; 274 275 if (val->type != SPDK_JSON_VAL_OBJECT_BEGIN) { 276 return -EINVAL; 277 } 278 279 *vptr = val; 280 return 0; 281 } 282 283 284 static int 285 cap_array_or_null(const struct spdk_json_val *val, void *out) 286 { 287 const struct spdk_json_val **vptr = out; 288 289 if (val->type != SPDK_JSON_VAL_ARRAY_BEGIN && val->type != SPDK_JSON_VAL_NULL) { 290 return -EINVAL; 291 } 292 293 *vptr = val; 294 return 0; 295 } 296 297 struct config_entry { 298 char *method; 299 struct spdk_json_val *params; 300 }; 301 302 static struct spdk_json_object_decoder jsonrpc_cmd_decoders[] = { 303 {"method", offsetof(struct config_entry, method), spdk_json_decode_string}, 304 {"params", offsetof(struct config_entry, params), cap_object, true} 305 }; 306 307 static void app_json_config_load_subsystem_config_entry(void *_ctx); 308 309 static void 310 app_json_config_load_subsystem_config_entry_next(struct load_json_config_ctx *ctx, 311 struct spdk_jsonrpc_client_response *resp) 312 { 313 /* Don't care about the response */ 314 spdk_jsonrpc_client_free_response(resp); 315 316 ctx->config_it = spdk_json_next(ctx->config_it); 317 app_json_config_load_subsystem_config_entry(ctx); 318 } 319 320 /* Load "config" entry */ 321 static void 322 app_json_config_load_subsystem_config_entry(void *_ctx) 323 { 324 struct load_json_config_ctx *ctx = _ctx; 325 struct spdk_jsonrpc_client_request *rpc_request; 326 struct spdk_json_write_ctx *w; 327 struct config_entry cfg = {}; 328 struct spdk_json_val *params_end; 329 size_t params_len = 0; 330 int rc; 331 332 if (ctx->config_it == NULL) { 333 SPDK_DEBUG_APP_CFG("Subsystem '%.*s': configuration done.\n", ctx->subsystem_name->len, 334 (char *)ctx->subsystem_name->start); 335 ctx->subsystems_it = spdk_json_next(ctx->subsystems_it); 336 /* Invoke later to avoid recurrency */ 337 spdk_thread_send_msg(ctx->thread, app_json_config_load_subsystem, ctx); 338 return; 339 } 340 341 if (spdk_json_decode_object(ctx->config_it, jsonrpc_cmd_decoders, 342 SPDK_COUNTOF(jsonrpc_cmd_decoders), &cfg)) { 343 SPDK_ERRLOG("Failed to decode config entry\n"); 344 app_json_config_load_done(ctx, -EINVAL); 345 goto out; 346 } 347 348 rc = spdk_rpc_is_method_allowed(cfg.method, spdk_rpc_get_state()); 349 if (rc == -EPERM) { 350 SPDK_DEBUG_APP_CFG("Method '%s' not allowed -> skipping\n", cfg.method); 351 /* Invoke later to avoid recurrency */ 352 ctx->config_it = spdk_json_next(ctx->config_it); 353 spdk_thread_send_msg(ctx->thread, app_json_config_load_subsystem_config_entry, ctx); 354 goto out; 355 } 356 357 SPDK_DEBUG_APP_CFG("\tmethod: %s\n", cfg.method); 358 359 if (cfg.params) { 360 /* Get _END by skipping params and going back by one element. */ 361 params_end = cfg.params + spdk_json_val_len(cfg.params) - 1; 362 363 /* Need to add one character to include '}' */ 364 params_len = params_end->start - cfg.params->start + 1; 365 366 SPDK_DEBUG_APP_CFG("\tparams: %.*s\n", (int)params_len, (char *)cfg.params->start); 367 } 368 369 rpc_request = spdk_jsonrpc_client_create_request(); 370 if (!rpc_request) { 371 app_json_config_load_done(ctx, -errno); 372 goto out; 373 } 374 375 w = spdk_jsonrpc_begin_request(rpc_request, ctx->rpc_request_id, NULL); 376 if (!w) { 377 spdk_jsonrpc_client_free_request(rpc_request); 378 app_json_config_load_done(ctx, -ENOMEM); 379 goto out; 380 } 381 382 spdk_json_write_named_string(w, "method", cfg.method); 383 384 if (cfg.params) { 385 /* No need to parse "params". Just dump the whole content of "params" 386 * directly into the request and let the remote side verify it. */ 387 spdk_json_write_name(w, "params"); 388 spdk_json_write_val_raw(w, cfg.params->start, params_len); 389 } 390 391 spdk_jsonrpc_end_request(rpc_request, w); 392 393 rc = client_send_request(ctx, rpc_request, app_json_config_load_subsystem_config_entry_next); 394 if (rc != 0) { 395 app_json_config_load_done(ctx, -rc); 396 goto out; 397 } 398 out: 399 free(cfg.method); 400 } 401 402 static void 403 subsystem_init_done(int rc, void *arg1) 404 { 405 struct load_json_config_ctx *ctx = arg1; 406 407 if (rc) { 408 app_json_config_load_done(ctx, rc); 409 return; 410 } 411 412 spdk_rpc_set_state(SPDK_RPC_RUNTIME); 413 /* Another round. This time for RUNTIME methods */ 414 SPDK_DEBUG_APP_CFG("'framework_start_init' done - continuing configuration\n"); 415 416 assert(ctx != NULL); 417 if (ctx->subsystems) { 418 ctx->subsystems_it = spdk_json_array_first(ctx->subsystems); 419 } 420 421 app_json_config_load_subsystem(ctx); 422 } 423 424 static struct spdk_json_object_decoder subsystem_decoders[] = { 425 {"subsystem", offsetof(struct load_json_config_ctx, subsystem_name), cap_string}, 426 {"config", offsetof(struct load_json_config_ctx, config), cap_array_or_null} 427 }; 428 429 /* 430 * Start loading subsystem pointed by ctx->subsystems_it. This must point to the 431 * beginning of the "subsystem" object in "subsystems" array or be NULL. If it is 432 * NULL then no more subsystems to load. 433 * 434 * There are two iterations: 435 * 436 * In first iteration only STARTUP RPC methods are used, other methods are ignored. When 437 * allsubsystems are walked the ctx->subsystems_it became NULL and "framework_start_init" 438 * is called to let the SPDK move to RUNTIME state (initialize all subsystems) and 439 * second iteration begins. 440 * 441 * In second iteration "subsystems" array is walked through again, this time only 442 * RUNTIME RPC methods are used. When ctx->subsystems_it became NULL second time it 443 * indicate that there is no more subsystems to load. The cb_fn is called to finish 444 * configuration. 445 */ 446 static void 447 app_json_config_load_subsystem(void *_ctx) 448 { 449 struct load_json_config_ctx *ctx = _ctx; 450 451 if (ctx->subsystems_it == NULL) { 452 if (spdk_rpc_get_state() == SPDK_RPC_STARTUP) { 453 SPDK_DEBUG_APP_CFG("No more entries for current state, calling 'framework_start_init'\n"); 454 spdk_subsystem_init(subsystem_init_done, ctx); 455 } else { 456 app_json_config_load_done(ctx, 0); 457 } 458 459 return; 460 } 461 462 /* Capture subsystem name and config array */ 463 if (spdk_json_decode_object(ctx->subsystems_it, subsystem_decoders, 464 SPDK_COUNTOF(subsystem_decoders), ctx)) { 465 SPDK_ERRLOG("Failed to parse subsystem configuration\n"); 466 app_json_config_load_done(ctx, -EINVAL); 467 return; 468 } 469 470 SPDK_DEBUG_APP_CFG("Loading subsystem '%.*s' configuration\n", ctx->subsystem_name->len, 471 (char *)ctx->subsystem_name->start); 472 473 /* Get 'config' array first configuration entry */ 474 ctx->config_it = spdk_json_array_first(ctx->config); 475 app_json_config_load_subsystem_config_entry(ctx); 476 } 477 478 static void * 479 read_file(const char *filename, size_t *size) 480 { 481 FILE *file = fopen(filename, "r"); 482 void *data; 483 484 if (file == NULL) { 485 /* errno is set by fopen */ 486 return NULL; 487 } 488 489 data = spdk_posix_file_load(file, size); 490 fclose(file); 491 return data; 492 } 493 494 static int 495 app_json_config_read(const char *config_file, struct load_json_config_ctx *ctx) 496 { 497 struct spdk_json_val *values = NULL; 498 void *json = NULL, *end; 499 ssize_t values_cnt, rc; 500 size_t json_size; 501 502 json = read_file(config_file, &json_size); 503 if (!json) { 504 SPDK_ERRLOG("Read JSON configuration file %s failed: %s\n", 505 config_file, spdk_strerror(errno)); 506 return -errno; 507 } 508 509 rc = spdk_json_parse(json, json_size, NULL, 0, &end, 510 SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); 511 if (rc < 0) { 512 SPDK_ERRLOG("Parsing JSON configuration failed (%zd)\n", rc); 513 goto err; 514 } 515 516 values_cnt = rc; 517 values = calloc(values_cnt, sizeof(struct spdk_json_val)); 518 if (values == NULL) { 519 SPDK_ERRLOG("Out of memory\n"); 520 goto err; 521 } 522 523 rc = spdk_json_parse(json, json_size, values, values_cnt, &end, 524 SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); 525 if (rc != values_cnt) { 526 SPDK_ERRLOG("Parsing JSON configuration failed (%zd)\n", rc); 527 goto err; 528 } 529 530 ctx->json_data = json; 531 ctx->json_data_size = json_size; 532 533 ctx->values = values; 534 ctx->values_cnt = values_cnt; 535 536 return 0; 537 err: 538 free(json); 539 free(values); 540 return rc; 541 } 542 543 void 544 spdk_subsystem_init_from_json_config(const char *json_config_file, const char *rpc_addr, 545 spdk_subsystem_init_fn cb_fn, void *cb_arg, 546 bool stop_on_error) 547 { 548 struct load_json_config_ctx *ctx = calloc(1, sizeof(*ctx)); 549 int rc; 550 551 assert(cb_fn); 552 if (!ctx) { 553 cb_fn(-ENOMEM, cb_arg); 554 return; 555 } 556 557 ctx->cb_fn = cb_fn; 558 ctx->cb_arg = cb_arg; 559 ctx->stop_on_error = stop_on_error; 560 ctx->thread = spdk_get_thread(); 561 562 rc = app_json_config_read(json_config_file, ctx); 563 if (rc) { 564 goto fail; 565 } 566 567 /* Capture subsystems array */ 568 rc = spdk_json_find_array(ctx->values, "subsystems", NULL, &ctx->subsystems); 569 if (rc) { 570 SPDK_WARNLOG("No 'subsystems' key JSON configuration file.\n"); 571 } else { 572 /* Get first subsystem */ 573 ctx->subsystems_it = spdk_json_array_first(ctx->subsystems); 574 if (ctx->subsystems_it == NULL) { 575 SPDK_NOTICELOG("'subsystems' configuration is empty\n"); 576 } 577 } 578 579 /* If rpc_addr is not an Unix socket use default address as prefix. */ 580 if (rpc_addr == NULL || rpc_addr[0] != '/') { 581 rpc_addr = SPDK_DEFAULT_RPC_ADDR; 582 } 583 584 /* FIXME: rpc client should use socketpair() instead of this temporary socket nonsense */ 585 rc = snprintf(ctx->rpc_socket_path_temp, sizeof(ctx->rpc_socket_path_temp), "%s.%d_config", 586 rpc_addr, getpid()); 587 if (rc >= (int)sizeof(ctx->rpc_socket_path_temp)) { 588 SPDK_ERRLOG("Socket name create failed\n"); 589 goto fail; 590 } 591 592 rc = spdk_rpc_initialize(ctx->rpc_socket_path_temp); 593 if (rc) { 594 goto fail; 595 } 596 597 ctx->client_conn = spdk_jsonrpc_client_connect(ctx->rpc_socket_path_temp, AF_UNIX); 598 if (ctx->client_conn == NULL) { 599 SPDK_ERRLOG("Failed to connect to '%s'\n", ctx->rpc_socket_path_temp); 600 goto fail; 601 } 602 603 rpc_client_set_timeout(ctx, RPC_CLIENT_CONNECT_TIMEOUT_US); 604 ctx->client_conn_poller = SPDK_POLLER_REGISTER(rpc_client_connect_poller, ctx, 100); 605 return; 606 607 fail: 608 app_json_config_load_done(ctx, -EINVAL); 609 } 610 611 SPDK_LOG_REGISTER_COMPONENT(app_config) 612