Commit aaebaece authored by Mike Gerdts's avatar Mike Gerdts Committed by Jim Harris
Browse files

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: default avatarMike Gerdts <mgerdts@nvidia.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16232


Community-CI: Mellanox Build Bot
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
parent 55199ed1
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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
+93 −0
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@
	spdk_xattr_names_free;
	spdk_bs_get_bstype;
	spdk_bs_set_bstype;
	spdk_blob_set_esnap_bs_dev;

	local: *;
};
+86 −0
Original line number Diff line number Diff line
@@ -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);