Commit 635a1aa8 authored by Tomasz Kulasek's avatar Tomasz Kulasek Committed by Jim Harris
Browse files

blobstore: add decouple parent function



This patch adds an API to decouple blobs parent removing dependency
on it. Blob stays thin after this operation.

Also unit tests for blobstore inflate are improved and reused with
decouple parent functionality.

Change-Id: I96dfee467c78cf4f4d929ec7bc05263f7a23a8aa
Signed-off-by: default avatarTomasz Kulasek <tomaszx.kulasek@intel.com>
Reviewed-on: https://review.gerrithub.io/410829


Tested-by: default avatarSPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
parent 826aac63
Loading
Loading
Loading
Loading
+22 −2
Original line number Diff line number Diff line
@@ -522,8 +522,10 @@ void spdk_bs_delete_blob(struct spdk_blob_store *bs, spdk_blob_id blobid,
			 spdk_blob_op_complete cb_fn, void *cb_arg);

/**
 * Allocate all unallocated clusters in this blob and copy data from backing blob.
 * This call removes dependency on backing blob.
 * Allocate all clusters in this blob. Data for allocated clusters is copied
 * from backing blob(s) if they exist.
 *
 * This call removes all dependencies on any backing blobs.
 *
 * \param bs blobstore.
 * \param channel IO channel used to inflate blob.
@@ -534,6 +536,24 @@ void spdk_bs_delete_blob(struct spdk_blob_store *bs, spdk_blob_id blobid,
void spdk_bs_inflate_blob(struct spdk_blob_store *bs, struct spdk_io_channel *channel,
			  spdk_blob_id blobid, spdk_blob_op_complete cb_fn, void *cb_arg);

/**
 * Remove dependency on parent blob.
 *
 * This call allocates and copies data for any clusters that are allocated in
 * the parent blob, and decouples parent updating dependencies of blob to
 * its ancestor.
 *
 * If blob have no parent -EINVAL error is reported.
 *
 * \param bs blobstore.
 * \param channel IO channel used to inflate blob.
 * \param blobid The id of the blob.
 * \param cb_fn Called when the operation is complete.
 * \param cb_arg Argument passed to function cb_fn.
 */
void spdk_bs_blob_decouple_parent(struct spdk_blob_store *bs, struct spdk_io_channel *channel,
				  spdk_blob_id blobid, spdk_blob_op_complete cb_fn, void *cb_arg);

/**
 * Open a blob from the given blobstore.
 *
+9 −0
Original line number Diff line number Diff line
@@ -260,6 +260,15 @@ void spdk_lvol_open(struct spdk_lvol *lvol, spdk_lvol_op_with_handle_complete cb
 */
void spdk_lvol_inflate(struct spdk_lvol *lvol, spdk_lvol_op_complete cb_fn, void *cb_arg);

/**
 * Decouple parent of lvol
 *
 * \param lvol Handle to lvol
 * \param cb_fn Completion callback
 * \param cb_arg Completion callback custom arguments
 */
void spdk_lvol_decouple_parent(struct spdk_lvol *lvol, spdk_lvol_op_complete cb_fn, void *cb_arg);

#ifdef __cplusplus
}
#endif
+45 −0
Original line number Diff line number Diff line
@@ -760,6 +760,51 @@ invalid:

SPDK_RPC_REGISTER("inflate_lvol_bdev", spdk_rpc_inflate_lvol_bdev, SPDK_RPC_RUNTIME)

