Spdk/lib/bdev/bdev_rpc.c
Shuhei Matsumoto d7ad7bca3c bdev: Add mode to bdev_reset_iostat RPC to reset only max/min fields
Both max and min should be reset periodically. We can use the queue
depth sampling poller to reset these but the queue depth sampling poller
is optional. We extend the bdev_reset_iostat RPC to support mode to
reset all or only max/min fields.

Signed-off-by: Shuhei Matsumoto <smatsumoto@nvidia.com>
Change-Id: I9ce54892f6e808f6a82754b6930092f3a16d51ff
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15444
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@nvidia.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
2022-12-08 12:54:23 +00:00

1213 lines
34 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
* All rights reserved.
*/
#include "spdk/bdev.h"
#include "spdk/env.h"
#include "spdk/rpc.h"
#include "spdk/util.h"
#include "spdk/string.h"
#include "spdk/base64.h"
#include "spdk/bdev_module.h"
#include "spdk/dma.h"
#include "spdk/log.h"
#include "bdev_internal.h"
static void
dummy_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *ctx)
{
}
struct spdk_rpc_set_bdev_opts {
uint32_t bdev_io_pool_size;
uint32_t bdev_io_cache_size;
bool bdev_auto_examine;
uint32_t small_buf_pool_size;
uint32_t large_buf_pool_size;
};
static const struct spdk_json_object_decoder rpc_set_bdev_opts_decoders[] = {
{"bdev_io_pool_size", offsetof(struct spdk_rpc_set_bdev_opts, bdev_io_pool_size), spdk_json_decode_uint32, true},
{"bdev_io_cache_size", offsetof(struct spdk_rpc_set_bdev_opts, bdev_io_cache_size), spdk_json_decode_uint32, true},
{"bdev_auto_examine", offsetof(struct spdk_rpc_set_bdev_opts, bdev_auto_examine), spdk_json_decode_bool, true},
{"small_buf_pool_size", offsetof(struct spdk_rpc_set_bdev_opts, small_buf_pool_size), spdk_json_decode_uint32, true},
{"large_buf_pool_size", offsetof(struct spdk_rpc_set_bdev_opts, large_buf_pool_size), spdk_json_decode_uint32, true},
};
static void
rpc_bdev_set_options(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
struct spdk_rpc_set_bdev_opts rpc_opts;
struct spdk_bdev_opts bdev_opts;
int rc;
rpc_opts.bdev_io_pool_size = UINT32_MAX;
rpc_opts.bdev_io_cache_size = UINT32_MAX;
rpc_opts.small_buf_pool_size = UINT32_MAX;
rpc_opts.large_buf_pool_size = UINT32_MAX;
rpc_opts.bdev_auto_examine = true;
if (params != NULL) {
if (spdk_json_decode_object(params, rpc_set_bdev_opts_decoders,
SPDK_COUNTOF(rpc_set_bdev_opts_decoders), &rpc_opts)) {
SPDK_ERRLOG("spdk_json_decode_object() failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Invalid parameters");
return;
}
}
spdk_bdev_get_opts(&bdev_opts, sizeof(bdev_opts));
if (rpc_opts.bdev_io_pool_size != UINT32_MAX) {
bdev_opts.bdev_io_pool_size = rpc_opts.bdev_io_pool_size;
}
if (rpc_opts.bdev_io_cache_size != UINT32_MAX) {
bdev_opts.bdev_io_cache_size = rpc_opts.bdev_io_cache_size;
}
bdev_opts.bdev_auto_examine = rpc_opts.bdev_auto_examine;
if (rpc_opts.small_buf_pool_size != UINT32_MAX) {
bdev_opts.small_buf_pool_size = rpc_opts.small_buf_pool_size;
}
if (rpc_opts.large_buf_pool_size != UINT32_MAX) {
bdev_opts.large_buf_pool_size = rpc_opts.large_buf_pool_size;
}
rc = spdk_bdev_set_opts(&bdev_opts);
if (rc != 0) {
spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Pool size %" PRIu32 " too small for cache size %" PRIu32,
bdev_opts.bdev_io_pool_size, bdev_opts.bdev_io_cache_size);
return;
}
spdk_jsonrpc_send_bool_response(request, true);
}
SPDK_RPC_REGISTER("bdev_set_options", rpc_bdev_set_options, SPDK_RPC_STARTUP)
static void
rpc_bdev_wait_for_examine_cpl(void *arg)
{
struct spdk_jsonrpc_request *request = arg;
spdk_jsonrpc_send_bool_response(request, true);
}
static void
rpc_bdev_wait_for_examine(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
int rc;
if (params != NULL) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"bdev_wait_for_examine requires no parameters");
return;
}
rc = spdk_bdev_wait_for_examine(rpc_bdev_wait_for_examine_cpl, request);
if (rc != 0) {
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
}
}
SPDK_RPC_REGISTER("bdev_wait_for_examine", rpc_bdev_wait_for_examine, SPDK_RPC_RUNTIME)
struct rpc_bdev_examine {
char *name;
};
static void
free_rpc_bdev_examine(struct rpc_bdev_examine *r)
{
free(r->name);
}
static const struct spdk_json_object_decoder rpc_examine_bdev_decoders[] = {
{"name", offsetof(struct rpc_bdev_examine, name), spdk_json_decode_string},
};
static void
rpc_bdev_examine_bdev(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_bdev_examine req = {NULL};
int rc;
if (spdk_json_decode_object(params, rpc_examine_bdev_decoders,
SPDK_COUNTOF(rpc_examine_bdev_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object() failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"spdk_json_decode_object failed");
goto cleanup;
}
rc = spdk_bdev_examine(req.name);
if (rc != 0) {
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
goto cleanup;
}
spdk_jsonrpc_send_bool_response(request, true);
cleanup:
free_rpc_bdev_examine(&req);
}
SPDK_RPC_REGISTER("bdev_examine", rpc_bdev_examine_bdev, SPDK_RPC_RUNTIME)
struct rpc_get_iostat_ctx {
int bdev_count;
int rc;
struct spdk_jsonrpc_request *request;
struct spdk_json_write_ctx *w;
bool per_channel;
};
struct bdev_get_iostat_ctx {
struct spdk_bdev_io_stat *stat;
struct rpc_get_iostat_ctx *rpc_ctx;
struct spdk_bdev_desc *desc;
};
static void
rpc_get_iostat_started(struct rpc_get_iostat_ctx *rpc_ctx, struct spdk_bdev_desc *desc)
{
struct spdk_bdev *bdev;
rpc_ctx->w = spdk_jsonrpc_begin_result(rpc_ctx->request);
spdk_json_write_object_begin(rpc_ctx->w);
spdk_json_write_named_uint64(rpc_ctx->w, "tick_rate", spdk_get_ticks_hz());
spdk_json_write_named_uint64(rpc_ctx->w, "ticks", spdk_get_ticks());
if (rpc_ctx->per_channel == false) {
spdk_json_write_named_array_begin(rpc_ctx->w, "bdevs");
} else {
bdev = spdk_bdev_desc_get_bdev(desc);
spdk_json_write_named_string(rpc_ctx->w, "name", spdk_bdev_get_name(bdev));
spdk_json_write_named_array_begin(rpc_ctx->w, "channels");
}
}
static void
rpc_get_iostat_done(struct rpc_get_iostat_ctx *rpc_ctx)
{
if (--rpc_ctx->bdev_count != 0) {
return;
}
if (rpc_ctx->rc == 0) {
spdk_json_write_array_end(rpc_ctx->w);
spdk_json_write_object_end(rpc_ctx->w);
spdk_jsonrpc_end_result(rpc_ctx->request, rpc_ctx->w);
} else {
/* Return error response after processing all specified bdevs
* completed or failed.
*/
spdk_jsonrpc_send_error_response(rpc_ctx->request, rpc_ctx->rc,
spdk_strerror(-rpc_ctx->rc));
}
free(rpc_ctx);
}
static struct bdev_get_iostat_ctx *
bdev_iostat_ctx_alloc(void)
{
struct bdev_get_iostat_ctx *ctx;
ctx = calloc(1, sizeof(struct bdev_get_iostat_ctx));
if (ctx == NULL) {
return NULL;
}
ctx->stat = bdev_io_stat_alloc();
if (ctx->stat == NULL) {
free(ctx);
return NULL;
}
return ctx;
}
static void
bdev_iostat_ctx_free(struct bdev_get_iostat_ctx *ctx)
{
bdev_io_stat_free(ctx->stat);
free(ctx);
}
static void
bdev_get_iostat_done(struct spdk_bdev *bdev, struct spdk_bdev_io_stat *stat,
void *cb_arg, int rc)
{
struct bdev_get_iostat_ctx *bdev_ctx = cb_arg;
struct rpc_get_iostat_ctx *rpc_ctx = bdev_ctx->rpc_ctx;
struct spdk_json_write_ctx *w = rpc_ctx->w;
if (rc != 0 || rpc_ctx->rc != 0) {
if (rpc_ctx->rc == 0) {
rpc_ctx->rc = rc;
}
goto done;
}
assert(stat == bdev_ctx->stat);
spdk_json_write_object_begin(w);
spdk_json_write_named_string(w, "name", spdk_bdev_get_name(bdev));
bdev_io_stat_dump_json(stat, w);
if (spdk_bdev_get_qd_sampling_period(bdev)) {
spdk_json_write_named_uint64(w, "queue_depth_polling_period",
spdk_bdev_get_qd_sampling_period(bdev));
spdk_json_write_named_uint64(w, "queue_depth", spdk_bdev_get_qd(bdev));
spdk_json_write_named_uint64(w, "io_time", spdk_bdev_get_io_time(bdev));
spdk_json_write_named_uint64(w, "weighted_io_time",
spdk_bdev_get_weighted_io_time(bdev));
}
spdk_json_write_object_end(w);
done:
rpc_get_iostat_done(rpc_ctx);
spdk_bdev_close(bdev_ctx->desc);
bdev_iostat_ctx_free(bdev_ctx);
}
static int
bdev_get_iostat(void *ctx, struct spdk_bdev *bdev)
{
struct rpc_get_iostat_ctx *rpc_ctx = ctx;
struct bdev_get_iostat_ctx *bdev_ctx;
int rc;
bdev_ctx = bdev_iostat_ctx_alloc();
if (bdev_ctx == NULL) {
SPDK_ERRLOG("Failed to allocate bdev_iostat_ctx struct\n");
return -ENOMEM;
}
rc = spdk_bdev_open_ext(spdk_bdev_get_name(bdev), false, dummy_bdev_event_cb, NULL,
&bdev_ctx->desc);
if (rc != 0) {
bdev_iostat_ctx_free(bdev_ctx);
SPDK_ERRLOG("Failed to open bdev\n");
return rc;
}
rpc_ctx->bdev_count++;
bdev_ctx->rpc_ctx = rpc_ctx;
spdk_bdev_get_device_stat(bdev, bdev_ctx->stat, bdev_get_iostat_done, bdev_ctx);
return 0;
}
static void
bdev_get_per_channel_stat_done(struct spdk_bdev *bdev, void *ctx, int status)
{
struct bdev_get_iostat_ctx *bdev_ctx = ctx;
rpc_get_iostat_done(bdev_ctx->rpc_ctx);
spdk_bdev_close(bdev_ctx->desc);
bdev_iostat_ctx_free(bdev_ctx);
}
static void
bdev_get_per_channel_stat(struct spdk_bdev_channel_iter *i, struct spdk_bdev *bdev,
struct spdk_io_channel *ch, void *ctx)
{
struct bdev_get_iostat_ctx *bdev_ctx = ctx;
struct spdk_json_write_ctx *w = bdev_ctx->rpc_ctx->w;
spdk_bdev_get_io_stat(bdev, ch, bdev_ctx->stat);
spdk_json_write_object_begin(w);
spdk_json_write_named_uint64(w, "thread_id", spdk_thread_get_id(spdk_get_thread()));
bdev_io_stat_dump_json(bdev_ctx->stat, w);
spdk_json_write_object_end(w);
spdk_bdev_for_each_channel_continue(i, 0);
}
struct rpc_bdev_get_iostat {
char *name;
bool per_channel;
};
static void
free_rpc_bdev_get_iostat(struct rpc_bdev_get_iostat *r)
{
free(r->name);
}
static const struct spdk_json_object_decoder rpc_bdev_get_iostat_decoders[] = {
{"name", offsetof(struct rpc_bdev_get_iostat, name), spdk_json_decode_string, true},
{"per_channel", offsetof(struct rpc_bdev_get_iostat, per_channel), spdk_json_decode_bool, true},
};
static void
rpc_bdev_get_iostat(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_bdev_get_iostat req = {};
struct spdk_bdev_desc *desc = NULL;
struct rpc_get_iostat_ctx *rpc_ctx;
struct bdev_get_iostat_ctx *bdev_ctx;
int rc;
if (params != NULL) {
if (spdk_json_decode_object(params, rpc_bdev_get_iostat_decoders,
SPDK_COUNTOF(rpc_bdev_get_iostat_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"spdk_json_decode_object failed");
free_rpc_bdev_get_iostat(&req);
return;
}
if (req.per_channel == true && !req.name) {
SPDK_ERRLOG("Bdev name is required for per channel IO statistics\n");
spdk_jsonrpc_send_error_response(request, -EINVAL, spdk_strerror(EINVAL));
free_rpc_bdev_get_iostat(&req);
return;
}
if (req.name) {
rc = spdk_bdev_open_ext(req.name, false, dummy_bdev_event_cb, NULL, &desc);
if (rc != 0) {
SPDK_ERRLOG("Failed to open bdev '%s': %d\n", req.name, rc);
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
free_rpc_bdev_get_iostat(&req);
return;
}
}
}
free_rpc_bdev_get_iostat(&req);
rpc_ctx = calloc(1, sizeof(struct rpc_get_iostat_ctx));
if (rpc_ctx == NULL) {
SPDK_ERRLOG("Failed to allocate rpc_iostat_ctx struct\n");
spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM));
return;
}
/*
* Increment initial bdev_count so that it will never reach 0 in the middle
* of iterating.
*/
rpc_ctx->bdev_count++;
rpc_ctx->request = request;
rpc_ctx->per_channel = req.per_channel;
if (desc != NULL) {
bdev_ctx = bdev_iostat_ctx_alloc();
if (bdev_ctx == NULL) {
SPDK_ERRLOG("Failed to allocate bdev_iostat_ctx struct\n");
rpc_ctx->rc = -ENOMEM;
spdk_bdev_close(desc);
} else {
bdev_ctx->desc = desc;
rpc_ctx->bdev_count++;
bdev_ctx->rpc_ctx = rpc_ctx;
if (req.per_channel == false) {
spdk_bdev_get_device_stat(spdk_bdev_desc_get_bdev(desc), bdev_ctx->stat,
bdev_get_iostat_done, bdev_ctx);
} else {
spdk_bdev_for_each_channel(spdk_bdev_desc_get_bdev(desc),
bdev_get_per_channel_stat,
bdev_ctx,
bdev_get_per_channel_stat_done);
}
}
} else {
rc = spdk_for_each_bdev(rpc_ctx, bdev_get_iostat);
if (rc != 0 && rpc_ctx->rc == 0) {
rpc_ctx->rc = rc;
}
}
if (rpc_ctx->rc == 0) {
/* We want to fail the RPC for all failures. The callback function to
* spdk_bdev_for_each_channel() is executed after stack unwinding if
* successful. Hence defer starting RPC response until it is ensured that
* all spdk_bdev_for_each_channel() calls will succeed or there is no bdev.
*/
rpc_get_iostat_started(rpc_ctx, desc);
}
rpc_get_iostat_done(rpc_ctx);
}
SPDK_RPC_REGISTER("bdev_get_iostat", rpc_bdev_get_iostat, SPDK_RPC_RUNTIME)
struct rpc_reset_iostat_ctx {
int bdev_count;
int rc;
struct spdk_jsonrpc_request *request;
struct spdk_json_write_ctx *w;
enum bdev_reset_stat_mode mode;
};
struct bdev_reset_iostat_ctx {
struct rpc_reset_iostat_ctx *rpc_ctx;
struct spdk_bdev_desc *desc;
};
static void
rpc_reset_iostat_done(struct rpc_reset_iostat_ctx *rpc_ctx)
{
if (--rpc_ctx->bdev_count != 0) {
return;
}
if (rpc_ctx->rc == 0) {
spdk_jsonrpc_send_bool_response(rpc_ctx->request, true);
} else {
spdk_jsonrpc_send_error_response(rpc_ctx->request, rpc_ctx->rc,
spdk_strerror(-rpc_ctx->rc));
}
free(rpc_ctx);
}
static void
bdev_reset_iostat_done(struct spdk_bdev *bdev, void *cb_arg, int rc)
{
struct bdev_reset_iostat_ctx *bdev_ctx = cb_arg;
struct rpc_reset_iostat_ctx *rpc_ctx = bdev_ctx->rpc_ctx;
if (rc != 0 || rpc_ctx->rc != 0) {
if (rpc_ctx->rc == 0) {
rpc_ctx->rc = rc;
}
}
rpc_reset_iostat_done(rpc_ctx);
spdk_bdev_close(bdev_ctx->desc);
free(bdev_ctx);
}
static int
bdev_reset_iostat(void *ctx, struct spdk_bdev *bdev)
{
struct rpc_reset_iostat_ctx *rpc_ctx = ctx;
struct bdev_reset_iostat_ctx *bdev_ctx;
int rc;
bdev_ctx = calloc(1, sizeof(struct bdev_reset_iostat_ctx));
if (bdev_ctx == NULL) {
SPDK_ERRLOG("Failed to allocate bdev_iostat_ctx struct\n");
return -ENOMEM;
}
rc = spdk_bdev_open_ext(spdk_bdev_get_name(bdev), false, dummy_bdev_event_cb, NULL,
&bdev_ctx->desc);
if (rc != 0) {
free(bdev_ctx);
SPDK_ERRLOG("Failed to open bdev\n");
return rc;
}
rpc_ctx->bdev_count++;
bdev_ctx->rpc_ctx = rpc_ctx;
bdev_reset_device_stat(bdev, rpc_ctx->mode, bdev_reset_iostat_done, bdev_ctx);
return 0;
}
struct rpc_bdev_reset_iostat {
char *name;
enum bdev_reset_stat_mode mode;
};
static void
free_rpc_bdev_reset_iostat(struct rpc_bdev_reset_iostat *r)
{
free(r->name);
}
static int
rpc_decode_reset_iostat_mode(const struct spdk_json_val *val, void *out)
{
enum bdev_reset_stat_mode *mode = out;
if (spdk_json_strequal(val, "all") == true) {
*mode = BDEV_RESET_STAT_ALL;
} else if (spdk_json_strequal(val, "maxmin") == true) {
*mode = BDEV_RESET_STAT_MAXMIN;
} else {
SPDK_NOTICELOG("Invalid parameter value: mode\n");
return -EINVAL;
}
return 0;
}
static const struct spdk_json_object_decoder rpc_bdev_reset_iostat_decoders[] = {
{"name", offsetof(struct rpc_bdev_reset_iostat, name), spdk_json_decode_string, true},
{"mode", offsetof(struct rpc_bdev_reset_iostat, mode), rpc_decode_reset_iostat_mode, true},
};
static void
rpc_bdev_reset_iostat(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
struct rpc_bdev_reset_iostat req = { .mode = BDEV_RESET_STAT_ALL, };
struct spdk_bdev_desc *desc = NULL;
struct rpc_reset_iostat_ctx *rpc_ctx;
struct bdev_reset_iostat_ctx *bdev_ctx;
int rc;
if (params != NULL) {
if (spdk_json_decode_object(params, rpc_bdev_reset_iostat_decoders,
SPDK_COUNTOF(rpc_bdev_reset_iostat_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"spdk_json_decode_object failed");
free_rpc_bdev_reset_iostat(&req);
return;
}
if (req.name) {
rc = spdk_bdev_open_ext(req.name, false, dummy_bdev_event_cb, NULL, &desc);
if (rc != 0) {
SPDK_ERRLOG("Failed to open bdev '%s': %d\n", req.name, rc);
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
free_rpc_bdev_reset_iostat(&req);
return;
}
}
}
rpc_ctx = calloc(1, sizeof(struct rpc_reset_iostat_ctx));
if (rpc_ctx == NULL) {
SPDK_ERRLOG("Failed to allocate rpc_iostat_ctx struct\n");
spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM));
free_rpc_bdev_reset_iostat(&req);
return;
}
/*
* Increment initial bdev_count so that it will never reach 0 in the middle
* of iterating.
*/
rpc_ctx->bdev_count++;
rpc_ctx->request = request;
rpc_ctx->mode = req.mode;
free_rpc_bdev_reset_iostat(&req);
if (desc != NULL) {
bdev_ctx = calloc(1, sizeof(struct bdev_reset_iostat_ctx));
if (bdev_ctx == NULL) {
SPDK_ERRLOG("Failed to allocate bdev_iostat_ctx struct\n");
rpc_ctx->rc = -ENOMEM;
spdk_bdev_close(desc);
} else {
bdev_ctx->desc = desc;
rpc_ctx->bdev_count++;
bdev_ctx->rpc_ctx = rpc_ctx;
bdev_reset_device_stat(spdk_bdev_desc_get_bdev(desc), rpc_ctx->mode,
bdev_reset_iostat_done, bdev_ctx);
}
} else {
rc = spdk_for_each_bdev(rpc_ctx, bdev_reset_iostat);
if (rc != 0 && rpc_ctx->rc == 0) {
rpc_ctx->rc = rc;
}
}
rpc_reset_iostat_done(rpc_ctx);
}
SPDK_RPC_REGISTER("bdev_reset_iostat", rpc_bdev_reset_iostat, SPDK_RPC_RUNTIME)
static int
rpc_dump_bdev_info(void *ctx, struct spdk_bdev *bdev)
{
struct spdk_json_write_ctx *w = ctx;
struct spdk_bdev_alias *tmp;
uint64_t qos_limits[SPDK_BDEV_QOS_NUM_RATE_LIMIT_TYPES];
struct spdk_memory_domain **domains;
int i, rc;
spdk_json_write_object_begin(w);
spdk_json_write_named_string(w, "name", spdk_bdev_get_name(bdev));
spdk_json_write_named_array_begin(w, "aliases");
TAILQ_FOREACH(tmp, spdk_bdev_get_aliases(bdev), tailq) {
spdk_json_write_string(w, tmp->alias.name);
}
spdk_json_write_array_end(w);
spdk_json_write_named_string(w, "product_name", spdk_bdev_get_product_name(bdev));
spdk_json_write_named_uint32(w, "block_size", spdk_bdev_get_block_size(bdev));
spdk_json_write_named_uint64(w, "num_blocks", spdk_bdev_get_num_blocks(bdev));
if (!spdk_mem_all_zero(&bdev->uuid, sizeof(bdev->uuid))) {
char uuid_str[SPDK_UUID_STRING_LEN];
spdk_uuid_fmt_lower(uuid_str, sizeof(uuid_str), &bdev->uuid);
spdk_json_write_named_string(w, "uuid", uuid_str);
}
if (spdk_bdev_get_md_size(bdev) != 0) {
spdk_json_write_named_uint32(w, "md_size", spdk_bdev_get_md_size(bdev));
spdk_json_write_named_bool(w, "md_interleave", spdk_bdev_is_md_interleaved(bdev));
spdk_json_write_named_uint32(w, "dif_type", spdk_bdev_get_dif_type(bdev));
if (spdk_bdev_get_dif_type(bdev) != SPDK_DIF_DISABLE) {
spdk_json_write_named_bool(w, "dif_is_head_of_md", spdk_bdev_is_dif_head_of_md(bdev));
spdk_json_write_named_object_begin(w, "enabled_dif_check_types");
spdk_json_write_named_bool(w, "reftag",
spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_REFTAG));
spdk_json_write_named_bool(w, "apptag",
spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_APPTAG));
spdk_json_write_named_bool(w, "guard",
spdk_bdev_is_dif_check_enabled(bdev, SPDK_DIF_CHECK_TYPE_GUARD));
spdk_json_write_object_end(w);
}
}
spdk_json_write_named_object_begin(w, "assigned_rate_limits");
spdk_bdev_get_qos_rate_limits(bdev, qos_limits);
for (i = 0; i < SPDK_BDEV_QOS_NUM_RATE_LIMIT_TYPES; i++) {
spdk_json_write_named_uint64(w, spdk_bdev_get_qos_rpc_type(i), qos_limits[i]);
}
spdk_json_write_object_end(w);
spdk_json_write_named_bool(w, "claimed", (bdev->internal.claim_module != NULL));
spdk_json_write_named_bool(w, "zoned", bdev->zoned);
if (bdev->zoned) {
spdk_json_write_named_uint64(w, "zone_size", bdev->zone_size);
spdk_json_write_named_uint64(w, "max_open_zones", bdev->max_open_zones);
spdk_json_write_named_uint64(w, "optimal_open_zones", bdev->optimal_open_zones);
}
spdk_json_write_named_object_begin(w, "supported_io_types");
spdk_json_write_named_bool(w, "read",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_READ));
spdk_json_write_named_bool(w, "write",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_WRITE));
spdk_json_write_named_bool(w, "unmap",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP));
spdk_json_write_named_bool(w, "write_zeroes",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_WRITE_ZEROES));
spdk_json_write_named_bool(w, "flush",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_FLUSH));
spdk_json_write_named_bool(w, "reset",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_RESET));
spdk_json_write_named_bool(w, "compare",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_COMPARE));
spdk_json_write_named_bool(w, "compare_and_write",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_COMPARE_AND_WRITE));
spdk_json_write_named_bool(w, "abort",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_ABORT));
spdk_json_write_named_bool(w, "nvme_admin",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_NVME_ADMIN));
spdk_json_write_named_bool(w, "nvme_io",
spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_NVME_IO));
spdk_json_write_object_end(w);
rc = spdk_bdev_get_memory_domains(bdev, NULL, 0);
if (rc > 0) {
domains = calloc(rc, sizeof(struct spdk_memory_domain *));
if (domains) {
i = spdk_bdev_get_memory_domains(bdev, domains, rc);
if (i == rc) {
spdk_json_write_named_array_begin(w, "memory_domains");
for (i = 0; i < rc; i++) {
spdk_json_write_object_begin(w);
spdk_json_write_named_string(w, "dma_device_id", spdk_memory_domain_get_dma_device_id(domains[i]));
spdk_json_write_named_int32(w, "dma_device_type",
spdk_memory_domain_get_dma_device_type(domains[i]));
spdk_json_write_object_end(w);
}
spdk_json_write_array_end(w);
} else {
SPDK_ERRLOG("Unexpected number of memory domains %d (should be %d)\n", i, rc);
}
free(domains);
} else {
SPDK_ERRLOG("Memory allocation failed\n");
}
}
spdk_json_write_named_object_begin(w, "driver_specific");
spdk_bdev_dump_info_json(bdev, w);
spdk_json_write_object_end(w);
spdk_json_write_object_end(w);
return 0;
}
struct rpc_bdev_get_bdevs {
char *name;
uint64_t timeout;
};
struct rpc_bdev_get_bdevs_ctx {
struct rpc_bdev_get_bdevs rpc;
struct spdk_jsonrpc_request *request;
struct spdk_poller *poller;
uint64_t timeout_ticks;
};
static void
free_rpc_bdev_get_bdevs(struct rpc_bdev_get_bdevs *r)
{
free(r->name);
}
static const struct spdk_json_object_decoder rpc_bdev_get_bdevs_decoders[] = {
{"name", offsetof(struct rpc_bdev_get_bdevs, name), spdk_json_decode_string, true},
{"timeout", offsetof(struct rpc_bdev_get_bdevs, timeout), spdk_json_decode_uint64, true},
};
static int
get_bdevs_poller(void *_ctx)
{
struct rpc_bdev_get_bdevs_ctx *ctx = _ctx;
struct spdk_json_write_ctx *w;
struct spdk_bdev_desc *desc;
int rc;
rc = spdk_bdev_open_ext(ctx->rpc.name, false, dummy_bdev_event_cb, NULL, &desc);
if (rc != 0 && spdk_get_ticks() < ctx->timeout_ticks) {
return SPDK_POLLER_BUSY;
}
if (rc != 0) {
SPDK_ERRLOG("Timed out while waiting for bdev '%s' to appear\n", ctx->rpc.name);
spdk_jsonrpc_send_error_response(ctx->request, -ENODEV, spdk_strerror(ENODEV));
} else {
w = spdk_jsonrpc_begin_result(ctx->request);
spdk_json_write_array_begin(w);
rpc_dump_bdev_info(w, spdk_bdev_desc_get_bdev(desc));
spdk_json_write_array_end(w);
spdk_jsonrpc_end_result(ctx->request, w);
spdk_bdev_close(desc);
}
spdk_poller_unregister(&ctx->poller);
free_rpc_bdev_get_bdevs(&ctx->rpc);
free(ctx);
return SPDK_POLLER_BUSY;
}
static void
rpc_bdev_get_bdevs(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_bdev_get_bdevs req = {};
struct rpc_bdev_get_bdevs_ctx *ctx;
struct spdk_json_write_ctx *w;
struct spdk_bdev_desc *desc = NULL;
int rc;
if (params && spdk_json_decode_object(params, rpc_bdev_get_bdevs_decoders,
SPDK_COUNTOF(rpc_bdev_get_bdevs_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"spdk_json_decode_object failed");
free_rpc_bdev_get_bdevs(&req);
return;
}
if (req.name) {
rc = spdk_bdev_open_ext(req.name, false, dummy_bdev_event_cb, NULL, &desc);
if (rc != 0) {
if (req.timeout == 0) {
SPDK_ERRLOG("bdev '%s' does not exist\n", req.name);
spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV));
free_rpc_bdev_get_bdevs(&req);
return;
}
ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL) {
SPDK_ERRLOG("Failed to allocate bdev_get_bdevs context\n");
spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM));
free_rpc_bdev_get_bdevs(&req);
return;
}
ctx->poller = SPDK_POLLER_REGISTER(get_bdevs_poller, ctx, 10 * 1000);
if (ctx->poller == NULL) {
SPDK_ERRLOG("Failed to register bdev_get_bdevs poller\n");
spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM));
free_rpc_bdev_get_bdevs(&req);
free(ctx);
return;
}
memcpy(&ctx->rpc, &req, sizeof(req));
ctx->timeout_ticks = spdk_get_ticks() + req.timeout *
spdk_get_ticks_hz() / 1000ull;
ctx->request = request;
return;
}
}
free_rpc_bdev_get_bdevs(&req);
w = spdk_jsonrpc_begin_result(request);
spdk_json_write_array_begin(w);
if (desc != NULL) {
rpc_dump_bdev_info(w, spdk_bdev_desc_get_bdev(desc));
spdk_bdev_close(desc);
} else {
spdk_for_each_bdev(w, rpc_dump_bdev_info);
}
spdk_json_write_array_end(w);
spdk_jsonrpc_end_result(request, w);
}
SPDK_RPC_REGISTER("bdev_get_bdevs", rpc_bdev_get_bdevs, SPDK_RPC_RUNTIME)
struct rpc_bdev_set_qd_sampling_period {
char *name;
uint64_t period;
};
static void
free_rpc_bdev_set_qd_sampling_period(struct rpc_bdev_set_qd_sampling_period *r)
{
free(r->name);
}
static const struct spdk_json_object_decoder
rpc_bdev_set_qd_sampling_period_decoders[] = {
{"name", offsetof(struct rpc_bdev_set_qd_sampling_period, name), spdk_json_decode_string},
{"period", offsetof(struct rpc_bdev_set_qd_sampling_period, period), spdk_json_decode_uint64},
};
static void
rpc_bdev_set_qd_sampling_period(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_bdev_set_qd_sampling_period req = {0};
struct spdk_bdev_desc *desc;
int rc;
if (spdk_json_decode_object(params, rpc_bdev_set_qd_sampling_period_decoders,
SPDK_COUNTOF(rpc_bdev_set_qd_sampling_period_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"spdk_json_decode_object failed");
goto cleanup;
}
rc = spdk_bdev_open_ext(req.name, false, dummy_bdev_event_cb, NULL, &desc);
if (rc != 0) {
SPDK_ERRLOG("Failed to open bdev '%s': %d\n", req.name, rc);
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
goto cleanup;
}
spdk_bdev_set_qd_sampling_period(spdk_bdev_desc_get_bdev(desc), req.period);
spdk_jsonrpc_send_bool_response(request, true);
spdk_bdev_close(desc);
cleanup:
free_rpc_bdev_set_qd_sampling_period(&req);
}
SPDK_RPC_REGISTER("bdev_set_qd_sampling_period",
rpc_bdev_set_qd_sampling_period,
SPDK_RPC_RUNTIME)
struct rpc_bdev_set_qos_limit {
char *name;
uint64_t limits[SPDK_BDEV_QOS_NUM_RATE_LIMIT_TYPES];
};
static void
free_rpc_bdev_set_qos_limit(struct rpc_bdev_set_qos_limit *r)
{
free(r->name);
}
static const struct spdk_json_object_decoder rpc_bdev_set_qos_limit_decoders[] = {
{"name", offsetof(struct rpc_bdev_set_qos_limit, name), spdk_json_decode_string},
{
"rw_ios_per_sec", offsetof(struct rpc_bdev_set_qos_limit,
limits[SPDK_BDEV_QOS_RW_IOPS_RATE_LIMIT]),
spdk_json_decode_uint64, true
},
{
"rw_mbytes_per_sec", offsetof(struct rpc_bdev_set_qos_limit,
limits[SPDK_BDEV_QOS_RW_BPS_RATE_LIMIT]),
spdk_json_decode_uint64, true
},
{
"r_mbytes_per_sec", offsetof(struct rpc_bdev_set_qos_limit,
limits[SPDK_BDEV_QOS_R_BPS_RATE_LIMIT]),
spdk_json_decode_uint64, true
},
{
"w_mbytes_per_sec", offsetof(struct rpc_bdev_set_qos_limit,
limits[SPDK_BDEV_QOS_W_BPS_RATE_LIMIT]),
spdk_json_decode_uint64, true
},
};
static void
rpc_bdev_set_qos_limit_complete(void *cb_arg, int status)
{
struct spdk_jsonrpc_request *request = cb_arg;
if (status != 0) {
spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Failed to configure rate limit: %s",
spdk_strerror(-status));
return;
}
spdk_jsonrpc_send_bool_response(request, true);
}
static void
rpc_bdev_set_qos_limit(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_bdev_set_qos_limit req = {NULL, {UINT64_MAX, UINT64_MAX, UINT64_MAX, UINT64_MAX}};
struct spdk_bdev_desc *desc;
int i, rc;
if (spdk_json_decode_object(params, rpc_bdev_set_qos_limit_decoders,
SPDK_COUNTOF(rpc_bdev_set_qos_limit_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"spdk_json_decode_object failed");
goto cleanup;
}
rc = spdk_bdev_open_ext(req.name, false, dummy_bdev_event_cb, NULL, &desc);
if (rc != 0) {
SPDK_ERRLOG("Failed to open bdev '%s': %d\n", req.name, rc);
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
goto cleanup;
}
for (i = 0; i < SPDK_BDEV_QOS_NUM_RATE_LIMIT_TYPES; i++) {
if (req.limits[i] != UINT64_MAX) {
break;
}
}
if (i == SPDK_BDEV_QOS_NUM_RATE_LIMIT_TYPES) {
SPDK_ERRLOG("no rate limits specified\n");
spdk_bdev_close(desc);
spdk_jsonrpc_send_error_response(request, -EINVAL, "No rate limits specified");
goto cleanup;
}
spdk_bdev_set_qos_rate_limits(spdk_bdev_desc_get_bdev(desc), req.limits,
rpc_bdev_set_qos_limit_complete, request);
spdk_bdev_close(desc);
cleanup:
free_rpc_bdev_set_qos_limit(&req);
}
SPDK_RPC_REGISTER("bdev_set_qos_limit", rpc_bdev_set_qos_limit, SPDK_RPC_RUNTIME)
/* SPDK_RPC_ENABLE_BDEV_HISTOGRAM */
struct rpc_bdev_enable_histogram_request {
char *name;
bool enable;
};
static void
free_rpc_bdev_enable_histogram_request(struct rpc_bdev_enable_histogram_request *r)
{
free(r->name);
}
static const struct spdk_json_object_decoder rpc_bdev_enable_histogram_request_decoders[] = {
{"name", offsetof(struct rpc_bdev_enable_histogram_request, name), spdk_json_decode_string},
{"enable", offsetof(struct rpc_bdev_enable_histogram_request, enable), spdk_json_decode_bool},
};
static void
bdev_histogram_status_cb(void *cb_arg, int status)
{
struct spdk_jsonrpc_request *request = cb_arg;
spdk_jsonrpc_send_bool_response(request, status == 0);
}
static void
rpc_bdev_enable_histogram(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_bdev_enable_histogram_request req = {NULL};
struct spdk_bdev_desc *desc;
int rc;
if (spdk_json_decode_object(params, rpc_bdev_enable_histogram_request_decoders,
SPDK_COUNTOF(rpc_bdev_enable_histogram_request_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"spdk_json_decode_object failed");
goto cleanup;
}
rc = spdk_bdev_open_ext(req.name, false, dummy_bdev_event_cb, NULL, &desc);
if (rc != 0) {
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
goto cleanup;
}
spdk_bdev_histogram_enable(spdk_bdev_desc_get_bdev(desc), bdev_histogram_status_cb,
request, req.enable);
spdk_bdev_close(desc);
cleanup:
free_rpc_bdev_enable_histogram_request(&req);
}
SPDK_RPC_REGISTER("bdev_enable_histogram", rpc_bdev_enable_histogram, SPDK_RPC_RUNTIME)
/* SPDK_RPC_GET_BDEV_HISTOGRAM */
struct rpc_bdev_get_histogram_request {
char *name;
};
static const struct spdk_json_object_decoder rpc_bdev_get_histogram_request_decoders[] = {
{"name", offsetof(struct rpc_bdev_get_histogram_request, name), spdk_json_decode_string}
};
static void
free_rpc_bdev_get_histogram_request(struct rpc_bdev_get_histogram_request *r)
{
free(r->name);
}
static void
_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);
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
rpc_bdev_get_histogram(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_bdev_get_histogram_request req = {NULL};
struct spdk_histogram_data *histogram;
struct spdk_bdev_desc *desc;
int rc;
if (spdk_json_decode_object(params, rpc_bdev_get_histogram_request_decoders,
SPDK_COUNTOF(rpc_bdev_get_histogram_request_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"spdk_json_decode_object failed");
goto cleanup;
}
rc = spdk_bdev_open_ext(req.name, false, dummy_bdev_event_cb, NULL, &desc);
if (rc != 0) {
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
goto cleanup;
}
histogram = spdk_histogram_data_alloc();
if (histogram == NULL) {
spdk_bdev_close(desc);
spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM));
goto cleanup;
}
spdk_bdev_histogram_get(spdk_bdev_desc_get_bdev(desc), histogram,
_rpc_bdev_histogram_data_cb, request);
spdk_bdev_close(desc);
cleanup:
free_rpc_bdev_get_histogram_request(&req);
}
SPDK_RPC_REGISTER("bdev_get_histogram", rpc_bdev_get_histogram, SPDK_RPC_RUNTIME)