Commit e5e427c9 authored by Piotr Pelplinski's avatar Piotr Pelplinski Committed by Darek Stojaczyk
Browse files

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: default avatarPiotr Pelplinski <piotr.pelplinski@intel.com>
Change-Id: Ib423a919dc1cde7dd7d92247db5482cfb9d66956
Reviewed-on: https://review.gerrithub.io/c/433573


Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarMaciej Szwed <maciej.szwed@intel.com>
Reviewed-by: default avatarDarek Stojaczyk <dariusz.stojaczyk@intel.com>
Reviewed-by: default avatarwuzhouhui <wuzhouhui@kingsoft.com>
Reviewed-by: default avatarPawel Wodkowski <pawelx.wodkowski@intel.com>
Chandler-Test-Pool: SPDK Automated Test System <sys_sgsw@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
parent 8f0d7e57
Loading
Loading
Loading
Loading
+82 −0
Original line number Diff line number Diff line
@@ -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.
+178 −0
Original line number Diff line number Diff line
@@ -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)
+16 −0
Original line number Diff line number Diff line
@@ -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,
+20 −0
Original line number Diff line number Diff line
@@ -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.