Commit 77c07d07 authored by Damiano Cipriani's avatar Damiano Cipriani Committed by Jim Harris
Browse files

blob: add blob set external parent



Implemented a new function to set an external snapshot as the parent
of a blob. The previous parent of the blob, if any, can be another
external snapshot or a snapshot; if the blob is not a clone, it must
be thin-provisioned.

Change-Id: Ib9eda0fba04428d058109a1eacdec084ef050a65
Signed-off-by: default avatarDamiano Cipriani <damiano.cipriani@suse.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/21689


Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
Reviewed-by: default avatarTomasz Zawadzki <tomasz.zawadzki@intel.com>
Community-CI: Mellanox Build Bot
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
parent 7b8c7efe
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -841,6 +841,26 @@ int spdk_bs_blob_shallow_copy(struct spdk_blob_store *bs, struct spdk_io_channel
void spdk_bs_blob_set_parent(struct spdk_blob_store *bs, spdk_blob_id blob_id,
			     spdk_blob_id snapshot_id, spdk_blob_op_complete cb_fn, void *cb_arg);

/**
 * Set an external snapshot as the parent of a blob
 *
 * This call set an external snapshot as the parent of a blob, making the blob a clone of this
 * external snapshot.
 * The previous parent of the blob, if any, can be another external snapshot or a snapshot; if
 * the blob is not a clone, it must be thin-provisioned.
 *
 * \param bs blobstore.
 * \param blob_id The id of the blob.
 * \param esnap_bs_dev The new blobstore device to use as an external snapshot.
 * \param esnap_id The identifier of the external snapshot.
 * \param esnap_id_len The length of esnap_id, in bytes.
 * \param cb_fn Called when the operation is complete.
 * \param cb_arg Argument passed to function cb_fn.
 */
void spdk_bs_blob_set_external_parent(struct spdk_blob_store *bs, spdk_blob_id blob_id,
				      struct spdk_bs_dev *esnap_bs_dev, const void *esnap_id,
				      uint32_t esnap_id_len, spdk_blob_op_complete cb_fn, void *cb_arg);


struct spdk_blob_open_opts {
	enum blob_clear_method  clear_method;
+186 −0
Original line number Diff line number Diff line
@@ -416,6 +416,12 @@ struct blob_parent {
			spdk_blob_id id;
			struct spdk_blob *blob;
		} snapshot;

		struct {
			void *id;
			uint32_t id_len;
			struct spdk_bs_dev *back_bs_dev;
		} esnap;
	} u;
};

@@ -7637,6 +7643,186 @@ spdk_bs_blob_set_parent(struct spdk_blob_store *bs, spdk_blob_id blob_id,
}
/* END spdk_bs_blob_set_parent */

/* START spdk_bs_blob_set_external_parent */

static void
bs_set_external_parent_cleanup_finish(void *cb_arg, int bserrno)
{
	struct set_parent_ctx *ctx = cb_arg;

	if (bserrno != 0) {
		SPDK_ERRLOG("blob set external parent finish error %d\n", bserrno);
		if (ctx->bserrno == 0) {
			ctx->bserrno = bserrno;
		}
	}

	ctx->cb_fn(ctx->cb_arg, ctx->bserrno);

	free(ctx->parent.u.esnap.id);
	free(ctx);
}

static void
bs_set_external_parent_close_blob(void *cb_arg, int bserrno)
{
	struct set_parent_ctx *ctx = cb_arg;
	struct spdk_blob *blob = ctx->blob;

	if (bserrno != 0 && ctx->bserrno == 0) {
		SPDK_ERRLOG("error %d in metadata sync\n", bserrno);
		ctx->bserrno = bserrno;
	}

	/* Revert md_ro to original state */
	blob->md_ro = ctx->blob_md_ro;

	blob->locked_operation_in_progress = false;

	spdk_blob_close(blob, bs_set_external_parent_cleanup_finish, ctx);
}

static void
bs_set_external_parent_unfrozen(void *cb_arg, int bserrno)
{
	struct set_parent_ctx *ctx = cb_arg;
	struct spdk_blob *blob = ctx->blob;

	if (bserrno != 0) {
		SPDK_ERRLOG("error %d setting back_bs_dev\n", bserrno);
		ctx->bserrno = bserrno;
		bs_set_external_parent_close_blob(ctx, bserrno);
		return;
	}

	spdk_blob_sync_md(blob, bs_set_external_parent_close_blob, ctx);
}

