]> git.proxmox.com Git - ceph.git/blob - ceph/src/spdk/lib/event/json_config.c
import 15.2.0 Octopus source
[ceph.git] / ceph / src / spdk / lib / event / json_config.c
1 /*-
2 * BSD LICENSE
3 *
4 * Copyright (c) Intel Corporation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include "json_config.h"
35
36 #include "spdk/stdinc.h"
37
38 #include "spdk/util.h"
39 #include "spdk/file.h"
40 #include "spdk/log.h"
41 #include "spdk/env.h"
42 #include "spdk/thread.h"
43 #include "spdk/jsonrpc.h"
44 #include "spdk/rpc.h"
45
46 #include "spdk_internal/event.h"
47 #include "spdk_internal/log.h"
48
49 #define SPDK_DEBUG_APP_CFG(...) SPDK_DEBUGLOG(SPDK_LOG_APP_CONFIG, __VA_ARGS__)
50
51 /* JSON configuration format is as follows
52 *
53 * {
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
61 * },
62 * << MORE "config" ARRY ENTRIES >>
63 * ]
64 * },
65 * << MORE "subsystems" ARRAY ENTRIES >>
66 * ]
67 *
68 * << ANYTHING ELSE IS IGNORRED IN ROOT OBJECT>>
69 * }
70 *
71 */
72
73 struct load_json_config_ctx;
74 typedef void (*client_resp_handler)(struct load_json_config_ctx *,
75 struct spdk_jsonrpc_client_response *);
76
77 #define RPC_SOCKET_PATH_MAX sizeof(((struct sockaddr_un *)0)->sun_path)
78
79 /* 1s connections timeout */
80 #define RPC_CLIENT_CONNECT_TIMEOUT_US (1U * 1000U * 1000U)
81
82 /*
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)
87
88 struct load_json_config_ctx {
89 /* Thread used during configuration. */
90 struct spdk_thread *thread;
91 spdk_msg_fn cb_fn;
92 void *cb_arg;
93
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 */
97
98 struct spdk_json_val *subsystem_name; /* current subsystem name */
99
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 */
103
104 /* Current request id we are sending. */
105 uint32_t rpc_request_id;
106
107 /* Whole configuration file read and parsed. */
108 size_t json_data_size;
109 char *json_data;
110
111 size_t values_cnt;
112 struct spdk_json_val *values;
113
114 char rpc_socket_path_temp[RPC_SOCKET_PATH_MAX + 1];
115
116 struct spdk_jsonrpc_client *client_conn;
117 struct spdk_poller *client_conn_poller;
118
119 client_resp_handler client_resp_cb;
120
121 /* Timeout for current RPC client action. */
122 uint64_t timeout;
123 };
124
125 static void spdk_app_json_config_load_subsystem(void *_ctx);
126
127 static void
128 spdk_app_json_config_load_done(struct load_json_config_ctx *ctx, int rc)
129 {
130 spdk_poller_unregister(&ctx->client_conn_poller);
131 spdk_jsonrpc_client_close(ctx->client_conn);
132 spdk_rpc_finish();
133
134 if (rc) {
135 SPDK_ERRLOG("Config load failed. Stopping SPDK application.\n");
136 spdk_app_stop(rc);
137 } else {
138 ctx->cb_fn(ctx->cb_arg);
139 }
140
141 SPDK_DEBUG_APP_CFG("Config load finished\n");
142 free(ctx->json_data);
143 free(ctx->values);
144 free(ctx);
145 }
146
147 static void
148 rpc_client_set_timeout(struct load_json_config_ctx *ctx, uint64_t timeout_us)
149 {
150 ctx->timeout = spdk_get_ticks() + timeout_us * spdk_get_ticks_hz() / (1000 * 1000);
151 }
152
153 static int
154 rpc_client_check_timeout(struct load_json_config_ctx *ctx)
155 {
156 if (ctx->timeout < spdk_get_ticks()) {
157 SPDK_WARNLOG("RPC client command timeout.\n");
158 return -ETIMEDOUT;
159 }
160
161 return 0;
162 }
163
164 static int
165 rpc_client_poller(void *arg)
166 {
167 struct load_json_config_ctx *ctx = arg;
168 struct spdk_jsonrpc_client_response *resp;
169 client_resp_handler cb;
170 int rc;
171
172 assert(spdk_get_thread() == ctx->thread);
173
174 rc = spdk_jsonrpc_client_poll(ctx->client_conn, 0);
175 if (rc == 0) {
176 rc = rpc_client_check_timeout(ctx);
177 if (rc == -ETIMEDOUT) {
178 rpc_client_set_timeout(ctx, RPC_CLIENT_REQUEST_TIMEOUT_US);
179 rc = 0;
180 }
181 }
182
183 if (rc == 0) {
184 /* No response yet */
185 return -1;
186 } else if (rc < 0) {
187 spdk_app_json_config_load_done(ctx, rc);
188 return -1;
189 }
190
191 resp = spdk_jsonrpc_client_get_response(ctx->client_conn);
192 assert(resp);
193
194 if (resp->error) {
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);
198 } else {
199 /* We have response so we must have callback for it. */
200 cb = ctx->client_resp_cb;
201 assert(cb != NULL);
202
203 /* Mark we are done with this handler. */
204 ctx->client_resp_cb = NULL;
205 cb(ctx, resp);
206 }
207
208
209 return -1;
210 }
211
212 static int
213 rpc_client_connect_poller(void *_ctx)
214 {
215 struct load_json_config_ctx *ctx = _ctx;
216 int rc;
217
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);
224 } else {
225 rc = rpc_client_check_timeout(ctx);
226 if (rc) {
227 spdk_app_json_config_load_done(ctx, rc);
228 }
229 }
230
231 return -1;
232 }
233
234 static int
235 client_send_request(struct load_json_config_ctx *ctx, struct spdk_jsonrpc_client_request *request,
236 client_resp_handler client_resp_cb)
237 {
238 int rc;
239
240 assert(spdk_get_thread() == ctx->thread);
241
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);
245
246 if (rc) {
247 SPDK_DEBUG_APP_CFG("Sending request to client failed (%d)\n", rc);
248 }
249
250 return rc;
251 }
252
253 static int
254 cap_string(const struct spdk_json_val *val, void *out)
255 {
256 const struct spdk_json_val **vptr = out;
257
258 if (val->type != SPDK_JSON_VAL_STRING) {
259 return -EINVAL;
260 }
261
262 *vptr = val;
263 return 0;
264 }
265
266 static int
267 cap_object(const struct spdk_json_val *val, void *out)
268 {
269 const struct spdk_json_val **vptr = out;
270
271 if (val->type != SPDK_JSON_VAL_OBJECT_BEGIN) {
272 return -EINVAL;
273 }
274
275 *vptr = val;
276 return 0;
277 }
278
279
280 static int
281 cap_array_or_null(const struct spdk_json_val *val, void *out)
282 {
283 const struct spdk_json_val **vptr = out;
284
285 if (val->type != SPDK_JSON_VAL_ARRAY_BEGIN && val->type != SPDK_JSON_VAL_NULL) {
286 return -EINVAL;
287 }
288
289 *vptr = val;
290 return 0;
291 }
292
293 struct config_entry {
294 char *method;
295 struct spdk_json_val *params;
296 };
297
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}
301 };
302
303 static void spdk_app_json_config_load_subsystem_config_entry(void *_ctx);
304
305 static void
306 spdk_app_json_config_load_subsystem_config_entry_next(struct load_json_config_ctx *ctx,
307 struct spdk_jsonrpc_client_response *resp)
308 {
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);
312
313 ctx->config_it = spdk_json_next(ctx->config_it);
314 spdk_app_json_config_load_subsystem_config_entry(ctx);
315 }
316
317 /* Load "config" entry */
318 static void
319 spdk_app_json_config_load_subsystem_config_entry(void *_ctx)
320 {
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;
326 size_t params_len;
327 int rc;
328
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);
335 return;
336 }
337
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);
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, spdk_app_json_config_load_subsystem_config_entry, ctx);
354 goto out;
355 }
356
357 /* Get _END by skipping params and going back by one element. */
358 params_end = cfg.params + spdk_json_val_len(cfg.params) - 1;
359
360 /* Need to add one character to include '}' */
361 params_len = params_end->start - cfg.params->start + 1;
362
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);
365
366 rpc_request = spdk_jsonrpc_client_create_request();
367 if (!rpc_request) {
368 spdk_app_json_config_load_done(ctx, -errno);
369 goto out;
370 }
371
372 w = spdk_jsonrpc_begin_request(rpc_request, ctx->rpc_request_id, NULL);
373 if (!w) {
374 spdk_jsonrpc_client_free_request(rpc_request);
375 spdk_app_json_config_load_done(ctx, -ENOMEM);
376 goto out;
377 }
378
379 spdk_json_write_named_string(w, "method", cfg.method);
380
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);
386
387 rc = client_send_request(ctx, rpc_request, spdk_app_json_config_load_subsystem_config_entry_next);
388 if (rc != 0) {
389 spdk_app_json_config_load_done(ctx, -rc);
390 goto out;
391 }
392 out:
393 free(cfg.method);
394 }
395
396 static void
397 subsystem_init_done_resp_cb(struct load_json_config_ctx *ctx,
398 struct spdk_jsonrpc_client_response *resp)
399 {
400 spdk_jsonrpc_client_free_response(resp);
401
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);
406 }
407
408 spdk_app_json_config_load_subsystem(ctx);
409 }
410
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}
414 };
415
416 /*
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.
420 *
421 * There are two iterations:
422 *
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.
427 *
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
431 * configuration.
432 */
433 static void
434 spdk_app_json_config_load_subsystem(void *_ctx)
435 {
436 struct load_json_config_ctx *ctx = _ctx;
437 struct spdk_jsonrpc_client_request *req;
438 struct spdk_json_write_ctx *w;
439
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();
444 if (!req) {
445 spdk_app_json_config_load_done(ctx, -ENOMEM);
446 return;
447 }
448
449 w = spdk_jsonrpc_begin_request(req, ctx->rpc_request_id++, "start_subsystem_init");
450 if (!w) {
451 spdk_jsonrpc_client_free_request(req);
452 spdk_app_json_config_load_done(ctx, -ENOMEM);
453 return;
454 }
455 spdk_jsonrpc_end_request(req, w);
456
457 client_send_request(ctx, req, subsystem_init_done_resp_cb);
458 } else {
459 spdk_app_json_config_load_done(ctx, 0);
460 }
461
462 return;
463 }
464
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);
470 return;
471 }
472
473 SPDK_DEBUG_APP_CFG("Loading subsystem '%.*s' configuration\n", ctx->subsystem_name->len,
474 (char *)ctx->subsystem_name->start);
475
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);
479 }
480
481 static void *
482 read_file(const char *filename, size_t *size)
483 {
484 FILE *file = fopen(filename, "r");
485 void *data;
486
487 if (file == NULL) {
488 /* errno is set by fopen */
489 return NULL;
490 }
491
492 data = spdk_posix_file_load(file, size);
493 fclose(file);
494 return data;
495 }
496
497 static int
498 spdk_app_json_config_read(const char *config_file, struct load_json_config_ctx *ctx)
499 {
500 struct spdk_json_val *values = NULL;
501 void *json = NULL, *end;
502 ssize_t values_cnt, rc;
503 size_t json_size;
504
505 json = read_file(config_file, &json_size);
506 if (!json) {
507 return -errno;
508 }
509
510 rc = spdk_json_parse(json, json_size, NULL, 0, &end,
511 SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
512 if (rc < 0) {
513 fprintf(stderr, "Parsing JSON configuration failed (%zd)\n", rc);
514 goto err;
515 }
516
517 values_cnt = rc;
518 values = calloc(values_cnt, sizeof(struct spdk_json_val));
519 if (values == NULL) {
520 fprintf(stderr, "Out of memory\n");
521 goto err;
522 }
523
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);
528 goto err;
529 }
530
531 ctx->json_data = json;
532 ctx->json_data_size = json_size;
533
534 ctx->values = values;
535 ctx->values_cnt = values_cnt;
536
537 return 0;
538 err:
539 free(json);
540 free(values);
541 return rc;
542 }
543
544 void
545 spdk_app_json_config_load(const char *json_config_file, const char *rpc_addr,
546 spdk_msg_fn cb_fn, void *cb_arg)
547 {
548 struct load_json_config_ctx *ctx = calloc(1, sizeof(*ctx));
549 int rc;
550
551 assert(cb_fn);
552 if (!ctx) {
553 spdk_app_stop(-ENOMEM);
554 return;
555 }
556
557 ctx->cb_fn = cb_fn;
558 ctx->cb_arg = cb_arg;
559 ctx->thread = spdk_get_thread();
560
561 rc = spdk_app_json_config_read(json_config_file, ctx);
562 if (rc) {
563 goto fail;
564 }
565
566 /* Capture subsystems array */
567 rc = spdk_json_find_array(ctx->values, "subsystems", NULL, &ctx->subsystems);
568 if (rc) {
569 SPDK_WARNLOG("No 'subsystems' key JSON configuration file.\n");
570 } else {
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");
575 }
576 }
577
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;
581 }
582
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",
585 rpc_addr, getpid());
586 if (rc >= (int)sizeof(ctx->rpc_socket_path_temp)) {
587 SPDK_ERRLOG("Socket name create failed\n");
588 goto fail;
589 }
590
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);
596 goto fail;
597 }
598
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);
601 return;
602
603 fail:
604 spdk_app_json_config_load_done(ctx, -EINVAL);
605 }
606
607 SPDK_LOG_REGISTER_COMPONENT("app_config", SPDK_LOG_APP_CONFIG)