bdev: add timeout option to bdev_get_bdevs RPC

This opption allows the bdev_get_bdevs RPC to block until a bdev with
specified name appears.  It can be useful, when a bdev is created
asynchronously and the exact moment at which it appears is not known.
For instance, with a discovery service, a bdev is created when a
namespace on a remote NVMeoF target is added, but it's not possible to
specify when that happens exactly.

Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: I6c1f974fba445376ca9d45aac2639202547410cc
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/11960
Community-CI: Mellanox Build Bot
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Shuhei Matsumoto <smatsumoto@nvidia.com>
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
This commit is contained in:
Konrad Sztyber 2022-03-16 12:51:50 +01:00 committed by Tomasz Zawadzki
parent c0415feda3
commit fa649869b9
5 changed files with 87 additions and 7 deletions

View File

@ -34,6 +34,11 @@ bdev_crypto_create RPC now requires hexlified 'key' and 'key2' params for all pm
Unhexlifying is performed during RPC command processing and the vbdev crypto module runs on Unhexlifying is performed during RPC command processing and the vbdev crypto module runs on
binary keys as before. binary keys as before.
### bdev
Added a timeout option to the `bdev_get_bdevs` RPC. It allows the user to specify the amount of
time to wait until a bdev with a given name appears in the system.
### bdev_nvme ### bdev_nvme
Added `bdev_nvme_add_error_injection` and `bdev_nvme_remove_error_injection` RPCs to add and Added `bdev_nvme_add_error_injection` and `bdev_nvme_remove_error_injection` RPCs to add and

View File

@ -1607,11 +1607,14 @@ Get information about block devices (bdevs).
#### Parameters #### Parameters
The user may specify no parameters in order to list all block devices, or a block device may be The user may specify no parameters in order to list all block devices, or a block device may be
specified by name. specified by name. If a timeout is specified, the method will block until a bdev with a specified
name appears or the timeout expires. By default, the timeout is zero, meaning the method returns
immediately whether the bdev exists or not.
Name | Optional | Type | Description Name | Optional | Type | Description
----------------------- | -------- | ----------- | ----------- ----------------------- | -------- | ----------- | -----------
name | Optional | string | Block device name name | Optional | string | Block device name
timeout | Optional | number | Time (ms) to wait for a bdev with specified name to appear
#### Response #### Response

View File

@ -442,6 +442,14 @@ rpc_dump_bdev_info(struct spdk_json_write_ctx *w,
struct rpc_bdev_get_bdevs { struct rpc_bdev_get_bdevs {
char *name; 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 static void
@ -452,13 +460,45 @@ free_rpc_bdev_get_bdevs(struct rpc_bdev_get_bdevs *r)
static const struct spdk_json_object_decoder rpc_bdev_get_bdevs_decoders[] = { 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}, {"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 *bdev;
bdev = spdk_bdev_get_by_name(ctx->rpc.name);
if (bdev == NULL && spdk_get_ticks() < ctx->timeout_ticks) {
return SPDK_POLLER_BUSY;
}
if (bdev == NULL) {
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, bdev);
spdk_json_write_array_end(w);
spdk_jsonrpc_end_result(ctx->request, w);
}
spdk_poller_unregister(&ctx->poller);
free_rpc_bdev_get_bdevs(&ctx->rpc);
free(ctx);
return SPDK_POLLER_BUSY;
}
static void static void
rpc_bdev_get_bdevs(struct spdk_jsonrpc_request *request, rpc_bdev_get_bdevs(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params) const struct spdk_json_val *params)
{ {
struct rpc_bdev_get_bdevs req = {}; struct rpc_bdev_get_bdevs req = {};
struct rpc_bdev_get_bdevs_ctx *ctx;
struct spdk_json_write_ctx *w; struct spdk_json_write_ctx *w;
struct spdk_bdev *bdev = NULL; struct spdk_bdev *bdev = NULL;
@ -475,11 +515,36 @@ rpc_bdev_get_bdevs(struct spdk_jsonrpc_request *request,
if (req.name) { if (req.name) {
bdev = spdk_bdev_get_by_name(req.name); bdev = spdk_bdev_get_by_name(req.name);
if (bdev == NULL) { if (bdev == NULL) {
if (req.timeout == 0) {
SPDK_ERRLOG("bdev '%s' does not exist\n", req.name); SPDK_ERRLOG("bdev '%s' does not exist\n", req.name);
spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV));
free_rpc_bdev_get_bdevs(&req); free_rpc_bdev_get_bdevs(&req);
return; 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); free_rpc_bdev_get_bdevs(&req);

View File

@ -1003,11 +1003,15 @@ if __name__ == "__main__":
def bdev_get_bdevs(args): def bdev_get_bdevs(args):
print_dict(rpc.bdev.bdev_get_bdevs(args.client, print_dict(rpc.bdev.bdev_get_bdevs(args.client,
name=args.name)) name=args.name, timeout=args.timeout_ms))
p = subparsers.add_parser('bdev_get_bdevs', aliases=['get_bdevs'], p = subparsers.add_parser('bdev_get_bdevs', aliases=['get_bdevs'],
help='Display current blockdev list or required blockdev') help='Display current blockdev list or required blockdev')
p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False) p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
p.add_argument('-t', '--timeout-ms', help="""Time in ms to wait for the bdev to appear (only used
with the -b|--name option). The default timeout is 0, meaning the RPC returns immediately
whether the bdev exists or not.""",
type=int, required=False)
p.set_defaults(func=bdev_get_bdevs) p.set_defaults(func=bdev_get_bdevs)
def bdev_get_iostat(args): def bdev_get_iostat(args):

View File

@ -1281,11 +1281,12 @@ def bdev_ftl_delete(client, name):
@deprecated_alias('get_bdevs') @deprecated_alias('get_bdevs')
def bdev_get_bdevs(client, name=None): def bdev_get_bdevs(client, name=None, timeout=None):
"""Get information about block devices. """Get information about block devices.
Args: Args:
name: bdev name to query (optional; if omitted, query all bdevs) name: bdev name to query (optional; if omitted, query all bdevs)
timeout: time in ms to wait for the bdev with specified name to appear
Returns: Returns:
List of bdev information objects. List of bdev information objects.
@ -1293,6 +1294,8 @@ def bdev_get_bdevs(client, name=None):
params = {} params = {}
if name: if name:
params['name'] = name params['name'] = name
if timeout:
params['timeout'] = timeout
return client.call('bdev_get_bdevs', params) return client.call('bdev_get_bdevs', params)