static int
bs_set_external_parent_refs(struct spdk_blob *blob, struct blob_parent *parent)
{
	int rc;

	bs_blob_list_remove(blob);

	if (spdk_blob_is_clone(blob)) {
		/* Remove the xattr that references the snapshot */
		blob->parent_id = SPDK_BLOBID_INVALID;
		blob_remove_xattr(blob, BLOB_SNAPSHOT, true);
	}

	rc = blob_set_xattr(blob, BLOB_EXTERNAL_SNAPSHOT_ID, parent->u.esnap.id,
			    parent->u.esnap.id_len, true);
	if (rc != 0) {
		SPDK_ERRLOG("error %d setting external snapshot xattr\n", rc);
		return rc;
	}
	blob->invalid_flags |= SPDK_BLOB_EXTERNAL_SNAPSHOT;

	bs_blob_list_add(blob);

	return 0;
}

static void
bs_set_external_parent_blob_open_cpl(void *cb_arg, struct spdk_blob *blob, int bserrno)
{
	struct set_parent_ctx *ctx = cb_arg;
	const void *esnap_id;
	size_t esnap_id_len;
	int rc;

	if (bserrno != 0) {
		SPDK_ERRLOG("blob open error %d\n", bserrno);
		ctx->bserrno = bserrno;
		bs_set_parent_cleanup_finish(ctx, 0);
		return;
	}

	ctx->blob = blob;
	ctx->blob_md_ro = blob->md_ro;

	rc = spdk_blob_get_esnap_id(blob, &esnap_id, &esnap_id_len);
	if (rc == 0 && esnap_id != NULL && esnap_id_len == ctx->parent.u.esnap.id_len &&
	    memcmp(esnap_id, ctx->parent.u.esnap.id, esnap_id_len) == 0) {
		SPDK_ERRLOG("external snapshot is already the parent of blob\n");
		ctx->bserrno = -EEXIST;
		goto error;
	}

	if (!spdk_blob_is_thin_provisioned(blob)) {
		SPDK_ERRLOG("blob is not thin-provisioned\n");
		ctx->bserrno = -EINVAL;
		goto error;
	}

	if (blob->locked_operation_in_progress) {
		SPDK_ERRLOG("cannot set external parent of blob, another operation in progress\n");
		ctx->bserrno = -EBUSY;
		goto error;
	}

	blob->locked_operation_in_progress = true;

	/* Temporarily override md_ro flag for MD modification */
	blob->md_ro = false;

	blob_set_back_bs_dev(blob, ctx->parent.u.esnap.back_bs_dev, bs_set_external_parent_refs,
			     &ctx->parent, bs_set_external_parent_unfrozen, ctx);
	return;

error:
	spdk_blob_close(blob, bs_set_external_parent_cleanup_finish, ctx);
}

void
spdk_bs_blob_set_external_parent(struct spdk_blob_store *bs, spdk_blob_id blob_id,
				 struct spdk_bs_dev *esnap_bs_dev, const void *esnap_id,
				 uint32_t esnap_id_len, spdk_blob_op_complete cb_fn, void *cb_arg)
{
	struct set_parent_ctx *ctx;
	uint64_t esnap_dev_size, cluster_sz;

	if (sizeof(blob_id) == esnap_id_len && memcmp(&blob_id, esnap_id, sizeof(blob_id)) == 0) {
		SPDK_ERRLOG("blob id and external snapshot id cannot be the same\n");
		cb_fn(cb_arg, -EINVAL);
		return;
	}

	esnap_dev_size = esnap_bs_dev->blockcnt * esnap_bs_dev->blocklen;
	cluster_sz = spdk_bs_get_cluster_size(bs);
	if ((esnap_dev_size % cluster_sz) != 0) {
		SPDK_ERRLOG("Esnap device size %" PRIu64 " is not an integer multiple of "
			    "cluster size %" PRIu64 "\n", esnap_dev_size, cluster_sz);
		cb_fn(cb_arg, -EINVAL);
		return;
	}

	ctx = calloc(1, sizeof(*ctx));
	if (!ctx) {
		cb_fn(cb_arg, -ENOMEM);
		return;
	}

	ctx->parent.u.esnap.id = calloc(1, esnap_id_len);
	if (!ctx->parent.u.esnap.id) {
		free(ctx);
		cb_fn(cb_arg, -ENOMEM);
		return;
	}

	ctx->bs = bs;
	ctx->parent.u.esnap.back_bs_dev = esnap_bs_dev;
	memcpy(ctx->parent.u.esnap.id, esnap_id, esnap_id_len);
	ctx->parent.u.esnap.id_len = esnap_id_len;
	ctx->cb_fn = cb_fn;
	ctx->cb_arg = cb_arg;
	ctx->bserrno = 0;

	spdk_bs_open_blob(bs, blob_id, bs_set_external_parent_blob_open_cpl, ctx);
}
/* END spdk_bs_blob_set_external_parent */

