/* SPDX-License-Identifier: BSD-3-Clause * Copyright (C) 2017 Intel Corporation. * All rights reserved. * Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ #include "spdk/rpc.h" #include "spdk/bdev.h" #include "spdk/util.h" #include "vbdev_lvol.h" #include "spdk/string.h" #include "spdk/log.h" #include "spdk/bdev_module.h" #include "spdk/bit_array.h" SPDK_LOG_REGISTER_COMPONENT(lvol_rpc) struct rpc_bdev_lvol_create_lvstore { char *lvs_name; char *bdev_name; uint32_t cluster_sz; char *clear_method; uint32_t num_md_pages_per_cluster_ratio; }; 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(lvol_rpc, "lvs UUID nor lvs name specified\n"); return -EINVAL; } else if ((uuid && lvs_name)) { SPDK_INFOLOG(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(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(lvol_rpc, "blobstore with name '%s' not found\n", lvs_name); return -ENODEV; } } return 0; } static void free_rpc_bdev_lvol_create_lvstore(struct rpc_bdev_lvol_create_lvstore *req) { free(req->bdev_name); free(req->lvs_name); free(req->clear_method); } static const struct spdk_json_object_decoder rpc_bdev_lvol_create_lvstore_decoders[] = { {"bdev_name", offsetof(struct rpc_bdev_lvol_create_lvstore, bdev_name), spdk_json_decode_string}, {"cluster_sz", offsetof(struct rpc_bdev_lvol_create_lvstore, cluster_sz), spdk_json_decode_uint32, true}, {"lvs_name", offsetof(struct rpc_bdev_lvol_create_lvstore, lvs_name), spdk_json_decode_string}, {"clear_method", offsetof(struct rpc_bdev_lvol_create_lvstore, clear_method), spdk_json_decode_string, true}, {"num_md_pages_per_cluster_ratio", offsetof(struct rpc_bdev_lvol_create_lvstore, num_md_pages_per_cluster_ratio), spdk_json_decode_uint32, true}, }; static void 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); 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 rpc_bdev_lvol_create_lvstore(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_create_lvstore req = {}; int rc = 0; enum lvs_clear_method clear_method; if (spdk_json_decode_object(params, rpc_bdev_lvol_create_lvstore_decoders, SPDK_COUNTOF(rpc_bdev_lvol_create_lvstore_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } if (req.clear_method != NULL) { if (!strcasecmp(req.clear_method, "none")) { clear_method = LVS_CLEAR_WITH_NONE; } else if (!strcasecmp(req.clear_method, "unmap")) { clear_method = LVS_CLEAR_WITH_UNMAP; } else if (!strcasecmp(req.clear_method, "write_zeroes")) { clear_method = LVS_CLEAR_WITH_WRITE_ZEROES; } else { spdk_jsonrpc_send_error_response(request, -EINVAL, "Invalid clear_method parameter"); goto cleanup; } } else { clear_method = LVS_CLEAR_WITH_UNMAP; } rc = vbdev_lvs_create(req.bdev_name, req.lvs_name, req.cluster_sz, clear_method, req.num_md_pages_per_cluster_ratio, rpc_lvol_store_construct_cb, request); if (rc < 0) { spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); goto cleanup; } free_rpc_bdev_lvol_create_lvstore(&req); return; cleanup: free_rpc_bdev_lvol_create_lvstore(&req); } SPDK_RPC_REGISTER("bdev_lvol_create_lvstore", rpc_bdev_lvol_create_lvstore, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_rename_lvstore { char *old_name; char *new_name; }; static void free_rpc_bdev_lvol_rename_lvstore(struct rpc_bdev_lvol_rename_lvstore *req) { free(req->old_name); free(req->new_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_rename_lvstore_decoders[] = { {"old_name", offsetof(struct rpc_bdev_lvol_rename_lvstore, old_name), spdk_json_decode_string}, {"new_name", offsetof(struct rpc_bdev_lvol_rename_lvstore, new_name), spdk_json_decode_string}, }; static void rpc_bdev_lvol_rename_lvstore_cb(void *cb_arg, int lvserrno) { struct spdk_jsonrpc_request *request = cb_arg; if (lvserrno != 0) { goto invalid; } spdk_jsonrpc_send_bool_response(request, true); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-lvserrno)); } static void rpc_bdev_lvol_rename_lvstore(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_rename_lvstore req = {}; struct spdk_lvol_store *lvs; if (spdk_json_decode_object(params, rpc_bdev_lvol_rename_lvstore_decoders, SPDK_COUNTOF(rpc_bdev_lvol_rename_lvstore_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } lvs = vbdev_get_lvol_store_by_name(req.old_name); if (lvs == NULL) { SPDK_INFOLOG(lvol_rpc, "no lvs existing for given name\n"); spdk_jsonrpc_send_error_response_fmt(request, -ENOENT, "Lvol store %s not found", req.old_name); goto cleanup; } vbdev_lvs_rename(lvs, req.new_name, rpc_bdev_lvol_rename_lvstore_cb, request); cleanup: free_rpc_bdev_lvol_rename_lvstore(&req); } SPDK_RPC_REGISTER("bdev_lvol_rename_lvstore", rpc_bdev_lvol_rename_lvstore, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_delete_lvstore { char *uuid; char *lvs_name; }; static void free_rpc_bdev_lvol_delete_lvstore(struct rpc_bdev_lvol_delete_lvstore *req) { free(req->uuid); free(req->lvs_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_delete_lvstore_decoders[] = { {"uuid", offsetof(struct rpc_bdev_lvol_delete_lvstore, uuid), spdk_json_decode_string, true}, {"lvs_name", offsetof(struct rpc_bdev_lvol_delete_lvstore, lvs_name), spdk_json_decode_string, true}, }; static void rpc_lvol_store_destroy_cb(void *cb_arg, int lvserrno) { struct spdk_jsonrpc_request *request = cb_arg; if (lvserrno != 0) { goto invalid; } spdk_jsonrpc_send_bool_response(request, true); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-lvserrno)); } static void rpc_bdev_lvol_delete_lvstore(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_delete_lvstore req = {}; struct spdk_lvol_store *lvs = NULL; int rc; if (spdk_json_decode_object(params, rpc_bdev_lvol_delete_lvstore_decoders, SPDK_COUNTOF(rpc_bdev_lvol_delete_lvstore_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "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 = vbdev_get_lvol_store_by_uuid_xor_name(req.uuid, req.lvs_name, &lvs); if (rc != 0) { spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); goto cleanup; } vbdev_lvs_destruct(lvs, rpc_lvol_store_destroy_cb, request); cleanup: free_rpc_bdev_lvol_delete_lvstore(&req); } SPDK_RPC_REGISTER("bdev_lvol_delete_lvstore", rpc_bdev_lvol_delete_lvstore, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_create { char *uuid; char *lvs_name; char *lvol_name; uint64_t size; uint64_t size_in_mib; bool thin_provision; char *clear_method; }; static void free_rpc_bdev_lvol_create(struct rpc_bdev_lvol_create *req) { free(req->uuid); free(req->lvs_name); free(req->lvol_name); free(req->clear_method); } static const struct spdk_json_object_decoder rpc_bdev_lvol_create_decoders[] = { {"uuid", offsetof(struct rpc_bdev_lvol_create, uuid), spdk_json_decode_string, true}, {"lvs_name", offsetof(struct rpc_bdev_lvol_create, lvs_name), spdk_json_decode_string, true}, {"lvol_name", offsetof(struct rpc_bdev_lvol_create, lvol_name), spdk_json_decode_string}, {"size", offsetof(struct rpc_bdev_lvol_create, size), spdk_json_decode_uint64, true}, {"size_in_mib", offsetof(struct rpc_bdev_lvol_create, size_in_mib), spdk_json_decode_uint64, true}, {"thin_provision", offsetof(struct rpc_bdev_lvol_create, thin_provision), spdk_json_decode_bool, true}, {"clear_method", offsetof(struct rpc_bdev_lvol_create, clear_method), spdk_json_decode_string, true}, }; static void rpc_bdev_lvol_create_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); 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)); } SPDK_LOG_DEPRECATION_REGISTER(vbdev_lvol_rpc_req_size, "rpc_bdev_lvol_create/resize req.size", "v23.09", 0); static void rpc_bdev_lvol_create(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_create req = {}; enum lvol_clear_method clear_method; int rc = 0; struct spdk_lvol_store *lvs = NULL; uint64_t size = 0; SPDK_INFOLOG(lvol_rpc, "Creating blob\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_create_decoders, SPDK_COUNTOF(rpc_bdev_lvol_create_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } if (req.size > 0 && req.size_in_mib > 0) { SPDK_LOG_DEPRECATED(vbdev_lvol_rpc_req_size); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "size is deprecated. Specify only size_in_mib instead."); goto cleanup; } else if (req.size_in_mib > 0) { size = req.size_in_mib * 1024 * 1024; } else { SPDK_LOG_DEPRECATED(vbdev_lvol_rpc_req_size); size = req.size; } rc = vbdev_get_lvol_store_by_uuid_xor_name(req.uuid, req.lvs_name, &lvs); if (rc != 0) { spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); goto cleanup; } if (req.clear_method != NULL) { if (!strcasecmp(req.clear_method, "none")) { clear_method = LVOL_CLEAR_WITH_NONE; } else if (!strcasecmp(req.clear_method, "unmap")) { clear_method = LVOL_CLEAR_WITH_UNMAP; } else if (!strcasecmp(req.clear_method, "write_zeroes")) { clear_method = LVOL_CLEAR_WITH_WRITE_ZEROES; } else { spdk_jsonrpc_send_error_response(request, -EINVAL, "Invalid clean_method option"); goto cleanup; } } else { clear_method = LVOL_CLEAR_WITH_DEFAULT; } rc = vbdev_lvol_create(lvs, req.lvol_name, size, req.thin_provision, clear_method, rpc_bdev_lvol_create_cb, request); if (rc < 0) { spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); goto cleanup; } cleanup: free_rpc_bdev_lvol_create(&req); } SPDK_RPC_REGISTER("bdev_lvol_create", rpc_bdev_lvol_create, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_snapshot { char *lvol_name; char *snapshot_name; }; static void free_rpc_bdev_lvol_snapshot(struct rpc_bdev_lvol_snapshot *req) { free(req->lvol_name); free(req->snapshot_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_snapshot_decoders[] = { {"lvol_name", offsetof(struct rpc_bdev_lvol_snapshot, lvol_name), spdk_json_decode_string}, {"snapshot_name", offsetof(struct rpc_bdev_lvol_snapshot, snapshot_name), spdk_json_decode_string}, }; static void rpc_bdev_lvol_snapshot_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); 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 rpc_bdev_lvol_snapshot(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_snapshot req = {}; struct spdk_bdev *bdev; struct spdk_lvol *lvol; SPDK_INFOLOG(lvol_rpc, "Snapshotting blob\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_snapshot_decoders, SPDK_COUNTOF(rpc_bdev_lvol_snapshot_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } bdev = spdk_bdev_get_by_name(req.lvol_name); if (bdev == NULL) { SPDK_INFOLOG(lvol_rpc, "bdev '%s' does not exist\n", req.lvol_name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol == NULL) { SPDK_ERRLOG("lvol does not exist\n"); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } vbdev_lvol_create_snapshot(lvol, req.snapshot_name, rpc_bdev_lvol_snapshot_cb, request); cleanup: free_rpc_bdev_lvol_snapshot(&req); } SPDK_RPC_REGISTER("bdev_lvol_snapshot", rpc_bdev_lvol_snapshot, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_clone { char *snapshot_name; char *clone_name; }; static void free_rpc_bdev_lvol_clone(struct rpc_bdev_lvol_clone *req) { free(req->snapshot_name); free(req->clone_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_clone_decoders[] = { {"snapshot_name", offsetof(struct rpc_bdev_lvol_clone, snapshot_name), spdk_json_decode_string}, {"clone_name", offsetof(struct rpc_bdev_lvol_clone, clone_name), spdk_json_decode_string, true}, }; static void rpc_bdev_lvol_clone_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); 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 rpc_bdev_lvol_clone(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_clone req = {}; struct spdk_bdev *bdev; struct spdk_lvol *lvol; SPDK_INFOLOG(lvol_rpc, "Cloning blob\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_clone_decoders, SPDK_COUNTOF(rpc_bdev_lvol_clone_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } bdev = spdk_bdev_get_by_name(req.snapshot_name); if (bdev == NULL) { SPDK_INFOLOG(lvol_rpc, "bdev '%s' does not exist\n", req.snapshot_name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol == NULL) { SPDK_ERRLOG("lvol does not exist\n"); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } vbdev_lvol_create_clone(lvol, req.clone_name, rpc_bdev_lvol_clone_cb, request); cleanup: free_rpc_bdev_lvol_clone(&req); } SPDK_RPC_REGISTER("bdev_lvol_clone", rpc_bdev_lvol_clone, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_clone_bdev { /* name or UUID. Whichever is used, the UUID will be stored in the lvol's metadata. */ char *bdev_name; char *lvs_name; char *clone_name; }; static void free_rpc_bdev_lvol_clone_bdev(struct rpc_bdev_lvol_clone_bdev *req) { free(req->bdev_name); free(req->lvs_name); free(req->clone_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_clone_bdev_decoders[] = { { "bdev", offsetof(struct rpc_bdev_lvol_clone_bdev, bdev_name), spdk_json_decode_string, false }, { "lvs_name", offsetof(struct rpc_bdev_lvol_clone_bdev, lvs_name), spdk_json_decode_string, false }, { "clone_name", offsetof(struct rpc_bdev_lvol_clone_bdev, clone_name), spdk_json_decode_string, false }, }; static void rpc_bdev_lvol_clone_bdev(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_clone_bdev req = {}; struct spdk_bdev *bdev; struct spdk_lvol_store *lvs = NULL; struct spdk_lvol *lvol; int rc; SPDK_INFOLOG(lvol_rpc, "Cloning bdev\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_clone_bdev_decoders, SPDK_COUNTOF(rpc_bdev_lvol_clone_bdev_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "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 = vbdev_get_lvol_store_by_uuid_xor_name(NULL, req.lvs_name, &lvs); if (rc != 0) { SPDK_INFOLOG(lvol_rpc, "lvs_name '%s' not found\n", req.lvs_name); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "lvs does not exist"); goto cleanup; } bdev = spdk_bdev_get_by_name(req.bdev_name); if (bdev == NULL) { SPDK_INFOLOG(lvol_rpc, "bdev '%s' does not exist\n", req.bdev_name); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "bdev does not exist"); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol != NULL && lvol->lvol_store == lvs) { SPDK_INFOLOG(lvol_rpc, "bdev '%s' is an lvol in lvstore '%s\n", req.bdev_name, req.lvs_name); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "bdev is an lvol in same lvs as clone; " "use bdev_lvol_clone instead"); goto cleanup; } vbdev_lvol_create_bdev_clone(req.bdev_name, lvs, req.clone_name, rpc_bdev_lvol_clone_cb, request); cleanup: free_rpc_bdev_lvol_clone_bdev(&req); } SPDK_RPC_REGISTER("bdev_lvol_clone_bdev", rpc_bdev_lvol_clone_bdev, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_rename { char *old_name; char *new_name; }; static void free_rpc_bdev_lvol_rename(struct rpc_bdev_lvol_rename *req) { free(req->old_name); free(req->new_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_rename_decoders[] = { {"old_name", offsetof(struct rpc_bdev_lvol_rename, old_name), spdk_json_decode_string}, {"new_name", offsetof(struct rpc_bdev_lvol_rename, new_name), spdk_json_decode_string}, }; static void rpc_bdev_lvol_rename_cb(void *cb_arg, int lvolerrno) { struct spdk_jsonrpc_request *request = cb_arg; if (lvolerrno != 0) { goto invalid; } spdk_jsonrpc_send_bool_response(request, true); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-lvolerrno)); } static void rpc_bdev_lvol_rename(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_rename req = {}; struct spdk_bdev *bdev; struct spdk_lvol *lvol; SPDK_INFOLOG(lvol_rpc, "Renaming lvol\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_rename_decoders, SPDK_COUNTOF(rpc_bdev_lvol_rename_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } bdev = spdk_bdev_get_by_name(req.old_name); if (bdev == NULL) { SPDK_ERRLOG("bdev '%s' does not exist\n", req.old_name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol == NULL) { SPDK_ERRLOG("lvol does not exist\n"); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } vbdev_lvol_rename(lvol, req.new_name, rpc_bdev_lvol_rename_cb, request); cleanup: free_rpc_bdev_lvol_rename(&req); } SPDK_RPC_REGISTER("bdev_lvol_rename", rpc_bdev_lvol_rename, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_set_xattr { char *name; char *xattr_name; char *xattr_value; }; static void free_rpc_bdev_lvol_set_xattr(struct rpc_bdev_lvol_set_xattr *req) { free(req->name); free(req->xattr_name); free(req->xattr_value); } static const struct spdk_json_object_decoder rpc_bdev_lvol_set_xattr_decoders[] = { {"name", offsetof(struct rpc_bdev_lvol_set_xattr, name), spdk_json_decode_string}, {"xattr_name", offsetof(struct rpc_bdev_lvol_set_xattr, xattr_name), spdk_json_decode_string}, {"xattr_value", offsetof(struct rpc_bdev_lvol_set_xattr, xattr_value), spdk_json_decode_string}, }; static void rpc_bdev_lvol_set_xattr_cb(void *cb_arg, int lvolerrno) { struct spdk_jsonrpc_request *request = cb_arg; if (lvolerrno != 0) { goto invalid; } spdk_jsonrpc_send_bool_response(request, true); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-lvolerrno)); } static void rpc_bdev_lvol_set_xattr(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_set_xattr req = {}; struct spdk_bdev *bdev; struct spdk_lvol *lvol; SPDK_INFOLOG(lvol_rpc, "Setting lvol xattr\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_set_xattr_decoders, SPDK_COUNTOF(rpc_bdev_lvol_set_xattr_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } bdev = spdk_bdev_get_by_name(req.name); if (bdev == NULL) { SPDK_ERRLOG("bdev '%s' does not exist\n", req.name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol == NULL) { SPDK_ERRLOG("lvol does not exist\n"); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } vbdev_lvol_set_xattr(lvol, req.xattr_name, req.xattr_value, rpc_bdev_lvol_set_xattr_cb, request); cleanup: free_rpc_bdev_lvol_set_xattr(&req); } SPDK_RPC_REGISTER("bdev_lvol_set_xattr", rpc_bdev_lvol_set_xattr, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_get_xattr { char *name; char *xattr_name; }; static void free_rpc_bdev_lvol_get_xattr(struct rpc_bdev_lvol_get_xattr *req) { free(req->name); free(req->xattr_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_get_xattr_decoders[] = { {"name", offsetof(struct rpc_bdev_lvol_get_xattr, name), spdk_json_decode_string}, {"xattr_name", offsetof(struct rpc_bdev_lvol_get_xattr, xattr_name), spdk_json_decode_string}, }; static void rpc_bdev_lvol_get_xattr(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_get_xattr req = {}; struct spdk_json_write_ctx *w; struct spdk_bdev *bdev; struct spdk_lvol *lvol; const void *xattr_value; size_t xattr_value_len; int rc; SPDK_INFOLOG(lvol_rpc, "Getting lvol xattr\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_get_xattr_decoders, SPDK_COUNTOF(rpc_bdev_lvol_get_xattr_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } bdev = spdk_bdev_get_by_name(req.name); if (bdev == NULL) { SPDK_ERRLOG("bdev '%s' does not exist\n", req.name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol == NULL) { SPDK_ERRLOG("lvol does not exist\n"); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } rc = vbdev_lvol_get_xattr(lvol, req.xattr_name, &xattr_value, &xattr_value_len); if (rc != 0) { spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, spdk_strerror(-rc)); goto cleanup; } w = spdk_jsonrpc_begin_result(request); spdk_json_write_string(w, (const char *)xattr_value); spdk_jsonrpc_end_result(request, w); cleanup: free_rpc_bdev_lvol_get_xattr(&req); } SPDK_RPC_REGISTER("bdev_lvol_get_xattr", rpc_bdev_lvol_get_xattr, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_inflate { char *name; }; static void free_rpc_bdev_lvol_inflate(struct rpc_bdev_lvol_inflate *req) { free(req->name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_inflate_decoders[] = { {"name", offsetof(struct rpc_bdev_lvol_inflate, name), spdk_json_decode_string}, }; static void rpc_bdev_lvol_inflate_cb(void *cb_arg, int lvolerrno) { struct spdk_jsonrpc_request *request = cb_arg; if (lvolerrno != 0) { goto invalid; } spdk_jsonrpc_send_bool_response(request, true); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-lvolerrno)); } static void rpc_bdev_lvol_inflate(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_inflate req = {}; struct spdk_bdev *bdev; struct spdk_lvol *lvol; SPDK_INFOLOG(lvol_rpc, "Inflating lvol\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_inflate_decoders, SPDK_COUNTOF(rpc_bdev_lvol_inflate_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } bdev = spdk_bdev_get_by_name(req.name); if (bdev == NULL) { SPDK_ERRLOG("bdev '%s' does not exist\n", req.name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol == NULL) { SPDK_ERRLOG("lvol does not exist\n"); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } spdk_lvol_inflate(lvol, rpc_bdev_lvol_inflate_cb, request); cleanup: free_rpc_bdev_lvol_inflate(&req); } SPDK_RPC_REGISTER("bdev_lvol_inflate", rpc_bdev_lvol_inflate, SPDK_RPC_RUNTIME) static void rpc_bdev_lvol_decouple_parent(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_inflate req = {}; struct spdk_bdev *bdev; struct spdk_lvol *lvol; SPDK_INFOLOG(lvol_rpc, "Decoupling parent of lvol\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_inflate_decoders, SPDK_COUNTOF(rpc_bdev_lvol_inflate_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } bdev = spdk_bdev_get_by_name(req.name); if (bdev == NULL) { SPDK_ERRLOG("bdev '%s' does not exist\n", req.name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol == NULL) { SPDK_ERRLOG("lvol does not exist\n"); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } spdk_lvol_decouple_parent(lvol, rpc_bdev_lvol_inflate_cb, request); cleanup: free_rpc_bdev_lvol_inflate(&req); } SPDK_RPC_REGISTER("bdev_lvol_decouple_parent", rpc_bdev_lvol_decouple_parent, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_resize { char *name; uint64_t size; uint64_t size_in_mib; }; static void free_rpc_bdev_lvol_resize(struct rpc_bdev_lvol_resize *req) { free(req->name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_resize_decoders[] = { {"name", offsetof(struct rpc_bdev_lvol_resize, name), spdk_json_decode_string}, {"size", offsetof(struct rpc_bdev_lvol_resize, size), spdk_json_decode_uint64, true}, {"size_in_mib", offsetof(struct rpc_bdev_lvol_resize, size_in_mib), spdk_json_decode_uint64, true}, }; static void rpc_bdev_lvol_resize_cb(void *cb_arg, int lvolerrno) { struct spdk_jsonrpc_request *request = cb_arg; if (lvolerrno != 0) { goto invalid; } spdk_jsonrpc_send_bool_response(request, true); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-lvolerrno)); } static void rpc_bdev_lvol_resize(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_resize req = {}; struct spdk_bdev *bdev; struct spdk_lvol *lvol; uint64_t size = 0; SPDK_INFOLOG(lvol_rpc, "Resizing lvol\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_resize_decoders, SPDK_COUNTOF(rpc_bdev_lvol_resize_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } if (req.size > 0 && req.size_in_mib > 0) { SPDK_LOG_DEPRECATED(vbdev_lvol_rpc_req_size); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "size is deprecated. Specify only size_in_mib instead."); goto cleanup; } else if (req.size_in_mib > 0) { size = req.size_in_mib * 1024 * 1024; } else { SPDK_LOG_DEPRECATED(vbdev_lvol_rpc_req_size); size = req.size; } bdev = spdk_bdev_get_by_name(req.name); if (bdev == NULL) { SPDK_ERRLOG("no bdev for provided name %s\n", req.name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol == NULL) { spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } vbdev_lvol_resize(lvol, size, rpc_bdev_lvol_resize_cb, request); cleanup: free_rpc_bdev_lvol_resize(&req); } SPDK_RPC_REGISTER("bdev_lvol_resize", rpc_bdev_lvol_resize, SPDK_RPC_RUNTIME) struct rpc_set_ro_lvol_bdev { char *name; }; static void free_rpc_set_ro_lvol_bdev(struct rpc_set_ro_lvol_bdev *req) { free(req->name); } static const struct spdk_json_object_decoder rpc_set_ro_lvol_bdev_decoders[] = { {"name", offsetof(struct rpc_set_ro_lvol_bdev, name), spdk_json_decode_string}, }; static void rpc_set_ro_lvol_bdev_cb(void *cb_arg, int lvolerrno) { struct spdk_jsonrpc_request *request = cb_arg; if (lvolerrno != 0) { goto invalid; } spdk_jsonrpc_send_bool_response(request, true); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-lvolerrno)); } static void rpc_bdev_lvol_set_read_only(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_set_ro_lvol_bdev req = {}; struct spdk_bdev *bdev; struct spdk_lvol *lvol; SPDK_INFOLOG(lvol_rpc, "Setting lvol as read only\n"); if (spdk_json_decode_object(params, rpc_set_ro_lvol_bdev_decoders, SPDK_COUNTOF(rpc_set_ro_lvol_bdev_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } if (req.name == NULL) { SPDK_ERRLOG("missing name param\n"); spdk_jsonrpc_send_error_response(request, -EINVAL, "Missing name parameter"); goto cleanup; } bdev = spdk_bdev_get_by_name(req.name); if (bdev == NULL) { SPDK_ERRLOG("no bdev for provided name %s\n", req.name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol == NULL) { spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } vbdev_lvol_set_read_only(lvol, rpc_set_ro_lvol_bdev_cb, request); cleanup: free_rpc_set_ro_lvol_bdev(&req); } SPDK_RPC_REGISTER("bdev_lvol_set_read_only", rpc_bdev_lvol_set_read_only, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_delete { char *name; }; static void free_rpc_bdev_lvol_delete(struct rpc_bdev_lvol_delete *req) { free(req->name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_delete_decoders[] = { {"name", offsetof(struct rpc_bdev_lvol_delete, name), spdk_json_decode_string}, }; static void rpc_bdev_lvol_delete_cb(void *cb_arg, int lvolerrno) { struct spdk_jsonrpc_request *request = cb_arg; if (lvolerrno != 0) { goto invalid; } spdk_jsonrpc_send_bool_response(request, true); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, spdk_strerror(-lvolerrno)); } static void rpc_bdev_lvol_delete(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_delete req = {}; struct spdk_bdev *bdev; struct spdk_lvol *lvol; struct spdk_uuid uuid; char *lvs_name, *lvol_name; if (spdk_json_decode_object(params, rpc_bdev_lvol_delete_decoders, SPDK_COUNTOF(rpc_bdev_lvol_delete_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } /* lvol is not degraded, get lvol via bdev name or alias */ bdev = spdk_bdev_get_by_name(req.name); if (bdev != NULL) { lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol != NULL) { goto done; } } /* lvol is degraded, get lvol via UUID */ if (spdk_uuid_parse(&uuid, req.name) == 0) { lvol = spdk_lvol_get_by_uuid(&uuid); if (lvol != NULL) { goto done; } } /* lvol is degraded, get lvol via lvs_name/lvol_name */ lvol_name = strchr(req.name, '/'); if (lvol_name != NULL) { *lvol_name = '\0'; lvol_name++; lvs_name = req.name; lvol = spdk_lvol_get_by_names(lvs_name, lvol_name); if (lvol != NULL) { goto done; } } /* Could not find lvol, degraded or not. */ spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; done: vbdev_lvol_destroy(lvol, rpc_bdev_lvol_delete_cb, request); cleanup: free_rpc_bdev_lvol_delete(&req); } SPDK_RPC_REGISTER("bdev_lvol_delete", rpc_bdev_lvol_delete, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_get_lvstores { char *uuid; char *lvs_name; }; static void free_rpc_bdev_lvol_get_lvstores(struct rpc_bdev_lvol_get_lvstores *req) { free(req->uuid); free(req->lvs_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_get_lvstores_decoders[] = { {"uuid", offsetof(struct rpc_bdev_lvol_get_lvstores, uuid), spdk_json_decode_string, true}, {"lvs_name", offsetof(struct rpc_bdev_lvol_get_lvstores, lvs_name), spdk_json_decode_string, true}, }; static void 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; char uuid[SPDK_UUID_STRING_LEN]; bs = lvs_bdev->lvs->blobstore; cluster_size = spdk_bs_get_cluster_size(bs); spdk_json_write_object_begin(w); spdk_uuid_fmt_lower(uuid, sizeof(uuid), &lvs_bdev->lvs->uuid); spdk_json_write_named_string(w, "uuid", uuid); spdk_json_write_named_string(w, "name", lvs_bdev->lvs->name); spdk_json_write_named_string(w, "base_bdev", spdk_bdev_get_name(lvs_bdev->bdev)); spdk_json_write_named_uint64(w, "total_data_clusters", spdk_bs_total_data_cluster_count(bs)); spdk_json_write_named_uint64(w, "free_clusters", spdk_bs_free_cluster_count(bs)); spdk_json_write_named_uint64(w, "block_size", spdk_bs_get_io_unit_size(bs)); spdk_json_write_named_uint64(w, "cluster_size", cluster_size); spdk_json_write_object_end(w); } static void rpc_bdev_lvol_get_lvstores(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_get_lvstores 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_bdev_lvol_get_lvstores_decoders, SPDK_COUNTOF(rpc_bdev_lvol_get_lvstores_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "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 = vbdev_get_lvol_store_by_uuid_xor_name(req.uuid, req.lvs_name, &lvs); if (rc != 0) { spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); goto cleanup; } lvs_bdev = vbdev_get_lvs_bdev_by_lvs(lvs); if (lvs_bdev == NULL) { spdk_jsonrpc_send_error_response(request, ENODEV, spdk_strerror(-ENODEV)); goto cleanup; } } w = spdk_jsonrpc_begin_result(request); spdk_json_write_array_begin(w); if (lvs_bdev != NULL) { 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)) { rpc_dump_lvol_store_info(w, lvs_bdev); } } spdk_json_write_array_end(w); spdk_jsonrpc_end_result(request, w); cleanup: free_rpc_bdev_lvol_get_lvstores(&req); } SPDK_RPC_REGISTER("bdev_lvol_get_lvstores", rpc_bdev_lvol_get_lvstores, SPDK_RPC_RUNTIME) SPDK_RPC_REGISTER_ALIAS_DEPRECATED(bdev_lvol_get_lvstores, get_lvol_stores) struct rpc_bdev_lvol_get_lvols { char *lvs_uuid; char *lvs_name; }; static void free_rpc_bdev_lvol_get_lvols(struct rpc_bdev_lvol_get_lvols *req) { free(req->lvs_uuid); free(req->lvs_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_get_lvols_decoders[] = { {"lvs_uuid", offsetof(struct rpc_bdev_lvol_get_lvols, lvs_uuid), spdk_json_decode_string, true}, {"lvs_name", offsetof(struct rpc_bdev_lvol_get_lvols, lvs_name), spdk_json_decode_string, true}, }; static void rpc_dump_lvol(struct spdk_json_write_ctx *w, struct spdk_lvol *lvol) { struct spdk_lvol_store *lvs = lvol->lvol_store; char uuid[SPDK_UUID_STRING_LEN]; spdk_json_write_object_begin(w); spdk_json_write_named_string_fmt(w, "alias", "%s/%s", lvs->name, lvol->name); spdk_json_write_named_string(w, "uuid", lvol->uuid_str); spdk_json_write_named_string(w, "name", lvol->name); spdk_json_write_named_string(w, "creation_time", lvol->creation_time); spdk_json_write_named_bool(w, "is_thin_provisioned", spdk_blob_is_thin_provisioned(lvol->blob)); spdk_json_write_named_bool(w, "is_snapshot", spdk_blob_is_snapshot(lvol->blob)); spdk_json_write_named_bool(w, "is_clone", spdk_blob_is_clone(lvol->blob)); spdk_json_write_named_bool(w, "is_esnap_clone", spdk_blob_is_esnap_clone(lvol->blob)); spdk_json_write_named_bool(w, "is_degraded", spdk_blob_is_degraded(lvol->blob)); spdk_json_write_named_object_begin(w, "lvs"); spdk_json_write_named_string(w, "name", lvs->name); spdk_uuid_fmt_lower(uuid, sizeof(uuid), &lvs->uuid); spdk_json_write_named_string(w, "uuid", uuid); spdk_json_write_object_end(w); spdk_json_write_object_end(w); } static void rpc_dump_lvols(struct spdk_json_write_ctx *w, struct lvol_store_bdev *lvs_bdev) { struct spdk_lvol_store *lvs = lvs_bdev->lvs; struct spdk_lvol *lvol; TAILQ_FOREACH(lvol, &lvs->lvols, link) { rpc_dump_lvol(w, lvol); } } static void rpc_bdev_lvol_get_lvols(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_get_lvols 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_bdev_lvol_get_lvols_decoders, SPDK_COUNTOF(rpc_bdev_lvol_get_lvols_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "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 = vbdev_get_lvol_store_by_uuid_xor_name(req.lvs_uuid, req.lvs_name, &lvs); if (rc != 0) { spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); goto cleanup; } lvs_bdev = vbdev_get_lvs_bdev_by_lvs(lvs); if (lvs_bdev == NULL) { spdk_jsonrpc_send_error_response(request, ENODEV, spdk_strerror(-ENODEV)); goto cleanup; } } w = spdk_jsonrpc_begin_result(request); spdk_json_write_array_begin(w); if (lvs_bdev != NULL) { rpc_dump_lvols(w, lvs_bdev); } else { for (lvs_bdev = vbdev_lvol_store_first(); lvs_bdev != NULL; lvs_bdev = vbdev_lvol_store_next(lvs_bdev)) { rpc_dump_lvols(w, lvs_bdev); } } spdk_json_write_array_end(w); spdk_jsonrpc_end_result(request, w); cleanup: free_rpc_bdev_lvol_get_lvols(&req); } SPDK_RPC_REGISTER("bdev_lvol_get_lvols", rpc_bdev_lvol_get_lvols, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_grow_lvstore { char *uuid; char *lvs_name; }; static void free_rpc_bdev_lvol_grow_lvstore(struct rpc_bdev_lvol_grow_lvstore *req) { free(req->uuid); free(req->lvs_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_grow_lvstore_decoders[] = { {"uuid", offsetof(struct rpc_bdev_lvol_grow_lvstore, uuid), spdk_json_decode_string, true}, {"lvs_name", offsetof(struct rpc_bdev_lvol_grow_lvstore, lvs_name), spdk_json_decode_string, true}, }; static void rpc_bdev_lvol_grow_lvstore_cb(void *cb_arg, int lvserrno) { struct spdk_jsonrpc_request *request = cb_arg; if (lvserrno != 0) { goto invalid; } spdk_jsonrpc_send_bool_response(request, true); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-lvserrno)); } static void rpc_bdev_lvol_grow_lvstore(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_grow_lvstore req = {}; struct spdk_lvol_store *lvs = NULL; int rc; if (spdk_json_decode_object(params, rpc_bdev_lvol_grow_lvstore_decoders, SPDK_COUNTOF(rpc_bdev_lvol_grow_lvstore_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "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 = vbdev_get_lvol_store_by_uuid_xor_name(req.uuid, req.lvs_name, &lvs); if (rc != 0) { spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); goto cleanup; } vbdev_lvs_grow(lvs, rpc_bdev_lvol_grow_lvstore_cb, request); cleanup: free_rpc_bdev_lvol_grow_lvstore(&req); } SPDK_RPC_REGISTER("bdev_lvol_grow_lvstore", rpc_bdev_lvol_grow_lvstore, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_shallow_copy { char *src_lvol_name; char *dst_bdev_name; }; static void free_rpc_bdev_lvol_shallow_copy(struct rpc_bdev_lvol_shallow_copy *req) { free(req->src_lvol_name); free(req->dst_bdev_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_shallow_copy_decoders[] = { {"src_lvol_name", offsetof(struct rpc_bdev_lvol_shallow_copy, src_lvol_name), spdk_json_decode_string}, {"dst_bdev_name", offsetof(struct rpc_bdev_lvol_shallow_copy, dst_bdev_name), spdk_json_decode_string}, }; static void rpc_bdev_lvol_shallow_copy_cb(void *cb_arg, int lvolerrno) { struct spdk_jsonrpc_request *request = cb_arg; if (lvolerrno != 0) { goto invalid; } spdk_jsonrpc_send_bool_response(request, true); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-lvolerrno)); } static void rpc_bdev_lvol_shallow_copy(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_shallow_copy req = {}; struct spdk_lvol *src_lvol; struct spdk_bdev *src_lvol_bdev; struct spdk_bdev *dst_bdev; SPDK_INFOLOG(lvol_rpc, "Shallow copying lvol\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_shallow_copy_decoders, SPDK_COUNTOF(rpc_bdev_lvol_shallow_copy_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } src_lvol_bdev = spdk_bdev_get_by_name(req.src_lvol_name); if (src_lvol_bdev == NULL) { SPDK_ERRLOG("lvol bdev '%s' does not exist\n", req.src_lvol_name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } src_lvol = vbdev_lvol_get_from_bdev(src_lvol_bdev); if (src_lvol == NULL) { SPDK_ERRLOG("lvol does not exist\n"); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } dst_bdev = spdk_bdev_get_by_name(req.dst_bdev_name); if (dst_bdev == NULL) { SPDK_ERRLOG("bdev '%s' does not exist\n", req.dst_bdev_name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } vbdev_lvol_shallow_copy(src_lvol, req.dst_bdev_name, rpc_bdev_lvol_shallow_copy_cb, request); cleanup: free_rpc_bdev_lvol_shallow_copy(&req); } SPDK_RPC_REGISTER("bdev_lvol_shallow_copy", rpc_bdev_lvol_shallow_copy, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_shallow_copy_status { char *src_lvol_name; }; static void free_rpc_bdev_lvol_shallow_copy_status(struct rpc_bdev_lvol_shallow_copy_status *req) { free(req->src_lvol_name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_shallow_copy_status_decoders[] = { {"src_lvol_name", offsetof(struct rpc_bdev_lvol_shallow_copy_status, src_lvol_name), spdk_json_decode_string}, }; static void rpc_bdev_lvol_shallow_copy_status(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_shallow_copy_status req = {}; struct spdk_bdev *src_lvol_bdev; struct spdk_lvol *src_lvol; struct spdk_json_write_ctx *w; uint64_t copied_clusters, total_clusters; int result; SPDK_INFOLOG(lvol_rpc, "Shallow copy status\n"); if (spdk_json_decode_object(params, rpc_bdev_lvol_shallow_copy_status_decoders, SPDK_COUNTOF(rpc_bdev_lvol_shallow_copy_status_decoders), &req)) { SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n"); spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "spdk_json_decode_object failed"); goto cleanup; } src_lvol_bdev = spdk_bdev_get_by_name(req.src_lvol_name); if (src_lvol_bdev == NULL) { SPDK_ERRLOG("lvol bdev '%s' does not exist\n", req.src_lvol_name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } src_lvol = vbdev_lvol_get_from_bdev(src_lvol_bdev); if (src_lvol == NULL) { SPDK_ERRLOG("lvol does not exist\n"); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } copied_clusters = spdk_blob_get_shallow_copy_copied_clusters(src_lvol->blob); total_clusters = spdk_blob_get_shallow_copy_total_clusters(src_lvol->blob); result = spdk_blob_get_shallow_copy_result(src_lvol->blob); w = spdk_jsonrpc_begin_result(request); spdk_json_write_object_begin(w); spdk_json_write_named_string_fmt(w, "progress", "%lu/%lu", copied_clusters, total_clusters); if (result > 0) { spdk_json_write_named_string(w, "state", "none"); } else if (copied_clusters < total_clusters && result == 0) { spdk_json_write_named_string(w, "state", "in progress"); } else if (copied_clusters == total_clusters && result == 0) { spdk_json_write_named_string(w, "state", "complete"); } else { spdk_json_write_named_string(w, "state", "error"); spdk_json_write_named_string(w, "error", spdk_strerror(-result)); } spdk_json_write_object_end(w); spdk_jsonrpc_end_result(request, w); cleanup: free_rpc_bdev_lvol_shallow_copy_status(&req); } SPDK_RPC_REGISTER("bdev_lvol_shallow_copy_status", rpc_bdev_lvol_shallow_copy_status, SPDK_RPC_RUNTIME) struct rpc_bdev_lvol_get_fragmap { char *name; uint64_t offset; uint64_t size; }; static void free_rpc_bdev_lvol_get_fragmap(struct rpc_bdev_lvol_get_fragmap *r) { free(r->name); } static const struct spdk_json_object_decoder rpc_bdev_lvol_get_fragmap_decoders[] = { {"name", offsetof(struct rpc_bdev_lvol_get_fragmap, name), spdk_json_decode_string, true}, {"offset", offsetof(struct rpc_bdev_lvol_get_fragmap, offset), spdk_json_decode_uint64, true}, {"size", offsetof(struct rpc_bdev_lvol_get_fragmap, size), spdk_json_decode_uint64, true}, }; static void rpc_bdev_lvol_get_fragmap_cb(void *cb_arg, struct spdk_fragmap *fragmap, int lvolerrno) { struct spdk_json_write_ctx *w; struct spdk_jsonrpc_request *request = cb_arg; char *encoded; if (lvolerrno != 0) { goto invalid; } encoded = spdk_bit_array_to_base64_string(fragmap->map); if (encoded == NULL) { SPDK_ERRLOG("Failed to encode fragmap to base64 string\n"); lvolerrno = -EINVAL; goto invalid; } w = spdk_jsonrpc_begin_result(request); spdk_json_write_object_begin(w); spdk_json_write_named_uint64(w, "cluster_size", fragmap->cluster_size); spdk_json_write_named_uint64(w, "num_clusters", fragmap->num_clusters); spdk_json_write_named_uint64(w, "num_allocated_clusters", fragmap->num_allocated_clusters); spdk_json_write_named_string(w, "fragmap", encoded); spdk_json_write_object_end(w); spdk_jsonrpc_end_result(request, w); free(encoded); return; invalid: spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-lvolerrno)); } static void rpc_bdev_lvol_get_fragmap(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_lvol_get_fragmap req = {}; struct spdk_bdev *bdev; struct spdk_lvol *lvol; if (spdk_json_decode_object(params, rpc_bdev_lvol_get_fragmap_decoders, SPDK_COUNTOF(rpc_bdev_lvol_get_fragmap_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; } bdev = spdk_bdev_get_by_name(req.name); if (bdev == NULL) { SPDK_ERRLOG("bdev '%s' does not exist\n", req.name); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } lvol = vbdev_lvol_get_from_bdev(bdev); if (lvol == NULL) { SPDK_ERRLOG("lvol does not exist\n"); spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); goto cleanup; } vbdev_lvol_get_fragmap(lvol, req.offset, req.size, rpc_bdev_lvol_get_fragmap_cb, request); cleanup: free_rpc_bdev_lvol_get_fragmap(&req); } SPDK_RPC_REGISTER("bdev_lvol_get_fragmap", rpc_bdev_lvol_get_fragmap, SPDK_RPC_RUNTIME)