Commit 7b8c7efe authored by Damiano Cipriani's avatar Damiano Cipriani Committed by Jim Harris
Browse files

blob: add blob set parent



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

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


Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarTomasz Zawadzki <tomasz.zawadzki@intel.com>
Community-CI: Mellanox Build Bot
parent 40b11d96
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -824,6 +824,24 @@ int spdk_bs_blob_shallow_copy(struct spdk_blob_store *bs, struct spdk_io_channel
			      spdk_blob_op_complete cb_fn, void *cb_arg);


/**
 * Set a snapshot as the parent of a blob
 *
 * This call set a snapshot as the parent of a blob, making the blob a clone of this snapshot.
 * The previous parent of the blob, if any, can be another snapshot or an external snapshot; if
 * the blob is not a clone, it must be thin-provisioned.
 * Blob and parent snapshot must have the same size.
 *
 * \param bs blobstore.
 * \param blob_id The id of the blob.
 * \param snapshot_id The id of the parent snapshot.
 * \param cb_fn Called when the operation is complete.
 * \param cb_arg Argument passed to function cb_fn.
 */
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);


struct spdk_blob_open_opts {
	enum blob_clear_method  clear_method;

+261 −1
Original line number Diff line number Diff line
@@ -410,10 +410,28 @@ blob_back_bs_destroy(struct spdk_blob *blob)
	blob->back_bs_dev = NULL;
}

struct blob_parent {
	union {
		struct {
			spdk_blob_id id;
			struct spdk_blob *blob;
		} snapshot;
	} u;
};

typedef int (*set_parent_refs_cb)(struct spdk_blob *blob, struct blob_parent *parent);

struct set_bs_dev_ctx {
	struct spdk_blob	*blob;
	struct spdk_bs_dev	*back_bs_dev;

	/*
	 * This callback is used during a set parent operation to change the references
	 * to the parent of the blob.
	 */
	set_parent_refs_cb	parent_refs_cb_fn;
	struct blob_parent	*parent_refs_cb_arg;

	spdk_blob_op_complete	cb_fn;
	void			*cb_arg;
	int			bserrno;
@@ -421,6 +439,7 @@ struct set_bs_dev_ctx {

static void
blob_set_back_bs_dev(struct spdk_blob *blob, struct spdk_bs_dev *back_bs_dev,
		     set_parent_refs_cb parent_refs_cb_fn, struct blob_parent *parent_refs_cb_arg,
		     spdk_blob_op_complete cb_fn, void *cb_arg)
{
	struct set_bs_dev_ctx	*ctx;
@@ -433,6 +452,8 @@ blob_set_back_bs_dev(struct spdk_blob *blob, struct spdk_bs_dev *back_bs_dev,
		return;
	}

	ctx->parent_refs_cb_fn = parent_refs_cb_fn;
	ctx->parent_refs_cb_arg = parent_refs_cb_arg;
	ctx->cb_fn = cb_fn;
	ctx->cb_arg = cb_arg;
	ctx->back_bs_dev = back_bs_dev;
@@ -7387,6 +7408,235 @@ spdk_bs_blob_shallow_copy(struct spdk_blob_store *bs, struct spdk_io_channel *ch
}
/* END spdk_bs_blob_shallow_copy */

/* START spdk_bs_blob_set_parent */

struct set_parent_ctx {
	struct spdk_blob_store *bs;
	int			bserrno;
	spdk_bs_op_complete	cb_fn;
	void			*cb_arg;

	struct spdk_blob	*blob;
	bool			blob_md_ro;

	struct blob_parent	parent;
};

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

	assert(ctx != NULL);

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

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

	free(ctx);
}

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

	if (ctx->bserrno != 0) {
		spdk_blob_close(ctx->parent.u.snapshot.blob, bs_set_parent_cleanup_finish, ctx);
		return;
	}

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

	bs_set_parent_cleanup_finish(ctx, ctx->bserrno);
}

static void
bs_set_parent_close_blob(void *cb_arg, int bserrno)
{
	struct set_parent_ctx *ctx = cb_arg;
	struct spdk_blob *blob = ctx->blob;
	struct spdk_blob *snapshot = ctx->parent.u.snapshot.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;
	snapshot->locked_operation_in_progress = false;

	spdk_blob_close(blob, bs_set_parent_close_snapshot, ctx);
}

static void
bs_set_parent_set_back_bs_dev_done(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_parent_close_blob(ctx, bserrno);
		return;
	}

