blob: hotplug new back_bs_dev

When an esnap clone blob's external snapshot arrives after the blob is
opened, it can now be hot-added to the blob. Presumably the new device
replaces a place-holder device that did not really atteempt IO.

Change-Id: I622feb84efa66628debf44f7e7cb88b6a012db6d
Signed-off-by: Mike Gerdts <mgerdts@nvidia.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16232
Community-CI: Mellanox Build Bot
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>
This commit is contained in:
Mike Gerdts 2022-10-14 13:48:42 -05:00 committed by Jim Harris
parent 55199ed166
commit aaebaece6d
4 changed files with 191 additions and 0 deletions

View File

@ -1120,6 +1120,17 @@ struct spdk_bs_type spdk_bs_get_bstype(struct spdk_blob_store *bs);
*/
void spdk_bs_set_bstype(struct spdk_blob_store *bs, struct spdk_bs_type bstype);
/**
* Replace the existing external snapshot device.
*
* \param blob The blob that is getting a new external snapshot device.
* \param back_bs_dev The new blobstore device to use as an external snapshot.
* \param cb_fn Callback to be called when complete.
* \param cb_arg Callback argument used with cb_fn.
*/
void spdk_blob_set_esnap_bs_dev(struct spdk_blob *blob, struct spdk_bs_dev *back_bs_dev,
spdk_blob_op_complete cb_fn, void *cb_arg);
#ifdef __cplusplus
}
#endif

View File

@ -8986,5 +8986,98 @@ blob_esnap_destroy_bs_channel(struct spdk_bs_channel *ch)
spdk_thread_get_name(spdk_get_thread()));
}
struct set_bs_dev_ctx {
struct spdk_blob *blob;
struct spdk_bs_dev *back_bs_dev;
spdk_blob_op_complete cb_fn;
void *cb_arg;
int bserrno;
};
static void
blob_set_back_bs_dev_done(void *_ctx, int bserrno)
{
struct set_bs_dev_ctx *ctx = _ctx;
if (bserrno != 0) {
/* Even though the unfreeze failed, the update may have succeed. */
SPDK_ERRLOG("blob 0x%" PRIx64 ": unfreeze failed with error %d\n", ctx->blob->id,
bserrno);
}
ctx->cb_fn(ctx->cb_arg, ctx->bserrno);
free(ctx);
}
static void
blob_frozen_set_back_bs_dev(void *_ctx, struct spdk_blob *blob, int bserrno)
{
struct set_bs_dev_ctx *ctx = _ctx;
if (bserrno != 0) {
SPDK_ERRLOG("blob 0x%" PRIx64 ": failed to release old back_bs_dev with error %d\n",
blob->id, bserrno);
ctx->bserrno = bserrno;
blob_unfreeze_io(blob, blob_set_back_bs_dev_done, ctx);
return;
}
if (blob->back_bs_dev != NULL) {
blob->back_bs_dev->destroy(blob->back_bs_dev);
}
SPDK_NOTICELOG("blob 0x%" PRIx64 ": hotplugged back_bs_dev\n", blob->id);
blob->back_bs_dev = ctx->back_bs_dev;
ctx->bserrno = 0;
blob_unfreeze_io(blob, blob_set_back_bs_dev_done, ctx);
}
static void
blob_frozen_destroy_esnap_channels(void *_ctx, int bserrno)
{
struct set_bs_dev_ctx *ctx = _ctx;
struct spdk_blob *blob = ctx->blob;
if (bserrno != 0) {
SPDK_ERRLOG("blob 0x%" PRIx64 ": failed to freeze with error %d\n", blob->id,
bserrno);
ctx->cb_fn(ctx->cb_arg, bserrno);
free(ctx);
return;
}
/*
* This does not prevent future reads from the esnap device because any future IO will
* lazily create a new esnap IO channel.
*/
blob_esnap_destroy_bs_dev_channels(blob, true, blob_frozen_set_back_bs_dev, ctx);
}
void
spdk_blob_set_esnap_bs_dev(struct spdk_blob *blob, struct spdk_bs_dev *back_bs_dev,
spdk_blob_op_complete cb_fn, void *cb_arg)
{
struct set_bs_dev_ctx *ctx;
if (!blob_is_esnap_clone(blob)) {
SPDK_ERRLOG("blob 0x%" PRIx64 ": not an esnap clone\n", blob->id);
cb_fn(cb_arg, -EINVAL);
return;
}
ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL) {
SPDK_ERRLOG("blob 0x%" PRIx64 ": out of memory while setting back_bs_dev\n",
blob->id);
cb_fn(cb_arg, -ENOMEM);
return;
}
ctx->cb_fn = cb_fn;
ctx->cb_arg = cb_arg;
ctx->back_bs_dev = back_bs_dev;
ctx->blob = blob;
blob_freeze_io(blob, blob_frozen_destroy_esnap_channels, ctx);
}
SPDK_LOG_REGISTER_COMPONENT(blob)
SPDK_LOG_REGISTER_COMPONENT(blob_esnap)

