4 * Copyright (c) Intel Corporation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
34 #include "json_config.h"
36 #include "spdk/stdinc.h"
38 #include "spdk/util.h"
39 #include "spdk/file.h"
42 #include "spdk/thread.h"
43 #include "spdk/jsonrpc.h"
46 #include "spdk_internal/event.h"
47 #include "spdk_internal/log.h"
49 #define SPDK_DEBUG_APP_CFG(...) SPDK_DEBUGLOG(SPDK_LOG_APP_CONFIG, __VA_ARGS__)
51 /* JSON configuration format is as follows
54 * "subsystems" : [ <<== *subsystems JSON array
55 * { <<== *subsystems_it array entry pointer (iterator)
56 * "subsystem": "<< SUBSYSTEM NAME >>",
57 * "config": [ <<== *config JSON array
58 * { <<== *config_it array entry pointer (iterator)
59 * "method": "<< METHOD NAME >>", <<== *method
60 * "params": { << PARAMS >> } <<== *params
62 * << MORE "config" ARRY ENTRIES >>
65 * << MORE "subsystems" ARRAY ENTRIES >>
68 * << ANYTHING ELSE IS IGNORRED IN ROOT OBJECT>>
73 struct load_json_config_ctx
;
74 typedef void (*client_resp_handler
)(struct load_json_config_ctx
*,
75 struct spdk_jsonrpc_client_response
*);
77 #define RPC_SOCKET_PATH_MAX sizeof(((struct sockaddr_un *)0)->sun_path)
79 /* 1s connections timeout */
80 #define RPC_CLIENT_CONNECT_TIMEOUT_US (1U * 1000U * 1000U)
83 * Currently there is no timeout in SPDK for any RPC command. This result that
84 * we can't put a hard limit during configuration load as it most likely randomly fail.
85 * So just print WARNLOG every 10s. */
86 #define RPC_CLIENT_REQUEST_TIMEOUT_US (10U * 1000 * 1000)
88 struct load_json_config_ctx
{
89 /* Thread used during configuration. */
90 struct spdk_thread
*thread
;
94 /* Current subsystem */
95 struct spdk_json_val
*subsystems
; /* "subsystems" array */
96 struct spdk_json_val
*subsystems_it
; /* current subsystem array position in "subsystems" array */
98 struct spdk_json_val
*subsystem_name
; /* current subsystem name */
100 /* Current "config" entry we are processing */
101 struct spdk_json_val
*config
; /* "config" array */
102 struct spdk_json_val
*config_it
; /* current config position in "config" array */
104 /* Current request id we are sending. */
105 uint32_t rpc_request_id
;
107 /* Whole configuration file read and parsed. */
108 size_t json_data_size
;
112 struct spdk_json_val
*values
;
114 char rpc_socket_path_temp
[RPC_SOCKET_PATH_MAX
+ 1];
116 struct spdk_jsonrpc_client
*client_conn
;
117 struct spdk_poller
*client_conn_poller
;
119 client_resp_handler client_resp_cb
;
121 /* Timeout for current RPC client action. */
125 static void spdk_app_json_config_load_subsystem(void *_ctx
);
128 spdk_app_json_config_load_done(struct load_json_config_ctx
*ctx
, int rc
)
130 spdk_poller_unregister(&ctx
->client_conn_poller
);
131 spdk_jsonrpc_client_close(ctx
->client_conn
);
135 SPDK_ERRLOG("Config load failed. Stopping SPDK application.\n");
138 ctx
->cb_fn(ctx
->cb_arg
);
141 SPDK_DEBUG_APP_CFG("Config load finished\n");
142 free(ctx
->json_data
);
148 rpc_client_set_timeout(struct load_json_config_ctx
*ctx
, uint64_t timeout_us
)
150 ctx
->timeout
= spdk_get_ticks() + timeout_us
* spdk_get_ticks_hz() / (1000 * 1000);
154 rpc_client_check_timeout(struct load_json_config_ctx
*ctx
)
156 if (ctx
->timeout
< spdk_get_ticks()) {
157 SPDK_WARNLOG("RPC client command timeout.\n");
165 rpc_client_poller(void *arg
)
167 struct load_json_config_ctx
*ctx
= arg
;
168 struct spdk_jsonrpc_client_response
*resp
;
169 client_resp_handler cb
;
172 assert(spdk_get_thread() == ctx
->thread
);
174 rc
= spdk_jsonrpc_client_poll(ctx
->client_conn
, 0);
176 rc
= rpc_client_check_timeout(ctx
);
177 if (rc
== -ETIMEDOUT
) {
178 rpc_client_set_timeout(ctx
, RPC_CLIENT_REQUEST_TIMEOUT_US
);
184 /* No response yet */
187 spdk_app_json_config_load_done(ctx
, rc
);
191 resp
= spdk_jsonrpc_client_get_response(ctx
->client_conn
);
195 SPDK_ERRLOG("error response: %*s", (int)resp
->error
->len
, (char *)resp
->error
->start
);
196 spdk_jsonrpc_client_free_response(resp
);
197 spdk_app_json_config_load_done(ctx
, -EINVAL
);
199 /* We have response so we must have callback for it. */
200 cb
= ctx
->client_resp_cb
;
203 /* Mark we are done with this handler. */
204 ctx
->client_resp_cb
= NULL
;
213 rpc_client_connect_poller(void *_ctx
)
215 struct load_json_config_ctx
*ctx
= _ctx
;
218 rc
= spdk_jsonrpc_client_poll(ctx
->client_conn
, 0);
219 if (rc
!= -ENOTCONN
) {
220 /* We are connected. Start regular poller and issue first request */
221 spdk_poller_unregister(&ctx
->client_conn_poller
);
222 ctx
->client_conn_poller
= spdk_poller_register(rpc_client_poller
, ctx
, 100);
223 spdk_app_json_config_load_subsystem(ctx
);
225 rc
= rpc_client_check_timeout(ctx
);
227 spdk_app_json_config_load_done(ctx
, rc
);
235 client_send_request(struct load_json_config_ctx
*ctx
, struct spdk_jsonrpc_client_request
*request
,
236 client_resp_handler client_resp_cb
)
240 assert(spdk_get_thread() == ctx
->thread
);
242 ctx
->client_resp_cb
= client_resp_cb
;
243 rpc_client_set_timeout(ctx
, RPC_CLIENT_REQUEST_TIMEOUT_US
);
244 rc
= spdk_jsonrpc_client_send_request(ctx
->client_conn
, request
);
247 SPDK_DEBUG_APP_CFG("Sending request to client failed (%d)\n", rc
);
254 cap_string(const struct spdk_json_val
*val
, void *out
)
256 const struct spdk_json_val
**vptr
= out
;
258 if (val
->type
!= SPDK_JSON_VAL_STRING
) {
267 cap_object(const struct spdk_json_val
*val
, void *out
)
269 const struct spdk_json_val
**vptr
= out
;
271 if (val
->type
!= SPDK_JSON_VAL_OBJECT_BEGIN
) {
281 cap_array_or_null(const struct spdk_json_val
*val
, void *out
)
283 const struct spdk_json_val
**vptr
= out
;
285 if (val
->type
!= SPDK_JSON_VAL_ARRAY_BEGIN
&& val
->type
!= SPDK_JSON_VAL_NULL
) {
293 struct config_entry
{
295 struct spdk_json_val
*params
;
298 static struct spdk_json_object_decoder jsonrpc_cmd_decoders
[] = {
299 {"method", offsetof(struct config_entry
, method
), spdk_json_decode_string
},
300 {"params", offsetof(struct config_entry
, params
), cap_object
, true}
303 static void spdk_app_json_config_load_subsystem_config_entry(void *_ctx
);
306 spdk_app_json_config_load_subsystem_config_entry_next(struct load_json_config_ctx
*ctx
,
307 struct spdk_jsonrpc_client_response
*resp
)
309 /* Don't care about the response as long it is not
310 * an error (which is validated by poller) */
311 spdk_jsonrpc_client_free_response(resp
);
313 ctx
->config_it
= spdk_json_next(ctx
->config_it
);
314 spdk_app_json_config_load_subsystem_config_entry(ctx
);
317 /* Load "config" entry */
319 spdk_app_json_config_load_subsystem_config_entry(void *_ctx
)
321 struct load_json_config_ctx
*ctx
= _ctx
;
322 struct spdk_jsonrpc_client_request
*rpc_request
;
323 struct spdk_json_write_ctx
*w
;
324 struct config_entry cfg
= {};
325 struct spdk_json_val
*params_end
;
329 if (ctx
->config_it
== NULL
) {
330 SPDK_DEBUG_APP_CFG("Subsystem '%.*s': configuration done.\n", ctx
->subsystem_name
->len
,
331 (char *)ctx
->subsystem_name
->start
);
332 ctx
->subsystems_it
= spdk_json_next(ctx
->subsystems_it
);
333 /* Invoke later to avoid recurrency */
334 spdk_thread_send_msg(ctx
->thread
, spdk_app_json_config_load_subsystem
, ctx
);
338 if (spdk_json_decode_object(ctx
->config_it
, jsonrpc_cmd_decoders
,
339 SPDK_COUNTOF(jsonrpc_cmd_decoders
), &cfg
)) {
340 params_end
= spdk_json_next(ctx
->config_it
);
341 assert(params_end
!= NULL
);
342 params_len
= params_end
->start
- ctx
->config
->start
+ 1;
343 SPDK_ERRLOG("Failed to decode config entry: %*s!\n", (int)params_len
, (char *)ctx
->config_it
);
344 spdk_app_json_config_load_done(ctx
, -EINVAL
);
348 rc
= spdk_rpc_is_method_allowed(cfg
.method
, spdk_rpc_get_state());
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
, spdk_app_json_config_load_subsystem_config_entry
, ctx
);
357 /* Get _END by skipping params and going back by one element. */
358 params_end
= cfg
.params
+ spdk_json_val_len(cfg
.params
) - 1;
360 /* Need to add one character to include '}' */
361 params_len
= params_end
->start
- cfg
.params
->start
+ 1;
363 SPDK_DEBUG_APP_CFG("\tmethod: %s\n", cfg
.method
);
364 SPDK_DEBUG_APP_CFG("\tparams: %.*s\n", (int)params_len
, (char *)cfg
.params
->start
);
366 rpc_request
= spdk_jsonrpc_client_create_request();
368 spdk_app_json_config_load_done(ctx
, -errno
);
372 w
= spdk_jsonrpc_begin_request(rpc_request
, ctx
->rpc_request_id
, NULL
);
374 spdk_jsonrpc_client_free_request(rpc_request
);
375 spdk_app_json_config_load_done(ctx
, -ENOMEM
);
379 spdk_json_write_named_string(w
, "method", cfg
.method
);
381 /* No need to parse "params". Just dump the whole content of "params"
382 * directly into the request and let the remote side verify it. */
383 spdk_json_write_name(w
, "params");
384 spdk_json_write_val_raw(w
, cfg
.params
->start
, params_len
);
385 spdk_jsonrpc_end_request(rpc_request
, w
);
387 rc
= client_send_request(ctx
, rpc_request
, spdk_app_json_config_load_subsystem_config_entry_next
);
389 spdk_app_json_config_load_done(ctx
, -rc
);
397 subsystem_init_done_resp_cb(struct load_json_config_ctx
*ctx
,
398 struct spdk_jsonrpc_client_response
*resp
)
400 spdk_jsonrpc_client_free_response(resp
);
402 /* Another round. This time for RUNTIME methods */
403 SPDK_DEBUG_APP_CFG("'start_subsystem_init' done - continuing configuration\n");
404 if (ctx
->subsystems
) {
405 ctx
->subsystems_it
= spdk_json_array_first(ctx
->subsystems
);
408 spdk_app_json_config_load_subsystem(ctx
);
411 static struct spdk_json_object_decoder subsystem_decoders
[] = {
412 {"subsystem", offsetof(struct load_json_config_ctx
, subsystem_name
), cap_string
},
413 {"config", offsetof(struct load_json_config_ctx
, config
), cap_array_or_null
}
417 * Start loading subsystem pointed by ctx->subsystems_it. This must point to the
418 * beginning of the "subsystem" object in "subsystems" array or be NULL. If it is
419 * NULL then no more subsystems to load.
421 * There are two iterations:
423 * In first iteration only STARTUP RPC methods are used, other methods are ignored. When
424 * allsubsystems are walked the ctx->subsystems_it became NULL and "start_subsystem_init"
425 * is called to let the SPDK move to RUNTIME state (initialize all subsystems) and
426 * second iteration begins.
428 * In second iteration "subsystems" array is walked through again, this time only
429 * RUNTIME RPC methods are used. When ctx->subsystems_it became NULL second time it
430 * indicate that there is no more subsystems to load. The cb_fn is called to finish
434 spdk_app_json_config_load_subsystem(void *_ctx
)
436 struct load_json_config_ctx
*ctx
= _ctx
;
437 struct spdk_jsonrpc_client_request
*req
;
438 struct spdk_json_write_ctx
*w
;
440 if (ctx
->subsystems_it
== NULL
) {
441 if (spdk_rpc_get_state() == SPDK_RPC_STARTUP
) {
442 SPDK_DEBUG_APP_CFG("No more entries for current state, calling 'start_subsystem_init'\n");
443 req
= spdk_jsonrpc_client_create_request();
445 spdk_app_json_config_load_done(ctx
, -ENOMEM
);
449 w
= spdk_jsonrpc_begin_request(req
, ctx
->rpc_request_id
++, "start_subsystem_init");
451 spdk_jsonrpc_client_free_request(req
);
452 spdk_app_json_config_load_done(ctx
, -ENOMEM
);
455 spdk_jsonrpc_end_request(req
, w
);
457 client_send_request(ctx
, req
, subsystem_init_done_resp_cb
);
459 spdk_app_json_config_load_done(ctx
, 0);
465 /* Capture subsystem name and config array */
466 if (spdk_json_decode_object(ctx
->subsystems_it
, subsystem_decoders
,
467 SPDK_COUNTOF(subsystem_decoders
), ctx
)) {
468 SPDK_ERRLOG("Failed to parse subsystem configuration\n");
469 spdk_app_json_config_load_done(ctx
, -EINVAL
);
473 SPDK_DEBUG_APP_CFG("Loading subsystem '%.*s' configuration\n", ctx
->subsystem_name
->len
,
474 (char *)ctx
->subsystem_name
->start
);
476 /* Get 'config' array first configuration entry */
477 ctx
->config_it
= spdk_json_array_first(ctx
->config
);
478 spdk_app_json_config_load_subsystem_config_entry(ctx
);
482 read_file(const char *filename
, size_t *size
)
484 FILE *file
= fopen(filename
, "r");
488 /* errno is set by fopen */
492 data
= spdk_posix_file_load(file
, size
);
498 spdk_app_json_config_read(const char *config_file
, struct load_json_config_ctx
*ctx
)
500 struct spdk_json_val
*values
= NULL
;
501 void *json
= NULL
, *end
;
502 ssize_t values_cnt
, rc
;
505 json
= read_file(config_file
, &json_size
);
510 rc
= spdk_json_parse(json
, json_size
, NULL
, 0, &end
,
511 SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS
);
513 fprintf(stderr
, "Parsing JSON configuration failed (%zd)\n", rc
);
518 values
= calloc(values_cnt
, sizeof(struct spdk_json_val
));
519 if (values
== NULL
) {
520 fprintf(stderr
, "Out of memory\n");
524 rc
= spdk_json_parse(json
, json_size
, values
, values_cnt
, &end
,
525 SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS
);
526 if (rc
!= values_cnt
) {
527 fprintf(stderr
, "Parsing JSON configuration failed (%zd)\n", rc
);
531 ctx
->json_data
= json
;
532 ctx
->json_data_size
= json_size
;
534 ctx
->values
= values
;
535 ctx
->values_cnt
= values_cnt
;
545 spdk_app_json_config_load(const char *json_config_file
, const char *rpc_addr
,
546 spdk_msg_fn cb_fn
, void *cb_arg
)
548 struct load_json_config_ctx
*ctx
= calloc(1, sizeof(*ctx
));
553 spdk_app_stop(-ENOMEM
);
558 ctx
->cb_arg
= cb_arg
;
559 ctx
->thread
= spdk_get_thread();
561 rc
= spdk_app_json_config_read(json_config_file
, ctx
);
566 /* Capture subsystems array */
567 rc
= spdk_json_find_array(ctx
->values
, "subsystems", NULL
, &ctx
->subsystems
);
569 SPDK_WARNLOG("No 'subsystems' key JSON configuration file.\n");
571 /* Get first subsystem */
572 ctx
->subsystems_it
= spdk_json_array_first(ctx
->subsystems
);
573 if (ctx
->subsystems_it
== NULL
) {
574 SPDK_NOTICELOG("'subsystems' configuration is empty\n");
578 /* If rpc_addr is not an Unix socket use default address as prefix. */
579 if (rpc_addr
== NULL
|| rpc_addr
[0] != '/') {
580 rpc_addr
= SPDK_DEFAULT_RPC_ADDR
;
583 /* FIXME: rpc client should use socketpair() instead of this temporary socket nonsense */
584 rc
= snprintf(ctx
->rpc_socket_path_temp
, sizeof(ctx
->rpc_socket_path_temp
), "%s.%d_config",
586 if (rc
>= (int)sizeof(ctx
->rpc_socket_path_temp
)) {
587 SPDK_ERRLOG("Socket name create failed\n");
591 /* FIXME: spdk_rpc_initialize() function should return error code. */
592 spdk_rpc_initialize(ctx
->rpc_socket_path_temp
);
593 ctx
->client_conn
= spdk_jsonrpc_client_connect(ctx
->rpc_socket_path_temp
, AF_UNIX
);
594 if (ctx
->client_conn
== NULL
) {
595 SPDK_ERRLOG("Failed to connect to '%s'\n", ctx
->rpc_socket_path_temp
);
599 rpc_client_set_timeout(ctx
, RPC_CLIENT_CONNECT_TIMEOUT_US
);
600 ctx
->client_conn_poller
= spdk_poller_register(rpc_client_connect_poller
, ctx
, 100);
604 spdk_app_json_config_load_done(ctx
, -EINVAL
);
607 SPDK_LOG_REGISTER_COMPONENT("app_config", SPDK_LOG_APP_CONFIG
)