diff --git a/include/spdk/bdev.h b/include/spdk/bdev.h index d9beb761c..87b4ff9fe 100644 --- a/include/spdk/bdev.h +++ b/include/spdk/bdev.h @@ -43,6 +43,7 @@ #include "spdk/scsi_spec.h" #include "spdk/nvme_spec.h" +#include "spdk/json.h" #ifdef __cplusplus extern "C" { @@ -147,6 +148,15 @@ void spdk_bdev_finish(spdk_bdev_fini_cb cb_fn, void *cb_arg); */ void spdk_bdev_config_text(FILE *fp); +/** + * Get the full configuration options for the registered block device modules and created bdevs. + * + * \param w The pointer to a JSON write context where the configuration will be written. + * \return result of \c spdk_bdev_dump_bdev_config_json() or negative error code: + * -EINVAL if w == NULL + */ +int spdk_bdev_config_json(struct spdk_json_write_ctx *w); + /** * Get block device by the block device name. * @@ -230,7 +240,7 @@ struct spdk_bdev *spdk_bdev_desc_get_bdev(struct spdk_bdev_desc *desc); bool spdk_bdev_io_type_supported(struct spdk_bdev *bdev, enum spdk_bdev_io_type io_type); /** - * Output driver-specific configuration to a JSON stream. + * Output driver-specific information to a JSON stream. * * The JSON write context will be initialized with an open object, so the bdev * driver should write a name(based on the driver name) followed by a JSON value @@ -242,6 +252,24 @@ bool spdk_bdev_io_type_supported(struct spdk_bdev *bdev, enum spdk_bdev_io_type */ int spdk_bdev_dump_info_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w); +/** + * Output bdev-specific configuration to a JSON stream. + * + * If supported, the object with following keys will be written: + * method - name of the constructor method + * params - parameters necessary to recreate this \c bdev + * + * If \c bdev does not support writing JSON configuration then object will be written + * with only one key - the name of this bdev. + * + * \param w JSON write context. It will store the driver-specific configuration context. + * \param bdev block device to query configuration. + * \return 0 on success, negated errno on failure. + * -EINVAL - bdev == NULL or w == NULL + * -ENOSYS - this block device doesn't support dumping configuration. + */ +int spdk_bdev_write_config_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w); + /** * Get block device name. * diff --git a/include/spdk_internal/bdev.h b/include/spdk_internal/bdev.h index 4db50225c..fc29fe471 100644 --- a/include/spdk_internal/bdev.h +++ b/include/spdk_internal/bdev.h @@ -100,6 +100,14 @@ struct spdk_bdev_module { */ void (*config_text)(FILE *fp); + /** + * Function called to return a text string representing the + * module's configuration options for inclusion in a configuration file. + * + * \return 0 on success or Bdev specific negative error code. + */ + int (*config_json)(struct spdk_json_write_ctx *w); + /** Name for the modules being defined. */ const char *name; @@ -172,6 +180,15 @@ struct spdk_bdev_fn_table { */ int (*dump_info_json)(void *ctx, struct spdk_json_write_ctx *w); + /** + * Output bdev-specific configuration to a JSON stream. Optional - may be NULL. + * + * The JSON write context will be initialized with an open object, so the bdev + * driver should write all data necessary to recreate this bdev by invoking + * constructor method. No other data should be written. + */ + void (*write_config_json)(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w); + /** Get spin-time per I/O channel in microseconds. * Optional - may be NULL. */ diff --git a/lib/bdev/bdev.c b/lib/bdev/bdev.c index dc2f34e8d..3b78defb4 100644 --- a/lib/bdev/bdev.c +++ b/lib/bdev/bdev.c @@ -426,6 +426,32 @@ spdk_bdev_config_text(FILE *fp) } } +int +spdk_bdev_config_json(struct spdk_json_write_ctx *w) +{ + struct spdk_bdev_module *bdev_module; + struct spdk_bdev *bdev; + + if (!w) { + return -EINVAL; + } + + spdk_json_write_array_begin(w); + + TAILQ_FOREACH(bdev_module, &g_bdev_mgr.bdev_modules, tailq) { + if (bdev_module->config_json) { + bdev_module->config_json(w); + } + } + + TAILQ_FOREACH(bdev, &g_bdev_mgr.bdevs, link) { + spdk_bdev_write_config_json(bdev, w); + } + + spdk_json_write_array_end(w); + return 0; +} + static int spdk_bdev_mgmt_channel_create(void *io_device, void *ctx_buf) { @@ -952,6 +978,24 @@ spdk_bdev_dump_info_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w) return 0; } +int +spdk_bdev_write_config_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w) +{ + if (bdev == NULL || w == NULL) { + return -EINVAL; + } + + if (bdev->fn_table->write_config_json) { + bdev->fn_table->write_config_json(bdev, w); + } else { + spdk_json_write_object_begin(w); + spdk_json_write_named_string(w, "name", bdev->name); + spdk_json_write_object_end(w); + } + + return 0; +} + static void spdk_bdev_qos_get_max_ios_per_timeslice(struct spdk_bdev *bdev) { diff --git a/lib/bdev/rpc/bdev_rpc.c b/lib/bdev/rpc/bdev_rpc.c index d8d64bcca..310477116 100644 --- a/lib/bdev/rpc/bdev_rpc.c +++ b/lib/bdev/rpc/bdev_rpc.c @@ -177,6 +177,67 @@ invalid: } SPDK_RPC_REGISTER("get_bdevs", spdk_rpc_get_bdevs) +struct rpc_get_bdevs_config { + char *name; +}; + +static void +free_rpc_get_bdevs_config(struct rpc_get_bdevs_config *r) +{ + free(r->name); +} + +static const struct spdk_json_object_decoder rpc_dump_bdevs_config_decoders[] = { + {"name", offsetof(struct rpc_get_bdevs_config, name), spdk_json_decode_string}, +}; + +static void +spdk_rpc_get_bdevs_config(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_get_bdevs_config req = {}; + struct spdk_json_write_ctx *w; + struct spdk_bdev *bdev = NULL; + + if (params && spdk_json_decode_object(params, rpc_dump_bdevs_config_decoders, + SPDK_COUNTOF(rpc_dump_bdevs_config_decoders), + &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + return; + } + + if (req.name) { + bdev = spdk_bdev_get_by_name(req.name); + if (bdev == NULL) { + SPDK_ERRLOG("bdev '%s' does not exist\n", req.name); + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Bdev '%s' not exist", req.name); + free_rpc_get_bdevs_config(&req); + return; + } + } + + free_rpc_get_bdevs_config(&req); + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_array_begin(w); + if (bdev != NULL) { + spdk_bdev_write_config_json(bdev, w); + } else { + for (bdev = spdk_bdev_first(); bdev != NULL; bdev = spdk_bdev_next(bdev)) { + spdk_bdev_write_config_json(bdev, w); + } + } + + spdk_json_write_array_end(w); + + spdk_jsonrpc_end_result(request, w); +} +SPDK_RPC_REGISTER("get_bdevs_config", spdk_rpc_get_bdevs_config) struct rpc_delete_bdev { char *name; diff --git a/lib/event/subsystems/bdev/bdev.c b/lib/event/subsystems/bdev/bdev.c index d71fd97ed..a63fd1890 100644 --- a/lib/event/subsystems/bdev/bdev.c +++ b/lib/event/subsystems/bdev/bdev.c @@ -69,6 +69,7 @@ static struct spdk_subsystem g_spdk_subsystem_bdev = { .init = spdk_bdev_subsystem_initialize, .fini = spdk_bdev_subsystem_finish, .config = spdk_bdev_config_text, + .write_config_json = spdk_bdev_config_json, }; SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_bdev); diff --git a/scripts/rpc.py b/scripts/rpc.py index 130d060ab..e47be7202 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -105,6 +105,11 @@ if __name__ == "__main__": p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False) p.set_defaults(func=rpc.bdev.get_bdevs) + p = subparsers.add_parser( + 'get_bdevs_config', help='Display current (live) blockdev configuration list or required blockdev') + p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False) + p.set_defaults(func=rpc.bdev.get_bdevs_config) + p = subparsers.add_parser('delete_bdev', help='Delete a blockdev') p.add_argument( 'bdev_name', help='Blockdev name to be deleted. Example: Malloc0.') diff --git a/scripts/rpc/bdev.py b/scripts/rpc/bdev.py index 1fb8fd87d..ed5a85f13 100755 --- a/scripts/rpc/bdev.py +++ b/scripts/rpc/bdev.py @@ -80,6 +80,13 @@ def get_bdevs(args): print_dict(args.client.call('get_bdevs', params)) +def get_bdevs_config(args): + params = {} + if args.name: + params['name'] = args.name + print_dict(args.client.call('get_bdevs_config', params)) + + def delete_bdev(args): params = {'name': args.bdev_name} args.client.call('delete_bdev', params) diff --git a/test/unit/lib/bdev/bdev.c/bdev_ut.c b/test/unit/lib/bdev/bdev.c/bdev_ut.c index 282691079..1e68a1ebe 100644 --- a/test/unit/lib/bdev/bdev.c/bdev_ut.c +++ b/test/unit/lib/bdev/bdev.c/bdev_ut.c @@ -34,6 +34,7 @@ #include "spdk_cunit.h" #include "lib/test_env.c" +#include "unit/lib/json_mock.c" /* HACK: disable VTune integration so the unit test doesn't need VTune headers and libs to build */ #undef SPDK_CONFIG_VTUNE diff --git a/test/unit/lib/bdev/mt/bdev.c/bdev_ut.c b/test/unit/lib/bdev/mt/bdev.c/bdev_ut.c index 2788e0559..bcd527112 100644 --- a/test/unit/lib/bdev/mt/bdev.c/bdev_ut.c +++ b/test/unit/lib/bdev/mt/bdev.c/bdev_ut.c @@ -34,6 +34,7 @@ #include "spdk_cunit.h" #include "lib/test_env.c" +#include "unit/lib/json_mock.c" #include "lib/ut_multithread.c" /* HACK: disable VTune integration so the unit test doesn't need VTune headers and libs to build */ diff --git a/test/unit/lib/bdev/part.c/part_ut.c b/test/unit/lib/bdev/part.c/part_ut.c index 2b7e35bed..437824919 100644 --- a/test/unit/lib/bdev/part.c/part_ut.c +++ b/test/unit/lib/bdev/part.c/part_ut.c @@ -34,6 +34,7 @@ #include "spdk_cunit.h" #include "lib/test_env.c" +#include "unit/lib/json_mock.c" /* HACK: disable VTune integration so the unit test doesn't need VTune headers and libs to build */ #undef SPDK_CONFIG_VTUNE diff --git a/test/unit/lib/bdev/pmem/bdev_pmem_ut.c b/test/unit/lib/bdev/pmem/bdev_pmem_ut.c index d08bf3f51..92b33ccde 100644 --- a/test/unit/lib/bdev/pmem/bdev_pmem_ut.c +++ b/test/unit/lib/bdev/pmem/bdev_pmem_ut.c @@ -34,6 +34,7 @@ #include "spdk_cunit.h" #include "lib/test_env.c" +#include "unit/lib/json_mock.c" #include "bdev/pmem/bdev_pmem.c" @@ -96,11 +97,6 @@ _pmem_send_msg(spdk_thread_fn fn, void *ctx, void *thread_ctx) fn(ctx); } -DEFINE_STUB(spdk_json_write_object_begin, int, (struct spdk_json_write_ctx *w), 0); -DEFINE_STUB(spdk_json_write_object_end, int, (struct spdk_json_write_ctx *w), 0); -DEFINE_STUB(spdk_json_write_string, int, (struct spdk_json_write_ctx *w, const char *val), 0); -DEFINE_STUB(spdk_json_write_name, int, (struct spdk_json_write_ctx *w, const char *name), 0); - static PMEMblkpool * find_pmemblk_pool(const char *path) {