1488570ebSJim Harris /* SPDX-License-Identifier: BSD-3-Clause 2a6dbe372Spaul luse * Copyright (C) 2018 Intel Corporation. 3dde41908SBen Walker * All rights reserved. 41e3d25b9SShuhei Matsumoto * Copyright (c) 2022, 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 5dde41908SBen Walker */ 6dde41908SBen Walker 7dde41908SBen Walker #include "spdk/stdinc.h" 8dde41908SBen Walker 9dde41908SBen Walker #include "spdk/init.h" 10dde41908SBen Walker #include "spdk/util.h" 11dde41908SBen Walker #include "spdk/file.h" 12dde41908SBen Walker #include "spdk/log.h" 13dde41908SBen Walker #include "spdk/env.h" 14dde41908SBen Walker #include "spdk/thread.h" 15dde41908SBen Walker #include "spdk/jsonrpc.h" 16dde41908SBen Walker #include "spdk/rpc.h" 1792d31eb2SJim Harris #include "spdk/string.h" 18dde41908SBen Walker 19dde41908SBen Walker #include "spdk_internal/event.h" 20dde41908SBen Walker 21dde41908SBen Walker #define SPDK_DEBUG_APP_CFG(...) SPDK_DEBUGLOG(app_config, __VA_ARGS__) 22dde41908SBen Walker 23dde41908SBen Walker /* JSON configuration format is as follows 24dde41908SBen Walker * 25dde41908SBen Walker * { 26dde41908SBen Walker * "subsystems" : [ <<== *subsystems JSON array 27dde41908SBen Walker * { <<== *subsystems_it array entry pointer (iterator) 28dde41908SBen Walker * "subsystem": "<< SUBSYSTEM NAME >>", 29dde41908SBen Walker * "config": [ <<== *config JSON array 30dde41908SBen Walker * { <<== *config_it array entry pointer (iterator) 31dde41908SBen Walker * "method": "<< METHOD NAME >>", <<== *method 32dde41908SBen Walker * "params": { << PARAMS >> } <<== *params 33dde41908SBen Walker * }, 34*34edd9f1SKamil Godzwon * << MORE "config" ARRAY ENTRIES >> 35dde41908SBen Walker * ] 36dde41908SBen Walker * }, 37dde41908SBen Walker * << MORE "subsystems" ARRAY ENTRIES >> 38dde41908SBen Walker * ] 39dde41908SBen Walker * 40cc6920a4SJosh Soref * << ANYTHING ELSE IS IGNORED IN ROOT OBJECT>> 41dde41908SBen Walker * } 42dde41908SBen Walker * 43dde41908SBen Walker */ 44dde41908SBen Walker 45dde41908SBen Walker struct load_json_config_ctx; 46dde41908SBen Walker typedef void (*client_resp_handler)(struct load_json_config_ctx *, 47dde41908SBen Walker struct spdk_jsonrpc_client_response *); 48dde41908SBen Walker 49320ab72fSShuhei Matsumoto #define RPC_SOCKET_PATH_MAX SPDK_SIZEOF_MEMBER(struct sockaddr_un, sun_path) 50dde41908SBen Walker 51dde41908SBen Walker /* 1s connections timeout */ 52dde41908SBen Walker #define RPC_CLIENT_CONNECT_TIMEOUT_US (1U * 1000U * 1000U) 53dde41908SBen Walker 54dde41908SBen Walker /* 55dde41908SBen Walker * Currently there is no timeout in SPDK for any RPC command. This result that 56dde41908SBen Walker * we can't put a hard limit during configuration load as it most likely randomly fail. 57dde41908SBen Walker * So just print WARNLOG every 10s. */ 58dde41908SBen Walker #define RPC_CLIENT_REQUEST_TIMEOUT_US (10U * 1000 * 1000) 59dde41908SBen Walker 60dde41908SBen Walker struct load_json_config_ctx { 61dde41908SBen Walker /* Thread used during configuration. */ 62dde41908SBen Walker struct spdk_thread *thread; 63dde41908SBen Walker spdk_subsystem_init_fn cb_fn; 64dde41908SBen Walker void *cb_arg; 65dde41908SBen Walker bool stop_on_error; 66dde41908SBen Walker 67dde41908SBen Walker /* Current subsystem */ 68dde41908SBen Walker struct spdk_json_val *subsystems; /* "subsystems" array */ 69dde41908SBen Walker struct spdk_json_val *subsystems_it; /* current subsystem array position in "subsystems" array */ 70dde41908SBen Walker 71dde41908SBen Walker struct spdk_json_val *subsystem_name; /* current subsystem name */ 72460a2e39SJim Harris char subsystem_name_str[128]; 73dde41908SBen Walker 74dde41908SBen Walker /* Current "config" entry we are processing */ 75dde41908SBen Walker struct spdk_json_val *config; /* "config" array */ 76dde41908SBen Walker struct spdk_json_val *config_it; /* current config position in "config" array */ 77dde41908SBen Walker 78dde41908SBen Walker /* Current request id we are sending. */ 79dde41908SBen Walker uint32_t rpc_request_id; 80dde41908SBen Walker 81dde41908SBen Walker /* Whole configuration file read and parsed. */ 82dde41908SBen Walker size_t json_data_size; 83dde41908SBen Walker char *json_data; 84dde41908SBen Walker 85dde41908SBen Walker size_t values_cnt; 86dde41908SBen Walker struct spdk_json_val *values; 87dde41908SBen Walker 88dde41908SBen Walker char rpc_socket_path_temp[RPC_SOCKET_PATH_MAX + 1]; 89dde41908SBen Walker 90dde41908SBen Walker struct spdk_jsonrpc_client *client_conn; 91dde41908SBen Walker struct spdk_poller *client_conn_poller; 92dde41908SBen Walker 93dde41908SBen Walker client_resp_handler client_resp_cb; 94dde41908SBen Walker 95dde41908SBen Walker /* Timeout for current RPC client action. */ 96dde41908SBen Walker uint64_t timeout; 979a86498dSKrzysztof Karas 989a86498dSKrzysztof Karas /* Signals that the code should follow deprecated path of execution. */ 999a86498dSKrzysztof Karas bool initalize_subsystems; 100dde41908SBen Walker }; 101dde41908SBen Walker 102dde41908SBen Walker static void app_json_config_load_subsystem(void *_ctx); 103dde41908SBen Walker 104dde41908SBen Walker static void 105dde41908SBen Walker app_json_config_load_done(struct load_json_config_ctx *ctx, int rc) 106dde41908SBen Walker { 107dde41908SBen Walker spdk_poller_unregister(&ctx->client_conn_poller); 108dde41908SBen Walker if (ctx->client_conn != NULL) { 109dde41908SBen Walker spdk_jsonrpc_client_close(ctx->client_conn); 110dde41908SBen Walker } 111dde41908SBen Walker 1129a86498dSKrzysztof Karas spdk_rpc_server_finish(ctx->rpc_socket_path_temp); 113dde41908SBen Walker 114dde41908SBen Walker SPDK_DEBUG_APP_CFG("Config load finished with rc %d\n", rc); 115dde41908SBen Walker ctx->cb_fn(rc, ctx->cb_arg); 116dde41908SBen Walker 117dde41908SBen Walker free(ctx->json_data); 118dde41908SBen Walker free(ctx->values); 119dde41908SBen Walker free(ctx); 120dde41908SBen Walker } 121dde41908SBen Walker 122dde41908SBen Walker static void 123dde41908SBen Walker rpc_client_set_timeout(struct load_json_config_ctx *ctx, uint64_t timeout_us) 124dde41908SBen Walker { 125dde41908SBen Walker ctx->timeout = spdk_get_ticks() + timeout_us * spdk_get_ticks_hz() / (1000 * 1000); 126dde41908SBen Walker } 127dde41908SBen Walker 128dde41908SBen Walker static int 129dde41908SBen Walker rpc_client_check_timeout(struct load_json_config_ctx *ctx) 130dde41908SBen Walker { 131dde41908SBen Walker if (ctx->timeout < spdk_get_ticks()) { 132dde41908SBen Walker SPDK_WARNLOG("RPC client command timeout.\n"); 133dde41908SBen Walker return -ETIMEDOUT; 134dde41908SBen Walker } 135dde41908SBen Walker 136dde41908SBen Walker return 0; 137dde41908SBen Walker } 138dde41908SBen Walker 139dde41908SBen Walker struct json_write_buf { 140dde41908SBen Walker char data[1024]; 141dde41908SBen Walker unsigned cur_off; 142dde41908SBen Walker }; 143dde41908SBen Walker 144dde41908SBen Walker static int 145dde41908SBen Walker json_write_stdout(void *cb_ctx, const void *data, size_t size) 146dde41908SBen Walker { 147dde41908SBen Walker struct json_write_buf *buf = cb_ctx; 148dde41908SBen Walker size_t rc; 149dde41908SBen Walker 150dde41908SBen Walker rc = snprintf(buf->data + buf->cur_off, sizeof(buf->data) - buf->cur_off, 151dde41908SBen Walker "%s", (const char *)data); 152dde41908SBen Walker if (rc > 0) { 153dde41908SBen Walker buf->cur_off += rc; 154dde41908SBen Walker } 155dde41908SBen Walker return rc == size ? 0 : -1; 156dde41908SBen Walker } 157dde41908SBen Walker 158dde41908SBen Walker static int 159dde41908SBen Walker rpc_client_poller(void *arg) 160dde41908SBen Walker { 161dde41908SBen Walker struct load_json_config_ctx *ctx = arg; 162dde41908SBen Walker struct spdk_jsonrpc_client_response *resp; 163dde41908SBen Walker client_resp_handler cb; 164dde41908SBen Walker int rc; 165dde41908SBen Walker 166dde41908SBen Walker assert(spdk_get_thread() == ctx->thread); 167dde41908SBen Walker 168dde41908SBen Walker rc = spdk_jsonrpc_client_poll(ctx->client_conn, 0); 169dde41908SBen Walker if (rc == 0) { 170dde41908SBen Walker rc = rpc_client_check_timeout(ctx); 171dde41908SBen Walker if (rc == -ETIMEDOUT) { 172dde41908SBen Walker rpc_client_set_timeout(ctx, RPC_CLIENT_REQUEST_TIMEOUT_US); 173dde41908SBen Walker rc = 0; 174dde41908SBen Walker } 175dde41908SBen Walker } 176dde41908SBen Walker 177dde41908SBen Walker if (rc == 0) { 178dde41908SBen Walker /* No response yet */ 179dde41908SBen Walker return SPDK_POLLER_BUSY; 180dde41908SBen Walker } else if (rc < 0) { 181dde41908SBen Walker app_json_config_load_done(ctx, rc); 182dde41908SBen Walker return SPDK_POLLER_BUSY; 183dde41908SBen Walker } 184dde41908SBen Walker 185dde41908SBen Walker resp = spdk_jsonrpc_client_get_response(ctx->client_conn); 186dde41908SBen Walker assert(resp); 187dde41908SBen Walker 188dde41908SBen Walker if (resp->error) { 189dde41908SBen Walker struct json_write_buf buf = {}; 190dde41908SBen Walker struct spdk_json_write_ctx *w = spdk_json_write_begin(json_write_stdout, 191dde41908SBen Walker &buf, SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE); 192dde41908SBen Walker 193dde41908SBen Walker if (w == NULL) { 194dde41908SBen Walker SPDK_ERRLOG("error response: (?)\n"); 195dde41908SBen Walker } else { 196dde41908SBen Walker spdk_json_write_val(w, resp->error); 197dde41908SBen Walker spdk_json_write_end(w); 198dde41908SBen Walker SPDK_ERRLOG("error response: \n%s\n", buf.data); 199dde41908SBen Walker } 200dde41908SBen Walker } 201dde41908SBen Walker 202dde41908SBen Walker if (resp->error && ctx->stop_on_error) { 203dde41908SBen Walker spdk_jsonrpc_client_free_response(resp); 204dde41908SBen Walker app_json_config_load_done(ctx, -EINVAL); 205dde41908SBen Walker } else { 206dde41908SBen Walker /* We have response so we must have callback for it. */ 207dde41908SBen Walker cb = ctx->client_resp_cb; 208dde41908SBen Walker assert(cb != NULL); 209dde41908SBen Walker 210dde41908SBen Walker /* Mark we are done with this handler. */ 211dde41908SBen Walker ctx->client_resp_cb = NULL; 212dde41908SBen Walker cb(ctx, resp); 213dde41908SBen Walker } 214dde41908SBen Walker 215dde41908SBen Walker 216dde41908SBen Walker return SPDK_POLLER_BUSY; 217dde41908SBen Walker } 218dde41908SBen Walker 219dde41908SBen Walker static int 220dde41908SBen Walker rpc_client_connect_poller(void *_ctx) 221dde41908SBen Walker { 222dde41908SBen Walker struct load_json_config_ctx *ctx = _ctx; 223dde41908SBen Walker int rc; 224dde41908SBen Walker 225dde41908SBen Walker rc = spdk_jsonrpc_client_poll(ctx->client_conn, 0); 226dde41908SBen Walker if (rc != -ENOTCONN) { 227dde41908SBen Walker /* We are connected. Start regular poller and issue first request */ 228dde41908SBen Walker spdk_poller_unregister(&ctx->client_conn_poller); 229dde41908SBen Walker ctx->client_conn_poller = SPDK_POLLER_REGISTER(rpc_client_poller, ctx, 100); 230dde41908SBen Walker app_json_config_load_subsystem(ctx); 231dde41908SBen Walker } else { 232dde41908SBen Walker rc = rpc_client_check_timeout(ctx); 233dde41908SBen Walker if (rc) { 234dde41908SBen Walker app_json_config_load_done(ctx, rc); 235dde41908SBen Walker } 236dde41908SBen Walker 237dde41908SBen Walker return SPDK_POLLER_IDLE; 238dde41908SBen Walker } 239dde41908SBen Walker 240dde41908SBen Walker return SPDK_POLLER_BUSY; 241dde41908SBen Walker } 242dde41908SBen Walker 243dde41908SBen Walker static int 244dde41908SBen Walker client_send_request(struct load_json_config_ctx *ctx, struct spdk_jsonrpc_client_request *request, 245dde41908SBen Walker client_resp_handler client_resp_cb) 246dde41908SBen Walker { 247dde41908SBen Walker int rc; 248dde41908SBen Walker 249dde41908SBen Walker assert(spdk_get_thread() == ctx->thread); 250dde41908SBen Walker 251dde41908SBen Walker ctx->client_resp_cb = client_resp_cb; 252dde41908SBen Walker rpc_client_set_timeout(ctx, RPC_CLIENT_REQUEST_TIMEOUT_US); 253dde41908SBen Walker rc = spdk_jsonrpc_client_send_request(ctx->client_conn, request); 254dde41908SBen Walker 255dde41908SBen Walker if (rc) { 256dde41908SBen Walker SPDK_DEBUG_APP_CFG("Sending request to client failed (%d)\n", rc); 257dde41908SBen Walker } 258dde41908SBen Walker 259dde41908SBen Walker return rc; 260dde41908SBen Walker } 261dde41908SBen Walker 262dde41908SBen Walker static int 263dde41908SBen Walker cap_string(const struct spdk_json_val *val, void *out) 264dde41908SBen Walker { 265dde41908SBen Walker const struct spdk_json_val **vptr = out; 266dde41908SBen Walker 267dde41908SBen Walker if (val->type != SPDK_JSON_VAL_STRING) { 268dde41908SBen Walker return -EINVAL; 269dde41908SBen Walker } 270dde41908SBen Walker 271dde41908SBen Walker *vptr = val; 272dde41908SBen Walker return 0; 273dde41908SBen Walker } 274dde41908SBen Walker 275dde41908SBen Walker static int 276dde41908SBen Walker cap_object(const struct spdk_json_val *val, void *out) 277dde41908SBen Walker { 278dde41908SBen Walker const struct spdk_json_val **vptr = out; 279dde41908SBen Walker 280dde41908SBen Walker if (val->type != SPDK_JSON_VAL_OBJECT_BEGIN) { 281dde41908SBen Walker return -EINVAL; 282dde41908SBen Walker } 283dde41908SBen Walker 284dde41908SBen Walker *vptr = val; 285dde41908SBen Walker return 0; 286dde41908SBen Walker } 287dde41908SBen Walker 288dde41908SBen Walker 289dde41908SBen Walker static int 290dde41908SBen Walker cap_array_or_null(const struct spdk_json_val *val, void *out) 291dde41908SBen Walker { 292dde41908SBen Walker const struct spdk_json_val **vptr = out; 293dde41908SBen Walker 294dde41908SBen Walker if (val->type != SPDK_JSON_VAL_ARRAY_BEGIN && val->type != SPDK_JSON_VAL_NULL) { 295dde41908SBen Walker return -EINVAL; 296dde41908SBen Walker } 297dde41908SBen Walker 298dde41908SBen Walker *vptr = val; 299dde41908SBen Walker return 0; 300dde41908SBen Walker } 301dde41908SBen Walker 302dde41908SBen Walker struct config_entry { 303dde41908SBen Walker char *method; 304dde41908SBen Walker struct spdk_json_val *params; 305dde41908SBen Walker }; 306dde41908SBen Walker 307dde41908SBen Walker static struct spdk_json_object_decoder jsonrpc_cmd_decoders[] = { 308dde41908SBen Walker {"method", offsetof(struct config_entry, method), spdk_json_decode_string}, 309dde41908SBen Walker {"params", offsetof(struct config_entry, params), cap_object, true} 310dde41908SBen Walker }; 311dde41908SBen Walker 312dde41908SBen Walker static void app_json_config_load_subsystem_config_entry(void *_ctx); 313dde41908SBen Walker 314dde41908SBen Walker static void 315dde41908SBen Walker app_json_config_load_subsystem_config_entry_next(struct load_json_config_ctx *ctx, 316dde41908SBen Walker struct spdk_jsonrpc_client_response *resp) 317dde41908SBen Walker { 318dde41908SBen Walker /* Don't care about the response */ 319dde41908SBen Walker spdk_jsonrpc_client_free_response(resp); 320dde41908SBen Walker 321dde41908SBen Walker ctx->config_it = spdk_json_next(ctx->config_it); 322dde41908SBen Walker app_json_config_load_subsystem_config_entry(ctx); 323dde41908SBen Walker } 324dde41908SBen Walker 325dde41908SBen Walker /* Load "config" entry */ 326dde41908SBen Walker static void 327dde41908SBen Walker app_json_config_load_subsystem_config_entry(void *_ctx) 328dde41908SBen Walker { 329dde41908SBen Walker struct load_json_config_ctx *ctx = _ctx; 330dde41908SBen Walker struct spdk_jsonrpc_client_request *rpc_request; 331dde41908SBen Walker struct spdk_json_write_ctx *w; 332dde41908SBen Walker struct config_entry cfg = {}; 333dde41908SBen Walker struct spdk_json_val *params_end; 334dde41908SBen Walker size_t params_len = 0; 3357a7f21b6SAleksey Marchuk uint32_t state_mask = 0, cur_state_mask, startup_runtime = SPDK_RPC_STARTUP | SPDK_RPC_RUNTIME; 336dde41908SBen Walker int rc; 337dde41908SBen Walker 338dde41908SBen Walker if (ctx->config_it == NULL) { 339dde41908SBen Walker SPDK_DEBUG_APP_CFG("Subsystem '%.*s': configuration done.\n", ctx->subsystem_name->len, 340dde41908SBen Walker (char *)ctx->subsystem_name->start); 341dde41908SBen Walker ctx->subsystems_it = spdk_json_next(ctx->subsystems_it); 34217ae5e07SJim Harris /* Invoke later to avoid recursion */ 343dde41908SBen Walker spdk_thread_send_msg(ctx->thread, app_json_config_load_subsystem, ctx); 344dde41908SBen Walker return; 345dde41908SBen Walker } 346dde41908SBen Walker 347dde41908SBen Walker if (spdk_json_decode_object(ctx->config_it, jsonrpc_cmd_decoders, 348dde41908SBen Walker SPDK_COUNTOF(jsonrpc_cmd_decoders), &cfg)) { 349dde41908SBen Walker SPDK_ERRLOG("Failed to decode config entry\n"); 350dde41908SBen Walker app_json_config_load_done(ctx, -EINVAL); 351dde41908SBen Walker goto out; 352dde41908SBen Walker } 353dde41908SBen Walker 3547a7f21b6SAleksey Marchuk rc = spdk_rpc_get_method_state_mask(cfg.method, &state_mask); 3557a7f21b6SAleksey Marchuk if (rc == -ENOENT) { 356874550d7SJim Harris if (!ctx->stop_on_error) { 357874550d7SJim Harris /* Invoke later to avoid recursion */ 358874550d7SJim Harris ctx->config_it = spdk_json_next(ctx->config_it); 359874550d7SJim Harris spdk_thread_send_msg(ctx->thread, app_json_config_load_subsystem_config_entry, ctx); 360460a2e39SJim Harris } else if (!spdk_subsystem_exists(ctx->subsystem_name_str)) { 361460a2e39SJim Harris /* If the subsystem does not exist, just skip it, even 362460a2e39SJim Harris * if we are supposed to stop_on_error. Users may generate 363460a2e39SJim Harris * a JSON config from one application, and want to use parts 364460a2e39SJim Harris * of it in another application that may not have all of the 365460a2e39SJim Harris * same subsystems linked - for example, nvmf_tgt => bdevperf. 366460a2e39SJim Harris * That's OK, we don't need to throw an error, since any nvmf 367460a2e39SJim Harris * configuration wouldn't be used by bdevperf anyways. That is 368460a2e39SJim Harris * different than if some subsystem does exist in bdevperf and 369460a2e39SJim Harris * one of its RPCs fails. 370460a2e39SJim Harris */ 371460a2e39SJim Harris SPDK_NOTICELOG("Skipping method '%s' because its subsystem '%s' " 372460a2e39SJim Harris "is not linked into this application.\n", 373460a2e39SJim Harris cfg.method, ctx->subsystem_name_str); 374460a2e39SJim Harris /* Invoke later to avoid recursion */ 375460a2e39SJim Harris ctx->config_it = spdk_json_next(ctx->config_it); 376460a2e39SJim Harris spdk_thread_send_msg(ctx->thread, app_json_config_load_subsystem_config_entry, ctx); 377874550d7SJim Harris } else { 3787a7f21b6SAleksey Marchuk SPDK_ERRLOG("Method '%s' was not found\n", cfg.method); 3797a7f21b6SAleksey Marchuk app_json_config_load_done(ctx, rc); 380874550d7SJim Harris } 3817a7f21b6SAleksey Marchuk goto out; 3827a7f21b6SAleksey Marchuk } 3837a7f21b6SAleksey Marchuk cur_state_mask = spdk_rpc_get_state(); 3847a7f21b6SAleksey Marchuk if ((state_mask & cur_state_mask) != cur_state_mask) { 385dde41908SBen Walker SPDK_DEBUG_APP_CFG("Method '%s' not allowed -> skipping\n", cfg.method); 38617ae5e07SJim Harris /* Invoke later to avoid recursion */ 387dde41908SBen Walker ctx->config_it = spdk_json_next(ctx->config_it); 388dde41908SBen Walker spdk_thread_send_msg(ctx->thread, app_json_config_load_subsystem_config_entry, ctx); 389dde41908SBen Walker goto out; 390dde41908SBen Walker } 3917a7f21b6SAleksey Marchuk if ((state_mask & startup_runtime) == startup_runtime && cur_state_mask == SPDK_RPC_RUNTIME) { 3927a7f21b6SAleksey Marchuk /* Some methods are allowed to be run in both STARTUP and RUNTIME states. 3937a7f21b6SAleksey Marchuk * We should not call such methods twice, so ignore the second attempt in RUNTIME state */ 3947a7f21b6SAleksey Marchuk SPDK_DEBUG_APP_CFG("Method '%s' has already been run in STARTUP state\n", cfg.method); 39517ae5e07SJim Harris /* Invoke later to avoid recursion */ 3967a7f21b6SAleksey Marchuk ctx->config_it = spdk_json_next(ctx->config_it); 3977a7f21b6SAleksey Marchuk spdk_thread_send_msg(ctx->thread, app_json_config_load_subsystem_config_entry, ctx); 3987a7f21b6SAleksey Marchuk goto out; 3997a7f21b6SAleksey Marchuk } 400dde41908SBen Walker 401dde41908SBen Walker SPDK_DEBUG_APP_CFG("\tmethod: %s\n", cfg.method); 402dde41908SBen Walker 403dde41908SBen Walker if (cfg.params) { 404dde41908SBen Walker /* Get _END by skipping params and going back by one element. */ 405dde41908SBen Walker params_end = cfg.params + spdk_json_val_len(cfg.params) - 1; 406dde41908SBen Walker 407dde41908SBen Walker /* Need to add one character to include '}' */ 408dde41908SBen Walker params_len = params_end->start - cfg.params->start + 1; 409dde41908SBen Walker 410dde41908SBen Walker SPDK_DEBUG_APP_CFG("\tparams: %.*s\n", (int)params_len, (char *)cfg.params->start); 411dde41908SBen Walker } 412dde41908SBen Walker 413dde41908SBen Walker rpc_request = spdk_jsonrpc_client_create_request(); 414dde41908SBen Walker if (!rpc_request) { 415dde41908SBen Walker app_json_config_load_done(ctx, -errno); 416dde41908SBen Walker goto out; 417dde41908SBen Walker } 418dde41908SBen Walker 419dde41908SBen Walker w = spdk_jsonrpc_begin_request(rpc_request, ctx->rpc_request_id, NULL); 420dde41908SBen Walker if (!w) { 421dde41908SBen Walker spdk_jsonrpc_client_free_request(rpc_request); 422dde41908SBen Walker app_json_config_load_done(ctx, -ENOMEM); 423dde41908SBen Walker goto out; 424dde41908SBen Walker } 425dde41908SBen Walker 426dde41908SBen Walker spdk_json_write_named_string(w, "method", cfg.method); 427dde41908SBen Walker 428dde41908SBen Walker if (cfg.params) { 429dde41908SBen Walker /* No need to parse "params". Just dump the whole content of "params" 430dde41908SBen Walker * directly into the request and let the remote side verify it. */ 431dde41908SBen Walker spdk_json_write_name(w, "params"); 432dde41908SBen Walker spdk_json_write_val_raw(w, cfg.params->start, params_len); 433dde41908SBen Walker } 434dde41908SBen Walker 435dde41908SBen Walker spdk_jsonrpc_end_request(rpc_request, w); 436dde41908SBen Walker 437dde41908SBen Walker rc = client_send_request(ctx, rpc_request, app_json_config_load_subsystem_config_entry_next); 438dde41908SBen Walker if (rc != 0) { 439dde41908SBen Walker app_json_config_load_done(ctx, -rc); 440dde41908SBen Walker goto out; 441dde41908SBen Walker } 442dde41908SBen Walker out: 443dde41908SBen Walker free(cfg.method); 444dde41908SBen Walker } 445dde41908SBen Walker 446dde41908SBen Walker static void 447dde41908SBen Walker subsystem_init_done(int rc, void *arg1) 448dde41908SBen Walker { 449dde41908SBen Walker struct load_json_config_ctx *ctx = arg1; 450dde41908SBen Walker 451dde41908SBen Walker if (rc) { 452dde41908SBen Walker app_json_config_load_done(ctx, rc); 453dde41908SBen Walker return; 454dde41908SBen Walker } 455dde41908SBen Walker 456dde41908SBen Walker spdk_rpc_set_state(SPDK_RPC_RUNTIME); 457dde41908SBen Walker /* Another round. This time for RUNTIME methods */ 458dde41908SBen Walker SPDK_DEBUG_APP_CFG("'framework_start_init' done - continuing configuration\n"); 459dde41908SBen Walker 460dde41908SBen Walker assert(ctx != NULL); 461dde41908SBen Walker if (ctx->subsystems) { 462dde41908SBen Walker ctx->subsystems_it = spdk_json_array_first(ctx->subsystems); 463dde41908SBen Walker } 464dde41908SBen Walker 465dde41908SBen Walker app_json_config_load_subsystem(ctx); 466dde41908SBen Walker } 467dde41908SBen Walker 468dde41908SBen Walker static struct spdk_json_object_decoder subsystem_decoders[] = { 469dde41908SBen Walker {"subsystem", offsetof(struct load_json_config_ctx, subsystem_name), cap_string}, 470dde41908SBen Walker {"config", offsetof(struct load_json_config_ctx, config), cap_array_or_null} 471dde41908SBen Walker }; 472dde41908SBen Walker 473dde41908SBen Walker /* 474dde41908SBen Walker * Start loading subsystem pointed by ctx->subsystems_it. This must point to the 475dde41908SBen Walker * beginning of the "subsystem" object in "subsystems" array or be NULL. If it is 476dde41908SBen Walker * NULL then no more subsystems to load. 477dde41908SBen Walker * 4789a86498dSKrzysztof Karas * If "initalize_subsystems" is unset, then the function performs one iteration 4799a86498dSKrzysztof Karas * and does not call subsystem initialization. 4809a86498dSKrzysztof Karas * 4819a86498dSKrzysztof Karas * There are two iterations, when "initalize_subsystems" context flag is set: 482dde41908SBen Walker * 483dde41908SBen Walker * In first iteration only STARTUP RPC methods are used, other methods are ignored. When 484dde41908SBen Walker * allsubsystems are walked the ctx->subsystems_it became NULL and "framework_start_init" 485dde41908SBen Walker * is called to let the SPDK move to RUNTIME state (initialize all subsystems) and 486dde41908SBen Walker * second iteration begins. 487dde41908SBen Walker * 488dde41908SBen Walker * In second iteration "subsystems" array is walked through again, this time only 489dde41908SBen Walker * RUNTIME RPC methods are used. When ctx->subsystems_it became NULL second time it 490dde41908SBen Walker * indicate that there is no more subsystems to load. The cb_fn is called to finish 491dde41908SBen Walker * configuration. 492dde41908SBen Walker */ 493dde41908SBen Walker static void 494dde41908SBen Walker app_json_config_load_subsystem(void *_ctx) 495dde41908SBen Walker { 496dde41908SBen Walker struct load_json_config_ctx *ctx = _ctx; 497dde41908SBen Walker 498dde41908SBen Walker if (ctx->subsystems_it == NULL) { 4999a86498dSKrzysztof Karas if (ctx->initalize_subsystems && spdk_rpc_get_state() == SPDK_RPC_STARTUP) { 500dde41908SBen Walker SPDK_DEBUG_APP_CFG("No more entries for current state, calling 'framework_start_init'\n"); 501dde41908SBen Walker spdk_subsystem_init(subsystem_init_done, ctx); 502dde41908SBen Walker } else { 5039a86498dSKrzysztof Karas SPDK_DEBUG_APP_CFG("No more entries for current state\n"); 504dde41908SBen Walker app_json_config_load_done(ctx, 0); 505dde41908SBen Walker } 506dde41908SBen Walker 507dde41908SBen Walker return; 508dde41908SBen Walker } 509dde41908SBen Walker 510dde41908SBen Walker /* Capture subsystem name and config array */ 511dde41908SBen Walker if (spdk_json_decode_object(ctx->subsystems_it, subsystem_decoders, 512dde41908SBen Walker SPDK_COUNTOF(subsystem_decoders), ctx)) { 513dde41908SBen Walker SPDK_ERRLOG("Failed to parse subsystem configuration\n"); 514dde41908SBen Walker app_json_config_load_done(ctx, -EINVAL); 515dde41908SBen Walker return; 516dde41908SBen Walker } 517dde41908SBen Walker 518460a2e39SJim Harris snprintf(ctx->subsystem_name_str, sizeof(ctx->subsystem_name_str), 519460a2e39SJim Harris "%.*s", ctx->subsystem_name->len, (char *)ctx->subsystem_name->start); 520460a2e39SJim Harris 521460a2e39SJim Harris SPDK_DEBUG_APP_CFG("Loading subsystem '%s' configuration\n", ctx->subsystem_name_str); 522dde41908SBen Walker 523dde41908SBen Walker /* Get 'config' array first configuration entry */ 524dde41908SBen Walker ctx->config_it = spdk_json_array_first(ctx->config); 525dde41908SBen Walker app_json_config_load_subsystem_config_entry(ctx); 526dde41908SBen Walker } 527dde41908SBen Walker 528dde41908SBen Walker static int 5299a86498dSKrzysztof Karas parse_json(void *json, ssize_t json_size, struct load_json_config_ctx *ctx) 530dde41908SBen Walker { 5319a86498dSKrzysztof Karas void *end; 5329a86498dSKrzysztof Karas ssize_t rc; 533dde41908SBen Walker 5349a86498dSKrzysztof Karas if (!json || json_size <= 0) { 5359a86498dSKrzysztof Karas SPDK_ERRLOG("JSON data cannot be empty\n"); 5369a86498dSKrzysztof Karas goto err; 537dde41908SBen Walker } 538dde41908SBen Walker 5399a86498dSKrzysztof Karas ctx->json_data = calloc(1, json_size); 5409a86498dSKrzysztof Karas if (!ctx->json_data) { 5419a86498dSKrzysztof Karas goto err; 5429a86498dSKrzysztof Karas } 5439a86498dSKrzysztof Karas memcpy(ctx->json_data, json, json_size); 5449a86498dSKrzysztof Karas ctx->json_data_size = json_size; 5459a86498dSKrzysztof Karas 5469a86498dSKrzysztof Karas rc = spdk_json_parse(ctx->json_data, ctx->json_data_size, NULL, 0, &end, 547dde41908SBen Walker SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); 548dde41908SBen Walker if (rc < 0) { 549dde41908SBen Walker SPDK_ERRLOG("Parsing JSON configuration failed (%zd)\n", rc); 550dde41908SBen Walker goto err; 551dde41908SBen Walker } 552dde41908SBen Walker 5539a86498dSKrzysztof Karas ctx->values_cnt = rc; 5549a86498dSKrzysztof Karas ctx->values = calloc(ctx->values_cnt, sizeof(struct spdk_json_val)); 5559a86498dSKrzysztof Karas if (ctx->values == NULL) { 556dde41908SBen Walker SPDK_ERRLOG("Out of memory\n"); 557dde41908SBen Walker goto err; 558dde41908SBen Walker } 559dde41908SBen Walker 5609a86498dSKrzysztof Karas rc = spdk_json_parse(ctx->json_data, ctx->json_data_size, ctx->values, 5619a86498dSKrzysztof Karas ctx->values_cnt, &end, 562dde41908SBen Walker SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); 5639a86498dSKrzysztof Karas if ((size_t)rc != ctx->values_cnt) { 564dde41908SBen Walker SPDK_ERRLOG("Parsing JSON configuration failed (%zd)\n", rc); 565dde41908SBen Walker goto err; 566dde41908SBen Walker } 567dde41908SBen Walker 568dde41908SBen Walker return 0; 569dde41908SBen Walker err: 5709a86498dSKrzysztof Karas free(ctx->values); 5719a86498dSKrzysztof Karas return -EINVAL; 572dde41908SBen Walker } 573dde41908SBen Walker 5749a86498dSKrzysztof Karas static void 5759a86498dSKrzysztof Karas json_config_prepare_ctx(spdk_subsystem_init_fn cb_fn, void *cb_arg, bool stop_on_error, void *json, 5769a86498dSKrzysztof Karas ssize_t json_size, bool initalize_subsystems) 577dde41908SBen Walker { 578dde41908SBen Walker struct load_json_config_ctx *ctx = calloc(1, sizeof(*ctx)); 579dde41908SBen Walker int rc; 580dde41908SBen Walker 581dde41908SBen Walker if (!ctx) { 582dde41908SBen Walker cb_fn(-ENOMEM, cb_arg); 583dde41908SBen Walker return; 584dde41908SBen Walker } 585dde41908SBen Walker 586dde41908SBen Walker ctx->cb_fn = cb_fn; 587dde41908SBen Walker ctx->cb_arg = cb_arg; 588dde41908SBen Walker ctx->stop_on_error = stop_on_error; 589dde41908SBen Walker ctx->thread = spdk_get_thread(); 5909a86498dSKrzysztof Karas ctx->initalize_subsystems = initalize_subsystems; 591dde41908SBen Walker 5929a86498dSKrzysztof Karas rc = parse_json(json, json_size, ctx); 5939a86498dSKrzysztof Karas if (rc < 0) { 594dde41908SBen Walker goto fail; 595dde41908SBen Walker } 596dde41908SBen Walker 597dde41908SBen Walker /* Capture subsystems array */ 598dde41908SBen Walker rc = spdk_json_find_array(ctx->values, "subsystems", NULL, &ctx->subsystems); 599bb432b4eStongkunkun switch (rc) { 600bb432b4eStongkunkun case 0: 601dde41908SBen Walker /* Get first subsystem */ 602dde41908SBen Walker ctx->subsystems_it = spdk_json_array_first(ctx->subsystems); 603dde41908SBen Walker if (ctx->subsystems_it == NULL) { 604dde41908SBen Walker SPDK_NOTICELOG("'subsystems' configuration is empty\n"); 605dde41908SBen Walker } 606bb432b4eStongkunkun break; 607bb432b4eStongkunkun case -EPROTOTYPE: 608bb432b4eStongkunkun SPDK_ERRLOG("Invalid JSON configuration: not enclosed in {}.\n"); 609bb432b4eStongkunkun goto fail; 610bb432b4eStongkunkun case -ENOENT: 611bb432b4eStongkunkun SPDK_WARNLOG("No 'subsystems' key JSON configuration file.\n"); 612bb432b4eStongkunkun break; 613bb432b4eStongkunkun case -EDOM: 614bb432b4eStongkunkun SPDK_ERRLOG("Invalid JSON configuration: 'subsystems' should be an array.\n"); 615bb432b4eStongkunkun goto fail; 616bb432b4eStongkunkun default: 617bb432b4eStongkunkun SPDK_ERRLOG("Failed to parse JSON configuration.\n"); 618bb432b4eStongkunkun goto fail; 619dde41908SBen Walker } 620dde41908SBen Walker 621dde41908SBen Walker /* FIXME: rpc client should use socketpair() instead of this temporary socket nonsense */ 6229a86498dSKrzysztof Karas rc = snprintf(ctx->rpc_socket_path_temp, sizeof(ctx->rpc_socket_path_temp), 6239a86498dSKrzysztof Karas "%s.%d_%"PRIu64"_config", SPDK_DEFAULT_RPC_ADDR, getpid(), spdk_get_ticks()); 624dde41908SBen Walker if (rc >= (int)sizeof(ctx->rpc_socket_path_temp)) { 625dde41908SBen Walker SPDK_ERRLOG("Socket name create failed\n"); 626dde41908SBen Walker goto fail; 627dde41908SBen Walker } 628dde41908SBen Walker 6291e3d25b9SShuhei Matsumoto rc = spdk_rpc_initialize(ctx->rpc_socket_path_temp, NULL); 630dde41908SBen Walker if (rc) { 631dde41908SBen Walker goto fail; 632dde41908SBen Walker } 633dde41908SBen Walker 634dde41908SBen Walker ctx->client_conn = spdk_jsonrpc_client_connect(ctx->rpc_socket_path_temp, AF_UNIX); 635dde41908SBen Walker if (ctx->client_conn == NULL) { 636dde41908SBen Walker SPDK_ERRLOG("Failed to connect to '%s'\n", ctx->rpc_socket_path_temp); 637dde41908SBen Walker goto fail; 638dde41908SBen Walker } 639dde41908SBen Walker 640dde41908SBen Walker rpc_client_set_timeout(ctx, RPC_CLIENT_CONNECT_TIMEOUT_US); 641dde41908SBen Walker ctx->client_conn_poller = SPDK_POLLER_REGISTER(rpc_client_connect_poller, ctx, 100); 642dde41908SBen Walker return; 643dde41908SBen Walker 644dde41908SBen Walker fail: 645dde41908SBen Walker app_json_config_load_done(ctx, -EINVAL); 646dde41908SBen Walker } 647dde41908SBen Walker 6489a86498dSKrzysztof Karas void 6499a86498dSKrzysztof Karas spdk_subsystem_load_config(void *json, ssize_t json_size, spdk_subsystem_init_fn cb_fn, 6509a86498dSKrzysztof Karas void *cb_arg, bool stop_on_error) 6519a86498dSKrzysztof Karas { 6529a86498dSKrzysztof Karas assert(cb_fn); 6539a86498dSKrzysztof Karas json_config_prepare_ctx(cb_fn, cb_arg, stop_on_error, json, json_size, false); 6549a86498dSKrzysztof Karas } 6559a86498dSKrzysztof Karas 656dde41908SBen Walker SPDK_LOG_REGISTER_COMPONENT(app_config) 657