	spdk_blob_sync_md(blob, bs_set_parent_close_blob, ctx);
}

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

	bs_blob_list_remove(blob);

	rc = blob_set_xattr(blob, BLOB_SNAPSHOT, &parent->u.snapshot.id, sizeof(spdk_blob_id), true);
	if (rc != 0) {
		SPDK_ERRLOG("error %d setting snapshot xattr\n", rc);
		return rc;
	}
	blob->parent_id = parent->u.snapshot.id;

	if (blob_is_esnap_clone(blob)) {
		/* Remove the xattr that references the external snapshot */
		blob->invalid_flags &= ~SPDK_BLOB_EXTERNAL_SNAPSHOT;
		blob_remove_xattr(blob, BLOB_EXTERNAL_SNAPSHOT_ID, true);
	}

	bs_blob_list_add(blob);

	return 0;
}

static void
bs_set_parent_snapshot_open_cpl(void *cb_arg, struct spdk_blob *snapshot, int bserrno)
{
	struct set_parent_ctx *ctx = cb_arg;
	struct spdk_blob *blob = ctx->blob;
	struct spdk_bs_dev *back_bs_dev;

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

	ctx->parent.u.snapshot.blob = snapshot;
	ctx->parent.u.snapshot.id = snapshot->id;

	if (!spdk_blob_is_snapshot(snapshot)) {
		SPDK_ERRLOG("parent blob is not a snapshot\n");
		ctx->bserrno = -EINVAL;
		spdk_blob_close(blob, bs_set_parent_close_snapshot, ctx);
		return;
	}

	if (blob->active.num_clusters != snapshot->active.num_clusters) {
		SPDK_ERRLOG("parent blob has a number of clusters different from child's ones\n");
		ctx->bserrno = -EINVAL;
		spdk_blob_close(blob, bs_set_parent_close_snapshot, ctx);
		return;
	}

	if (blob->locked_operation_in_progress || snapshot->locked_operation_in_progress) {
		SPDK_ERRLOG("cannot set parent of blob, another operation in progress\n");
		ctx->bserrno = -EBUSY;
		spdk_blob_close(blob, bs_set_parent_close_snapshot, ctx);
		return;
	}

	blob->locked_operation_in_progress = true;
	snapshot->locked_operation_in_progress = true;

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

	back_bs_dev = bs_create_blob_bs_dev(snapshot);

	blob_set_back_bs_dev(blob, back_bs_dev, bs_set_parent_refs, &ctx->parent,
			     bs_set_parent_set_back_bs_dev_done,
			     ctx);
}

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

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

	if (!spdk_blob_is_thin_provisioned(blob)) {
		SPDK_ERRLOG("blob is not thin-provisioned\n");
		ctx->bserrno = -EINVAL;
		spdk_blob_close(blob, bs_set_parent_cleanup_finish, ctx);
		return;
	}

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

	spdk_bs_open_blob(ctx->bs, ctx->parent.u.snapshot.id, bs_set_parent_snapshot_open_cpl, ctx);
}

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)
{
	struct set_parent_ctx *ctx;

	if (snapshot_id == SPDK_BLOBID_INVALID) {
		SPDK_ERRLOG("snapshot id not valid\n");
		cb_fn(cb_arg, -EINVAL);
		return;
	}

	if (blob_id == snapshot_id) {
		SPDK_ERRLOG("blob id and snapshot id cannot be the same\n");
		cb_fn(cb_arg, -EINVAL);
		return;
	}

	if (spdk_blob_get_parent_snapshot(bs, blob_id) == snapshot_id) {
		SPDK_NOTICELOG("snapshot is already the parent of blob\n");
		cb_fn(cb_arg, -EEXIST);
		return;
	}

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

	ctx->bs = bs;
	ctx->parent.u.snapshot.id = snapshot_id;
	ctx->cb_fn = cb_fn;
	ctx->cb_arg = cb_arg;
	ctx->bserrno = 0;

	spdk_bs_open_blob(bs, blob_id, bs_set_parent_blob_open_cpl, ctx);
}
/* END spdk_bs_blob_set_parent */

