lvol: add support for external snapshots

This provides the lib/lvol wrapper around blobstore's external
snapshots. Later commits make this work with vbdev_lvol.

The blobstore external snapshot implementation stores an opaque
identifier in an internal xattr. Lvstore uses this to store the
stringified UUID of the bdev that will act as the external snapshot.
This is used by the newly introduced spdk_lvol_create_esnap_clone() to
store the bdev UUID in the blob's metadata.

Change-Id: I58c7b32b656ad1d21a446e3b91e59e655efac7e4
Signed-off-by: Mike Gerdts <mgerdts@nvidia.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/14977
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Community-CI: Mellanox Build Bot
This commit is contained in:
Mike Gerdts 2022-10-12 15:01:10 -05:00 committed by Jim Harris
parent c894388d4b
commit 609205739e
6 changed files with 431 additions and 26 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
{

View File

@ -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);