View File

@ -66,6 +66,7 @@
spdk_xattr_names_free;
spdk_bs_get_bstype;
spdk_bs_set_bstype;
spdk_blob_set_esnap_bs_dev;
local: *;
};

View File

@ -8420,6 +8420,91 @@ blob_esnap_clone_decouple(void)
_blob_esnap_clone_hydrate(false);
}
static void
blob_esnap_hotplug(void)
{
struct spdk_blob_store *bs = g_bs;
struct ut_esnap_opts esnap1_opts, esnap2_opts;
struct spdk_blob_opts opts;
struct spdk_blob *blob;
struct spdk_bs_dev *bs_dev;
struct ut_esnap_dev *esnap_dev;
uint32_t cluster_sz = spdk_bs_get_cluster_size(bs);
uint32_t block_sz = spdk_bs_get_io_unit_size(bs);
const uint32_t esnap_num_clusters = 4;
uint64_t esnap_num_blocks = cluster_sz * esnap_num_clusters / block_sz;
bool destroyed1 = false, destroyed2 = false;
uint64_t start_thread = g_ut_thread_id;
struct spdk_io_channel *ch0, *ch1;
char buf[block_sz];
/* Create and open an esnap clone blob */
ut_spdk_blob_opts_init(&opts);
ut_esnap_opts_init(block_sz, esnap_num_blocks, "esnap1", &destroyed1, &esnap1_opts);
opts.esnap_id = &esnap1_opts;
opts.esnap_id_len = sizeof(esnap1_opts);
opts.num_clusters = esnap_num_clusters;
blob = ut_blob_create_and_open(bs, &opts);
CU_ASSERT(blob != NULL);
CU_ASSERT(spdk_blob_is_esnap_clone(blob));
SPDK_CU_ASSERT_FATAL(blob->back_bs_dev != NULL);
esnap_dev = (struct ut_esnap_dev *)blob->back_bs_dev;
CU_ASSERT(strcmp(esnap_dev->ut_opts.name, "esnap1") == 0);
/* Replace the external snapshot */
ut_esnap_opts_init(block_sz, esnap_num_blocks, "esnap2", &destroyed2, &esnap2_opts);
bs_dev = ut_esnap_dev_alloc(&esnap2_opts);
CU_ASSERT(!destroyed1);
CU_ASSERT(!destroyed2);
g_bserrno = 0xbad;
spdk_blob_set_esnap_bs_dev(blob, bs_dev, bs_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(destroyed1);
CU_ASSERT(!destroyed2);
SPDK_CU_ASSERT_FATAL(blob->back_bs_dev != NULL);
esnap_dev = (struct ut_esnap_dev *)blob->back_bs_dev;
CU_ASSERT(strcmp(esnap_dev->ut_opts.name, "esnap2") == 0);
/* Create a couple channels */
set_thread(0);
ch0 = spdk_bs_alloc_io_channel(bs);
CU_ASSERT(ch0 != NULL);
spdk_blob_io_read(blob, ch0, buf, 0, 1, bs_op_complete, NULL);
set_thread(1);
ch1 = spdk_bs_alloc_io_channel(bs);
CU_ASSERT(ch1 != NULL);
spdk_blob_io_read(blob, ch1, buf, 0, 1, bs_op_complete, NULL);
set_thread(start_thread);
poll_threads();
CU_ASSERT(esnap_dev->num_channels == 2);
/* Replace the external snapshot */
ut_esnap_opts_init(block_sz, esnap_num_blocks, "esnap1a", &destroyed1, &esnap1_opts);
bs_dev = ut_esnap_dev_alloc(&esnap1_opts);
destroyed1 = destroyed2 = false;
g_bserrno = 0xbad;
spdk_blob_set_esnap_bs_dev(blob, bs_dev, bs_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(!destroyed1);
CU_ASSERT(destroyed2);
SPDK_CU_ASSERT_FATAL(blob->back_bs_dev != NULL);
esnap_dev = (struct ut_esnap_dev *)blob->back_bs_dev;
CU_ASSERT(strcmp(esnap_dev->ut_opts.name, "esnap1a") == 0);
/* Clean up */
set_thread(0);
spdk_bs_free_io_channel(ch0);
set_thread(1);
spdk_bs_free_io_channel(ch1);
set_thread(start_thread);
g_bserrno = 0xbad;
spdk_blob_close(blob, bs_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
}
static void
suite_bs_setup(void)
{
@ -8630,6 +8715,7 @@ main(int argc, char **argv)
CU_ADD_TEST(suite_esnap_bs, blob_esnap_clone_inflate);
CU_ADD_TEST(suite_esnap_bs, blob_esnap_clone_decouple);
CU_ADD_TEST(suite_esnap_bs, blob_esnap_clone_reload);
CU_ADD_TEST(suite_esnap_bs, blob_esnap_hotplug);
allocate_threads(2);
set_thread(0);