xref: /spdk/lib/init/json_config.c (revision 77be4b721c3bd5083994465767924549439a8365)
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