bdev/nvme: Add bdev_nvme_get_io_paths RPC to monitor I/O path states

Add an new RPC bdev_nvme_get_io_paths to query all active I/O paths.

One io_path belongs to One nvme_bdev_channel.
Each nvme_bdev_channel is associated with one nvme_bdev.

If the RPC bdev_nvme_get_io_paths has a bdev name as a parameter
it can use spdk_for_each_channel() simply for the corresponding
nvme_bdev.

However, users will want to know I/O paths of all nvme_bdevs like
the RPC bdev_get_bdevs.

One io_path has one nvme_qpair. One nvme_qpair belongs to one
nvme_poll_group. By relying on these relationships, the RPC
bdev_nvme_get_io_paths traverses all nvme_poll_groups by using
spdk_for_each_channel() to g_bdev_nvme_ctrlrs.

The RPC bdev_nvme_get_io_paths has two modes, display all or
the specified NVMe bdev's active I/O paths.

The specified bdev name is used just for comparison and empty
array is returned if no matched io_path is found.

Signed-off-by: Shuhei Matsumoto <smatsumoto@nvidia.com>
Change-Id: I4a0dbf3ef7aaa9a7b7345fc03dc493cc6d37bc99
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/12146
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Shuhei Matsumoto 2022-04-06 11:47:33 +09:00 committed by Tomasz Zawadzki
parent 2730f5cac0
commit 2a6a64485c
7 changed files with 209 additions and 0 deletions

View File

@ -21,6 +21,8 @@ A new API `spdk_bdev_unregister_by_name` was added to handle race conditions cor
New APIs, `spdk_for_each_bdev` and `spdk_for_each_bdev_leaf`, were added to provide iteration New APIs, `spdk_for_each_bdev` and `spdk_for_each_bdev_leaf`, were added to provide iteration
safe for race conditions. safe for race conditions.
A new RPC `bdev_nvme_get_io_paths` was added to get all active I/O paths.
### idxd ### idxd
A new parameter `flags` was added to all low level submission and preparation A new parameter `flags` was added to all low level submission and preparation

View File

@ -3344,6 +3344,56 @@ Example response:
} }
~~~ ~~~
### bdev_nvme_get_io_paths {#rpc_bdev_nvme_get_io_paths}
Display all or the specified NVMe bdev's active I/O paths.
#### Parameters
Name | Optional | Type | Description
----------------------- | -------- | ----------- | -----------
name | Optional | string | Name of the NVMe bdev
#### Example
Example request:
~~~json
{
"jsonrpc": "2.0",
"method": "bdev_nvme_get_io_paths",
"id": 1,
"params": {
"name": "Nvme0n1"
}
}
~~~
Example response:
~~~json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"poll_groups": [
{
"thread": "app_thread",
"io_paths": [
{
"bdev_name": "Nvme0n1",
"cntlid": 0,
"current": true,
"connected": true,
"accessible": true
}
]
}
]
}
}
~~~
### bdev_nvme_cuse_register {#rpc_bdev_nvme_cuse_register} ### bdev_nvme_cuse_register {#rpc_bdev_nvme_cuse_register}
Register CUSE device on NVMe controller. Register CUSE device on NVMe controller.

View File

@ -6160,4 +6160,28 @@ bdev_nvme_get_ctrlr(struct spdk_bdev *bdev)
return nvme_ns->ctrlr->ctrlr; return nvme_ns->ctrlr->ctrlr;
} }
void
nvme_io_path_info_json(struct spdk_json_write_ctx *w, struct nvme_io_path *io_path)
{
struct nvme_ns *nvme_ns = io_path->nvme_ns;
struct nvme_ctrlr *nvme_ctrlr = io_path->qpair->ctrlr;
const struct spdk_nvme_ctrlr_data *cdata;
spdk_json_write_object_begin(w);
spdk_json_write_named_string(w, "bdev_name", nvme_ns->bdev->disk.name);
cdata = spdk_nvme_ctrlr_get_data(nvme_ctrlr->ctrlr);
spdk_json_write_named_uint32(w, "cntlid", cdata->cntlid);
spdk_json_write_named_bool(w, "current", io_path == io_path->nbdev_ch->current_io_path);
spdk_json_write_named_bool(w, "connected", nvme_io_path_is_connected(io_path));
spdk_json_write_named_bool(w, "accessible", nvme_ns_is_accessible(nvme_ns));
spdk_json_write_object_end(w);
}
SPDK_LOG_REGISTER_COMPONENT(bdev_nvme) SPDK_LOG_REGISTER_COMPONENT(bdev_nvme)

View File

@ -223,6 +223,8 @@ struct nvme_poll_group {
TAILQ_HEAD(, nvme_qpair) qpair_list; TAILQ_HEAD(, nvme_qpair) qpair_list;
}; };
void nvme_io_path_info_json(struct spdk_json_write_ctx *w, struct nvme_io_path *io_path);
struct nvme_ctrlr *nvme_ctrlr_get_by_name(const char *name); struct nvme_ctrlr *nvme_ctrlr_get_by_name(const char *name);
struct nvme_bdev_ctrlr *nvme_bdev_ctrlr_get_by_name(const char *name); struct nvme_bdev_ctrlr *nvme_bdev_ctrlr_get_by_name(const char *name);

View File