/* START spdk_blob_resize */
struct spdk_bs_resize_ctx {
	spdk_blob_op_complete cb_fn;
+1 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@
	spdk_bs_blob_decouple_parent;
	spdk_bs_blob_shallow_copy;
	spdk_bs_blob_set_parent;
	spdk_bs_blob_set_external_parent;
	spdk_blob_open_opts_init;
	spdk_bs_open_blob;
	spdk_bs_open_blob_ext;
+127 −0
Original line number Diff line number Diff line
@@ -9687,6 +9687,132 @@ blob_set_parent(void)
	CU_ASSERT(g_bserrno == 0);
}

static void
blob_set_external_parent(void)
{
	struct spdk_blob_store *bs = g_bs;
	struct spdk_blob_opts opts;
	struct ut_esnap_opts esnap_opts, esnap_opts2;
	struct spdk_blob *blob1, *blob2, *blob3, *blob4;
	spdk_blob_id blobid1, blobid2, blobid3, blobid4, snapshotid;
	uint32_t cluster_sz, block_sz;
	const uint32_t esnap_num_clusters = 4;
	uint64_t esnap_num_blocks;
	struct spdk_bs_dev *esnap_dev1, *esnap_dev2, *esnap_dev3;
	const void *esnap_id;
	size_t esnap_id_len;
	int rc;

	cluster_sz = spdk_bs_get_cluster_size(bs);
	block_sz = spdk_bs_get_io_unit_size(bs);
	esnap_num_blocks = cluster_sz * esnap_num_clusters / block_sz;
	esnap_dev1 = init_dev();
	esnap_dev2 = init_dev();
	esnap_dev3 = init_dev();

	/* Create an esnap clone blob */
	ut_spdk_blob_opts_init(&opts);
	ut_esnap_opts_init(block_sz, esnap_num_blocks, __func__, NULL, &esnap_opts);
	opts.esnap_id = &esnap_opts;
	opts.esnap_id_len = sizeof(esnap_opts);
	opts.num_clusters = esnap_num_clusters;
	blob1 = ut_blob_create_and_open(bs, &opts);
	SPDK_CU_ASSERT_FATAL(blob1 != NULL);
	blobid1 = spdk_blob_get_id(blob1);
	CU_ASSERT(spdk_blob_is_esnap_clone(blob1));

	/* Call set_esternal_parent with blobid and esnapid the same */
	spdk_bs_blob_set_external_parent(bs, blobid1, esnap_dev1, &blobid1, sizeof(blobid1),
					 blob_op_complete, NULL);
	CU_ASSERT(g_bserrno == -EINVAL);

	/* Call set_external_parent with esnap of incompatible size */
	esnap_dev1->blockcnt = esnap_num_blocks - 1;
	spdk_bs_blob_set_external_parent(bs, blobid1, esnap_dev1, opts.esnap_id, opts.esnap_id_len,
					 blob_op_complete, NULL);
	CU_ASSERT(g_bserrno == -EINVAL);

	/* Call set_external_parent with a blob and its parent esnap */
	esnap_dev1->blocklen = block_sz;
	esnap_dev1->blockcnt = esnap_num_blocks;
	spdk_bs_blob_set_external_parent(bs, blobid1, esnap_dev1, opts.esnap_id, opts.esnap_id_len,
					 blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == -EEXIST);

	/* Create a blob that is a clone of a snapshots */
	ut_spdk_blob_opts_init(&opts);
	blob2 = ut_blob_create_and_open(bs, &opts);
	SPDK_CU_ASSERT_FATAL(blob2 != NULL);
	blobid2 = spdk_blob_get_id(blob2);
	spdk_bs_create_snapshot(bs, blobid2, NULL, blob_op_with_id_complete, NULL);
	poll_threads();
	SPDK_CU_ASSERT_FATAL(g_bserrno == 0);
	SPDK_CU_ASSERT_FATAL(g_blobid != SPDK_BLOBID_INVALID);
	snapshotid = g_blobid;

	/* Call set_parent correctly with a snapshot's clone blob */
	esnap_dev2->blocklen = block_sz;
	esnap_dev2->blockcnt = esnap_num_blocks;
	ut_esnap_opts_init(block_sz, esnap_num_blocks, __func__, NULL, &esnap_opts2);
	spdk_bs_blob_set_external_parent(bs, blobid2, esnap_dev2, &esnap_opts2, sizeof(esnap_opts2),
					 blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == 0);

	/* Check relations */
	rc = spdk_blob_get_esnap_id(blob2, &esnap_id, &esnap_id_len);
	CU_ASSERT(spdk_blob_is_esnap_clone(blob2));
	CU_ASSERT(!spdk_blob_is_clone(blob2));
	CU_ASSERT(rc == 0 && esnap_id_len == sizeof(esnap_opts2) &&
		  memcmp(esnap_id, &esnap_opts2, esnap_id_len) == 0);

	/* Create a not thin-provisioned blob that is not a clone */
	ut_spdk_blob_opts_init(&opts);
	opts.thin_provision = false;
	blob3 = ut_blob_create_and_open(bs, &opts);
	SPDK_CU_ASSERT_FATAL(blob3 != NULL);
	blobid3 = spdk_blob_get_id(blob3);

	/* Call set_external_parent with a blob that isn't a clone and that isn't thin-provisioned */
	spdk_bs_blob_set_external_parent(bs, blobid3, esnap_dev1, &esnap_opts, sizeof(esnap_opts),
					 blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == -EINVAL);

	/* Create a thin-provisioned blob that is not a clone */
	ut_spdk_blob_opts_init(&opts);
	opts.thin_provision = true;
	blob4 = ut_blob_create_and_open(bs, &opts);
	SPDK_CU_ASSERT_FATAL(blob4 != NULL);
	blobid4 = spdk_blob_get_id(blob4);

	/* Call set_external_parent correctly with a blob that isn't a clone */
	esnap_dev3->blocklen = block_sz;
	esnap_dev3->blockcnt = esnap_num_blocks;
	ut_esnap_opts_init(block_sz, esnap_num_blocks, __func__, NULL, &esnap_opts);
	spdk_bs_blob_set_external_parent(bs, blobid4, esnap_dev3, &esnap_opts, sizeof(esnap_opts),
					 blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == 0);

	/* Check relations */
	rc = spdk_blob_get_esnap_id(blob4, &esnap_id, &esnap_id_len);
	CU_ASSERT(spdk_blob_is_esnap_clone(blob4));
	CU_ASSERT(!spdk_blob_is_clone(blob4));
	CU_ASSERT(rc == 0 && esnap_id_len == sizeof(esnap_opts) &&
		  memcmp(esnap_id, &esnap_opts, esnap_id_len) == 0);

	ut_blob_close_and_delete(bs, blob4);
	ut_blob_close_and_delete(bs, blob3);
	ut_blob_close_and_delete(bs, blob2);
	ut_blob_close_and_delete(bs, blob1);
	spdk_bs_delete_blob(bs, snapshotid, blob_op_complete, NULL);
	dev_destroy(esnap_dev1);
	poll_threads();
	CU_ASSERT(g_bserrno == 0);
}

static void
suite_bs_setup(void)
{
@@ -9965,6 +10091,7 @@ main(int argc, char **argv)
		CU_ADD_TEST(suite, blob_esnap_clone_resize);
		CU_ADD_TEST(suite_bs, blob_shallow_copy);
		CU_ADD_TEST(suite_esnap_bs, blob_set_parent);
		CU_ADD_TEST(suite_esnap_bs, blob_set_external_parent);
	}

	allocate_threads(2);