static void
spdk_rpc_decouple_parent_lvol_bdev(struct spdk_jsonrpc_request *request,
				   const struct spdk_json_val *params)
{
	struct rpc_inflate_lvol_bdev req = {};
	struct spdk_bdev *bdev;
	struct spdk_lvol *lvol;
	int rc = 0;

	SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "Decoupling parent of lvol\n");

	if (spdk_json_decode_object(params, rpc_inflate_lvol_bdev_decoders,
				    SPDK_COUNTOF(rpc_inflate_lvol_bdev_decoders),
				    &req)) {
		SPDK_INFOLOG(SPDK_LOG_LVOL_RPC, "spdk_json_decode_object failed\n");
		rc = -EINVAL;
		goto invalid;
	}

	bdev = spdk_bdev_get_by_name(req.name);
	if (bdev == NULL) {
		SPDK_ERRLOG("bdev '%s' does not exist\n", req.name);
		rc = -ENODEV;
		goto invalid;
	}

	lvol = vbdev_lvol_get_from_bdev(bdev);
	if (lvol == NULL) {
		SPDK_ERRLOG("lvol does not exist\n");
		rc = -ENODEV;
		goto invalid;
	}

	spdk_lvol_decouple_parent(lvol, _spdk_rpc_inflate_lvol_bdev_cb, request);

	free_rpc_inflate_lvol_bdev(&req);
	return;

invalid:
	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc));
	free_rpc_inflate_lvol_bdev(&req);
}

SPDK_RPC_REGISTER("decouple_parent_lvol_bdev", spdk_rpc_decouple_parent_lvol_bdev, SPDK_RPC_RUNTIME)

