summaryrefslogtreecommitdiffstats
path: root/src/spdk/lib/bdev/lvol
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
commit483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch)
treee5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/spdk/lib/bdev/lvol
parentInitial commit. (diff)
downloadceph-upstream.tar.xz
ceph-upstream.zip
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/lib/bdev/lvol')
-rw-r--r--src/spdk/lib/bdev/lvol/Makefile41
-rw-r--r--src/spdk/lib/bdev/lvol/vbdev_lvol.c1321
-rw-r--r--src/spdk/lib/bdev/lvol/vbdev_lvol.h120
-rw-r--r--src/spdk/lib/bdev/lvol/vbdev_lvol_rpc.c1089
4 files changed, 2571 insertions, 0 deletions
diff --git a/src/spdk/lib/bdev/lvol/Makefile b/src/spdk/lib/bdev/lvol/Makefile
new file mode 100644
index 00000000..569b14cf
--- /dev/null
+++ b/src/spdk/lib/bdev/lvol/Makefile
@@ -0,0 +1,41 @@
+#
+# BSD LICENSE
+#
+# Copyright (c) Intel Corporation.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..)
+include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
+
+C_SRCS = vbdev_lvol.c vbdev_lvol_rpc.c
+LIBNAME = vbdev_lvol
+LOCAL_SYS_LIBS = -luuid
+
+include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk
diff --git a/src/spdk/lib/bdev/lvol/vbdev_lvol.c b/src/spdk/lib/bdev/lvol/vbdev_lvol.c
new file mode 100644
index 00000000..74df81e4
--- /dev/null
+++ b/src/spdk/lib/bdev/lvol/vbdev_lvol.c
@@ -0,0 +1,1321 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/blob_bdev.h"
+#include "spdk/rpc.h"
+#include "spdk/bdev_module.h"
+#include "spdk_internal/log.h"
+#include "spdk/string.h"
+#include "spdk/uuid.h"
+
+#include "vbdev_lvol.h"
+
+static TAILQ_HEAD(, lvol_store_bdev) g_spdk_lvol_pairs = TAILQ_HEAD_INITIALIZER(
+ g_spdk_lvol_pairs);
+
+static int vbdev_lvs_init(void);
+static int vbdev_lvs_get_ctx_size(void);
+static void vbdev_lvs_examine(struct spdk_bdev *bdev);
+
+static struct spdk_bdev_module g_lvol_if = {
+ .name = "lvol",
+ .module_init = vbdev_lvs_init,
+ .examine_disk = vbdev_lvs_examine,
+ .get_ctx_size = vbdev_lvs_get_ctx_size,
+
+};
+
+SPDK_BDEV_MODULE_REGISTER(&g_lvol_if)
+
+struct lvol_store_bdev *
+vbdev_get_lvs_bdev_by_lvs(struct spdk_lvol_store *lvs_orig)
+{
+ struct spdk_lvol_store *lvs = NULL;
+ struct lvol_store_bdev *lvs_bdev = vbdev_lvol_store_first();
+
+ while (lvs_bdev != NULL) {
+ lvs = lvs_bdev->lvs;
+ if (lvs == lvs_orig) {
+ if (lvs_bdev->req != NULL) {
+ /* We do not allow access to lvs that are being destroyed */
+ return NULL;
+ } else {
+ return lvs_bdev;
+ }
+ }
+ lvs_bdev = vbdev_lvol_store_next(lvs_bdev);
+ }
+
+ return NULL;
+}
+
+static int
+_vbdev_lvol_change_bdev_alias(struct spdk_lvol *lvol, const char *new_lvol_name)
+{
+ struct spdk_bdev_alias *tmp;
+ char *old_alias;
+ char *alias;
+ int rc;
+ int alias_number = 0;
+
+ /* bdev representing lvols have only one alias,
+ * while we changed lvs name earlier, we have to iterate alias list to get one,
+ * and check if there is only one alias */
+
+ TAILQ_FOREACH(tmp, &lvol->bdev->aliases, tailq) {
+ if (++alias_number > 1) {
+ SPDK_ERRLOG("There is more than 1 alias in bdev %s\n", lvol->bdev->name);
+ return -EINVAL;
+ }
+
+ old_alias = tmp->alias;
+ }
+
+ if (alias_number == 0) {
+ SPDK_ERRLOG("There are no aliases in bdev %s\n", lvol->bdev->name);
+ return -EINVAL;
+ }
+
+ alias = spdk_sprintf_alloc("%s/%s", lvol->lvol_store->name, new_lvol_name);
+ if (alias == NULL) {
+ SPDK_ERRLOG("Cannot alloc memory for alias\n");
+ return -ENOMEM;
+ }
+
+ rc = spdk_bdev_alias_add(lvol->bdev, alias);
+ if (rc != 0) {
+ SPDK_ERRLOG("cannot add alias '%s'\n", alias);
+ free(alias);
+ return rc;
+ }
+ free(alias);
+
+ rc = spdk_bdev_alias_del(lvol->bdev, old_alias);
+ if (rc != 0) {
+ SPDK_ERRLOG("cannot remove alias '%s'\n", old_alias);
+ return rc;
+ }
+
+ return 0;
+}
+
+static struct lvol_store_bdev *
+vbdev_get_lvs_bdev_by_bdev(struct spdk_bdev *bdev_orig)
+{
+ struct lvol_store_bdev *lvs_bdev = vbdev_lvol_store_first();
+
+ while (lvs_bdev != NULL) {
+ if (lvs_bdev->bdev == bdev_orig) {
+ if (lvs_bdev->req != NULL) {
+ /* We do not allow access to lvs that are being destroyed */
+ return NULL;
+ } else {
+ return lvs_bdev;
+ }
+ }
+ lvs_bdev = vbdev_lvol_store_next(lvs_bdev);
+ }
+
+ return NULL;
+}
+
+static void
+vbdev_lvs_hotremove_cb(void *ctx)
+{
+ struct spdk_bdev *bdev = ctx;
+ struct lvol_store_bdev *lvs_bdev;
+
+ lvs_bdev = vbdev_get_lvs_bdev_by_bdev(bdev);
+ if (lvs_bdev != NULL) {
+ vbdev_lvs_unload(lvs_bdev->lvs, NULL, NULL);
+ }
+}
+
+static void
+_vbdev_lvs_create_cb(void *cb_arg, struct spdk_lvol_store *lvs, int lvserrno)
+{
+ struct spdk_lvs_with_handle_req *req = cb_arg;
+ struct lvol_store_bdev *lvs_bdev;
+ struct spdk_bdev *bdev = req->base_bdev;
+ struct spdk_bs_dev *bs_dev = req->bs_dev;
+
+ if (lvserrno != 0) {
+ assert(lvs == NULL);
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Cannot create lvol store bdev\n");
+ goto end;
+ }
+
+ lvserrno = spdk_bs_bdev_claim(bs_dev, &g_lvol_if);
+ if (lvserrno != 0) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Lvol store base bdev already claimed by another bdev\n");
+ req->bs_dev->destroy(req->bs_dev);
+ goto end;
+ }
+
+ assert(lvs != NULL);
+
+ lvs_bdev = calloc(1, sizeof(*lvs_bdev));
+ if (!lvs_bdev) {
+ lvserrno = -ENOMEM;
+ goto end;
+ }
+ lvs_bdev->lvs = lvs;
+ lvs_bdev->bdev = bdev;
+ lvs_bdev->req = NULL;
+
+ TAILQ_INSERT_TAIL(&g_spdk_lvol_pairs, lvs_bdev, lvol_stores);
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Lvol store bdev inserted\n");
+
+end:
+ req->cb_fn(req->cb_arg, lvs, lvserrno);
+ free(req);
+
+ return;
+}
+
+int
+vbdev_lvs_create(struct spdk_bdev *base_bdev, const char *name, uint32_t cluster_sz,
+ spdk_lvs_op_with_handle_complete cb_fn, void *cb_arg)
+{
+ struct spdk_bs_dev *bs_dev;
+ struct spdk_lvs_with_handle_req *lvs_req;
+ struct spdk_lvs_opts opts;
+ int rc;
+ int len;
+
+ if (base_bdev == NULL) {
+ SPDK_ERRLOG("Bdev does not exist\n");
+ return -ENODEV;
+ }
+
+ spdk_lvs_opts_init(&opts);
+ if (cluster_sz != 0) {
+ opts.cluster_sz = cluster_sz;
+ }
+
+ if (name == NULL) {
+ SPDK_ERRLOG("missing name param\n");
+ return -EINVAL;
+ }
+
+ len = strnlen(name, SPDK_LVS_NAME_MAX);
+
+ if (len == 0 || len == SPDK_LVS_NAME_MAX) {
+ SPDK_ERRLOG("name must be between 1 and %d characters\n", SPDK_LVS_NAME_MAX - 1);
+ return -EINVAL;
+ }
+ snprintf(opts.name, sizeof(opts.name), "%s", name);
+
+ lvs_req = calloc(1, sizeof(*lvs_req));
+ if (!lvs_req) {
+ SPDK_ERRLOG("Cannot alloc memory for vbdev lvol store request pointer\n");
+ return -ENOMEM;
+ }
+
+ bs_dev = spdk_bdev_create_bs_dev(base_bdev, vbdev_lvs_hotremove_cb, base_bdev);
+ if (!bs_dev) {
+ SPDK_ERRLOG("Cannot create blobstore device\n");
+ free(lvs_req);
+ return -ENODEV;
+ }
+
+ lvs_req->bs_dev = bs_dev;
+ lvs_req->base_bdev = base_bdev;
+ lvs_req->cb_fn = cb_fn;
+ lvs_req->cb_arg = cb_arg;
+
+ rc = spdk_lvs_init(bs_dev, &opts, _vbdev_lvs_create_cb, lvs_req);
+ if (rc < 0) {
+ free(lvs_req);
+ bs_dev->destroy(bs_dev);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void
+_vbdev_lvs_rename_cb(void *cb_arg, int lvserrno)
+{
+ struct spdk_lvs_req *req = cb_arg;
+ struct spdk_lvol *tmp;
+
+ if (lvserrno != 0) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Lvol store rename failed\n");
+ } else {
+ TAILQ_FOREACH(tmp, &req->lvol_store->lvols, link) {
+ /* We have to pass current lvol name, since only lvs name changed */
+ _vbdev_lvol_change_bdev_alias(tmp, tmp->name);
+ }
+ }
+
+ req->cb_fn(req->cb_arg, lvserrno);
+ free(req);
+}
+
+void
+vbdev_lvs_rename(struct spdk_lvol_store *lvs, const char *new_lvs_name,
+ spdk_lvs_op_complete cb_fn, void *cb_arg)
+{
+ struct lvol_store_bdev *lvs_bdev;
+
+ struct spdk_lvs_req *req;
+
+ lvs_bdev = vbdev_get_lvs_bdev_by_lvs(lvs);
+ if (!lvs_bdev) {
+ SPDK_ERRLOG("No such lvol store found\n");
+ cb_fn(cb_arg, -ENODEV);
+ return;
+ }
+
+ req = calloc(1, sizeof(*req));
+ if (!req) {
+ SPDK_ERRLOG("Cannot alloc memory for vbdev lvol store request pointer\n");
+ cb_fn(cb_arg, -ENOMEM);
+ return;
+ }
+ req->cb_fn = cb_fn;
+ req->cb_arg = cb_arg;
+ req->lvol_store = lvs;
+
+ spdk_lvs_rename(lvs, new_lvs_name, _vbdev_lvs_rename_cb, req);
+}
+
+static void
+_vbdev_lvs_remove_cb(void *cb_arg, int lvserrno)
+{
+ struct lvol_store_bdev *lvs_bdev = cb_arg;
+ struct spdk_lvs_req *req = lvs_bdev->req;
+
+ if (lvserrno != 0) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Could not remove lvol store bdev\n");
+ } else {
+ TAILQ_REMOVE(&g_spdk_lvol_pairs, lvs_bdev, lvol_stores);
+ free(lvs_bdev);
+ }
+
+ if (req->cb_fn != NULL) {
+ req->cb_fn(req->cb_arg, lvserrno);
+ }
+ free(req);
+}
+
+static void
+_vbdev_lvs_remove_lvol_cb(void *cb_arg, int lvolerrno)
+{
+ struct lvol_store_bdev *lvs_bdev = cb_arg;
+ struct spdk_lvol_store *lvs = lvs_bdev->lvs;
+ struct spdk_lvol *lvol;
+
+ if (lvolerrno != 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_VBDEV_LVOL, "Lvol removed with errno %d\n", lvolerrno);
+ }
+
+ if (TAILQ_EMPTY(&lvs->lvols)) {
+ spdk_lvs_destroy(lvs, _vbdev_lvs_remove_cb, lvs_bdev);
+ return;
+ }
+
+ lvol = TAILQ_FIRST(&lvs->lvols);
+ while (lvol != NULL) {
+ if (spdk_lvol_deletable(lvol)) {
+ vbdev_lvol_destroy(lvol, _vbdev_lvs_remove_lvol_cb, lvs_bdev);
+ return;
+ }
+ lvol = TAILQ_NEXT(lvol, link);
+ }
+
+ /* If no lvol is deletable, that means there is circular dependency. */
+ SPDK_ERRLOG("Lvols left in lvs, but unable to delete.\n");
+ assert(false);
+}
+
+static void
+_vbdev_lvs_remove_bdev_unregistered_cb(void *cb_arg, int bdeverrno)
+{
+ struct lvol_store_bdev *lvs_bdev = cb_arg;
+ struct spdk_lvol_store *lvs = lvs_bdev->lvs;
+ struct spdk_lvol *lvol, *tmp;
+
+ if (bdeverrno != 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_VBDEV_LVOL, "Lvol unregistered with errno %d\n", bdeverrno);
+ }
+
+ TAILQ_FOREACH_SAFE(lvol, &lvs->lvols, link, tmp) {
+ if (lvol->ref_count != 0) {
+ /* An lvol is still open, don't unload whole lvol store. */
+ return;
+ }
+ }
+ spdk_lvs_unload(lvs, _vbdev_lvs_remove_cb, lvs_bdev);
+}
+
+static void
+_vbdev_lvs_remove(struct spdk_lvol_store *lvs, spdk_lvs_op_complete cb_fn, void *cb_arg,
+ bool destroy)
+{
+ struct spdk_lvs_req *req;
+ struct lvol_store_bdev *lvs_bdev;
+ struct spdk_lvol *lvol, *tmp;
+ bool all_lvols_closed = true;
+
+ lvs_bdev = vbdev_get_lvs_bdev_by_lvs(lvs);
+ if (!lvs_bdev) {
+ SPDK_ERRLOG("No such lvol store found\n");
+ if (cb_fn != NULL) {
+ cb_fn(cb_arg, -ENODEV);
+ }
+ return;
+ }
+
+ req = calloc(1, sizeof(*req));
+ if (!req) {
+ SPDK_ERRLOG("Cannot alloc memory for vbdev lvol store request pointer\n");
+ if (cb_fn != NULL) {
+ cb_fn(cb_arg, -ENOMEM);
+ }
+ return;
+ }
+
+ req->cb_fn = cb_fn;
+ req->cb_arg = cb_arg;
+ lvs_bdev->req = req;
+
+ TAILQ_FOREACH_SAFE(lvol, &lvs->lvols, link, tmp) {
+ if (lvol->ref_count != 0) {
+ all_lvols_closed = false;
+ }
+ }
+
+ if (all_lvols_closed == true) {
+ if (destroy) {
+ spdk_lvs_destroy(lvs, _vbdev_lvs_remove_cb, lvs_bdev);
+ } else {
+ spdk_lvs_unload(lvs, _vbdev_lvs_remove_cb, lvs_bdev);
+ }
+ } else {
+ lvs->destruct = destroy;
+ if (destroy) {
+ _vbdev_lvs_remove_lvol_cb(lvs_bdev, 0);
+ } else {
+ TAILQ_FOREACH_SAFE(lvol, &lvs->lvols, link, tmp) {
+ spdk_bdev_unregister(lvol->bdev, _vbdev_lvs_remove_bdev_unregistered_cb, lvs_bdev);
+ }
+ }
+ }
+}
+
+void
+vbdev_lvs_unload(struct spdk_lvol_store *lvs, spdk_lvs_op_complete cb_fn, void *cb_arg)
+{
+ _vbdev_lvs_remove(lvs, cb_fn, cb_arg, false);
+}
+
+void
+vbdev_lvs_destruct(struct spdk_lvol_store *lvs, spdk_lvs_op_complete cb_fn, void *cb_arg)
+{
+ _vbdev_lvs_remove(lvs, cb_fn, cb_arg, true);
+}
+
+struct lvol_store_bdev *
+vbdev_lvol_store_first(void)
+{
+ struct lvol_store_bdev *lvs_bdev;
+
+ lvs_bdev = TAILQ_FIRST(&g_spdk_lvol_pairs);
+ if (lvs_bdev) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Starting lvolstore iteration at %p\n", lvs_bdev->lvs);
+ }
+
+ return lvs_bdev;
+}
+
+struct lvol_store_bdev *
+vbdev_lvol_store_next(struct lvol_store_bdev *prev)
+{
+ struct lvol_store_bdev *lvs_bdev;
+
+ if (prev == NULL) {
+ SPDK_ERRLOG("prev argument cannot be NULL\n");
+ return NULL;
+ }
+
+ lvs_bdev = TAILQ_NEXT(prev, lvol_stores);
+ if (lvs_bdev) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Continuing lvolstore iteration at %p\n", lvs_bdev->lvs);
+ }
+
+ return lvs_bdev;
+}
+
+static struct spdk_lvol_store *
+_vbdev_get_lvol_store_by_uuid(const struct spdk_uuid *uuid)
+{
+ struct spdk_lvol_store *lvs = NULL;
+ struct lvol_store_bdev *lvs_bdev = vbdev_lvol_store_first();
+
+ while (lvs_bdev != NULL) {
+ lvs = lvs_bdev->lvs;
+ if (spdk_uuid_compare(&lvs->uuid, uuid) == 0) {
+ return lvs;
+ }
+ lvs_bdev = vbdev_lvol_store_next(lvs_bdev);
+ }
+ return NULL;
+}
+
+struct spdk_lvol_store *
+vbdev_get_lvol_store_by_uuid(const char *uuid_str)
+{
+ struct spdk_uuid uuid;
+
+ if (spdk_uuid_parse(&uuid, uuid_str)) {
+ return NULL;
+ }
+
+ return _vbdev_get_lvol_store_by_uuid(&uuid);
+}
+
+struct spdk_lvol_store *
+vbdev_get_lvol_store_by_name(const char *name)
+{
+ struct spdk_lvol_store *lvs = NULL;
+ struct lvol_store_bdev *lvs_bdev = vbdev_lvol_store_first();
+
+ while (lvs_bdev != NULL) {
+ lvs = lvs_bdev->lvs;
+ if (strncmp(lvs->name, name, sizeof(lvs->name)) == 0) {
+ return lvs;
+ }
+ lvs_bdev = vbdev_lvol_store_next(lvs_bdev);
+ }
+ return NULL;
+}
+
+struct vbdev_lvol_destroy_ctx {
+ struct spdk_lvol *lvol;
+ spdk_lvol_op_complete cb_fn;
+ void *cb_arg;
+};
+
+static void
+_vbdev_lvol_unregister_cb(void *ctx, int lvolerrno)
+{
+ struct spdk_bdev *bdev = ctx;
+
+ spdk_bdev_destruct_done(bdev, lvolerrno);
+ free(bdev);
+}
+
+static int
+vbdev_lvol_unregister(void *ctx)
+{
+ struct spdk_lvol *lvol = ctx;
+
+ assert(lvol != NULL);
+
+ spdk_bdev_alias_del_all(lvol->bdev);
+ spdk_lvol_close(lvol, _vbdev_lvol_unregister_cb, lvol->bdev);
+
+ /* return 1 to indicate we have an operation that must finish asynchronously before the
+ * lvol is closed
+ */
+ return 1;
+}
+
+static void
+_vbdev_lvol_destroy_cb(void *cb_arg, int bdeverrno)
+{
+ struct vbdev_lvol_destroy_ctx *ctx = cb_arg;
+ struct spdk_lvol *lvol = ctx->lvol;
+
+ if (bdeverrno < 0) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Could not unregister bdev during lvol (%s) destroy\n",
+ lvol->unique_id);
+ ctx->cb_fn(ctx->cb_arg, bdeverrno);
+ free(ctx);
+ return;
+ }
+
+ spdk_lvol_destroy(lvol, ctx->cb_fn, ctx->cb_arg);
+ free(ctx);
+}
+
+void
+vbdev_lvol_destroy(struct spdk_lvol *lvol, spdk_lvol_op_complete cb_fn, void *cb_arg)
+{
+ struct vbdev_lvol_destroy_ctx *ctx;
+
+ assert(lvol != NULL);
+ assert(cb_fn != NULL);
+
+ /* Check if it is possible to delete lvol */
+ if (spdk_lvol_deletable(lvol) == false) {
+ /* throw an error */
+ SPDK_ERRLOG("Cannot delete lvol\n");
+ cb_fn(cb_arg, -EPERM);
+ return;
+ }
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ cb_fn(cb_arg, -ENOMEM);
+ return;
+ }
+
+ ctx->lvol = lvol;
+ ctx->cb_fn = cb_fn;
+ ctx->cb_arg = cb_arg;
+
+ spdk_bdev_unregister(lvol->bdev, _vbdev_lvol_destroy_cb, ctx);
+}
+
+static char *
+vbdev_lvol_find_name(struct spdk_lvol *lvol, spdk_blob_id blob_id)
+{
+ struct spdk_lvol_store *lvs;
+ struct spdk_lvol *_lvol;
+
+ assert(lvol != NULL);
+
+ lvs = lvol->lvol_store;
+
+ assert(lvs);
+
+ TAILQ_FOREACH(_lvol, &lvs->lvols, link) {
+ if (_lvol->blob_id == blob_id) {
+ return _lvol->name;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+vbdev_lvol_dump_info_json(void *ctx, struct spdk_json_write_ctx *w)
+{
+ struct spdk_lvol *lvol = ctx;
+ struct lvol_store_bdev *lvs_bdev;
+ struct spdk_bdev *bdev;
+ struct spdk_blob *blob;
+ char lvol_store_uuid[SPDK_UUID_STRING_LEN];
+ spdk_blob_id *ids = NULL;
+ size_t count, i;
+ char *name;
+ int rc = 0;
+
+ spdk_json_write_name(w, "lvol");
+ spdk_json_write_object_begin(w);
+
+ lvs_bdev = vbdev_get_lvs_bdev_by_lvs(lvol->lvol_store);
+ if (!lvs_bdev) {
+ SPDK_ERRLOG("No such lvol store found\n");
+ rc = -ENODEV;
+ goto end;
+ }
+
+ bdev = lvs_bdev->bdev;
+
+ spdk_uuid_fmt_lower(lvol_store_uuid, sizeof(lvol_store_uuid), &lvol->lvol_store->uuid);
+ spdk_json_write_name(w, "lvol_store_uuid");
+ spdk_json_write_string(w, lvol_store_uuid);
+
+ spdk_json_write_name(w, "base_bdev");
+ spdk_json_write_string(w, spdk_bdev_get_name(bdev));
+
+ blob = lvol->blob;
+
+ spdk_json_write_name(w, "thin_provision");
+ spdk_json_write_bool(w, spdk_blob_is_thin_provisioned(blob));
+
+ spdk_json_write_name(w, "snapshot");
+ spdk_json_write_bool(w, spdk_blob_is_snapshot(blob));
+
+ spdk_json_write_name(w, "clone");
+ spdk_json_write_bool(w, spdk_blob_is_clone(blob));
+
+ if (spdk_blob_is_clone(blob)) {
+ spdk_blob_id snapshotid = spdk_blob_get_parent_snapshot(lvol->lvol_store->blobstore, lvol->blob_id);
+ if (snapshotid != SPDK_BLOBID_INVALID) {
+ name = vbdev_lvol_find_name(lvol, snapshotid);
+ if (name != NULL) {
+ spdk_json_write_name(w, "base_snapshot");
+ spdk_json_write_string(w, name);
+ } else {
+ SPDK_ERRLOG("Cannot obtain snapshots name\n");
+ }
+ }
+ }
+
+ if (spdk_blob_is_snapshot(blob)) {
+ /* Take a number of clones */
+ rc = spdk_blob_get_clones(lvol->lvol_store->blobstore, lvol->blob_id, NULL, &count);
+ if (rc == -ENOMEM && count > 0) {
+ ids = malloc(sizeof(spdk_blob_id) * count);
+ if (ids == NULL) {
+ SPDK_ERRLOG("Cannot allocate memory\n");
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ rc = spdk_blob_get_clones(lvol->lvol_store->blobstore, lvol->blob_id, ids, &count);
+ if (rc == 0) {
+ spdk_json_write_name(w, "clones");
+ spdk_json_write_array_begin(w);
+ for (i = 0; i < count; i++) {
+ name = vbdev_lvol_find_name(lvol, ids[i]);
+ if (name != NULL) {
+ spdk_json_write_string(w, name);
+ } else {
+ SPDK_ERRLOG("Cannot obtain clone name\n");
+ }
+
+ }
+ spdk_json_write_array_end(w);
+ }
+ free(ids);
+ }
+
+ }
+
+end:
+ spdk_json_write_object_end(w);
+
+ return rc;
+}
+
+static void
+vbdev_lvol_write_config_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w)
+{
+ /* Nothing to dump as lvol configuration is saved on physical device. */
+}
+
+static struct spdk_io_channel *
+vbdev_lvol_get_io_channel(void *ctx)
+{
+ struct spdk_lvol *lvol = ctx;
+
+ return spdk_lvol_get_io_channel(lvol);
+}
+
+static bool
+vbdev_lvol_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
+{
+ struct spdk_lvol *lvol = ctx;
+
+ switch (io_type) {
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
+ return !spdk_blob_is_read_only(lvol->blob);
+ case SPDK_BDEV_IO_TYPE_RESET:
+ case SPDK_BDEV_IO_TYPE_READ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void
+lvol_op_comp(void *cb_arg, int bserrno)
+{
+ struct lvol_task *task = cb_arg;
+ struct spdk_bdev_io *bdev_io = spdk_bdev_io_from_ctx(task);
+
+ if (bserrno != 0) {
+ if (bserrno == -ENOMEM) {
+ task->status = SPDK_BDEV_IO_STATUS_NOMEM;
+ } else {
+ task->status = SPDK_BDEV_IO_STATUS_FAILED;
+ }
+ }
+
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Vbdev processing callback on device %s with type %d\n",
+ bdev_io->bdev->name, bdev_io->type);
+ spdk_bdev_io_complete(bdev_io, task->status);
+}
+
+static void
+lvol_unmap(struct spdk_lvol *lvol, struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ uint64_t start_page, num_pages;
+ struct spdk_blob *blob = lvol->blob;
+ struct lvol_task *task = (struct lvol_task *)bdev_io->driver_ctx;
+
+ start_page = bdev_io->u.bdev.offset_blocks;
+ num_pages = bdev_io->u.bdev.num_blocks;
+
+ task->status = SPDK_BDEV_IO_STATUS_SUCCESS;
+
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL,
+ "Vbdev doing unmap at offset %" PRIu64 " using %" PRIu64 " pages on device %s\n", start_page,
+ num_pages, bdev_io->bdev->name);
+ spdk_blob_io_unmap(blob, ch, start_page, num_pages, lvol_op_comp, task);
+}
+
+static void
+lvol_write_zeroes(struct spdk_lvol *lvol, struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ uint64_t start_page, num_pages;
+ struct spdk_blob *blob = lvol->blob;
+ struct lvol_task *task = (struct lvol_task *)bdev_io->driver_ctx;
+
+ start_page = bdev_io->u.bdev.offset_blocks;
+ num_pages = bdev_io->u.bdev.num_blocks;
+
+ task->status = SPDK_BDEV_IO_STATUS_SUCCESS;
+
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL,
+ "Vbdev doing write zeros at offset %" PRIu64 " using %" PRIu64 " pages on device %s\n", start_page,
+ num_pages, bdev_io->bdev->name);
+ spdk_blob_io_write_zeroes(blob, ch, start_page, num_pages, lvol_op_comp, task);
+}
+
+static void
+lvol_read(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ uint64_t start_page, num_pages;
+ struct spdk_lvol *lvol = bdev_io->bdev->ctxt;
+ struct spdk_blob *blob = lvol->blob;
+ struct lvol_task *task = (struct lvol_task *)bdev_io->driver_ctx;
+
+ start_page = bdev_io->u.bdev.offset_blocks;
+ num_pages = bdev_io->u.bdev.num_blocks;
+
+ task->status = SPDK_BDEV_IO_STATUS_SUCCESS;
+
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL,
+ "Vbdev doing read at offset %" PRIu64 " using %" PRIu64 " pages on device %s\n", start_page,
+ num_pages, bdev_io->bdev->name);
+ spdk_blob_io_readv(blob, ch, bdev_io->u.bdev.iovs, bdev_io->u.bdev.iovcnt, start_page,
+ num_pages, lvol_op_comp, task);
+}
+
+static void
+lvol_write(struct spdk_lvol *lvol, struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ uint64_t start_page, num_pages;
+ struct spdk_blob *blob = lvol->blob;
+ struct lvol_task *task = (struct lvol_task *)bdev_io->driver_ctx;
+
+ start_page = bdev_io->u.bdev.offset_blocks;
+ num_pages = bdev_io->u.bdev.num_blocks;
+
+ task->status = SPDK_BDEV_IO_STATUS_SUCCESS;
+
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL,
+ "Vbdev doing write at offset %" PRIu64 " using %" PRIu64 " pages on device %s\n", start_page,
+ num_pages, bdev_io->bdev->name);
+ spdk_blob_io_writev(blob, ch, bdev_io->u.bdev.iovs, bdev_io->u.bdev.iovcnt, start_page,
+ num_pages, lvol_op_comp, task);
+}
+
+static int
+lvol_reset(struct spdk_bdev_io *bdev_io)
+{
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+
+ return 0;
+}
+
+static void
+vbdev_lvol_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ struct spdk_lvol *lvol = bdev_io->bdev->ctxt;
+
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Vbdev request type %d submitted\n", bdev_io->type);
+
+ switch (bdev_io->type) {
+ case SPDK_BDEV_IO_TYPE_READ:
+ spdk_bdev_io_get_buf(bdev_io, lvol_read,
+ bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen);
+ break;
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ lvol_write(lvol, ch, bdev_io);
+ break;
+ case SPDK_BDEV_IO_TYPE_RESET:
+ lvol_reset(bdev_io);
+ break;
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ lvol_unmap(lvol, ch, bdev_io);
+ break;
+ case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
+ lvol_write_zeroes(lvol, ch, bdev_io);
+ break;
+ default:
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "lvol: unsupported I/O type %d\n", bdev_io->type);
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ return;
+ }
+ return;
+}
+
+static struct spdk_bdev_fn_table vbdev_lvol_fn_table = {
+ .destruct = vbdev_lvol_unregister,
+ .io_type_supported = vbdev_lvol_io_type_supported,
+ .submit_request = vbdev_lvol_submit_request,
+ .get_io_channel = vbdev_lvol_get_io_channel,
+ .dump_info_json = vbdev_lvol_dump_info_json,
+ .write_config_json = vbdev_lvol_write_config_json,
+};
+
+static void
+_spdk_lvol_destroy_cb(void *cb_arg, int bdeverrno)
+{
+}
+
+static void
+_create_lvol_disk_destroy_cb(void *cb_arg, int bdeverrno)
+{
+ struct spdk_lvol *lvol = cb_arg;
+
+ if (bdeverrno < 0) {
+ SPDK_ERRLOG("Could not unregister bdev for lvol %s\n",
+ lvol->unique_id);
+ return;
+ }
+
+ spdk_lvol_destroy(lvol, _spdk_lvol_destroy_cb, NULL);
+}
+
+static void
+_create_lvol_disk_unload_cb(void *cb_arg, int bdeverrno)
+{
+ struct spdk_lvol *lvol = cb_arg;
+
+ if (bdeverrno < 0) {
+ SPDK_ERRLOG("Could not unregister bdev for lvol %s\n",
+ lvol->unique_id);
+ return;
+ }
+
+ TAILQ_REMOVE(&lvol->lvol_store->lvols, lvol, link);
+ free(lvol->unique_id);
+ free(lvol);
+}
+
+static int
+_create_lvol_disk(struct spdk_lvol *lvol, bool destroy)
+{
+ struct spdk_bdev *bdev;
+ struct lvol_store_bdev *lvs_bdev;
+ uint64_t total_size;
+ unsigned char *alias;
+ int rc;
+
+ if (!lvol->unique_id) {
+ return -EINVAL;
+ }
+
+ lvs_bdev = vbdev_get_lvs_bdev_by_lvs(lvol->lvol_store);
+ if (lvs_bdev == NULL) {
+ SPDK_ERRLOG("No spdk lvs-bdev pair found for lvol %s\n", lvol->unique_id);
+ return -ENODEV;
+ }
+
+ bdev = calloc(1, sizeof(struct spdk_bdev));
+ if (!bdev) {
+ SPDK_ERRLOG("Cannot alloc memory for lvol bdev\n");
+ return -ENOMEM;
+ }
+
+ bdev->name = lvol->unique_id;
+ bdev->product_name = "Logical Volume";
+ bdev->blocklen = spdk_bs_get_io_unit_size(lvol->lvol_store->blobstore);
+ total_size = spdk_blob_get_num_clusters(lvol->blob) *
+ spdk_bs_get_cluster_size(lvol->lvol_store->blobstore);
+ assert((total_size % bdev->blocklen) == 0);
+ bdev->blockcnt = total_size / bdev->blocklen;
+ bdev->uuid = lvol->uuid;
+ bdev->need_aligned_buffer = lvs_bdev->bdev->need_aligned_buffer;
+ bdev->split_on_optimal_io_boundary = true;
+ bdev->optimal_io_boundary = spdk_bs_get_cluster_size(lvol->lvol_store->blobstore) / bdev->blocklen;
+
+ bdev->ctxt = lvol;
+ bdev->fn_table = &vbdev_lvol_fn_table;
+ bdev->module = &g_lvol_if;
+
+ rc = spdk_vbdev_register(bdev, &lvs_bdev->bdev, 1);
+ if (rc) {
+ free(bdev);
+ return rc;
+ }
+ lvol->bdev = bdev;
+
+ alias = spdk_sprintf_alloc("%s/%s", lvs_bdev->lvs->name, lvol->name);
+ if (alias == NULL) {
+ SPDK_ERRLOG("Cannot alloc memory for alias\n");
+ spdk_bdev_unregister(lvol->bdev, (destroy ? _create_lvol_disk_destroy_cb :
+ _create_lvol_disk_unload_cb), lvol);
+ return -ENOMEM;
+ }
+
+ rc = spdk_bdev_alias_add(bdev, alias);
+ if (rc != 0) {
+ SPDK_ERRLOG("Cannot add alias to lvol bdev\n");
+ spdk_bdev_unregister(lvol->bdev, (destroy ? _create_lvol_disk_destroy_cb :
+ _create_lvol_disk_unload_cb), lvol);
+ }
+ free(alias);
+
+ return rc;
+}
+
+static void
+_vbdev_lvol_create_cb(void *cb_arg, struct spdk_lvol *lvol, int lvolerrno)
+{
+ struct spdk_lvol_with_handle_req *req = cb_arg;
+
+ if (lvolerrno < 0) {
+ goto end;
+ }
+
+ lvolerrno = _create_lvol_disk(lvol, true);
+
+end:
+ req->cb_fn(req->cb_arg, lvol, lvolerrno);
+ free(req);
+}
+
+int
+vbdev_lvol_create(struct spdk_lvol_store *lvs, const char *name, uint64_t sz,
+ bool thin_provision, spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg)
+{
+ struct spdk_lvol_with_handle_req *req;
+ int rc;
+
+ req = calloc(1, sizeof(*req));
+ if (req == NULL) {
+ return -ENOMEM;
+ }
+ req->cb_fn = cb_fn;
+ req->cb_arg = cb_arg;
+
+ rc = spdk_lvol_create(lvs, name, sz, thin_provision, _vbdev_lvol_create_cb, req);
+ if (rc != 0) {
+ free(req);
+ }
+
+ return rc;
+}
+
+void
+vbdev_lvol_create_snapshot(struct spdk_lvol *lvol, const char *snapshot_name,
+ spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg)
+{
+ struct spdk_lvol_with_handle_req *req;
+
+ req = calloc(1, sizeof(*req));
+ if (req == NULL) {
+ cb_fn(cb_arg, NULL, -ENOMEM);
+ return;
+ }
+
+ req->cb_fn = cb_fn;
+ req->cb_arg = cb_arg;
+
+ spdk_lvol_create_snapshot(lvol, snapshot_name, _vbdev_lvol_create_cb, req);
+}
+
+void
+vbdev_lvol_create_clone(struct spdk_lvol *lvol, const char *clone_name,
+ spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg)
+{
+ struct spdk_lvol_with_handle_req *req;
+
+ req = calloc(1, sizeof(*req));
+ if (req == NULL) {
+ cb_fn(cb_arg, NULL, -ENOMEM);
+ return;
+ }
+
+ req->cb_fn = cb_fn;
+ req->cb_arg = cb_arg;
+
+ spdk_lvol_create_clone(lvol, clone_name, _vbdev_lvol_create_cb, req);
+}
+
+static void
+_vbdev_lvol_rename_cb(void *cb_arg, int lvolerrno)
+{
+ struct spdk_lvol_req *req = cb_arg;
+
+ if (lvolerrno != 0) {
+ SPDK_ERRLOG("Renaming lvol failed\n");
+ }
+
+ req->cb_fn(req->cb_arg, lvolerrno);
+ free(req);
+}
+
+void
+vbdev_lvol_rename(struct spdk_lvol *lvol, const char *new_lvol_name,
+ spdk_lvol_op_complete cb_fn, void *cb_arg)
+{
+ struct spdk_lvol_req *req;
+ int rc;
+
+ rc = _vbdev_lvol_change_bdev_alias(lvol, new_lvol_name);
+ if (rc != 0) {
+ SPDK_ERRLOG("renaming lvol to '%s' does not succeed\n", new_lvol_name);
+ cb_fn(cb_arg, rc);
+ return;
+ }
+
+ req = calloc(1, sizeof(*req));
+ if (req == NULL) {
+ cb_fn(cb_arg, -ENOMEM);
+ return;
+ }
+ req->cb_fn = cb_fn;
+ req->cb_arg = cb_arg;
+
+ spdk_lvol_rename(lvol, new_lvol_name, _vbdev_lvol_rename_cb, req);
+}
+
+static void
+_vbdev_lvol_resize_cb(void *cb_arg, int lvolerrno)
+{
+ struct spdk_lvol_req *req = cb_arg;
+ struct spdk_lvol *lvol = req->lvol;
+ uint64_t total_size;
+
+ /* change bdev size */
+ if (lvolerrno != 0) {
+ SPDK_ERRLOG("CB function for bdev lvol %s receive error no: %d.\n", lvol->name, lvolerrno);
+ goto finish;
+ }
+
+ total_size = spdk_blob_get_num_clusters(lvol->blob) *
+ spdk_bs_get_cluster_size(lvol->lvol_store->blobstore);
+ assert((total_size % lvol->bdev->blocklen) == 0);
+
+ lvolerrno = spdk_bdev_notify_blockcnt_change(lvol->bdev, total_size / lvol->bdev->blocklen);
+ if (lvolerrno != 0) {
+ SPDK_ERRLOG("Could not change num blocks for bdev lvol %s with error no: %d.\n",
+ lvol->name, lvolerrno);
+ }
+
+finish:
+ req->cb_fn(req->cb_arg, lvolerrno);
+ free(req);
+}
+
+void
+vbdev_lvol_resize(struct spdk_lvol *lvol, uint64_t sz, spdk_lvol_op_complete cb_fn, void *cb_arg)
+{
+ struct spdk_lvol_req *req;
+
+ if (lvol == NULL) {
+ SPDK_ERRLOG("lvol does not exist\n");
+ cb_fn(cb_arg, -EINVAL);
+ return;
+ }
+
+ assert(lvol->bdev != NULL);
+
+ req = calloc(1, sizeof(*req));
+ if (req == NULL) {
+ cb_fn(cb_arg, -ENOMEM);
+ return;
+ }
+
+ req->cb_fn = cb_fn;
+ req->cb_arg = cb_arg;
+ req->sz = sz;
+ req->lvol = lvol;
+
+ spdk_lvol_resize(req->lvol, req->sz, _vbdev_lvol_resize_cb, req);
+}
+
+static int
+vbdev_lvs_init(void)
+{
+ return 0;
+}
+
+static int
+vbdev_lvs_get_ctx_size(void)
+{
+ return sizeof(struct lvol_task);
+}
+
+static void
+_vbdev_lvs_examine_failed(void *cb_arg, int lvserrno)
+{
+ spdk_bdev_module_examine_done(&g_lvol_if);
+}
+
+static void
+_vbdev_lvol_examine_close_cb(struct spdk_lvol_store *lvs)
+{
+ if (lvs->lvols_opened >= lvs->lvol_count) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Opening lvols finished\n");
+ spdk_bdev_module_examine_done(&g_lvol_if);
+ }
+}
+
+static void
+_vbdev_lvs_examine_finish(void *cb_arg, struct spdk_lvol *lvol, int lvolerrno)
+{
+ struct spdk_lvol_store *lvs = cb_arg;
+
+ if (lvolerrno != 0) {
+ SPDK_ERRLOG("Error opening lvol %s\n", lvol->unique_id);
+ TAILQ_REMOVE(&lvs->lvols, lvol, link);
+ lvs->lvol_count--;
+ free(lvol->unique_id);
+ free(lvol);
+ goto end;
+ }
+
+ if (_create_lvol_disk(lvol, false)) {
+ SPDK_ERRLOG("Cannot create bdev for lvol %s\n", lvol->unique_id);
+ lvs->lvol_count--;
+ _vbdev_lvol_examine_close_cb(lvs);
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Opening lvol %s failed\n", lvol->unique_id);
+ return;
+ }
+
+ lvs->lvols_opened++;
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Opening lvol %s succeeded\n", lvol->unique_id);
+
+end:
+
+ if (lvs->lvols_opened >= lvs->lvol_count) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Opening lvols finished\n");
+ spdk_bdev_module_examine_done(&g_lvol_if);
+ }
+}
+
+static void
+_vbdev_lvs_examine_cb(void *arg, struct spdk_lvol_store *lvol_store, int lvserrno)
+{
+ struct lvol_store_bdev *lvs_bdev;
+ struct spdk_lvs_with_handle_req *req = (struct spdk_lvs_with_handle_req *)arg;
+ struct spdk_lvol *lvol, *tmp;
+
+ if (lvserrno == -EEXIST) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL,
+ "Name for lvolstore on device %s conflicts with name for already loaded lvs\n",
+ req->base_bdev->name);
+ /* On error blobstore destroys bs_dev itself */
+ spdk_bdev_module_examine_done(&g_lvol_if);
+ goto end;
+ } else if (lvserrno != 0) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Lvol store not found on %s\n", req->base_bdev->name);
+ /* On error blobstore destroys bs_dev itself */
+ spdk_bdev_module_examine_done(&g_lvol_if);
+ goto end;
+ }
+
+ lvserrno = spdk_bs_bdev_claim(lvol_store->bs_dev, &g_lvol_if);
+ if (lvserrno != 0) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Lvol store base bdev already claimed by another bdev\n");
+ spdk_lvs_unload(lvol_store, _vbdev_lvs_examine_failed, NULL);
+ goto end;
+ }
+
+ lvs_bdev = calloc(1, sizeof(*lvs_bdev));
+ if (!lvs_bdev) {
+ SPDK_ERRLOG("Cannot alloc memory for lvs_bdev\n");
+ spdk_lvs_unload(lvol_store, _vbdev_lvs_examine_failed, NULL);
+ goto end;
+ }
+
+ lvs_bdev->lvs = lvol_store;
+ lvs_bdev->bdev = req->base_bdev;
+
+ TAILQ_INSERT_TAIL(&g_spdk_lvol_pairs, lvs_bdev, lvol_stores);
+
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Lvol store found on %s - begin parsing\n",
+ req->base_bdev->name);
+
+ lvol_store->lvols_opened = 0;
+
+ if (TAILQ_EMPTY(&lvol_store->lvols)) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Lvol store examination done\n");
+ spdk_bdev_module_examine_done(&g_lvol_if);
+ } else {
+ /* Open all lvols */
+ TAILQ_FOREACH_SAFE(lvol, &lvol_store->lvols, link, tmp) {
+ spdk_lvol_open(lvol, _vbdev_lvs_examine_finish, lvol_store);
+ }
+ }
+
+end:
+ free(req);
+}
+
+static void
+vbdev_lvs_examine(struct spdk_bdev *bdev)
+{
+ struct spdk_bs_dev *bs_dev;
+ struct spdk_lvs_with_handle_req *req;
+
+ req = calloc(1, sizeof(*req));
+ if (req == NULL) {
+ spdk_bdev_module_examine_done(&g_lvol_if);
+ SPDK_ERRLOG("Cannot alloc memory for vbdev lvol store request pointer\n");
+ return;
+ }
+
+ bs_dev = spdk_bdev_create_bs_dev(bdev, vbdev_lvs_hotremove_cb, bdev);
+ if (!bs_dev) {
+ SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Cannot create bs dev on %s\n", bdev->name);
+ spdk_bdev_module_examine_done(&g_lvol_if);
+ free(req);
+ return;
+ }
+
+ req->base_bdev = bdev;
+
+ spdk_lvs_load(bs_dev, _vbdev_lvs_examine_cb, req);
+}
+
+struct spdk_lvol *
+vbdev_lvol_get_from_bdev(struct spdk_bdev *bdev)
+{
+ if (!bdev || bdev->module != &g_lvol_if) {
+ return NULL;
+ }
+
+ if (bdev->ctxt == NULL) {
+ SPDK_ERRLOG("No lvol ctx assigned to bdev %s\n", bdev->name);
+ return NULL;
+ }
+
+ return (struct spdk_lvol *)bdev->ctxt;
+}
+
+SPDK_LOG_REGISTER_COMPONENT("vbdev_lvol", SPDK_LOG_VBDEV_LVOL);
diff --git a/src/spdk/lib/bdev/lvol/vbdev_lvol.h b/src/spdk/lib/bdev/lvol/vbdev_lvol.h
new file mode 100644
index 00000000..93991d08
--- /dev/null
+++ b/src/spdk/lib/bdev/lvol/vbdev_lvol.h
@@ -0,0 +1,120 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPDK_VBDEV_LVOL_H
+#define SPDK_VBDEV_LVOL_H
+
+#include "spdk/lvol.h"
+#include "spdk/bdev_module.h"
+
+#include "spdk_internal/lvolstore.h"
+
+struct lvol_store_bdev {
+ struct spdk_lvol_store *lvs;
+ struct spdk_bdev *bdev;
+ struct spdk_lvs_req *req;
+
+ TAILQ_ENTRY(lvol_store_bdev) lvol_stores;
+};
+
+int vbdev_lvs_create(struct spdk_bdev *base_bdev, const char *name, uint32_t cluster_sz,
+ spdk_lvs_op_with_handle_complete cb_fn, void *cb_arg);
+void vbdev_lvs_destruct(struct spdk_lvol_store *lvs, spdk_lvs_op_complete cb_fn, void *cb_arg);
+void vbdev_lvs_unload(struct spdk_lvol_store *lvs, spdk_lvs_op_complete cb_fn, void *cb_arg);
+
+int vbdev_lvol_create(struct spdk_lvol_store *lvs, const char *name, uint64_t sz,
+ bool thin_provisioned, spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg);
+
+void vbdev_lvol_create_snapshot(struct spdk_lvol *lvol, const char *snapshot_name,
+ spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg);
+
+void vbdev_lvol_create_clone(struct spdk_lvol *lvol, const char *clone_name,
+ spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg);
+
+/**
+ * \brief Change size of lvol
+ * \param lvol Handle to lvol
+ * \param sz Size of lvol to change
+ * \param cb_fn Completion callback
+ * \param cb_arg Completion callback custom arguments
+ * \return error
+ */
+void vbdev_lvol_resize(struct spdk_lvol *lvol, uint64_t sz, spdk_lvol_op_complete cb_fn,
+ void *cb_arg);
+
+void vbdev_lvol_rename(struct spdk_lvol *lvol, const char *new_lvol_name,
+ spdk_lvol_op_complete cb_fn, void *cb_arg);
+
+/**
+ * Destroy a logical volume
+ * \param lvol Handle to lvol
+ * \param cb_fn Completion callback
+ * \param cb_arg Completion callback custom arguments
+ */
+void vbdev_lvol_destroy(struct spdk_lvol *lvol, spdk_lvol_op_complete cb_fn, void *cb_arg);
+
+/**
+ * \brief Renames given lvolstore.
+ *
+ * \param lvs Pointer to lvolstore
+ * \param new_name New name of lvs
+ * \param cb_fn Completion callback
+ * \param cb_arg Completion callback custom arguments
+ */
+void vbdev_lvs_rename(struct spdk_lvol_store *lvs, const char *new_lvs_name,
+ spdk_lvs_op_complete cb_fn, void *cb_arg);
+
+/**
+ * \brief Search for handle lvolstore
+ * \param uuid_str UUID of lvolstore
+ * \return Handle to spdk_lvol_store or NULL if not found.
+ */
+struct spdk_lvol_store *vbdev_get_lvol_store_by_uuid(const char *uuid_str);
+
+/**
+ * \brief Search for handle to lvolstore
+ * \param name name of lvolstore
+ * \return Handle to spdk_lvol_store or NULL if not found.
+ */
+struct spdk_lvol_store *vbdev_get_lvol_store_by_name(const char *name);
+
+/**
+ * \brief Search for handle to lvol_store_bdev
+ * \param lvs handle to lvolstore
+ * \return Handle to lvol_store_bdev or NULL if not found.
+ */
+struct lvol_store_bdev *vbdev_get_lvs_bdev_by_lvs(struct spdk_lvol_store *lvs);
+
+struct spdk_lvol *vbdev_lvol_get_from_bdev(struct spdk_bdev *bdev);
+
+#endif /* SPDK_VBDEV_LVOL_H */
diff --git a/src/spdk/lib/bdev/lvol/vbdev_lvol_rpc.c b/src/spdk/lib/bdev/lvol/vbdev_lvol_rpc.c
new file mode 100644
index 00000000..30f67f35
--- /dev/null
+++ b/src/spdk/lib/bdev/lvol/vbdev_lvol_rpc.c
@@ -0,0 +1,1089 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/rpc.h"
+#include "spdk/bdev.h"
+#include "spdk/util.h"
+#include "vbdev_lvol.h"
+#include "spdk/string.h"
+#include "spdk_internal/log.h"
+
+SPDK_LOG_REGISTER_COMPONENT("lvolrpc", SPDK_LOG_LVOL_RPC)
+
+struct rpc_construct_lvol_store {
+ char *lvs_name;
+ char *bdev_name;
+ uint32_t cluster_sz;
+};
+
+static int
+vbdev_get_lvol_store_by_uuid_xor_name(const char *uuid, const char *lvs_name,
+ struct spdk_lvol_store **lvs)
+{
+ if ((uuid == NULL && lvs_name == NULL)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "lvs UUID nor lvs name specified\n");
+ return -EINVAL;
+ } else if ((uuid && lvs_name)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "both lvs UUID '%s' and lvs name '%s' specified\n", uuid,
+ lvs_name);
+ return -EINVAL;
+ } else if (uuid) {
+ *lvs = vbdev_get_lvol_store_by_uuid(uuid);
+
+ if (*lvs == NULL) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "blobstore with UUID '%s' not found\n", uuid);
+ return -ENODEV;
+ }
+ } else if (lvs_name) {
+
+ *lvs = vbdev_get_lvol_store_by_name(lvs_name);
+
+ if (*lvs == NULL) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "blobstore with name '%s' not found\n", lvs_name);
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
+
+static void
+free_rpc_construct_lvol_store(struct rpc_construct_lvol_store *req)
+{
+ free(req->bdev_name);
+ free(req->lvs_name);
+}
+
+static const struct spdk_json_object_decoder rpc_construct_lvol_store_decoders[] = {
+ {"bdev_name", offsetof(struct rpc_construct_lvol_store, bdev_name), spdk_json_decode_string},
+ {"cluster_sz", offsetof(struct rpc_construct_lvol_store, cluster_sz), spdk_json_decode_uint32, true},
+ {"lvs_name", offsetof(struct rpc_construct_lvol_store, lvs_name), spdk_json_decode_string},
+};
+
+static void
+_spdk_rpc_lvol_store_construct_cb(void *cb_arg, struct spdk_lvol_store *lvol_store, int lvserrno)
+{
+ struct spdk_json_write_ctx *w;
+ char lvol_store_uuid[SPDK_UUID_STRING_LEN];
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (lvserrno != 0) {
+ goto invalid;
+ }
+
+ spdk_uuid_fmt_lower(lvol_store_uuid, sizeof(lvol_store_uuid), &lvol_store->uuid);
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ return;
+ }
+
+ spdk_json_write_string(w, lvol_store_uuid);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-lvserrno));
+}
+
+static void
+spdk_rpc_construct_lvol_store(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_construct_lvol_store req = {};
+ struct spdk_bdev *bdev;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_construct_lvol_store_decoders,
+ SPDK_COUNTOF(rpc_construct_lvol_store_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ if (req.bdev_name == NULL) {
+ SPDK_ERRLOG("missing bdev_name param\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ if (req.lvs_name == NULL) {
+ SPDK_ERRLOG("missing lvs_name param\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+ bdev = spdk_bdev_get_by_name(req.bdev_name);
+ if (bdev == NULL) {
+ SPDK_ERRLOG("bdev '%s' does not exist\n", req.bdev_name);
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ rc = vbdev_lvs_create(bdev, req.lvs_name, req.cluster_sz, _spdk_rpc_lvol_store_construct_cb,
+ request);
+ if (rc < 0) {
+ goto invalid;
+ }
+ free_rpc_construct_lvol_store(&req);
+
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-rc));
+ free_rpc_construct_lvol_store(&req);
+}
+SPDK_RPC_REGISTER("construct_lvol_store", spdk_rpc_construct_lvol_store, SPDK_RPC_RUNTIME)
+
+struct rpc_rename_lvol_store {
+ char *old_name;
+ char *new_name;
+};
+
+static void
+free_rpc_rename_lvol_store(struct rpc_rename_lvol_store *req)
+{
+ free(req->old_name);
+ free(req->new_name);
+}
+
+static const struct spdk_json_object_decoder rpc_rename_lvol_store_decoders[] = {
+ {"old_name", offsetof(struct rpc_rename_lvol_store, old_name), spdk_json_decode_string},
+ {"new_name", offsetof(struct rpc_rename_lvol_store, new_name), spdk_json_decode_string},
+};
+
+static void
+_spdk_rpc_rename_lvol_store_cb(void *cb_arg, int lvserrno)
+{
+ struct spdk_json_write_ctx *w;
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (lvserrno != 0) {
+ goto invalid;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ return;
+ }
+
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-lvserrno));
+}
+
+static void
+spdk_rpc_rename_lvol_store(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_rename_lvol_store req = {};
+ struct spdk_lvol_store *lvs;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_rename_lvol_store_decoders,
+ SPDK_COUNTOF(rpc_rename_lvol_store_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ lvs = vbdev_get_lvol_store_by_name(req.old_name);
+ if (lvs == NULL) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "no lvs existing for given name\n");
+ rc = -ENOENT;
+ goto invalid;
+ }
+
+ vbdev_lvs_rename(lvs, req.new_name, _spdk_rpc_rename_lvol_store_cb, request);
+
+ free_rpc_rename_lvol_store(&req);
+
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc));
+ free_rpc_rename_lvol_store(&req);
+}
+SPDK_RPC_REGISTER("rename_lvol_store", spdk_rpc_rename_lvol_store, SPDK_RPC_RUNTIME)
+
+struct rpc_destroy_lvol_store {
+ char *uuid;
+ char *lvs_name;
+};
+
+static void
+free_rpc_destroy_lvol_store(struct rpc_destroy_lvol_store *req)
+{
+ free(req->uuid);
+ free(req->lvs_name);
+}
+
+static const struct spdk_json_object_decoder rpc_destroy_lvol_store_decoders[] = {
+ {"uuid", offsetof(struct rpc_destroy_lvol_store, uuid), spdk_json_decode_string, true},
+ {"lvs_name", offsetof(struct rpc_destroy_lvol_store, lvs_name), spdk_json_decode_string, true},
+};
+
+static void
+_spdk_rpc_lvol_store_destroy_cb(void *cb_arg, int lvserrno)
+{
+ struct spdk_json_write_ctx *w;
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (lvserrno != 0) {
+ goto invalid;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ return;
+ }
+
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-lvserrno));
+}
+
+static void
+spdk_rpc_destroy_lvol_store(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_destroy_lvol_store req = {};
+ struct spdk_lvol_store *lvs = NULL;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_destroy_lvol_store_decoders,
+ SPDK_COUNTOF(rpc_destroy_lvol_store_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ rc = vbdev_get_lvol_store_by_uuid_xor_name(req.uuid, req.lvs_name, &lvs);
+ if (rc != 0) {
+ goto invalid;
+ }
+
+ vbdev_lvs_destruct(lvs, _spdk_rpc_lvol_store_destroy_cb, request);
+
+ free_rpc_destroy_lvol_store(&req);
+
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-rc));
+ free_rpc_destroy_lvol_store(&req);
+}
+SPDK_RPC_REGISTER("destroy_lvol_store", spdk_rpc_destroy_lvol_store, SPDK_RPC_RUNTIME)
+
+struct rpc_construct_lvol_bdev {
+ char *uuid;
+ char *lvs_name;
+ char *lvol_name;
+ uint64_t size;
+ bool thin_provision;
+};
+
+static void
+free_rpc_construct_lvol_bdev(struct rpc_construct_lvol_bdev *req)
+{
+ free(req->uuid);
+ free(req->lvs_name);
+ free(req->lvol_name);
+}
+
+static const struct spdk_json_object_decoder rpc_construct_lvol_bdev_decoders[] = {
+ {"uuid", offsetof(struct rpc_construct_lvol_bdev, uuid), spdk_json_decode_string, true},
+ {"lvs_name", offsetof(struct rpc_construct_lvol_bdev, lvs_name), spdk_json_decode_string, true},
+ {"lvol_name", offsetof(struct rpc_construct_lvol_bdev, lvol_name), spdk_json_decode_string, true},
+ {"size", offsetof(struct rpc_construct_lvol_bdev, size), spdk_json_decode_uint64},
+ {"thin_provision", offsetof(struct rpc_construct_lvol_bdev, thin_provision), spdk_json_decode_bool, true},
+};
+
+static void
+_spdk_rpc_construct_lvol_bdev_cb(void *cb_arg, struct spdk_lvol *lvol, int lvolerrno)
+{
+ struct spdk_json_write_ctx *w;
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (lvolerrno != 0) {
+ goto invalid;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ return;
+ }
+
+ spdk_json_write_string(w, lvol->unique_id);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-lvolerrno));
+}
+
+static void
+spdk_rpc_construct_lvol_bdev(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_construct_lvol_bdev req = {};
+ int rc;
+ struct spdk_lvol_store *lvs = NULL;
+
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "Creating blob\n");
+
+ if (spdk_json_decode_object(params, rpc_construct_lvol_bdev_decoders,
+ SPDK_COUNTOF(rpc_construct_lvol_bdev_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ rc = vbdev_get_lvol_store_by_uuid_xor_name(req.uuid, req.lvs_name, &lvs);
+ if (rc != 0) {
+ goto invalid;
+ }
+
+ if (req.lvol_name == NULL) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "no bdev name\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ rc = vbdev_lvol_create(lvs, req.lvol_name, req.size, req.thin_provision,
+ _spdk_rpc_construct_lvol_bdev_cb, request);
+ if (rc < 0) {
+ goto invalid;
+ }
+
+ free_rpc_construct_lvol_bdev(&req);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-rc));
+ free_rpc_construct_lvol_bdev(&req);
+}
+
+SPDK_RPC_REGISTER("construct_lvol_bdev", spdk_rpc_construct_lvol_bdev, SPDK_RPC_RUNTIME)
+
+struct rpc_snapshot_lvol_bdev {
+ char *lvol_name;
+ char *snapshot_name;
+};
+
+static void
+free_rpc_snapshot_lvol_bdev(struct rpc_snapshot_lvol_bdev *req)
+{
+ free(req->lvol_name);
+ free(req->snapshot_name);
+}
+
+static const struct spdk_json_object_decoder rpc_snapshot_lvol_bdev_decoders[] = {
+ {"lvol_name", offsetof(struct rpc_snapshot_lvol_bdev, lvol_name), spdk_json_decode_string},
+ {"snapshot_name", offsetof(struct rpc_snapshot_lvol_bdev, snapshot_name), spdk_json_decode_string},
+};
+
+static void
+_spdk_rpc_snapshot_lvol_bdev_cb(void *cb_arg, struct spdk_lvol *lvol, int lvolerrno)
+{
+ struct spdk_json_write_ctx *w;
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (lvolerrno != 0) {
+ goto invalid;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ return;
+ }
+
+ spdk_json_write_string(w, lvol->unique_id);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-lvolerrno));
+}
+
+static void
+spdk_rpc_snapshot_lvol_bdev(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_snapshot_lvol_bdev req = {};
+ struct spdk_bdev *bdev;
+ struct spdk_lvol *lvol;
+ int rc;
+
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "Snapshotting blob\n");
+
+ if (spdk_json_decode_object(params, rpc_snapshot_lvol_bdev_decoders,
+ SPDK_COUNTOF(rpc_snapshot_lvol_bdev_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ bdev = spdk_bdev_get_by_name(req.lvol_name);
+ if (bdev == NULL) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "bdev '%s' does not exist\n", req.lvol_name);
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ lvol = vbdev_lvol_get_from_bdev(bdev);
+ if (lvol == NULL) {
+ SPDK_ERRLOG("lvol does not exist\n");
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ vbdev_lvol_create_snapshot(lvol, req.snapshot_name, _spdk_rpc_snapshot_lvol_bdev_cb, request);
+
+ free_rpc_snapshot_lvol_bdev(&req);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc));
+ free_rpc_snapshot_lvol_bdev(&req);
+}
+
+SPDK_RPC_REGISTER("snapshot_lvol_bdev", spdk_rpc_snapshot_lvol_bdev, SPDK_RPC_RUNTIME)
+
+struct rpc_clone_lvol_bdev {
+ char *snapshot_name;
+ char *clone_name;
+};
+
+static void
+free_rpc_clone_lvol_bdev(struct rpc_clone_lvol_bdev *req)
+{
+ free(req->snapshot_name);
+ free(req->clone_name);
+}
+
+static const struct spdk_json_object_decoder rpc_clone_lvol_bdev_decoders[] = {
+ {"snapshot_name", offsetof(struct rpc_clone_lvol_bdev, snapshot_name), spdk_json_decode_string},
+ {"clone_name", offsetof(struct rpc_clone_lvol_bdev, clone_name), spdk_json_decode_string, true},
+};
+
+static void
+_spdk_rpc_clone_lvol_bdev_cb(void *cb_arg, struct spdk_lvol *lvol, int lvolerrno)
+{
+ struct spdk_json_write_ctx *w;
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (lvolerrno != 0) {
+ goto invalid;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ return;
+ }
+
+ spdk_json_write_string(w, lvol->unique_id);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-lvolerrno));
+}
+
+static void
+spdk_rpc_clone_lvol_bdev(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_clone_lvol_bdev req = {};
+ struct spdk_bdev *bdev;
+ struct spdk_lvol *lvol;
+ int rc;
+
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "Cloning blob\n");
+
+ if (spdk_json_decode_object(params, rpc_clone_lvol_bdev_decoders,
+ SPDK_COUNTOF(rpc_clone_lvol_bdev_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ bdev = spdk_bdev_get_by_name(req.snapshot_name);
+ if (bdev == NULL) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "bdev '%s' does not exist\n", req.snapshot_name);
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ lvol = vbdev_lvol_get_from_bdev(bdev);
+ if (lvol == NULL) {
+ SPDK_ERRLOG("lvol does not exist\n");
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ vbdev_lvol_create_clone(lvol, req.clone_name, _spdk_rpc_clone_lvol_bdev_cb, request);
+
+ free_rpc_clone_lvol_bdev(&req);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc));
+ free_rpc_clone_lvol_bdev(&req);
+}
+
+SPDK_RPC_REGISTER("clone_lvol_bdev", spdk_rpc_clone_lvol_bdev, SPDK_RPC_RUNTIME)
+
+struct rpc_rename_lvol_bdev {
+ char *old_name;
+ char *new_name;
+};
+
+static void
+free_rpc_rename_lvol_bdev(struct rpc_rename_lvol_bdev *req)
+{
+ free(req->old_name);
+ free(req->new_name);
+}
+
+static const struct spdk_json_object_decoder rpc_rename_lvol_bdev_decoders[] = {
+ {"old_name", offsetof(struct rpc_rename_lvol_bdev, old_name), spdk_json_decode_string},
+ {"new_name", offsetof(struct rpc_rename_lvol_bdev, new_name), spdk_json_decode_string},
+};
+
+static void
+_spdk_rpc_rename_lvol_bdev_cb(void *cb_arg, int lvolerrno)
+{
+ struct spdk_json_write_ctx *w;
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (lvolerrno != 0) {
+ goto invalid;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ return;
+ }
+
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-lvolerrno));
+}
+
+static void
+spdk_rpc_rename_lvol_bdev(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_rename_lvol_bdev req = {};
+ struct spdk_bdev *bdev;
+ struct spdk_lvol *lvol;
+ int rc = 0;
+
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "Renaming lvol\n");
+
+ if (spdk_json_decode_object(params, rpc_rename_lvol_bdev_decoders,
+ SPDK_COUNTOF(rpc_rename_lvol_bdev_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ bdev = spdk_bdev_get_by_name(req.old_name);
+ if (bdev == NULL) {
+ SPDK_ERRLOG("bdev '%s' does not exist\n", req.old_name);
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ lvol = vbdev_lvol_get_from_bdev(bdev);
+ if (lvol == NULL) {
+ SPDK_ERRLOG("lvol does not exist\n");
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ vbdev_lvol_rename(lvol, req.new_name, _spdk_rpc_rename_lvol_bdev_cb, request);
+
+ free_rpc_rename_lvol_bdev(&req);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc));
+ free_rpc_rename_lvol_bdev(&req);
+}
+
+SPDK_RPC_REGISTER("rename_lvol_bdev", spdk_rpc_rename_lvol_bdev, SPDK_RPC_RUNTIME)
+
+struct rpc_inflate_lvol_bdev {
+ char *name;
+};
+
+static void
+free_rpc_inflate_lvol_bdev(struct rpc_inflate_lvol_bdev *req)
+{
+ free(req->name);
+}
+
+static const struct spdk_json_object_decoder rpc_inflate_lvol_bdev_decoders[] = {
+ {"name", offsetof(struct rpc_inflate_lvol_bdev, name), spdk_json_decode_string},
+};
+
+static void
+_spdk_rpc_inflate_lvol_bdev_cb(void *cb_arg, int lvolerrno)
+{
+ struct spdk_json_write_ctx *w;
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (lvolerrno != 0) {
+ goto invalid;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ return;
+ }
+
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-lvolerrno));
+}
+
+static void
+spdk_rpc_inflate_lvol_bdev(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_inflate_lvol_bdev req = {};
+ struct spdk_bdev *bdev;
+ struct spdk_lvol *lvol;
+ int rc = 0;
+
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "Inflating lvol\n");
+
+ if (spdk_json_decode_object(params, rpc_inflate_lvol_bdev_decoders,
+ SPDK_COUNTOF(rpc_inflate_lvol_bdev_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ bdev = spdk_bdev_get_by_name(req.name);
+ if (bdev == NULL) {
+ SPDK_ERRLOG("bdev '%s' does not exist\n", req.name);
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ lvol = vbdev_lvol_get_from_bdev(bdev);
+ if (lvol == NULL) {
+ SPDK_ERRLOG("lvol does not exist\n");
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ spdk_lvol_inflate(lvol, _spdk_rpc_inflate_lvol_bdev_cb, request);
+
+ free_rpc_inflate_lvol_bdev(&req);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc));
+ free_rpc_inflate_lvol_bdev(&req);
+}
+
+SPDK_RPC_REGISTER("inflate_lvol_bdev", spdk_rpc_inflate_lvol_bdev, SPDK_RPC_RUNTIME)
+
+static void
+spdk_rpc_decouple_parent_lvol_bdev(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_inflate_lvol_bdev req = {};
+ struct spdk_bdev *bdev;
+ struct spdk_lvol *lvol;
+ int rc = 0;
+
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "Decoupling parent of lvol\n");
+
+ if (spdk_json_decode_object(params, rpc_inflate_lvol_bdev_decoders,
+ SPDK_COUNTOF(rpc_inflate_lvol_bdev_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ bdev = spdk_bdev_get_by_name(req.name);
+ if (bdev == NULL) {
+ SPDK_ERRLOG("bdev '%s' does not exist\n", req.name);
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ lvol = vbdev_lvol_get_from_bdev(bdev);
+ if (lvol == NULL) {
+ SPDK_ERRLOG("lvol does not exist\n");
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ spdk_lvol_decouple_parent(lvol, _spdk_rpc_inflate_lvol_bdev_cb, request);
+
+ free_rpc_inflate_lvol_bdev(&req);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc));
+ free_rpc_inflate_lvol_bdev(&req);
+}
+
+SPDK_RPC_REGISTER("decouple_parent_lvol_bdev", spdk_rpc_decouple_parent_lvol_bdev, SPDK_RPC_RUNTIME)
+
+struct rpc_resize_lvol_bdev {
+ char *name;
+ uint64_t size;
+};
+
+static void
+free_rpc_resize_lvol_bdev(struct rpc_resize_lvol_bdev *req)
+{
+ free(req->name);
+}
+
+static const struct spdk_json_object_decoder rpc_resize_lvol_bdev_decoders[] = {
+ {"name", offsetof(struct rpc_resize_lvol_bdev, name), spdk_json_decode_string},
+ {"size", offsetof(struct rpc_resize_lvol_bdev, size), spdk_json_decode_uint64},
+};
+
+static void
+_spdk_rpc_resize_lvol_bdev_cb(void *cb_arg, int lvolerrno)
+{
+ struct spdk_json_write_ctx *w;
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (lvolerrno != 0) {
+ goto invalid;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ return;
+ }
+
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-lvolerrno));
+}
+
+static void
+spdk_rpc_resize_lvol_bdev(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_resize_lvol_bdev req = {};
+ struct spdk_bdev *bdev;
+ struct spdk_lvol *lvol;
+ int rc = 0;
+
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "Resizing lvol\n");
+
+ if (spdk_json_decode_object(params, rpc_resize_lvol_bdev_decoders,
+ SPDK_COUNTOF(rpc_resize_lvol_bdev_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ if (req.name == NULL) {
+ SPDK_ERRLOG("missing name param\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ bdev = spdk_bdev_get_by_name(req.name);
+ if (bdev == NULL) {
+ SPDK_ERRLOG("no bdev for provided name %s\n", req.name);
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ lvol = vbdev_lvol_get_from_bdev(bdev);
+ if (lvol == NULL) {
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ vbdev_lvol_resize(lvol, req.size, _spdk_rpc_resize_lvol_bdev_cb, request);
+
+ free_rpc_resize_lvol_bdev(&req);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-rc));
+ free_rpc_resize_lvol_bdev(&req);
+}
+
+SPDK_RPC_REGISTER("resize_lvol_bdev", spdk_rpc_resize_lvol_bdev, SPDK_RPC_RUNTIME)
+
+struct rpc_destroy_lvol_bdev {
+ char *name;
+};
+
+static void
+free_rpc_destroy_lvol_bdev(struct rpc_destroy_lvol_bdev *req)
+{
+ free(req->name);
+}
+
+static const struct spdk_json_object_decoder rpc_destroy_lvol_bdev_decoders[] = {
+ {"name", offsetof(struct rpc_destroy_lvol_bdev, name), spdk_json_decode_string},
+};
+
+static void
+_spdk_rpc_destroy_lvol_bdev_cb(void *cb_arg, int lvolerrno)
+{
+ struct spdk_json_write_ctx *w;
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (lvolerrno != 0) {
+ goto invalid;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ return;
+ }
+
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
+ spdk_strerror(-lvolerrno));
+}
+
+static void
+spdk_rpc_destroy_lvol_bdev(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_destroy_lvol_bdev req = {};
+ struct spdk_bdev *bdev;
+ struct spdk_lvol *lvol;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_destroy_lvol_bdev_decoders,
+ SPDK_COUNTOF(rpc_destroy_lvol_bdev_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ bdev = spdk_bdev_get_by_name(req.name);
+ if (bdev == NULL) {
+ SPDK_ERRLOG("no bdev for provided name %s\n", req.name);
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ lvol = vbdev_lvol_get_from_bdev(bdev);
+ if (lvol == NULL) {
+ rc = -ENODEV;
+ goto invalid;
+ }
+
+ vbdev_lvol_destroy(lvol, _spdk_rpc_destroy_lvol_bdev_cb, request);
+
+ free_rpc_destroy_lvol_bdev(&req);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-rc));
+ free_rpc_destroy_lvol_bdev(&req);
+}
+
+SPDK_RPC_REGISTER("destroy_lvol_bdev", spdk_rpc_destroy_lvol_bdev, SPDK_RPC_RUNTIME)
+
+struct rpc_get_lvol_stores {
+ char *uuid;
+ char *lvs_name;
+};
+
+static void
+free_rpc_get_lvol_stores(struct rpc_get_lvol_stores *req)
+{
+ free(req->uuid);
+ free(req->lvs_name);
+}
+
+static const struct spdk_json_object_decoder rpc_get_lvol_stores_decoders[] = {
+ {"uuid", offsetof(struct rpc_get_lvol_stores, uuid), spdk_json_decode_string, true},
+ {"lvs_name", offsetof(struct rpc_get_lvol_stores, lvs_name), spdk_json_decode_string, true},
+};
+
+static void
+spdk_rpc_dump_lvol_store_info(struct spdk_json_write_ctx *w, struct lvol_store_bdev *lvs_bdev)
+{
+ struct spdk_blob_store *bs;
+ uint64_t cluster_size, block_size;
+ char uuid[SPDK_UUID_STRING_LEN];
+
+ bs = lvs_bdev->lvs->blobstore;
+ cluster_size = spdk_bs_get_cluster_size(bs);
+ /* Block size of lvols is always size of blob store page */
+ block_size = spdk_bs_get_page_size(bs);
+
+ spdk_json_write_object_begin(w);
+
+ spdk_uuid_fmt_lower(uuid, sizeof(uuid), &lvs_bdev->lvs->uuid);
+ spdk_json_write_name(w, "uuid");
+ spdk_json_write_string(w, uuid);
+
+ spdk_json_write_name(w, "name");
+ spdk_json_write_string(w, lvs_bdev->lvs->name);
+
+ spdk_json_write_name(w, "base_bdev");
+ spdk_json_write_string(w, spdk_bdev_get_name(lvs_bdev->bdev));
+
+ spdk_json_write_name(w, "total_data_clusters");
+ spdk_json_write_uint64(w, spdk_bs_total_data_cluster_count(bs));
+
+ spdk_json_write_name(w, "free_clusters");
+ spdk_json_write_uint64(w, spdk_bs_free_cluster_count(bs));
+
+ spdk_json_write_name(w, "block_size");
+ spdk_json_write_uint64(w, block_size);
+
+ spdk_json_write_name(w, "cluster_size");
+ spdk_json_write_uint64(w, cluster_size);
+
+ spdk_json_write_object_end(w);
+}
+
+static void
+spdk_rpc_get_lvol_stores(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_get_lvol_stores req = {};
+ struct spdk_json_write_ctx *w;
+ struct lvol_store_bdev *lvs_bdev = NULL;
+ struct spdk_lvol_store *lvs = NULL;
+ int rc;
+
+ if (params != NULL) {
+ if (spdk_json_decode_object(params, rpc_get_lvol_stores_decoders,
+ SPDK_COUNTOF(rpc_get_lvol_stores_decoders),
+ &req)) {
+ SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
+ rc = -EINVAL;
+ goto invalid;
+ }
+
+ rc = vbdev_get_lvol_store_by_uuid_xor_name(req.uuid, req.lvs_name, &lvs);
+ if (rc != 0) {
+ goto invalid;
+ }
+
+ lvs_bdev = vbdev_get_lvs_bdev_by_lvs(lvs);
+ if (lvs_bdev == NULL) {
+ rc = -ENODEV;
+ goto invalid;
+ }
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ if (w == NULL) {
+ free_rpc_get_lvol_stores(&req);
+ return;
+ }
+
+ spdk_json_write_array_begin(w);
+
+ if (lvs_bdev != NULL) {
+ spdk_rpc_dump_lvol_store_info(w, lvs_bdev);
+ } else {
+ for (lvs_bdev = vbdev_lvol_store_first(); lvs_bdev != NULL;
+ lvs_bdev = vbdev_lvol_store_next(lvs_bdev)) {
+ spdk_rpc_dump_lvol_store_info(w, lvs_bdev);
+ }
+ }
+ spdk_json_write_array_end(w);
+
+ spdk_jsonrpc_end_result(request, w);
+
+ free_rpc_get_lvol_stores(&req);
+
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(-rc));
+ free_rpc_get_lvol_stores(&req);
+}
+
+SPDK_RPC_REGISTER("get_lvol_stores", spdk_rpc_get_lvol_stores, SPDK_RPC_RUNTIME)