histograms: add rpc calls

This patch adds RPC calls for histograms in bdev layer.
Following calls are added:
 - enable_bdev_histogram - enable/disable histogram structures for specified bdev and each of its channels.
 - get_bdev_histogram - merges histograms from all channnels and encodes histogram as base64


Signed-off-by: Piotr Pelplinski <piotr.pelplinski@intel.com>
Change-Id: Ib423a919dc1cde7dd7d92247db5482cfb9d66956
Reviewed-on: https://review.gerrithub.io/c/433573
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Maciej Szwed <maciej.szwed@intel.com>
Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
Reviewed-by: wuzhouhui <wuzhouhui@kingsoft.com>
Reviewed-by: Pawel Wodkowski <pawelx.wodkowski@intel.com>
Chandler-Test-Pool: SPDK Automated Test System <sys_sgsw@intel.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
Piotr Pelplinski 2019-01-18 14:00:04 +01:00 committed by Darek Stojaczyk
parent 8f0d7e57fa
commit e5e427c94b
4 changed files with 296 additions and 0 deletions

View File

@ -632,6 +632,88 @@ Example response:
}
~~~
## enable_bdev_histogram {#rpc_enable_bdev_histogram}
Control whether collecting data for histogram is enabled for specified bdev.
### Parameters
Name | Optional | Type | Description
----------------------- | -------- | ----------- | -----------
name | Required | string | Block device name
enable | Required | boolean | Enable or disable histogram on specified device
### Example
Example request:
~~~
{
"jsonrpc": "2.0",
"id": 1,
"method": "enable_bdev_histogram",
"params": {
"name": "Nvme0n1"
"enable": true
}
}
~~~
Example response:
~~~
{
"jsonrpc": "2.0",
"id": 1,
"result": true
}
~~~
## get_bdev_histogram {#rpc_get_bdev_histogram}
Get latency histogram for specified bdev.
### Parameters
Name | Optional | Type | Description
----------------------- | -------- | ----------- | -----------
name | Required | string | Block device name
### Result
Name | Description
------------------------| -----------
histogram | Base64 encoded histogram
bucket_shift | Granularity of the histogram buckets
tsc_rate | Ticks per second
### Example
Example request:
~~~
{
"jsonrpc": "2.0",
"id": 1,
"method": "get_bdev_histogram",
"params": {
"name": "Nvme0n1"
}
}
~~~
Example response:
Note that histogram field is trimmed, actual encoded histogram length is ~80kb.
~~~
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"histogram": "AAAAAAAAAAAAAA...AAAAAAAAA==",
"tsc_rate": 2300000000,
"bucket_shift": 7
}
}
~~~
## delete_bdev {#rpc_delete_bdev}
Unregister a block device.

View File

