Commit fa649869 authored by Konrad Sztyber's avatar Konrad Sztyber Committed by Tomasz Zawadzki
Browse files

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: default avatarKonrad 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: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarShuhei Matsumoto <smatsumoto@nvidia.com>
Reviewed-by: default avatarTomasz Zawadzki <tomasz.zawadzki@intel.com>
parent c0415fed
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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
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

Added `bdev_nvme_add_error_injection` and `bdev_nvme_remove_error_injection` RPCs to add and
+4 −1
Original line number Diff line number Diff line
@@ -1607,11 +1607,14 @@ Get information about block devices (bdevs).
#### Parameters

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 | string      | Block device name
timeout                 | Optional | number      | Time (ms) to wait for a bdev with specified name to appear

#### Response

+69 −4
Original line number Diff line number Diff line
@@ -442,6 +442,14 @@ rpc_dump_bdev_info(struct spdk_json_write_ctx *w,

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
@@ -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[] = {
	{"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
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 *bdev = NULL;

@@ -475,11 +515,36 @@ rpc_bdev_get_bdevs(struct spdk_jsonrpc_request *request,
	if (req.name) {
		bdev = spdk_bdev_get_by_name(req.name);
		if (bdev == NULL) {
			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);
+5 −1
Original line number Diff line number Diff line
@@ -1003,11 +1003,15 @@ if __name__ == "__main__":

    def bdev_get_bdevs(args):
        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'],
                              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('-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)

    def bdev_get_iostat(args):
+4 −1
Original line number Diff line number Diff line
@@ -1281,11 +1281,12 @@ def bdev_ftl_delete(client, name):


@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.

    Args:
        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:
        List of bdev information objects.
@@ -1293,6 +1294,8 @@ def bdev_get_bdevs(client, name=None):
    params = {}
    if name:
        params['name'] = name
    if timeout:
        params['timeout'] = timeout
    return client.call('bdev_get_bdevs', params)