diff --git a/include/spdk/lvol.h b/include/spdk/lvol.h index cebbab419..7633b29eb 100644 --- a/include/spdk/lvol.h +++ b/include/spdk/lvol.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright (C) 2017 Intel Corporation. * All rights reserved. - * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ /** \file @@ -62,8 +62,14 @@ struct spdk_lvs_opts { * values. After that, new added fields should be put in the end of the struct. */ uint32_t opts_size; + + /** + * A function to be called to load external snapshots. If this is NULL while the lvolstore + * is being loaded, the lvolstore will not support external snapshots. + */ + spdk_bs_esnap_dev_create esnap_bs_dev_create; } __attribute__((packed)); -SPDK_STATIC_ASSERT(sizeof(struct spdk_lvs_opts) == 80, "Incorrect size"); +SPDK_STATIC_ASSERT(sizeof(struct spdk_lvs_opts) == 88, "Incorrect size"); /** * Initialize an spdk_lvs_opts structure to the defaults. @@ -200,6 +206,24 @@ void spdk_lvol_create_snapshot(struct spdk_lvol *lvol, const char *snapshot_name void spdk_lvol_create_clone(struct spdk_lvol *lvol, const char *clone_name, spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg); +/** + * Create clone of given non-lvol device. + * + * The bdev that is being cloned is commonly called an external snapshot or esnap. The clone is + * commonly called an esnap clone. + * + * \param esnap_id The identifier that will be passed to the spdk_bs_esnap_dev_create callback. + * \param id_len The length of esnap_id, in bytes. + * \param size_bytes The size of the external snapshot device, in bytes. + * \param lvs Handle to lvolstore. + * \param clone_name Name of created clone. + * \param cb_fn Completion callback. + * \param cb_arg Completion callback custom arguments. + */ +int spdk_lvol_create_esnap_clone(const void *esnap_id, uint32_t id_len, uint64_t size_bytes, + struct spdk_lvol_store *lvs, const char *clone_name, + spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg); + /** * Rename lvol with new_name. * @@ -255,6 +279,20 @@ struct spdk_io_channel *spdk_lvol_get_io_channel(struct spdk_lvol *lvol); void spdk_lvs_load(struct spdk_bs_dev *bs_dev, spdk_lvs_op_with_handle_complete cb_fn, void *cb_arg); +/** + * Load lvolstore from the given blobstore device with options. + * + * If lvs_opts is not NULL, it should be initalized with spdk_lvs_opts_init(). + * + * \param bs_dev Pointer to the blobstore device. + * \param lvs_opts lvolstore options. + * \param cb_fn Completion callback. + * \param cb_arg Completion callback custom arguments. + * blobstore. + */ +void spdk_lvs_load_ext(struct spdk_bs_dev *bs_dev, const struct spdk_lvs_opts *lvs_opts, + spdk_lvs_op_with_handle_complete cb_fn, void *cb_arg); + /** * Grow a lvstore to fill the underlying device * diff --git a/lib/lvol/lvol.c b/lib/lvol/lvol.c index 14fe93004..909fa155c 100644 --- a/lib/lvol/lvol.c +++ b/lib/lvol/lvol.c @@ -22,6 +22,7 @@ static TAILQ_HEAD(, spdk_lvol_store) g_lvol_stores = TAILQ_HEAD_INITIALIZER(g_lv static pthread_mutex_t g_lvol_stores_mutex = PTHREAD_MUTEX_INITIALIZER; static inline int lvs_opts_copy(const struct spdk_lvs_opts *src, struct spdk_lvs_opts *dst); + static int add_lvs_to_list(struct spdk_lvol_store *lvs) { @@ -394,11 +395,13 @@ lvs_bs_opts_init(struct spdk_bs_opts *opts) opts->max_channel_ops = SPDK_LVOL_BLOB_OPTS_CHANNEL_OPS; } -void -spdk_lvs_load(struct spdk_bs_dev *bs_dev, spdk_lvs_op_with_handle_complete cb_fn, void *cb_arg) +static void +lvs_load(struct spdk_bs_dev *bs_dev, const struct spdk_lvs_opts *_lvs_opts, + spdk_lvs_op_with_handle_complete cb_fn, void *cb_arg) { struct spdk_lvs_with_handle_req *req; - struct spdk_bs_opts opts = {}; + struct spdk_bs_opts bs_opts = {}; + struct spdk_lvs_opts lvs_opts; assert(cb_fn != NULL); @@ -408,6 +411,14 @@ spdk_lvs_load(struct spdk_bs_dev *bs_dev, spdk_lvs_op_with_handle_complete cb_fn return; } + spdk_lvs_opts_init(&lvs_opts); + if (_lvs_opts != NULL) { + if (lvs_opts_copy(_lvs_opts, &lvs_opts) != 0) { + SPDK_ERRLOG("Invalid options\n"); + cb_fn(cb_arg, NULL, -EINVAL); + } + } + req = calloc(1, sizeof(*req)); if (req == NULL) { SPDK_ERRLOG("Cannot alloc memory for request structure\n"); @@ -426,10 +437,28 @@ spdk_lvs_load(struct spdk_bs_dev *bs_dev, spdk_lvs_op_with_handle_complete cb_fn req->cb_arg = cb_arg; req->bs_dev = bs_dev; - lvs_bs_opts_init(&opts); - snprintf(opts.bstype.bstype, sizeof(opts.bstype.bstype), "LVOLSTORE"); + lvs_bs_opts_init(&bs_opts); + snprintf(bs_opts.bstype.bstype, sizeof(bs_opts.bstype.bstype), "LVOLSTORE"); - spdk_bs_load(bs_dev, &opts, lvs_load_cb, req); + if (lvs_opts.esnap_bs_dev_create != NULL) { + bs_opts.esnap_bs_dev_create = lvs_opts.esnap_bs_dev_create; + bs_opts.esnap_ctx = req->lvol_store; + } + + spdk_bs_load(bs_dev, &bs_opts, lvs_load_cb, req); +} + +void +spdk_lvs_load(struct spdk_bs_dev *bs_dev, spdk_lvs_op_with_handle_complete cb_fn, void *cb_arg) +{ + lvs_load(bs_dev, NULL, cb_fn, cb_arg); +} + +void +spdk_lvs_load_ext(struct spdk_bs_dev *bs_dev, const struct spdk_lvs_opts *opts, + spdk_lvs_op_with_handle_complete cb_fn, void *cb_arg) +{ + lvs_load(bs_dev, opts, cb_fn, cb_arg); } static void @@ -591,12 +620,13 @@ lvs_opts_copy(const struct spdk_lvs_opts *src, struct spdk_lvs_opts *dst) } SET_FIELD(num_md_pages_per_cluster_ratio); SET_FIELD(opts_size); + SET_FIELD(esnap_bs_dev_create); dst->opts_size = src->opts_size; /* You should not remove this statement, but need to update the assert statement * if you add a new field, and also add a corresponding SET_FIELD statement */ - SPDK_STATIC_ASSERT(sizeof(struct spdk_lvs_opts) == 80, "Incorrect size"); + SPDK_STATIC_ASSERT(sizeof(struct spdk_lvs_opts) == 88, "Incorrect size"); #undef FIELD_OK #undef SET_FIELD @@ -605,13 +635,17 @@ lvs_opts_copy(const struct spdk_lvs_opts *src, struct spdk_lvs_opts *dst) } static void -setup_lvs_opts(struct spdk_bs_opts *bs_opts, struct spdk_lvs_opts *o, uint32_t total_clusters) +setup_lvs_opts(struct spdk_bs_opts *bs_opts, struct spdk_lvs_opts *o, uint32_t total_clusters, + void *esnap_ctx) { assert(o != NULL); lvs_bs_opts_init(bs_opts); bs_opts->cluster_sz = o->cluster_sz; bs_opts->clear_method = (enum bs_clear_method)o->clear_method; bs_opts->num_md_pages = (o->num_md_pages_per_cluster_ratio * total_clusters) / 100; + bs_opts->esnap_bs_dev_create = o->esnap_bs_dev_create; + bs_opts->esnap_ctx = esnap_ctx; + snprintf(bs_opts->bstype.bstype, sizeof(bs_opts->bstype.bstype), "LVOLSTORE"); } int @@ -648,24 +682,26 @@ spdk_lvs_init(struct spdk_bs_dev *bs_dev, struct spdk_lvs_opts *o, } total_clusters = bs_dev->blockcnt / (lvs_opts.cluster_sz / bs_dev->blocklen); - setup_lvs_opts(&opts, o, total_clusters); + lvs = lvs_alloc(); + if (!lvs) { + SPDK_ERRLOG("Cannot alloc memory for lvol store base pointer\n"); + return -ENOMEM; + } + + setup_lvs_opts(&opts, o, total_clusters, lvs); if (strnlen(lvs_opts.name, SPDK_LVS_NAME_MAX) == SPDK_LVS_NAME_MAX) { SPDK_ERRLOG("Name has no null terminator.\n"); + lvs_free(lvs); return -EINVAL; } if (strnlen(lvs_opts.name, SPDK_LVS_NAME_MAX) == 0) { SPDK_ERRLOG("No name specified.\n"); + lvs_free(lvs); return -EINVAL; } - lvs = lvs_alloc(); - if (!lvs) { - SPDK_ERRLOG("Cannot alloc memory for lvol store base pointer\n"); - return -ENOMEM; - } - spdk_uuid_generate(&lvs->uuid); snprintf(lvs->name, sizeof(lvs->name), "%s", lvs_opts.name); @@ -689,8 +725,6 @@ spdk_lvs_init(struct spdk_bs_dev *bs_dev, struct spdk_lvs_opts *o, lvs_req->lvol_store = lvs; lvs->bs_dev = bs_dev; - snprintf(opts.bstype.bstype, sizeof(opts.bstype.bstype), "LVOLSTORE"); - SPDK_INFOLOG(lvol, "Initializing lvol store\n"); spdk_bs_init(bs_dev, &opts, lvs_init_cb, lvs_req); @@ -1017,6 +1051,16 @@ lvol_create_cb(void *cb_arg, spdk_blob_id blobid, int lvolerrno) spdk_blob_open_opts_init(&opts, sizeof(opts)); opts.clear_method = req->lvol->clear_method; + /* + * If the lvol that is being created is an esnap clone, the blobstore needs to be able to + * pass the lvol to the esnap_bs_dev_create callback. In order for that to happen, we need + * to pass it here. + * + * This does set ensap_ctx in cases where it's not needed, but we don't know that it's not + * needed until after the blob is open. When the blob is not an esnap clone, a reference to + * the value stored in opts.esnap_ctx is not retained by the blobstore. + */ + opts.esnap_ctx = req->lvol; bs = req->lvol->lvol_store->blobstore; spdk_bs_open_blob_ext(bs, blobid, &opts, lvol_create_open_cb, req); @@ -1128,6 +1172,62 @@ spdk_lvol_create(struct spdk_lvol_store *lvs, const char *name, uint64_t sz, return 0; } +int +spdk_lvol_create_esnap_clone(const void *esnap_id, uint32_t id_len, uint64_t size_bytes, + struct spdk_lvol_store *lvs, const char *clone_name, + spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg) +{ + struct spdk_lvol_with_handle_req *req; + struct spdk_blob_store *bs; + struct spdk_lvol *lvol; + struct spdk_blob_opts opts; + char *xattr_names[] = {LVOL_NAME, "uuid"}; + int rc; + + if (lvs == NULL) { + SPDK_ERRLOG("lvol store does not exist\n"); + return -EINVAL; + } + + rc = lvs_verify_lvol_name(lvs, clone_name); + if (rc < 0) { + return rc; + } + + bs = lvs->blobstore; + + req = calloc(1, sizeof(*req)); + if (!req) { + SPDK_ERRLOG("Cannot alloc memory for lvol request pointer\n"); + return -ENOMEM; + } + req->cb_fn = cb_fn; + req->cb_arg = cb_arg; + + lvol = lvol_alloc(lvs, clone_name, true, LVOL_CLEAR_WITH_DEFAULT); + if (!lvol) { + free(req); + SPDK_ERRLOG("Cannot alloc memory for lvol base pointer\n"); + return -ENOMEM; + } + req->lvol = lvol; + + spdk_blob_opts_init(&opts, sizeof(opts)); + opts.esnap_id = esnap_id; + opts.esnap_id_len = id_len; + opts.thin_provision = true; + opts.num_clusters = spdk_divide_round_up(size_bytes, spdk_bs_get_cluster_size(bs)); + opts.clear_method = lvol->clear_method; + opts.xattrs.count = SPDK_COUNTOF(xattr_names); + opts.xattrs.names = xattr_names; + opts.xattrs.ctx = lvol; + opts.xattrs.get_value = lvol_get_xattr_value; + + spdk_bs_create_blob_ext(lvs->blobstore, &opts, lvol_create_cb, req); + + 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) diff --git a/lib/lvol/spdk_lvol.map b/lib/lvol/spdk_lvol.map index 015b81174..1cb1de998 100644 --- a/lib/lvol/spdk_lvol.map +++ b/lib/lvol/spdk_lvol.map @@ -17,9 +17,11 @@ spdk_lvol_close; spdk_lvol_get_io_channel; spdk_lvs_load; + spdk_lvs_load_ext; spdk_lvol_open; spdk_lvol_inflate; spdk_lvol_decouple_parent; + spdk_lvol_create_esnap_clone; # internal functions spdk_lvol_resize; diff --git a/mk/spdk.lib_deps.mk b/mk/spdk.lib_deps.mk index 2ddf7d0bc..4a343a63a 100644 --- a/mk/spdk.lib_deps.mk +++ b/mk/spdk.lib_deps.mk @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (C) 2015 Intel Corporation. -# Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES # All rights reserved. +# Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # A quick note on organization: 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 3c0fd17c8..813155cdd 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 @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright (C) 2017 Intel Corporation. * All rights reserved. - * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ #include "spdk_cunit.h" @@ -52,6 +52,12 @@ spdk_bdev_get_md_size(const struct spdk_bdev *bdev) return bdev->md_len; } +const struct spdk_uuid * +spdk_bdev_get_uuid(const struct spdk_bdev *bdev) +{ + return &bdev->uuid; +} + int spdk_bdev_alias_add(struct spdk_bdev *bdev, const char *alias) { diff --git a/test/unit/lib/lvol/lvol.c/lvol_ut.c b/test/unit/lib/lvol/lvol.c/lvol_ut.c index 63d922c50..9e1536e45 100644 --- a/test/unit/lib/lvol/lvol.c/lvol_ut.c +++ b/test/unit/lib/lvol/lvol.c/lvol_ut.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright (C) 2017 Intel Corporation. * All rights reserved. - * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ #include "spdk_cunit.h" @@ -12,7 +12,6 @@ #include "thread/thread_internal.h" #include "common/lib/ut_multithread.c" - #include "lvol/lvol.c" #define DEV_BUFFER_SIZE (64 * 1024 * 1024) @@ -29,6 +28,14 @@ #define SPDK_BLOB_THIN_PROV (1ULL << 0) +DEFINE_STUB(spdk_blob_get_esnap_id, int, + (struct spdk_blob *blob, const void **id, size_t *len), -ENOTSUP); +DEFINE_STUB(spdk_bdev_get_name, const char *, (const struct spdk_bdev *bdev), NULL); +DEFINE_STUB(spdk_bdev_get_by_name, struct spdk_bdev *, (const char *name), NULL); +DEFINE_STUB(spdk_bdev_create_bs_dev_ro, int, + (const char *bdev_name, spdk_bdev_event_cb_t event_cb, void *event_ctx, + struct spdk_bs_dev **bs_dev), -ENOTSUP); + const char *uuid = "828d9766-ae50-11e7-bd8d-001e67edf350"; struct spdk_blob { @@ -54,12 +61,14 @@ struct spdk_lvol_store *g_lvol_store; struct spdk_lvol *g_lvol; spdk_blob_id g_blobid = 1; struct spdk_io_channel *g_io_channel; +struct lvol_ut_bs_dev g_esnap_dev; struct spdk_blob_store { struct spdk_bs_opts bs_opts; spdk_blob_id super_blobid; TAILQ_HEAD(, spdk_blob) blobs; int get_super_status; + spdk_bs_esnap_dev_create esnap_bs_dev_create; }; struct lvol_ut_bs_dev { @@ -69,6 +78,11 @@ struct lvol_ut_bs_dev { struct spdk_blob_store *bs; }; +struct ut_cb_res { + void *data; + int err; +}; + void spdk_bs_inflate_blob(struct spdk_blob_store *bs, struct spdk_io_channel *channel, spdk_blob_id blobid, spdk_blob_op_complete cb_fn, void *cb_arg) @@ -234,6 +248,24 @@ spdk_bdev_notify_blockcnt_change(struct spdk_bdev *bdev, uint64_t size) return 0; } +const struct spdk_uuid * +spdk_bdev_get_uuid(const struct spdk_bdev *bdev) +{ + return &bdev->uuid; +} + +uint64_t +spdk_bdev_get_num_blocks(const struct spdk_bdev *bdev) +{ + return bdev->blockcnt; +} + +uint32_t +spdk_bdev_get_block_size(const struct spdk_bdev *bdev) +{ + return bdev->blocklen; +} + static void init_dev(struct lvol_ut_bs_dev *dev) { @@ -261,6 +293,17 @@ free_dev(struct lvol_ut_bs_dev *dev) dev->bs = NULL; } +static void +init_bdev(struct spdk_bdev *bdev, char *name, size_t size) +{ + memset(bdev, 0, sizeof(*bdev)); + bdev->name = name; + spdk_uuid_generate(&bdev->uuid); + bdev->blocklen = BS_PAGE_SIZE; + bdev->phys_blocklen = BS_PAGE_SIZE; + bdev->blockcnt = size / BS_PAGE_SIZE; +} + void spdk_bs_init(struct spdk_bs_dev *dev, struct spdk_bs_opts *o, spdk_bs_op_with_handle_complete cb_fn, void *cb_arg) @@ -274,6 +317,7 @@ spdk_bs_init(struct spdk_bs_dev *dev, struct spdk_bs_opts *o, TAILQ_INIT(&bs->blobs); ut_dev->bs = bs; + bs->esnap_bs_dev_create = o->esnap_bs_dev_create; memcpy(&bs->bs_opts, o, sizeof(struct spdk_bs_opts)); @@ -321,12 +365,12 @@ spdk_blob_get_id(struct spdk_blob *blob) void spdk_bs_opts_init(struct spdk_bs_opts *opts, size_t opts_size) { + memset(opts, 0, sizeof(*opts)); opts->opts_size = opts_size; opts->cluster_sz = SPDK_BLOB_OPTS_CLUSTER_SZ; opts->num_md_pages = SPDK_BLOB_OPTS_NUM_MD_PAGES; opts->max_md_ops = SPDK_BLOB_OPTS_MAX_MD_OPS; opts->max_channel_ops = SPDK_BLOB_OPTS_MAX_CHANNEL_OPS; - memset(&opts->bstype, 0, sizeof(opts->bstype)); } DEFINE_STUB(spdk_bs_get_cluster_size, uint64_t, (struct spdk_blob_store *bs), BS_CLUSTER_SIZE); @@ -464,12 +508,31 @@ lvol_op_with_handle_complete(void *cb_arg, struct spdk_lvol *lvol, int lvserrno) { g_lvol = lvol; g_lvserrno = lvserrno; + if (cb_arg != NULL) { + struct ut_cb_res *res = cb_arg; + + res->data = lvol; + res->err = lvserrno; + } } static void op_complete(void *cb_arg, int lvserrno) { g_lvserrno = lvserrno; + if (cb_arg != NULL) { + struct ut_cb_res *res = cb_arg; + + res->err = lvserrno; + } +} + +static struct ut_cb_res * +ut_cb_res_clear(struct ut_cb_res *res) +{ + res->data = (void *)(uintptr_t)(-1); + res->err = 0xbad; + return res; } static void @@ -1032,7 +1095,7 @@ null_cb(void *ctx, struct spdk_blob_store *bs, int bserrno) } static void -lvs_load(void) +test_lvs_load(void) { int rc = -1; struct lvol_ut_bs_dev dev; @@ -2089,6 +2152,199 @@ lvol_get_xattr(void) free_dev(&dev); } +static int +ut_esnap_bs_dev_create(void *bs_ctx, void *blob_ctx, struct spdk_blob *blob, + const void *esnap_id, uint32_t id_len, + struct spdk_bs_dev **_bs_dev) +{ + CU_ASSERT(false); + return -ENOTSUP; +} + +static void +lvol_esnap_reload(void) +{ + struct lvol_ut_bs_dev dev; + struct spdk_lvs_with_handle_req *req; + struct spdk_lvs_opts opts; + int rc; + + req = calloc(1, sizeof(*req)); + SPDK_CU_ASSERT_FATAL(req != NULL); + + init_dev(&dev); + + /* Create an lvstore with external snapshot support */ + spdk_lvs_opts_init(&opts); + snprintf(opts.name, sizeof(opts.name), "lvs"); + opts.esnap_bs_dev_create = ut_esnap_bs_dev_create; + 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); + CU_ASSERT(dev.bs->esnap_bs_dev_create == ut_esnap_bs_dev_create); + SPDK_CU_ASSERT_FATAL(g_lvol_store != NULL); + + /* Unload the lvstore */ + g_lvserrno = -1; + rc = spdk_lvs_unload(g_lvol_store, op_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + g_lvol_store = NULL; + + /* Load the lvstore with external snapshot support */ + g_lvserrno = -1; + spdk_lvs_opts_init(&opts); + opts.esnap_bs_dev_create = ut_esnap_bs_dev_create; + spdk_lvs_load_ext(&dev.bs_dev, &opts, lvol_store_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol_store != NULL); + CU_ASSERT(dev.bs->esnap_bs_dev_create == ut_esnap_bs_dev_create); + + g_lvserrno = -1; + rc = spdk_lvs_destroy(g_lvol_store, op_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + g_lvol_store = NULL; + + free(req); +} + +static void +lvol_esnap_create_bad_args(void) +{ + struct lvol_ut_bs_dev dev; + struct spdk_bdev esnap_bdev; + struct spdk_lvs_opts opts; + char long_name[SPDK_LVOL_NAME_MAX + 1]; + int rc; + struct ut_cb_res lvres1, lvres2; + struct spdk_lvol *lvol; + char uuid_str[SPDK_UUID_STRING_LEN]; + + init_dev(&dev); + + spdk_lvs_opts_init(&opts); + snprintf(opts.name, sizeof(opts.name), "lvs"); + opts.esnap_bs_dev_create = ut_esnap_bs_dev_create; + 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); + + init_bdev(&esnap_bdev, "bdev1", BS_CLUSTER_SIZE); + CU_ASSERT(spdk_uuid_fmt_lower(uuid_str, sizeof(uuid_str), &esnap_bdev.uuid) == 0); + MOCK_SET(spdk_bdev_get_by_name, &esnap_bdev); + + /* error with lvs == NULL */ + rc = spdk_lvol_create_esnap_clone(uuid_str, strlen(uuid_str), 1, NULL, "clone1", + lvol_op_with_handle_complete, NULL); + CU_ASSERT(rc == -EINVAL); + + /* error with clone name that is too short */ + rc = spdk_lvol_create_esnap_clone(uuid_str, strlen(uuid_str), 1, g_lvol_store, "", + lvol_op_with_handle_complete, NULL); + CU_ASSERT(rc == -EINVAL); + + /* error with clone name that is too long */ + memset(long_name, 'a', sizeof(long_name)); + rc = spdk_lvol_create_esnap_clone(uuid_str, strlen(uuid_str), 1, g_lvol_store, long_name, + lvol_op_with_handle_complete, NULL); + CU_ASSERT(rc == -EINVAL); + + /* error when an lvol with that name already exists */ + spdk_lvol_create(g_lvol_store, "lvol", 10, false, LVOL_CLEAR_WITH_DEFAULT, + lvol_op_with_handle_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + lvol = g_lvol; + rc = spdk_lvol_create_esnap_clone(uuid_str, strlen(uuid_str), 1, g_lvol_store, "lvol", + lvol_op_with_handle_complete, NULL); + CU_ASSERT(rc == -EEXIST); + spdk_lvol_close(lvol, op_complete, ut_cb_res_clear(&lvres1)); + spdk_lvol_destroy(lvol, op_complete, ut_cb_res_clear(&lvres2)); + poll_threads(); + CU_ASSERT(lvres1.err == 0); + CU_ASSERT(lvres2.err == 0); + g_lvol = NULL; + + /* error when two clones created at the same time with the same name */ + rc = spdk_lvol_create_esnap_clone(uuid_str, strlen(uuid_str), 1, g_lvol_store, "clone1", + lvol_op_with_handle_complete, ut_cb_res_clear(&lvres1)); + CU_ASSERT(rc == 0); + rc = spdk_lvol_create_esnap_clone(uuid_str, strlen(uuid_str), 1, g_lvol_store, "clone1", + lvol_op_with_handle_complete, ut_cb_res_clear(&lvres2)); + CU_ASSERT(rc == -EEXIST); + poll_threads(); + CU_ASSERT(g_lvol != NULL); + CU_ASSERT(lvres1.err == 0); + CU_ASSERT(lvres2.err == 0xbad); + CU_ASSERT(TAILQ_EMPTY(&g_lvol_store->pending_lvols)); + spdk_lvol_close(g_lvol, op_complete, ut_cb_res_clear(&lvres1)); + spdk_lvol_destroy(g_lvol, op_complete, ut_cb_res_clear(&lvres2)); + poll_threads(); + CU_ASSERT(lvres1.err == 0); + CU_ASSERT(lvres2.err == 0); + g_lvol = NULL; + + g_lvserrno = -1; + rc = spdk_lvs_unload(g_lvol_store, op_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + g_lvol_store = NULL; + + free_dev(&dev); +} + +static void +lvol_esnap_create_delete(void) +{ + struct lvol_ut_bs_dev dev; + struct spdk_bdev esnap_bdev; + struct spdk_lvs_opts opts; + char uuid_str[SPDK_UUID_STRING_LEN]; + int rc; + + init_dev(&dev); + init_dev(&g_esnap_dev); + + spdk_lvs_opts_init(&opts); + snprintf(opts.name, sizeof(opts.name), "lvs"); + opts.esnap_bs_dev_create = ut_esnap_bs_dev_create; + 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); + + g_lvserrno = 0xbad; + init_bdev(&esnap_bdev, "bdev1", BS_CLUSTER_SIZE); + CU_ASSERT(spdk_uuid_fmt_lower(uuid_str, sizeof(uuid_str), &esnap_bdev.uuid) == 0); + MOCK_SET(spdk_bdev_get_by_name, &esnap_bdev); + rc = spdk_lvol_create_esnap_clone(uuid_str, strlen(uuid_str), 1, g_lvol_store, "clone1", + lvol_op_with_handle_complete, NULL); + CU_ASSERT(rc == 0); + poll_threads(); + CU_ASSERT(g_lvserrno == 0); + SPDK_CU_ASSERT_FATAL(g_lvol != NULL); + MOCK_CLEAR(spdk_bdev_get_by_name); + + g_lvserrno = 0xbad; + spdk_lvol_close(g_lvol, op_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvserrno = 0xbad; + spdk_lvol_destroy(g_lvol, op_complete, NULL); + CU_ASSERT(g_lvserrno == 0); + g_lvol = NULL; + + g_lvserrno = -1; + rc = spdk_lvs_destroy(g_lvol_store, op_complete, NULL); + CU_ASSERT(rc == 0); + CU_ASSERT(g_lvserrno == 0); + g_lvol_store = NULL; +} + int main(int argc, char **argv) { @@ -2112,7 +2368,7 @@ main(int argc, char **argv) CU_ADD_TEST(suite, lvol_close_success); CU_ADD_TEST(suite, lvol_resize); CU_ADD_TEST(suite, lvol_set_read_only); - CU_ADD_TEST(suite, lvs_load); + CU_ADD_TEST(suite, test_lvs_load); CU_ADD_TEST(suite, lvols_load); CU_ADD_TEST(suite, lvol_open); CU_ADD_TEST(suite, lvol_snapshot); @@ -2127,6 +2383,9 @@ main(int argc, char **argv) CU_ADD_TEST(suite, lvol_inflate); CU_ADD_TEST(suite, lvol_decouple_parent); CU_ADD_TEST(suite, lvol_get_xattr); + CU_ADD_TEST(suite, lvol_esnap_reload); + CU_ADD_TEST(suite, lvol_esnap_create_bad_args); + CU_ADD_TEST(suite, lvol_esnap_create_delete); allocate_threads(1); set_thread(0);