struct rpc_resize_lvol_bdev {
	char *name;
	uint64_t size;
+86 −13
Original line number Diff line number Diff line
@@ -4153,6 +4153,10 @@ struct spdk_clone_snapshot_ctx {
	/* Current cluster for inflate operation */
	uint64_t cluster;

	/* For inflation force allocation of all unallocated clusters and remove
	 * thin-provisioning. Otherwise only decouple parent and keep clone thin. */
	bool allocate_all;

	struct {
		spdk_blob_id id;
		struct spdk_blob *blob;
@@ -4581,7 +4585,7 @@ void spdk_bs_create_clone(struct spdk_blob_store *bs, spdk_blob_id blobid,
/* START spdk_bs_inflate_blob */

static void
_spdk_bs_inflate_blob_sync(void *cb_arg, int bserrno)
_spdk_bs_inflate_blob_set_parent_cpl(void *cb_arg, struct spdk_blob *_parent, int bserrno)
{
	struct spdk_clone_snapshot_ctx *ctx = (struct spdk_clone_snapshot_ctx *)cb_arg;
	struct spdk_blob *_blob = ctx->original.blob;
@@ -4591,11 +4595,18 @@ _spdk_bs_inflate_blob_sync(void *cb_arg, int bserrno)
		return;
	}

	/* Destroy back_bs_dev */
	assert(_parent != NULL);

	_spdk_bs_blob_list_remove(_blob);
	_blob->parent_id = _parent->id;
	_spdk_blob_set_xattr(_blob, BLOB_SNAPSHOT, &_blob->parent_id,
			     sizeof(spdk_blob_id), true);

	_blob->back_bs_dev->destroy(_blob->back_bs_dev);
	_blob->back_bs_dev = NULL;
	_blob->back_bs_dev = spdk_bs_create_blob_bs_dev(_parent);
	_spdk_bs_blob_list_add(_blob);

	_spdk_bs_clone_snapshot_origblob_cleanup(ctx, 0);
	spdk_blob_sync_md(_blob, _spdk_bs_clone_snapshot_origblob_cleanup, ctx);
}

static void
@@ -4603,21 +4614,61 @@ _spdk_bs_inflate_blob_done(void *cb_arg, int bserrno)
{
	struct spdk_clone_snapshot_ctx *ctx = (struct spdk_clone_snapshot_ctx *)cb_arg;
	struct spdk_blob *_blob = ctx->original.blob;
	struct spdk_blob *_parent;

	if (bserrno != 0) {
		_spdk_bs_clone_snapshot_origblob_cleanup(ctx, bserrno);
		return;
	}

	if (ctx->allocate_all) {
		/* remove thin provisioning */
		_spdk_bs_blob_list_remove(_blob);
		_spdk_blob_remove_xattr(_blob, BLOB_SNAPSHOT, true);
		_blob->invalid_flags = _blob->invalid_flags & ~SPDK_BLOB_THIN_PROV;
		_blob->back_bs_dev->destroy(_blob->back_bs_dev);
		_blob->back_bs_dev = NULL;
		_blob->parent_id = SPDK_BLOBID_INVALID;
	} else {
		_parent = ((struct spdk_blob_bs_dev *)(_blob->back_bs_dev))->blob;
		if (_parent->parent_id != SPDK_BLOBID_INVALID) {
			/* We must change the parent of the inflated blob */
			spdk_bs_open_blob(_blob->bs, _parent->parent_id,
					  _spdk_bs_inflate_blob_set_parent_cpl, ctx);
			return;
		}

		_spdk_bs_blob_list_remove(_blob);
		_spdk_blob_remove_xattr(_blob, BLOB_SNAPSHOT, true);
		_blob->parent_id = SPDK_BLOBID_INVALID;
		_blob->back_bs_dev->destroy(_blob->back_bs_dev);
		_blob->back_bs_dev = spdk_bs_create_zeroes_dev();
	}

	/* Unset thin provision */
	_blob->invalid_flags = _blob->invalid_flags & ~SPDK_BLOB_THIN_PROV;
	_blob->state = SPDK_BLOB_STATE_DIRTY;
	spdk_blob_sync_md(_blob, _spdk_bs_clone_snapshot_origblob_cleanup, ctx);
}

/* Check if cluster needs allocation */
static inline bool
_spdk_bs_cluster_needs_allocation(struct spdk_blob *blob, uint64_t cluster, bool allocate_all)
{
	struct spdk_blob_bs_dev *b;

	assert(blob != NULL);

	if (blob->active.clusters[cluster] != 0) {
		/* Cluster is already allocated */
		return false;
	}

	if (blob->parent_id == SPDK_BLOBID_INVALID) {
		/* Blob have no parent blob */
		return allocate_all;
	}

	spdk_blob_sync_md(_blob, _spdk_bs_inflate_blob_sync, ctx);
	b = (struct spdk_blob_bs_dev *)blob->back_bs_dev;
	return (allocate_all || b->blob->active.clusters[cluster] != 0);
}

static void
@@ -4633,7 +4684,7 @@ _spdk_bs_inflate_blob_touch_next(void *cb_arg, int bserrno)
	}

	for (; ctx->cluster < _blob->active.num_clusters; ctx->cluster++) {
		if (_blob->active.clusters[ctx->cluster] == 0) {
		if (_spdk_bs_cluster_needs_allocation(_blob, ctx->cluster, ctx->allocate_all)) {
			break;
		}
	}
@@ -4665,6 +4716,13 @@ _spdk_bs_inflate_blob_open_cpl(void *cb_arg, struct spdk_blob *_blob, int bserrn
	}
	ctx->original.blob = _blob;

	if (!ctx->allocate_all && _blob->parent_id == SPDK_BLOBID_INVALID) {
		/* This blob have no parent, so we cannot decouple it. */
		SPDK_ERRLOG("Cannot decouple parent of blob with no parent.\n");
		_spdk_bs_clone_snapshot_origblob_cleanup(ctx, -EINVAL);
		return;
	}

	if (spdk_blob_is_thin_provisioned(_blob) == false) {
		/* This is not thin provisioned blob. No need to inflate. */
		_spdk_bs_clone_snapshot_origblob_cleanup(ctx, 0);
@@ -4676,7 +4734,7 @@ _spdk_bs_inflate_blob_open_cpl(void *cb_arg, struct spdk_blob *_blob, int bserrn
	 */
	lfc = 0;
	for (i = 0; i < _blob->active.num_clusters; i++) {
		if (_blob->active.clusters[i] == 0) {
		if (_spdk_bs_cluster_needs_allocation(_blob, i, ctx->allocate_all)) {
			lfc = spdk_bit_array_find_first_clear(_blob->bs->used_clusters, lfc);
			if (lfc >= _blob->bs->total_clusters) {
				/* No more free clusters. Cannot satisfy the request */
@@ -4691,8 +4749,9 @@ _spdk_bs_inflate_blob_open_cpl(void *cb_arg, struct spdk_blob *_blob, int bserrn
	_spdk_bs_inflate_blob_touch_next(ctx, 0);
}

void spdk_bs_inflate_blob(struct spdk_blob_store *bs, struct spdk_io_channel *channel,
			  spdk_blob_id blobid, spdk_blob_op_complete cb_fn, void *cb_arg)
static void
_spdk_bs_inflate_blob(struct spdk_blob_store *bs, struct spdk_io_channel *channel,
		      spdk_blob_id blobid, bool allocate_all, spdk_blob_op_complete cb_fn, void *cb_arg)
{
	struct spdk_clone_snapshot_ctx *ctx = calloc(1, sizeof(*ctx));

@@ -4706,10 +4765,24 @@ void spdk_bs_inflate_blob(struct spdk_blob_store *bs, struct spdk_io_channel *ch
	ctx->bserrno = 0;
	ctx->original.id = blobid;
	ctx->channel = channel;
	ctx->allocate_all = allocate_all;

	spdk_bs_open_blob(bs, ctx->original.id, _spdk_bs_inflate_blob_open_cpl, ctx);
}

void
spdk_bs_inflate_blob(struct spdk_blob_store *bs, struct spdk_io_channel *channel,
		     spdk_blob_id blobid, spdk_blob_op_complete cb_fn, void *cb_arg)
{
	_spdk_bs_inflate_blob(bs, channel, blobid, true, cb_fn, cb_arg);
}

void
spdk_bs_blob_decouple_parent(struct spdk_blob_store *bs, struct spdk_io_channel *channel,
			     spdk_blob_id blobid, spdk_blob_op_complete cb_fn, void *cb_arg)
{
	_spdk_bs_inflate_blob(bs, channel, blobid, false, cb_fn, cb_arg);
}
/* END spdk_bs_inflate_blob */

/* START spdk_blob_resize */
+36 −0
Original line number Diff line number Diff line
@@ -1474,3 +1474,39 @@ spdk_lvol_inflate(struct spdk_lvol *lvol, spdk_lvol_op_complete cb_fn, void *cb_
	spdk_bs_inflate_blob(lvol->lvol_store->blobstore, req->channel, blob_id, _spdk_lvol_inflate_cb,
			     req);
}

void
spdk_lvol_decouple_parent(struct spdk_lvol *lvol, spdk_lvol_op_complete cb_fn, void *cb_arg)
{
	struct spdk_lvol_req *req;
	struct spdk_blob *blob = lvol->blob;
	spdk_blob_id blob_id = spdk_blob_get_id(blob);

	assert(cb_fn != NULL);

	if (lvol == NULL) {
		SPDK_ERRLOG("Lvol does not exist\n");
		cb_fn(cb_arg, -ENODEV);
		return;
	}

	req = calloc(1, sizeof(*req));
	if (!req) {
		SPDK_ERRLOG("Cannot alloc memory for lvol request pointer\n");
		cb_fn(cb_arg, -ENOMEM);
		return;
	}

	req->cb_fn = cb_fn;
	req->cb_arg = cb_arg;
	req->channel = spdk_bs_alloc_io_channel(lvol->lvol_store->blobstore);
	if (req->channel == NULL) {
		SPDK_ERRLOG("Cannot alloc io channel for lvol inflate request\n");
		free(req);
		cb_fn(cb_arg, -ENOMEM);
		return;
	}

	spdk_bs_blob_decouple_parent(lvol->lvol_store->blobstore, req->channel, blob_id,
				     _spdk_lvol_inflate_cb, req);
}
Loading