From 27e0190b8413628de0f498e91feaa1444f9374d5 Mon Sep 17 00:00:00 2001 From: Vitaliy Mysak Date: Fri, 28 Dec 2018 18:59:40 +0000 Subject: [PATCH] OCF: rpc: add stats method Add get_ocf_stats rpc method that returns available OCF statistics for specific OCF bdev Change-Id: If043a18c847fbeeddd8fdde1af9397e24bd90718 Signed-off-by: Vitaliy Mysak Reviewed-on: https://review.gerrithub.io/c/438411 Tested-by: SPDK CI Jenkins Reviewed-by: Changpeng Liu Reviewed-by: Tomasz Zawadzki Reviewed-by: Darek Stojaczyk --- doc/jsonrpc.md | 204 +++++++++++++++++++++++++ lib/bdev/ocf/stats.c | 126 +++++++++++++++ lib/bdev/ocf/stats.h | 51 +++++++ lib/bdev/ocf/vbdev_ocf_rpc.c | 60 ++++++++ scripts/rpc.py | 8 + scripts/rpc/bdev.py | 14 ++ test/ocf/integrity/bdevperf-iotypes.sh | 8 + 7 files changed, 471 insertions(+) create mode 100644 lib/bdev/ocf/stats.c create mode 100644 lib/bdev/ocf/stats.h diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index c8a53196d8..f3dea70006 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -872,6 +872,210 @@ Example response: } ~~~ +## get_ocf_stats {#rpc_get_ocf_stats} + +Get statistics of chosen OCF block device. + +### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +name | Required | string | Block device name + +### Response + +Statistics as json object. + +### Example + +Example request: + +~~~ +{ + "jsonrpc": "2.0", + "method": "get_ocf_stats", + "id": 1 +} +~~~ + +Example response: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": [ + "usage": { + "clean": { + "count": 76033, + "units": "4KiB blocks", + "percentage": "100.0" + }, + "free": { + "count": 767, + "units": "4KiB blocks", + "percentage": "0.9" + }, + "occupancy": { + "count": 76033, + "units": "4KiB blocks", + "percentage": "99.0" + }, + "dirty": { + "count": 0, + "units": "4KiB blocks", + "percentage": "0.0" + } + }, + "requests": { + "rd_total": { + "count": 2, + "units": "Requests", + "percentage": "0.0" + }, + "wr_full_misses": { + "count": 76280, + "units": "Requests", + "percentage": "35.6" + }, + "rd_full_misses": { + "count": 1, + "units": "Requests", + "percentage": "0.0" + }, + "rd_partial_misses": { + "count": 0, + "units": "Requests", + "percentage": "0.0" + }, + "wr_total": { + "count": 212416, + "units": "Requests", + "percentage": "99.2" + }, + "wr_pt": { + "count": 1535, + "units": "Requests", + "percentage": "0.7" + }, + "wr_partial_misses": { + "count": 0, + "units": "Requests", + "percentage": "0.0" + }, + "serviced": { + "count": 212418, + "units": "Requests", + "percentage": "99.2" + }, + "rd_pt": { + "count": 0, + "units": "Requests", + "percentage": "0.0" + }, + "total": { + "count": 213953, + "units": "Requests", + "percentage": "100.0" + }, + "rd_hits": { + "count": 1, + "units": "Requests", + "percentage": "0.0" + }, + "wr_hits": { + "count": 136136, + "units": "Requests", + "percentage": "63.6" + } + }, + "errors": { + "total": { + "count": 0, + "units": "Requests", + "percentage": "0.0" + }, + "cache_obj_total": { + "count": 0, + "units": "Requests", + "percentage": "0.0" + }, + "core_obj_total": { + "count": 0, + "units": "Requests", + "percentage": "0.0" + }, + "cache_obj_rd": { + "count": 0, + "units": "Requests", + "percentage": "0.0" + }, + "core_obj_wr": { + "count": 0, + "units": "Requests", + "percentage": "0.0" + }, + "core_obj_rd": { + "count": 0, + "units": "Requests", + "percentage": "0.0" + }, + "cache_obj_wr": { + "count": 0, + "units": "Requests", + "percentage": "0.0" + } + }, + "blocks": { + "volume_rd": { + "count": 9, + "units": "4KiB blocks", + "percentage": "0.0" + }, + "volume_wr": { + "count": 213951, + "units": "4KiB blocks", + "percentage": "99.9" + }, + "cache_obj_total": { + "count": 212425, + "units": "4KiB blocks", + "percentage": "100.0" + }, + "core_obj_total": { + "count": 213959, + "units": "4KiB blocks", + "percentage": "100.0" + }, + "cache_obj_rd": { + "count": 1, + "units": "4KiB blocks", + "percentage": "0.0" + }, + "core_obj_wr": { + "count": 213951, + "units": "4KiB blocks", + "percentage": "99.9" + }, + "volume_total": { + "count": 213960, + "units": "4KiB blocks", + "percentage": "100.0" + }, + "core_obj_rd": { + "count": 8, + "units": "4KiB blocks", + "percentage": "0.0" + }, + "cache_obj_wr": { + "count": 212424, + "units": "4KiB blocks", + "percentage": "99.9" + } + ] +} +~~~ + ## construct_malloc_bdev {#rpc_construct_malloc_bdev} Construct @ref bdev_config_malloc diff --git a/lib/bdev/ocf/stats.c b/lib/bdev/ocf/stats.c new file mode 100644 index 0000000000..337adefd0d --- /dev/null +++ b/lib/bdev/ocf/stats.c @@ -0,0 +1,126 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ctx.h" +#include "stats.h" + +int +vbdev_ocf_stats_get(int cache_id, int core_id, struct vbdev_ocf_stats *stats) +{ + int status; + struct ocf_stats_core core_stats; + ocf_cache_t cache; + ocf_core_t core; + + status = ocf_mngt_cache_get(vbdev_ocf_ctx, cache_id, &cache); + if (status) { + return status; + } + + status = ocf_core_get(cache, 0, &core); + if (status) { + return status; + } + + status = ocf_core_get_stats(core, &core_stats); + if (status) { + return status; + } + + status = ocf_stats_collect_core(core, &stats->usage, &stats->reqs, &stats->blocks, &stats->errors); + if (status) { + return status; + } + + return 0; +} + +#define WJSON_STAT(w, stats, group, field, units) \ + spdk_json_write_named_object_begin(w, #field); \ + spdk_json_write_named_uint64(w, "count", stats->group.field.value); \ + spdk_json_write_named_string_fmt(w, "percentage", "%lu.%lu", \ + stats->group.field.percent / 10, stats->group.field.percent % 10); \ + spdk_json_write_named_string(w, "units", units); \ + spdk_json_write_object_end(w); + +void +vbdev_ocf_stats_write_json(struct spdk_json_write_ctx *w, struct vbdev_ocf_stats *stats) +{ + spdk_json_write_object_begin(w); + + spdk_json_write_named_object_begin(w, "usage"); + WJSON_STAT(w, stats, usage, occupancy, "4KiB blocks"); + WJSON_STAT(w, stats, usage, free, "4KiB blocks"); + WJSON_STAT(w, stats, usage, clean, "4KiB blocks"); + WJSON_STAT(w, stats, usage, dirty, "4KiB blocks"); + spdk_json_write_object_end(w); + + spdk_json_write_named_object_begin(w, "requests"); + WJSON_STAT(w, stats, reqs, rd_hits, "Requests"); + WJSON_STAT(w, stats, reqs, rd_partial_misses, "Requests"); + WJSON_STAT(w, stats, reqs, rd_full_misses, "Requests"); + WJSON_STAT(w, stats, reqs, rd_total, "Requests"); + WJSON_STAT(w, stats, reqs, wr_hits, "Requests"); + WJSON_STAT(w, stats, reqs, wr_partial_misses, "Requests"); + WJSON_STAT(w, stats, reqs, wr_full_misses, "Requests"); + WJSON_STAT(w, stats, reqs, wr_total, "Requests"); + WJSON_STAT(w, stats, reqs, rd_pt, "Requests"); + WJSON_STAT(w, stats, reqs, wr_pt, "Requests"); + WJSON_STAT(w, stats, reqs, serviced, "Requests"); + WJSON_STAT(w, stats, reqs, total, "Requests"); + spdk_json_write_object_end(w); + + spdk_json_write_named_object_begin(w, "blocks"); + WJSON_STAT(w, stats, blocks, core_obj_rd, "4KiB blocks"); + WJSON_STAT(w, stats, blocks, core_obj_wr, "4KiB blocks"); + WJSON_STAT(w, stats, blocks, core_obj_total, "4KiB blocks"); + WJSON_STAT(w, stats, blocks, cache_obj_rd, "4KiB blocks"); + WJSON_STAT(w, stats, blocks, cache_obj_wr, "4KiB blocks"); + WJSON_STAT(w, stats, blocks, cache_obj_total, "4KiB blocks"); + WJSON_STAT(w, stats, blocks, volume_rd, "4KiB blocks"); + WJSON_STAT(w, stats, blocks, volume_wr, "4KiB blocks"); + WJSON_STAT(w, stats, blocks, volume_total, "4KiB blocks"); + spdk_json_write_object_end(w); + + spdk_json_write_named_object_begin(w, "errors"); + WJSON_STAT(w, stats, errors, core_obj_rd, "Requests"); + WJSON_STAT(w, stats, errors, core_obj_wr, "Requests"); + WJSON_STAT(w, stats, errors, core_obj_total, "Requests"); + WJSON_STAT(w, stats, errors, cache_obj_rd, "Requests"); + WJSON_STAT(w, stats, errors, cache_obj_wr, "Requests"); + WJSON_STAT(w, stats, errors, cache_obj_total, "Requests"); + WJSON_STAT(w, stats, errors, total, "Requests"); + spdk_json_write_object_end(w); + + spdk_json_write_object_end(w); +} diff --git a/lib/bdev/ocf/stats.h b/lib/bdev/ocf/stats.h new file mode 100644 index 0000000000..e63bfe913f --- /dev/null +++ b/lib/bdev/ocf/stats.h @@ -0,0 +1,51 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VBDEV_OCF_STATS_H +#define VBDEV_OCF_STATS_H + +#include "spdk/json.h" +#include + +struct vbdev_ocf_stats { + struct ocf_stats_usage usage; + struct ocf_stats_requests reqs; + struct ocf_stats_blocks blocks; + struct ocf_stats_errors errors; +}; + +int vbdev_ocf_stats_get(int cache_id, int core_id, struct vbdev_ocf_stats *stats); + +void vbdev_ocf_stats_write_json(struct spdk_json_write_ctx *w, struct vbdev_ocf_stats *stats); + +#endif diff --git a/lib/bdev/ocf/vbdev_ocf_rpc.c b/lib/bdev/ocf/vbdev_ocf_rpc.c index 83fc54210f..75502b95b7 100644 --- a/lib/bdev/ocf/vbdev_ocf_rpc.c +++ b/lib/bdev/ocf/vbdev_ocf_rpc.c @@ -32,6 +32,7 @@ */ #include "vbdev_ocf.h" +#include "stats.h" #include "spdk/log.h" #include "spdk/rpc.h" #include "spdk/string.h" @@ -158,3 +159,62 @@ end: free_rpc_delete_ocf_bdev(&req); } SPDK_RPC_REGISTER("delete_ocf_bdev", spdk_rpc_delete_ocf_bdev, SPDK_RPC_RUNTIME) + +/* Structure to hold the parameters for this RPC method. */ +struct rpc_get_ocf_stats { + char *name; /* master vbdev name */ +}; + +static void +free_rpc_get_ocf_stats(struct rpc_get_ocf_stats *r) +{ + free(r->name); +} + +/* Structure to decode the input parameters for this RPC method. */ +static const struct spdk_json_object_decoder rpc_get_ocf_stats_decoders[] = { + {"name", offsetof(struct rpc_get_ocf_stats, name), spdk_json_decode_string}, +}; + +static void +spdk_rpc_get_ocf_stats(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) +{ + struct rpc_get_ocf_stats req = {NULL}; + struct spdk_json_write_ctx *w; + struct vbdev_ocf *vbdev; + struct vbdev_ocf_stats stats; + int status; + + if (spdk_json_decode_object(params, rpc_get_ocf_stats_decoders, + SPDK_COUNTOF(rpc_get_ocf_stats_decoders), + &req)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + goto end; + } + + vbdev = vbdev_ocf_get_by_name(req.name); + if (vbdev == NULL) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + spdk_strerror(ENODEV)); + goto end; + } + + status = vbdev_ocf_stats_get(vbdev->cache.id, vbdev->core.id, &stats); + if (status) { + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Could not get stats: %s", + spdk_strerror(-status)); + goto end; + } + + w = spdk_jsonrpc_begin_result(request); + if (w) { + vbdev_ocf_stats_write_json(w, &stats); + spdk_jsonrpc_end_result(request, w); + } + +end: + free_rpc_get_ocf_stats(&req); +} +SPDK_RPC_REGISTER("get_ocf_stats", spdk_rpc_get_ocf_stats, SPDK_RPC_RUNTIME) diff --git a/scripts/rpc.py b/scripts/rpc.py index 937e098708..ba2310d365 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -167,6 +167,14 @@ if __name__ == "__main__": p.add_argument('name', help='Name of OCF bdev') p.set_defaults(func=delete_ocf_bdev) + def get_ocf_stats(args): + print_dict(rpc.bdev.get_ocf_stats(args.client, + name=args.name)) + p = subparsers.add_parser('get_ocf_stats', + help='Get statistics of chosen OCF block device') + p.add_argument('name', help='Name of OCF bdev') + p.set_defaults(func=get_ocf_stats) + def construct_malloc_bdev(args): num_blocks = (args.total_size * 1024 * 1024) // args.block_size print(rpc.bdev.construct_malloc_bdev(args.client, diff --git a/scripts/rpc/bdev.py b/scripts/rpc/bdev.py index 8a4e4ddacf..03b7966c27 100644 --- a/scripts/rpc/bdev.py +++ b/scripts/rpc/bdev.py @@ -71,6 +71,20 @@ def delete_ocf_bdev(client, name): return client.call('delete_ocf_bdev', params) +def get_ocf_stats(client, name): + """Get statistics of chosen OCF block device + + Args: + name: name of OCF bdev + + Returns: + Statistics as json object + """ + params = {'name': name} + + return client.call('get_ocf_stats', params) + + def construct_malloc_bdev(client, num_blocks, block_size, name=None, uuid=None): """Construct a malloc block device. diff --git a/test/ocf/integrity/bdevperf-iotypes.sh b/test/ocf/integrity/bdevperf-iotypes.sh index fd368d3c0b..4c2a67ad10 100755 --- a/test/ocf/integrity/bdevperf-iotypes.sh +++ b/test/ocf/integrity/bdevperf-iotypes.sh @@ -5,6 +5,14 @@ rootdir=$(readlink -f $curdir/../../..) source $rootdir/test/common/autotest_common.sh bdevperf=$rootdir/test/bdev/bdevperf/bdevperf +rpc_py="python $rootdir/scripts/rpc.py" $bdevperf -c $curdir/mallocs.conf -q 128 -o 4096 -t 4 -w flush $bdevperf -c $curdir/mallocs.conf -q 128 -o 4096 -t 4 -w unmap + +$bdevperf -c $curdir/mallocs.conf -q 128 -o 4096 -t 4 -w write -r /var/tmp/spdk.sock & +bdev_perf_pid=$! +waitforlisten $bdev_perf_pid +sleep 1 +$rpc_py get_ocf_stats MalCache1 +wait $bdev_perf_pid