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:
parent
55199ed166
commit
aaebaece6d
@ -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);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -8986,5 +8986,98 @@ blob_esnap_destroy_bs_channel(struct spdk_bs_channel *ch)
|
|||||||
spdk_thread_get_name(spdk_get_thread()));
|
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)
|
||||||
SPDK_LOG_REGISTER_COMPONENT(blob_esnap)
|
SPDK_LOG_REGISTER_COMPONENT(blob_esnap)
|
||||||
|
@ -66,6 +66,7 @@
|
|||||||
spdk_xattr_names_free;
|
spdk_xattr_names_free;
|
||||||
spdk_bs_get_bstype;
|
spdk_bs_get_bstype;
|
||||||
spdk_bs_set_bstype;
|
spdk_bs_set_bstype;
|
||||||
|
spdk_blob_set_esnap_bs_dev;
|
||||||
|
|
||||||
local: *;
|
local: *;
|
||||||
};
|
};
|
||||||
|
@ -8420,6 +8420,91 @@ blob_esnap_clone_decouple(void)
|
|||||||
_blob_esnap_clone_hydrate(false);
|
_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
|
static void
|
||||||
suite_bs_setup(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_inflate);
|
||||||
CU_ADD_TEST(suite_esnap_bs, blob_esnap_clone_decouple);
|
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_clone_reload);
|
||||||
|
CU_ADD_TEST(suite_esnap_bs, blob_esnap_hotplug);
|
||||||
|
|
||||||
allocate_threads(2);
|
allocate_threads(2);
|
||||||
set_thread(0);
|
set_thread(0);
|
||||||
|
Loading…
Reference in New Issue
Block a user