/* START spdk_blob_resize */
struct spdk_bs_resize_ctx {
	spdk_blob_op_complete cb_fn;
@@ -9770,6 +10020,7 @@ static void
blob_frozen_set_back_bs_dev(void *_ctx, struct spdk_blob *blob, int bserrno)
{
	struct set_bs_dev_ctx	*ctx = _ctx;
	int rc;

	if (bserrno != 0) {
		SPDK_ERRLOG("blob 0x%" PRIx64 ": failed to release old back_bs_dev with error %d\n",
@@ -9784,6 +10035,15 @@ blob_frozen_set_back_bs_dev(void *_ctx, struct spdk_blob *blob, int bserrno)
		blob->back_bs_dev = NULL;
	}

	if (ctx->parent_refs_cb_fn) {
		rc = ctx->parent_refs_cb_fn(blob, ctx->parent_refs_cb_arg);
		if (rc != 0) {
			ctx->bserrno = rc;
			blob_unfreeze_io(blob, blob_set_back_bs_dev_done, ctx);
			return;
		}
	}

	SPDK_NOTICELOG("blob 0x%" PRIx64 ": hotplugged back_bs_dev\n", blob->id);
	blob->back_bs_dev = ctx->back_bs_dev;
	ctx->bserrno = 0;
@@ -9822,7 +10082,7 @@ spdk_blob_set_esnap_bs_dev(struct spdk_blob *blob, struct spdk_bs_dev *back_bs_d
		return;
	}

	blob_set_back_bs_dev(blob, back_bs_dev, cb_fn, cb_arg);
	blob_set_back_bs_dev(blob, back_bs_dev, NULL, NULL, cb_fn, cb_arg);
}

struct spdk_bs_dev *
+1 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@
	spdk_bs_inflate_blob;
	spdk_bs_blob_decouple_parent;
	spdk_bs_blob_shallow_copy;
	spdk_bs_blob_set_parent;
	spdk_blob_open_opts_init;
	spdk_bs_open_blob;
	spdk_bs_open_blob_ext;
+158 −0
Original line number Diff line number Diff line
@@ -9530,6 +9530,163 @@ blob_shallow_copy(void)
	poll_threads();
}

static void
blob_set_parent(void)
{
	struct spdk_blob_store *bs = g_bs;
	struct spdk_blob_opts opts;
	struct ut_esnap_opts esnap_opts;
	struct spdk_blob *blob1, *blob2, *blob3, *blob4, *blob5;
	spdk_blob_id blobid1, blobid2, blobid3, blobid4, blobid5,
		     snapshotid1, snapshotid2, snapshotid3;
	uint32_t cluster_sz, block_sz;
	const uint32_t esnap_num_clusters = 4;
	uint64_t esnap_num_blocks;
	spdk_blob_id ids[2];
	size_t clone_count = 2;

	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;

	/* Create a normal blob and make a couple of snapshots */
	ut_spdk_blob_opts_init(&opts);
	blob1 = ut_blob_create_and_open(bs, &opts);
	SPDK_CU_ASSERT_FATAL(blob1 != NULL);
	blobid1 = spdk_blob_get_id(blob1);
	spdk_bs_create_snapshot(bs, blobid1, 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);
	snapshotid1 = g_blobid;
	spdk_bs_create_snapshot(bs, blobid1, 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);
	snapshotid2 = g_blobid;

	/* Call set_parent with an invalid snapshotid */
	spdk_bs_blob_set_parent(bs, blobid1, SPDK_BLOBID_INVALID, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == -EINVAL);

	/* Call set_parent with blobid and snapshotid the same */
	spdk_bs_blob_set_parent(bs, blobid1, blobid1, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == -EINVAL);

	/* Call set_parent with a blob and its parent snapshot */
	spdk_bs_blob_set_parent(bs, blobid1, snapshotid2, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == -EEXIST);

	/* 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;
	blob2 = ut_blob_create_and_open(bs, &opts);
	SPDK_CU_ASSERT_FATAL(blob2 != NULL);
	blobid2 = spdk_blob_get_id(blob2);
	CU_ASSERT(spdk_blob_is_esnap_clone(blob2));

	/* Call set_parent with a non snapshot parent */
	spdk_bs_blob_set_parent(bs, blobid2, blobid1, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == -EINVAL);

	/* Call set_parent with blob and snapshot of different size */
	spdk_bs_blob_set_parent(bs, blobid2, snapshotid1, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == -EINVAL);

	/* Call set_parent correctly with a snapshot's clone blob */
	spdk_bs_blob_set_parent(bs, blobid1, snapshotid1, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == 0);

	/* Check relations */
	CU_ASSERT(spdk_blob_is_clone(blob1));
	CU_ASSERT(spdk_blob_get_parent_snapshot(bs, blobid1) == snapshotid1);
	CU_ASSERT(spdk_blob_get_clones(bs, snapshotid1, ids, &clone_count) == 0);
	CU_ASSERT(clone_count == 2);
	CU_ASSERT(ids[1] == blobid1);

	/* Create another normal blob with size equal to esnap size and make a snapshot */
	ut_spdk_blob_opts_init(&opts);
	opts.num_clusters = esnap_num_clusters;
	opts.thin_provision = true;
	blob3 = ut_blob_create_and_open(bs, &opts);
	SPDK_CU_ASSERT_FATAL(blob3 != NULL);
	blobid3 = spdk_blob_get_id(blob3);
	spdk_bs_create_snapshot(bs, blobid3, 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);
	snapshotid3 = g_blobid;

	/* Call set_parent correctly with an esnap's clone blob */
	spdk_bs_blob_set_parent(bs, blobid2, snapshotid3, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == 0);

	/* Check relations */
	CU_ASSERT(!spdk_blob_is_esnap_clone(blob2));
	CU_ASSERT(spdk_blob_is_clone(blob2));
	CU_ASSERT(spdk_blob_get_parent_snapshot(bs, blobid2) == snapshotid3);
	CU_ASSERT(spdk_blob_get_clones(bs, snapshotid3, ids, &clone_count) == 0);
	CU_ASSERT(clone_count == 2);
	CU_ASSERT(ids[1] == blobid2);

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

	/* Call set_parent with a blob that isn't a clone and that isn't thin-provisioned */
	spdk_bs_blob_set_parent(bs, blobid4, snapshotid2, 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;
	blob5 = ut_blob_create_and_open(bs, &opts);
	SPDK_CU_ASSERT_FATAL(blob5 != NULL);
	blobid5 = spdk_blob_get_id(blob5);

	/* Call set_parent correctly with a blob that isn't a clone */
	spdk_bs_blob_set_parent(bs, blobid5, snapshotid2, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == 0);

	/* Check relations */
	CU_ASSERT(spdk_blob_is_clone(blob5));
	CU_ASSERT(spdk_blob_get_parent_snapshot(bs, blobid5) == snapshotid2);
	CU_ASSERT(spdk_blob_get_clones(bs, snapshotid2, ids, &clone_count) == 0);
	CU_ASSERT(clone_count == 1);
	CU_ASSERT(ids[0] == blobid5);

	/* Clean up */
	ut_blob_close_and_delete(bs, blob5);
	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, snapshotid3, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == 0);
	spdk_bs_delete_blob(bs, snapshotid2, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == 0);
	spdk_bs_delete_blob(bs, snapshotid1, blob_op_complete, NULL);
	poll_threads();
	CU_ASSERT(g_bserrno == 0);
}

static void
suite_bs_setup(void)
{
@@ -9807,6 +9964,7 @@ main(int argc, char **argv)
		CU_ADD_TEST(suite_bs, blob_clone_resize);
		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);
	}

	allocate_threads(2);