diff --git a/include/spdk/lvol.h b/include/spdk/lvol.h index ee6c3d96d..224a2c63a 100644 --- a/include/spdk/lvol.h +++ b/include/spdk/lvol.h @@ -161,6 +161,26 @@ int spdk_lvs_destroy(struct spdk_lvol_store *lvol_store, */ int spdk_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); +/** + * \brief Create snapshot of given lvol + * \param lvol Handle to lvol + * \param snapshot_name Name of created snapshot + * \param cb_fn Completion callback + * \param cb_arg Completion callback custom arguments + */ +void spdk_lvol_create_snapshot(struct spdk_lvol *lvol, const char *snapshot_name, + spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg); + +/** + * \brief Create clone of given snapshot + * \param lvol Handle to lvol snapshot + * \param clone_name Name of created clone + * \param cb_fn Completion callback + * \param cb_arg Completion callback custom arguments + */ +void spdk_lvol_create_clone(struct spdk_lvol *lvol, const char *clone_name, + spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg); + /** * \brief Renames lvol with new_name. diff --git a/include/spdk_internal/bdev.h b/include/spdk_internal/bdev.h index 39d434a1c..b3bbcaf35 100644 --- a/include/spdk_internal/bdev.h +++ b/include/spdk_internal/bdev.h @@ -448,7 +448,7 @@ struct spdk_bdev_io { int spdk_bdev_register(struct spdk_bdev *bdev); void spdk_bdev_unregister(struct spdk_bdev *bdev, spdk_bdev_unregister_cb cb_fn, void *cb_arg); -void spdk_bdev_unregister_done(struct spdk_bdev *bdev, int bdeverrno); +void spdk_bdev_destruct_done(struct spdk_bdev *bdev, int bdeverrno); int spdk_vbdev_register(struct spdk_bdev *vbdev, struct spdk_bdev **base_bdevs, int base_bdev_count); diff --git a/lib/bdev/bdev.c b/lib/bdev/bdev.c index d98a2f47b..614e34e68 100644 --- a/lib/bdev/bdev.c +++ b/lib/bdev/bdev.c @@ -2648,7 +2648,7 @@ spdk_vbdev_register(struct spdk_bdev *vbdev, struct spdk_bdev **base_bdevs, int } void -spdk_bdev_unregister_done(struct spdk_bdev *bdev, int bdeverrno) +spdk_bdev_destruct_done(struct spdk_bdev *bdev, int bdeverrno) { if (bdev->unregister_cb != NULL) { bdev->unregister_cb(bdev->unregister_ctx, bdeverrno); diff --git a/lib/bdev/lvol/vbdev_lvol.c b/lib/bdev/lvol/vbdev_lvol.c index cbdd37524..d073792a6 100644 --- a/lib/bdev/lvol/vbdev_lvol.c +++ b/lib/bdev/lvol/vbdev_lvol.c @@ -523,9 +523,13 @@ _vbdev_lvol_destroy_cb(void *cb_arg, int lvserrno) { struct spdk_bdev *bdev = cb_arg; + if (lvserrno == -EBUSY) { + /* TODO: Handle reporting error to spdk_bdev_unregister */ + } + SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Lvol destroyed\n"); - spdk_bdev_unregister_done(bdev, lvserrno); + spdk_bdev_destruct_done(bdev, lvserrno); free(bdev->name); free(bdev); } @@ -538,7 +542,7 @@ _vbdev_lvol_destroy_after_close_cb(void *cb_arg, int lvserrno) if (lvserrno != 0) { SPDK_INFOLOG(SPDK_LOG_VBDEV_LVOL, "Could not close Lvol %s\n", lvol->unique_id); - spdk_bdev_unregister_done(bdev, lvserrno); + spdk_bdev_destruct_done(bdev, lvserrno); free(bdev->name); free(bdev); return; @@ -619,11 +623,13 @@ static bool vbdev_lvol_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type) { switch (io_type) { - case SPDK_BDEV_IO_TYPE_READ: case SPDK_BDEV_IO_TYPE_WRITE: - case SPDK_BDEV_IO_TYPE_RESET: case SPDK_BDEV_IO_TYPE_UNMAP: case SPDK_BDEV_IO_TYPE_WRITE_ZEROES: + /* TODO: Report false if snapshot */ + return true; + case SPDK_BDEV_IO_TYPE_RESET: + case SPDK_BDEV_IO_TYPE_READ: return true; default: return false; @@ -887,6 +893,42 @@ vbdev_lvol_create(struct spdk_lvol_store *lvs, const char *name, size_t sz, 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) { diff --git a/lib/bdev/lvol/vbdev_lvol.h b/lib/bdev/lvol/vbdev_lvol.h index 93fd7cb1c..152ebdb80 100644 --- a/lib/bdev/lvol/vbdev_lvol.h +++ b/lib/bdev/lvol/vbdev_lvol.h @@ -55,6 +55,12 @@ void vbdev_lvs_unload(struct spdk_lvol_store *lvs, spdk_lvs_op_complete cb_fn, v int vbdev_lvol_create(struct spdk_lvol_store *lvs, const char *name, size_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); + int vbdev_lvol_resize(char *name, size_t sz, spdk_lvol_op_complete cb_fn, void *cb_arg); void vbdev_lvol_rename(struct spdk_lvol *lvol, const char *new_lvol_name, diff --git a/lib/bdev/lvol/vbdev_lvol_rpc.c b/lib/bdev/lvol/vbdev_lvol_rpc.c index fba323a18..3203f6130 100644 --- a/lib/bdev/lvol/vbdev_lvol_rpc.c +++ b/lib/bdev/lvol/vbdev_lvol_rpc.c @@ -418,6 +418,182 @@ invalid: SPDK_RPC_REGISTER("construct_lvol_bdev", spdk_rpc_construct_lvol_bdev) +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, true}, + {"snapshot_name", offsetof(struct rpc_snapshot_lvol_bdev, snapshot_name), spdk_json_decode_string, true}, +}; + +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_array_begin(w); + spdk_json_write_string(w, lvol->bdev->name); + spdk_json_write_array_end(w); + 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) + +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_array_begin(w); + spdk_json_write_string(w, lvol->bdev->name); + spdk_json_write_array_end(w); + 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) + struct rpc_rename_lvol_bdev { char *old_name; char *new_name; diff --git a/lib/lvol/lvol.c b/lib/lvol/lvol.c index 283c68a79..35d008602 100644 --- a/lib/lvol/lvol.c +++ b/lib/lvol/lvol.c @@ -1103,6 +1103,117 @@ spdk_lvol_create(struct spdk_lvol_store *lvs, const char *name, uint64_t sz, return 0; } +void +spdk_lvol_create_snapshot(struct spdk_lvol *origlvol, const char *snapshot_name, + spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg) +{ + struct spdk_lvol_store *lvs; + struct spdk_lvol *newlvol; + struct spdk_blob *origblob; + struct spdk_lvol_with_handle_req *req; + struct spdk_blob_xattr_opts snapshot_xattrs; + char *xattr_names = LVOL_NAME; + int rc; + + if (origlvol == NULL) { + SPDK_INFOLOG(SPDK_LOG_LVOL, "Lvol not provided.\n"); + cb_fn(cb_arg, NULL, -EINVAL); + return; + } + + origblob = origlvol->blob; + lvs = origlvol->lvol_store; + rc = _spdk_lvs_verify_lvol_name(lvs, snapshot_name); + if (rc < 0) { + cb_fn(cb_arg, NULL, rc); + return; + } + + req = calloc(1, sizeof(*req)); + if (!req) { + SPDK_ERRLOG("Cannot alloc memory for lvol request pointer\n"); + cb_fn(cb_arg, NULL, -ENOMEM); + return; + } + + newlvol = calloc(1, sizeof(*newlvol)); + if (!newlvol) { + SPDK_ERRLOG("Cannot alloc memory for lvol base pointer\n"); + free(req); + cb_fn(cb_arg, NULL, -ENOMEM); + return; + } + + newlvol->lvol_store = origlvol->lvol_store; + strncpy(newlvol->name, snapshot_name, SPDK_LVOL_NAME_MAX); + snapshot_xattrs.count = 1; + snapshot_xattrs.ctx = newlvol; + snapshot_xattrs.names = &xattr_names; + snapshot_xattrs.get_value = spdk_lvol_get_xattr_value; + req->lvol = newlvol; + req->cb_fn = cb_fn; + req->cb_arg = cb_arg; + + spdk_bs_create_snapshot(lvs->blobstore, spdk_blob_get_id(origblob), &snapshot_xattrs, + _spdk_lvol_create_cb, req); +} + +void +spdk_lvol_create_clone(struct spdk_lvol *origlvol, const char *clone_name, + spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg) +{ + struct spdk_lvol *newlvol; + struct spdk_lvol_with_handle_req *req; + struct spdk_lvol_store *lvs; + struct spdk_blob *origblob; + struct spdk_blob_xattr_opts clone_xattrs; + char *xattr_names = LVOL_NAME; + int rc; + + if (origlvol == NULL) { + SPDK_INFOLOG(SPDK_LOG_LVOL, "Lvol not provided.\n"); + cb_fn(cb_arg, NULL, -EINVAL); + return; + } + + origblob = origlvol->blob; + lvs = origlvol->lvol_store; + rc = _spdk_lvs_verify_lvol_name(lvs, clone_name); + if (rc < 0) { + cb_fn(cb_arg, NULL, rc); + return; + } + + req = calloc(1, sizeof(*req)); + if (!req) { + SPDK_ERRLOG("Cannot alloc memory for lvol request pointer\n"); + cb_fn(cb_arg, NULL, -ENOMEM); + return; + } + + newlvol = calloc(1, sizeof(*newlvol)); + if (!newlvol) { + SPDK_ERRLOG("Cannot alloc memory for lvol base pointer\n"); + free(req); + cb_fn(cb_arg, NULL, -ENOMEM); + return; + } + + newlvol->lvol_store = lvs; + strncpy(newlvol->name, clone_name, SPDK_LVOL_NAME_MAX); + clone_xattrs.count = 1; + clone_xattrs.ctx = newlvol; + clone_xattrs.names = &xattr_names; + clone_xattrs.get_value = spdk_lvol_get_xattr_value; + req->lvol = newlvol; + req->cb_fn = cb_fn; + req->cb_arg = cb_arg; + + spdk_bs_create_clone(lvs->blobstore, spdk_blob_get_id(origblob), &clone_xattrs, + _spdk_lvol_create_cb, + req); +} + static void _spdk_lvol_resize_done(void *cb_arg, int lvolerrno) { diff --git a/scripts/rpc.py b/scripts/rpc.py index 87b297b38..e1c37c0c2 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -509,6 +509,24 @@ if __name__ == "__main__": p.add_argument('size', help='size in MiB for this bdev', type=int) p.set_defaults(func=construct_lvol_bdev) + @call_cmd + def snapshot_lvol_bdev(args): + rpc.lvol.snapshot_lvol_bdev(args.client, args) + + p = subparsers.add_parser('snapshot_lvol_bdev', help='Create a snapshot of an lvol bdev') + p.add_argument('lvol_name', help='lvol bdev name') + p.add_argument('snapshot_name', help='lvol snapshot name') + p.set_defaults(func=snapshot_lvol_bdev) + + @call_cmd + def clone_lvol_bdev(args): + rpc.lvol.clone_lvol_bdev(args.client, args) + + p = subparsers.add_parser('clone_lvol_bdev', help='Create a clone of an lvol snapshot') + p.add_argument('snapshot_name', help='lvol snapshot name') + p.add_argument('clone_name', help='lvol clone name') + p.set_defaults(func=clone_lvol_bdev) + @call_cmd def rename_lvol_bdev(args): rpc.lvol.rename_lvol_bdev(args.client, args) diff --git a/scripts/rpc/lvol.py b/scripts/rpc/lvol.py index 0696aaeb2..542711382 100755 --- a/scripts/rpc/lvol.py +++ b/scripts/rpc/lvol.py @@ -28,6 +28,22 @@ def construct_lvol_bdev(client, args): return client.call('construct_lvol_bdev', params) +def snapshot_lvol_bdev(client, args): + params = { + 'lvol_name': args.lvol_name, + 'snapshot_name': args.snapshot_name + } + return client.call('snapshot_lvol_bdev', params) + + +def clone_lvol_bdev(client, args): + params = { + 'snapshot_name': args.snapshot_name, + 'clone_name': args.clone_name + } + return client.call('clone_lvol_bdev', params) + + def rename_lvol_bdev(client, args): params = { 'old_name': args.old_name, diff --git a/test/unit/lib/bdev/vbdev_lvol.c/vbdev_lvol_ut.c b/test/unit/lib/bdev/vbdev_lvol.c/vbdev_lvol_ut.c index 152c344f6..d3ffd9af3 100644 --- a/test/unit/lib/bdev/vbdev_lvol.c/vbdev_lvol_ut.c +++ b/test/unit/lib/bdev/vbdev_lvol.c/vbdev_lvol_ut.c @@ -104,7 +104,7 @@ spdk_bdev_alias_del(struct spdk_bdev *bdev, const char *alias) } void -spdk_bdev_unregister_done(struct spdk_bdev *bdev, int bdeverrno) +spdk_bdev_destruct_done(struct spdk_bdev *bdev, int bdeverrno) { } @@ -577,6 +577,28 @@ spdk_lvol_create(struct spdk_lvol_store *lvs, const char *name, size_t sz, return 0; } +void +spdk_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 *snap; + + snap = _lvol_create(lvol->lvol_store); + strncpy(snap->name, snapshot_name, SPDK_LVOL_NAME_MAX); + cb_fn(cb_arg, snap, 0); +} + +void +spdk_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 *clone; + + clone = _lvol_create(lvol->lvol_store); + strncpy(clone->name, clone_name, SPDK_LVS_NAME_MAX); + cb_fn(cb_arg, clone, 0); +} + static void lvol_store_op_complete(void *cb_arg, int lvserrno) { @@ -696,6 +718,139 @@ ut_lvol_init(void) free(g_base_bdev); } +static void +ut_lvol_snapshot(void) +{ + int sz = 10; + int rc; + struct spdk_lvol *lvol = NULL; + + g_lvs = calloc(1, sizeof(*g_lvs)); + SPDK_CU_ASSERT_FATAL(g_lvs != NULL); + TAILQ_INIT(&g_lvs->lvols); + g_lvs_bdev = calloc(1, sizeof(*g_lvs_bdev)); + SPDK_CU_ASSERT_FATAL(g_lvs_bdev != NULL); + g_base_bdev = calloc(1, sizeof(*g_base_bdev)); + SPDK_CU_ASSERT_FATAL(g_base_bdev != NULL); + + /* Assign name to lvs */ + strncpy(g_lvs->name, "UNIT_TEST_LVS_NAME", SPDK_LVS_NAME_MAX); + SPDK_CU_ASSERT_FATAL(g_lvs->name != NULL); + + g_lvs_bdev->lvs = g_lvs; + g_lvs_bdev->bdev = g_base_bdev; + + spdk_uuid_generate(&g_lvs->uuid); + + TAILQ_INSERT_TAIL(&g_spdk_lvol_pairs, g_lvs_bdev, lvol_stores); + + /* Successful lvol create */ + g_lvolerrno = -1; + rc = vbdev_lvol_create(g_lvs, "lvol", sz, false, vbdev_lvol_create_complete, NULL); + SPDK_CU_ASSERT_FATAL(rc == 0); + CU_ASSERT(g_lvol != NULL); + CU_ASSERT(g_lvolerrno == 0); + + lvol = g_lvol; + + /* Successful snap create */ + vbdev_lvol_create_snapshot(lvol, "snap", vbdev_lvol_create_complete, NULL); + SPDK_CU_ASSERT_FATAL(rc == 0); + CU_ASSERT(g_lvol != NULL); + CU_ASSERT(g_lvolerrno == 0); + + /* Successful lvol destruct */ + vbdev_lvol_destruct(g_lvol); + CU_ASSERT(g_lvol == NULL); + + /* Successful snap destruct */ + g_lvol = lvol; + vbdev_lvol_destruct(g_lvol); + CU_ASSERT(g_lvol == NULL); + + TAILQ_REMOVE(&g_spdk_lvol_pairs, g_lvs_bdev, lvol_stores); + + free(g_lvs); + free(g_lvs_bdev); + free(g_base_bdev); +} + +static void +ut_lvol_clone(void) +{ + int sz = 10; + int rc; + struct spdk_lvol *lvol = NULL; + struct spdk_lvol *snap = NULL; + struct spdk_lvol *clone = NULL; + + g_lvs = calloc(1, sizeof(*g_lvs)); + SPDK_CU_ASSERT_FATAL(g_lvs != NULL); + TAILQ_INIT(&g_lvs->lvols); + g_lvs_bdev = calloc(1, sizeof(*g_lvs_bdev)); + SPDK_CU_ASSERT_FATAL(g_lvs_bdev != NULL); + g_base_bdev = calloc(1, sizeof(*g_base_bdev)); + SPDK_CU_ASSERT_FATAL(g_base_bdev != NULL); + + /* Assign name to lvs */ + strncpy(g_lvs->name, "UNIT_TEST_LVS_NAME", SPDK_LVS_NAME_MAX); + SPDK_CU_ASSERT_FATAL(g_lvs->name != NULL); + + g_lvs_bdev->lvs = g_lvs; + g_lvs_bdev->bdev = g_base_bdev; + + spdk_uuid_generate(&g_lvs->uuid); + + TAILQ_INSERT_TAIL(&g_spdk_lvol_pairs, g_lvs_bdev, lvol_stores); + + /* Successful lvol create */ + g_lvolerrno = -1; + rc = vbdev_lvol_create(g_lvs, "lvol", sz, false, vbdev_lvol_create_complete, NULL); + SPDK_CU_ASSERT_FATAL(rc == 0); + CU_ASSERT(g_lvol != NULL); + CU_ASSERT(g_lvolerrno == 0); + + lvol = g_lvol; + + /* Successful snap create */ + vbdev_lvol_create_snapshot(lvol, "snap", vbdev_lvol_create_complete, NULL); + SPDK_CU_ASSERT_FATAL(rc == 0); + CU_ASSERT(g_lvol != NULL); + CU_ASSERT(g_lvolerrno == 0); + + snap = g_lvol; + + /* Successful clone create */ + vbdev_lvol_create_clone(snap, "clone", vbdev_lvol_create_complete, NULL); + + SPDK_CU_ASSERT_FATAL(rc == 0); + CU_ASSERT(g_lvol != NULL); + CU_ASSERT(g_lvolerrno == 0); + + clone = g_lvol; + + /* Successful lvol destruct */ + g_lvol = lvol; + vbdev_lvol_destruct(g_lvol); + CU_ASSERT(g_lvol == NULL); + + /* Successful clone destruct */ + g_lvol = clone; + vbdev_lvol_destruct(g_lvol); + CU_ASSERT(g_lvol == NULL); + + /* Successful snap destruct */ + g_lvol = snap; + vbdev_lvol_destruct(g_lvol); + CU_ASSERT(g_lvol == NULL); + + TAILQ_REMOVE(&g_spdk_lvol_pairs, g_lvs_bdev, lvol_stores); + + free(g_lvs); + free(g_lvs_bdev); + free(g_base_bdev); +} + static void ut_lvol_hotremove(void) { @@ -1233,6 +1388,8 @@ int main(int argc, char **argv) if ( CU_add_test(suite, "ut_lvs_init", ut_lvs_init) == NULL || CU_add_test(suite, "ut_lvol_init", ut_lvol_init) == NULL || + CU_add_test(suite, "ut_lvol_snapshot", ut_lvol_snapshot) == NULL || + CU_add_test(suite, "ut_lvol_clone", ut_lvol_clone) == NULL || CU_add_test(suite, "ut_lvs_destroy", ut_lvs_destroy) == NULL || CU_add_test(suite, "ut_lvs_unload", ut_lvs_unload) == NULL || CU_add_test(suite, "ut_lvol_resize", ut_lvol_resize) == NULL || diff --git a/test/unit/lib/lvol/lvol.c/lvol_ut.c b/test/unit/lib/lvol/lvol.c/lvol_ut.c index 1d370aed5..9de9c5f40 100644 --- a/test/unit/lib/lvol/lvol.c/lvol_ut.c +++ b/test/unit/lib/lvol/lvol.c/lvol_ut.c @@ -59,6 +59,7 @@ const char *uuid = "828d9766-ae50-11e7-bd8d-001e67edf350"; struct spdk_blob { spdk_blob_id id; uint32_t ref; + struct spdk_blob_store *bs; int close_status; int open_status; int load_status; @@ -399,11 +400,28 @@ spdk_bs_create_blob_ext(struct spdk_blob_store *bs, const struct spdk_blob_opts if (opts != NULL && opts->thin_provision) { b->thin_provisioned = true; } + b->bs = bs; TAILQ_INSERT_TAIL(&bs->blobs, b, link); cb_fn(cb_arg, b->id, 0); } +void +spdk_bs_create_snapshot(struct spdk_blob_store *bs, spdk_blob_id blobid, + const struct spdk_blob_xattr_opts *snapshot_xattrs, + spdk_blob_op_with_id_complete cb_fn, void *cb_arg) +{ + spdk_bs_create_blob_ext(bs, NULL, cb_fn, cb_arg); +} + +void +spdk_bs_create_clone(struct spdk_blob_store *bs, spdk_blob_id blobid, + const struct spdk_blob_xattr_opts *clone_xattrs, + spdk_blob_op_with_id_complete cb_fn, void *cb_arg) +{ + spdk_bs_create_blob_ext(bs, NULL, cb_fn, cb_arg); +} + static void _lvol_send_msg(spdk_thread_fn fn, void *ctx, void *thread_ctx) { @@ -1281,6 +1299,265 @@ lvol_open(void) spdk_free_thread(); } +static void +lvol_snapshot(void) +{ + struct lvol_ut_bs_dev dev; + struct spdk_lvol *lvol; + struct spdk_lvs_opts opts; + int rc = 0; + + init_dev(&dev); + + spdk_allocate_thread(_lvol_send_msg, NULL, NULL, NULL, NULL); + + spdk_lvs_opts_init(&opts); + strncpy(opts.name, "lvs", sizeof(opts.name)); + + g_lvserrno = -1; + rc = spdk_lvs_init(&dev.bs_dev, &opts, lvol_store_op_with_handle_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol_store != NULL); + + spdk_lvol_create(g_lvol_store, "lvol", 10, true, lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + + lvol = g_lvol; + + spdk_lvol_create_snapshot(lvol, "snap", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + CU_ASSERT_STRING_EQUAL(g_lvol->name, "snap"); + + /* Lvol has to be closed (or destroyed) before unloading lvol store. */ + spdk_lvol_close(g_lvol, close_cb, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = -1; + + spdk_lvol_close(lvol, close_cb, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = -1; + + rc = spdk_lvs_unload(g_lvol_store, lvol_store_op_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + g_lvol_store = NULL; + + free_dev(&dev); + + spdk_free_thread(); +} + +static void +lvol_snapshot_fail(void) +{ + struct lvol_ut_bs_dev dev; + struct spdk_lvol *lvol, *snap; + struct spdk_lvs_opts opts; + int rc = 0; + + init_dev(&dev); + + spdk_allocate_thread(_lvol_send_msg, NULL, NULL, NULL, NULL); + + spdk_lvs_opts_init(&opts); + strncpy(opts.name, "lvs", sizeof(opts.name)); + + g_lvserrno = -1; + rc = spdk_lvs_init(&dev.bs_dev, &opts, lvol_store_op_with_handle_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol_store != NULL); + + spdk_lvol_create(g_lvol_store, "lvol", 10, true, lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + + lvol = g_lvol; + + spdk_lvol_create_snapshot(NULL, "snap", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno < 0); + SPDK_CU_ASSERT_FATAL(g_lvol == NULL); + + spdk_lvol_create_snapshot(lvol, "", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno < 0); + SPDK_CU_ASSERT_FATAL(g_lvol == NULL); + + spdk_lvol_create_snapshot(lvol, NULL, lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno < 0); + SPDK_CU_ASSERT_FATAL(g_lvol == NULL); + + spdk_lvol_create_snapshot(lvol, "snap", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + CU_ASSERT_STRING_EQUAL(g_lvol->name, "snap"); + + snap = g_lvol; + + spdk_lvol_create_snapshot(lvol, "snap", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno < 0); + + spdk_lvol_close(lvol, close_cb, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = -1; + + spdk_lvol_close(snap, close_cb, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = -1; + + rc = spdk_lvs_unload(g_lvol_store, lvol_store_op_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + g_lvol_store = NULL; + + free_dev(&dev); + + spdk_free_thread(); +} + +static void +lvol_clone(void) +{ + struct lvol_ut_bs_dev dev; + struct spdk_lvol *lvol; + struct spdk_lvol *snap; + struct spdk_lvs_opts opts; + int rc = 0; + + init_dev(&dev); + + spdk_allocate_thread(_lvol_send_msg, NULL, NULL, NULL, NULL); + + spdk_lvs_opts_init(&opts); + strncpy(opts.name, "lvs", sizeof(opts.name)); + + g_lvserrno = -1; + rc = spdk_lvs_init(&dev.bs_dev, &opts, lvol_store_op_with_handle_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol_store != NULL); + + spdk_lvol_create(g_lvol_store, "lvol", 10, true, lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + + lvol = g_lvol; + + spdk_lvol_create_snapshot(lvol, "snap", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + CU_ASSERT_STRING_EQUAL(g_lvol->name, "snap"); + + snap = g_lvol; + + spdk_lvol_create_clone(snap, "clone", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + CU_ASSERT_STRING_EQUAL(g_lvol->name, "clone"); + + /* Lvol has to be closed (or destroyed) before unloading lvol store. */ + spdk_lvol_close(g_lvol, close_cb, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = -1; + + spdk_lvol_close(snap, close_cb, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = -1; + + spdk_lvol_close(lvol, close_cb, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = -1; + + rc = spdk_lvs_unload(g_lvol_store, lvol_store_op_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + g_lvol_store = NULL; + + free_dev(&dev); + + spdk_free_thread(); +} + +static void +lvol_clone_fail(void) +{ + struct lvol_ut_bs_dev dev; + struct spdk_lvol *lvol; + struct spdk_lvol *snap; + struct spdk_lvol *clone; + struct spdk_lvs_opts opts; + int rc = 0; + + init_dev(&dev); + + spdk_allocate_thread(_lvol_send_msg, NULL, NULL, NULL, NULL); + + spdk_lvs_opts_init(&opts); + strncpy(opts.name, "lvs", sizeof(opts.name)); + + g_lvserrno = -1; + rc = spdk_lvs_init(&dev.bs_dev, &opts, lvol_store_op_with_handle_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol_store != NULL); + + spdk_lvol_create(g_lvol_store, "lvol", 10, true, lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + + lvol = g_lvol; + + spdk_lvol_create_snapshot(lvol, "snap", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + CU_ASSERT_STRING_EQUAL(g_lvol->name, "snap"); + + snap = g_lvol; + + spdk_lvol_create_clone(NULL, "clone", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno < 0); + + spdk_lvol_create_clone(snap, "", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno < 0); + + spdk_lvol_create_clone(snap, NULL, lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno < 0); + + spdk_lvol_create_clone(snap, "clone", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + CU_ASSERT_STRING_EQUAL(g_lvol->name, "clone"); + + clone = g_lvol; + + spdk_lvol_create_clone(snap, "clone", lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno < 0); + + /* Lvol has to be closed (or destroyed) before unloading lvol store. */ + spdk_lvol_close(clone, close_cb, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = -1; + + spdk_lvol_close(snap, close_cb, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = -1; + + spdk_lvol_close(lvol, close_cb, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = -1; + + rc = spdk_lvs_unload(g_lvol_store, lvol_store_op_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + g_lvol_store = NULL; + + free_dev(&dev); + + spdk_free_thread(); +} + static void lvol_names(void) { @@ -1659,6 +1936,10 @@ int main(int argc, char **argv) CU_add_test(suite, "lvol_load", lvs_load) == NULL || CU_add_test(suite, "lvs_load", lvols_load) == NULL || CU_add_test(suite, "lvol_open", lvol_open) == NULL || + CU_add_test(suite, "lvol_snapshot", lvol_snapshot) == NULL || + CU_add_test(suite, "lvol_snapshot_fail", lvol_snapshot_fail) == NULL || + CU_add_test(suite, "lvol_clone", lvol_clone) == NULL || + CU_add_test(suite, "lvol_clone_fail", lvol_clone_fail) == NULL || CU_add_test(suite, "lvol_refcnt", lvol_refcnt) == NULL || CU_add_test(suite, "lvol_names", lvol_names) == NULL || CU_add_test(suite, "lvol_create_thin_provisioned", lvol_create_thin_provisioned) == NULL ||