@ -2017,3 +2017,112 @@ cleanup:
} }
SPDK_RPC_REGISTER("bdev_nvme_remove_error_injection", rpc_bdev_nvme_remove_error_injection, SPDK_RPC_REGISTER("bdev_nvme_remove_error_injection", rpc_bdev_nvme_remove_error_injection,
SPDK_RPC_RUNTIME) SPDK_RPC_RUNTIME)
struct rpc_get_io_paths {
char *name;
};
static void
free_rpc_get_io_paths(struct rpc_get_io_paths *r)
{
free(r->name);
}
static const struct spdk_json_object_decoder rpc_get_io_paths_decoders[] = {
{"name", offsetof(struct rpc_get_io_paths, name), spdk_json_decode_string, true},
};
struct rpc_get_io_paths_ctx {
struct rpc_get_io_paths req;
struct spdk_jsonrpc_request *request;
struct spdk_json_write_ctx *w;
};
static void
rpc_bdev_nvme_get_io_paths_done(struct spdk_io_channel_iter *i, int status)
{
struct rpc_get_io_paths_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
spdk_json_write_array_end(ctx->w);
spdk_json_write_object_end(ctx->w);
spdk_jsonrpc_end_result(ctx->request, ctx->w);
free_rpc_get_io_paths(&ctx->req);
free(ctx);
}
static void
_rpc_bdev_nvme_get_io_paths(struct spdk_io_channel_iter *i)
{
struct spdk_io_channel *_ch = spdk_io_channel_iter_get_channel(i);
struct nvme_poll_group *group = spdk_io_channel_get_ctx(_ch);
struct rpc_get_io_paths_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
struct nvme_qpair *qpair;
struct nvme_io_path *io_path;
struct nvme_bdev *nbdev;
spdk_json_write_object_begin(ctx->w);
spdk_json_write_named_string(ctx->w, "thread", spdk_thread_get_name(spdk_get_thread()));
spdk_json_write_named_array_begin(ctx->w, "io_paths");
TAILQ_FOREACH(qpair, &group->qpair_list, tailq) {
TAILQ_FOREACH(io_path, &qpair->io_path_list, tailq) {
nbdev = io_path->nvme_ns->bdev;
if (ctx->req.name != NULL &&
strcmp(ctx->req.name, nbdev->disk.name) != 0) {
continue;
}
nvme_io_path_info_json(ctx->w, io_path);
}
}
spdk_json_write_array_end(ctx->w);
spdk_json_write_object_end(ctx->w);
spdk_for_each_channel_continue(i, 0);
}
static void
rpc_bdev_nvme_get_io_paths(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_get_io_paths_ctx *ctx;
ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL) {
spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM));
return;
}
if (params != NULL &&
spdk_json_decode_object(params, rpc_get_io_paths_decoders,
SPDK_COUNTOF(rpc_get_io_paths_decoders),
&ctx->req)) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"bdev_nvme_get_io_paths requires no parameters");
free_rpc_get_io_paths(&ctx->req);
free(ctx);
return;
}
ctx->request = request;
ctx->w = spdk_jsonrpc_begin_result(request);
spdk_json_write_object_begin(ctx->w);
spdk_json_write_named_array_begin(ctx->w, "poll_groups");
spdk_for_each_channel(&g_nvme_bdev_ctrlrs,
_rpc_bdev_nvme_get_io_paths,
ctx,
rpc_bdev_nvme_get_io_paths_done);
}
SPDK_RPC_REGISTER("bdev_nvme_get_io_paths", rpc_bdev_nvme_get_io_paths, SPDK_RPC_RUNTIME)

View File

@ -811,6 +811,21 @@ def bdev_nvme_stop_discovery(client, name):
return client.call('bdev_nvme_stop_discovery', params) return client.call('bdev_nvme_stop_discovery', params)
def bdev_nvme_get_io_paths(client, name):
"""Display all or the specified NVMe bdev's active I/O paths
Args:
name: Name of the NVMe bdev (optional)
Returns:
List of active I/O paths
"""
params = {}
if name:
params['name'] = name
return client.call('bdev_nvme_get_io_paths', params)
def bdev_nvme_cuse_register(client, name): def bdev_nvme_cuse_register(client, name):
"""Register CUSE devices on NVMe controller. """Register CUSE devices on NVMe controller.

View File

@ -761,6 +761,13 @@ if __name__ == "__main__":
p.add_argument('-b', '--name', help="Name of the service to stop", required=True) p.add_argument('-b', '--name', help="Name of the service to stop", required=True)
p.set_defaults(func=bdev_nvme_stop_discovery) p.set_defaults(func=bdev_nvme_stop_discovery)
def bdev_nvme_get_io_paths(args):
print_dict(rpc.bdev.bdev_nvme_get_io_paths(args.client, name=args.name))
p = subparsers.add_parser('bdev_nvme_get_io_paths', help='Display active I/O paths')
p.add_argument('-n', '--name', help="Name of the NVMe bdev", required=False)
p.set_defaults(func=bdev_nvme_get_io_paths)
def bdev_nvme_cuse_register(args): def bdev_nvme_cuse_register(args):
rpc.bdev.bdev_nvme_cuse_register(args.client, rpc.bdev.bdev_nvme_cuse_register(args.client,
name=args.name) name=args.name)