@ -36,6 +36,8 @@
#include "spdk/rpc.h"
#include "spdk/string.h"
#include "spdk/util.h"
#include "spdk/histogram_data.h"
#include "spdk/base64.h"
#include "spdk/bdev_module.h"
@ -602,3 +604,179 @@ exit:
}
SPDK_RPC_REGISTER("set_bdev_qos_limit", spdk_rpc_set_bdev_qos_limit, SPDK_RPC_RUNTIME)
/* SPDK_RPC_ENABLE_BDEV_HISTOGRAM */
struct rpc_enable_bdev_histogram_request {
char *name;
bool enable;
};
static void
free_rpc_enable_bdev_histogram_request(struct rpc_enable_bdev_histogram_request *r)
{
free(r->name);
}
static const struct spdk_json_object_decoder rpc_enable_bdev_histogram_request_decoders[] = {
{"name", offsetof(struct rpc_enable_bdev_histogram_request, name), spdk_json_decode_string},
{"enable", offsetof(struct rpc_enable_bdev_histogram_request, enable), spdk_json_decode_bool},
};
static void
_spdk_bdev_histogram_status_cb(void *cb_arg, int status)
{
struct spdk_jsonrpc_request *request = cb_arg;
struct spdk_json_write_ctx *w;
w = spdk_jsonrpc_begin_result(request);
if (w == NULL) {
return;
}
spdk_json_write_bool(w, status == 0);
spdk_jsonrpc_end_result(request, w);
}
static void
spdk_rpc_enable_bdev_histogram(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_enable_bdev_histogram_request req = {NULL};
struct spdk_bdev *bdev;
int rc;
if (spdk_json_decode_object(params, rpc_enable_bdev_histogram_request_decoders,
SPDK_COUNTOF(rpc_enable_bdev_histogram_request_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
rc = -EINVAL;
goto invalid;
}
bdev = spdk_bdev_get_by_name(req.name);
if (bdev == NULL) {
rc = -ENODEV;
goto invalid;
}
spdk_bdev_histogram_enable(bdev, _spdk_bdev_histogram_status_cb, request, req.enable);
free_rpc_enable_bdev_histogram_request(&req);
return;
invalid:
free_rpc_enable_bdev_histogram_request(&req);
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc));
}
SPDK_RPC_REGISTER("enable_bdev_histogram", spdk_rpc_enable_bdev_histogram, SPDK_RPC_RUNTIME)
/* SPDK_RPC_GET_BDEV_HISTOGRAM */
struct rpc_get_bdev_histogram_request {
char *name;
};
static const struct spdk_json_object_decoder rpc_get_bdev_histogram_request_decoders[] = {
{"name", offsetof(struct rpc_get_bdev_histogram_request, name), spdk_json_decode_string}
};
static void
free_rpc_get_bdev_histogram_request(struct rpc_get_bdev_histogram_request *r)
{
free(r->name);
}
static void
_spdk_rpc_bdev_histogram_data_cb(void *cb_arg, int status, struct spdk_histogram_data *histogram)
{
struct spdk_jsonrpc_request *request = cb_arg;
struct spdk_json_write_ctx *w;
int rc;
char *encoded_histogram;
size_t src_len, dst_len;
if (status != 0) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
spdk_strerror(-status));
goto invalid;
}
src_len = SPDK_HISTOGRAM_NUM_BUCKETS(histogram) * sizeof(uint64_t);
dst_len = spdk_base64_get_encoded_strlen(src_len) + 1;
encoded_histogram = malloc(dst_len);
if (encoded_histogram == NULL) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
spdk_strerror(ENOMEM));
goto invalid;
}
rc = spdk_base64_encode(encoded_histogram, histogram->bucket, src_len);
if (rc != 0) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
spdk_strerror(-rc));
goto free_encoded_histogram;
}
w = spdk_jsonrpc_begin_result(request);
if (w == NULL) {
goto free_encoded_histogram;
}
spdk_json_write_object_begin(w);
spdk_json_write_named_string(w, "histogram", encoded_histogram);
spdk_json_write_named_int64(w, "bucket_shift", histogram->bucket_shift);
spdk_json_write_named_int64(w, "tsc_rate", spdk_get_ticks_hz());
spdk_json_write_object_end(w);
spdk_jsonrpc_end_result(request, w);
free_encoded_histogram:
free(encoded_histogram);
invalid:
spdk_histogram_data_free(histogram);
}
static void
spdk_rpc_get_bdev_histogram(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_get_bdev_histogram_request req = {NULL};
struct spdk_histogram_data *histogram;
struct spdk_bdev *bdev;
int rc;
if (spdk_json_decode_object(params, rpc_get_bdev_histogram_request_decoders,
SPDK_COUNTOF(rpc_get_bdev_histogram_request_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
rc = -EINVAL;
goto invalid;
}
bdev = spdk_bdev_get_by_name(req.name);
if (bdev == NULL) {
rc = -ENODEV;
goto invalid;
}
histogram = spdk_histogram_data_alloc();
if (histogram == NULL) {
rc = -ENOMEM;
goto invalid;
}
spdk_bdev_histogram_get(bdev, histogram, _spdk_rpc_bdev_histogram_data_cb, request);
free_rpc_get_bdev_histogram_request(&req);
return;
invalid:
free_rpc_get_bdev_histogram_request(&req);
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc));
}
SPDK_RPC_REGISTER("get_bdev_histogram", spdk_rpc_get_bdev_histogram, SPDK_RPC_RUNTIME)

View File

@ -431,6 +431,22 @@ if __name__ == "__main__":
'bdev_name', help='Blockdev name to be deleted. Example: Malloc0.')
p.set_defaults(func=delete_bdev)
def enable_bdev_histogram(args):
rpc.bdev.enable_bdev_histogram(args.client, name=args.name, enable=args.enable)
p = subparsers.add_parser('enable_bdev_histogram', help='Enable or disable histogram for specified bdev')
p.add_argument('-e', '--enable', default=True, dest='enable', action='store_true', help='Enable histograms on specified device')
p.add_argument('-d', '--disable', dest='enable', action='store_false', help='Disable histograms on specified device')
p.add_argument('name', help='bdev name')
p.set_defaults(func=enable_bdev_histogram)
def get_bdev_histogram(args):
print_dict(rpc.bdev.get_bdev_histogram(args.client, name=args.name))
p = subparsers.add_parser('get_bdev_histogram', help='Get histogram for specified bdev')
p.add_argument('name', help='bdev name')
p.set_defaults(func=get_bdev_histogram)
def set_bdev_qd_sampling_period(args):
rpc.bdev.set_bdev_qd_sampling_period(args.client,
name=args.name,

View File

@ -485,6 +485,26 @@ def delete_bdev(client, bdev_name):
return client.call('delete_bdev', params)
def enable_bdev_histogram(client, name, enable):
"""Control whether histogram is enabled for specified bdev.
Args:
bdev_name: name of bdev
"""
params = {'name': name, "enable": enable}
return client.call('enable_bdev_histogram', params)
def get_bdev_histogram(client, name):
"""Get histogram for specified bdev.
Args:
bdev_name: name of bdev
"""
params = {'name': name}
return client.call('get_bdev_histogram', params)
def bdev_inject_error(client, name, io_type, error_type, num=1):
"""Inject an error via an error bdev.