blobstore: add inflate call

Inflate call can be used on thin provisioned blob or clones.

Function allocates all unallocated clusters on specified blob and:
 - For clones, copies data from backing blob.
 - For thin provisioned blobs, clusters are zeroed.

After this call all dependency from specified blob is removed
what allows deletion i.e. snapshots.

Signed-off-by: Piotr Pelplinski <piotr.pelplinski@intel.com>
Signed-off-by: Tomasz Kulasek <tomaszx.kulasek@intel.com>
Change-Id: Ibff569e45b12068b2fb46557156be348b36c252b
Reviewed-on: https://review.gerrithub.io/399367
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
Piotr Pelplinski 2018-02-02 12:56:14 +01:00 committed by Daniel Verkamp
parent b1ccb095e4
commit ede6d97e3c
3 changed files with 420 additions and 0 deletions

View File

@ -467,6 +467,19 @@ bool spdk_blob_is_thin_provisioned(struct spdk_blob *blob);
void spdk_bs_delete_blob(struct spdk_blob_store *bs, spdk_blob_id blobid, void spdk_bs_delete_blob(struct spdk_blob_store *bs, spdk_blob_id blobid,
spdk_blob_op_complete cb_fn, void *cb_arg); 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.
*
* \param bs blobstore.
* \param channel IO channel used to inflate blob.
* \param blobid The id of the blob to inflate.
* \param cb_fn Called when the operation is complete.
* \param cb_arg Argument passed to function cb_fn.
*/
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);
/** /**
* Open a blob from the given blobstore. * Open a blob from the given blobstore.
* *

View File

@ -1699,6 +1699,11 @@ _spdk_blob_request_submit_op_single(struct spdk_io_channel *_ch, struct spdk_blo
/* Write to the blob */ /* Write to the blob */
spdk_bs_batch_t *batch; spdk_bs_batch_t *batch;
if (lba_count == 0) {
cb_fn(cb_arg, 0);
return;
}
batch = spdk_bs_batch_open(_ch, &cpl); batch = spdk_bs_batch_open(_ch, &cpl);
if (!batch) { if (!batch) {
cb_fn(cb_arg, -ENOMEM); cb_fn(cb_arg, -ENOMEM);
@ -3706,6 +3711,11 @@ struct spdk_clone_snapshot_ctx {
struct spdk_bs_cpl cpl; struct spdk_bs_cpl cpl;
int bserrno; int bserrno;
struct spdk_io_channel *channel;
/* Current cluster for inflate operation */
uint64_t cluster;
struct { struct {
spdk_blob_id id; spdk_blob_id id;
struct spdk_blob *blob; struct spdk_blob *blob;
@ -3738,6 +3748,9 @@ _spdk_bs_clone_snapshot_cleanup_finish(void *cb_arg, int bserrno)
case SPDK_BS_CPL_TYPE_BLOBID: case SPDK_BS_CPL_TYPE_BLOBID:
cpl->u.blobid.cb_fn(cpl->u.blobid.cb_arg, cpl->u.blobid.blobid, ctx->bserrno); cpl->u.blobid.cb_fn(cpl->u.blobid.cb_arg, cpl->u.blobid.blobid, ctx->bserrno);
break; break;
case SPDK_BS_CPL_TYPE_BLOB_BASIC:
cpl->u.blob_basic.cb_fn(cpl->u.blob_basic.cb_arg, ctx->bserrno);
break;
default: default:
SPDK_UNREACHABLE(); SPDK_UNREACHABLE();
break; break;
@ -4072,6 +4085,140 @@ void spdk_bs_create_clone(struct spdk_blob_store *bs, spdk_blob_id blobid,
/* END spdk_bs_create_clone */ /* END spdk_bs_create_clone */
/* START spdk_bs_inflate_blob */
static void
_spdk_bs_inflate_blob_sync(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;
if (bserrno != 0) {
_spdk_bs_clone_snapshot_origblob_cleanup(ctx, bserrno);
return;
}
/* Destroy back_bs_dev */
_blob->back_bs_dev->destroy(_blob->back_bs_dev);
_blob->back_bs_dev = NULL;
_spdk_bs_clone_snapshot_origblob_cleanup(ctx, 0);
}
static void
_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;
if (bserrno != 0) {
_spdk_bs_clone_snapshot_origblob_cleanup(ctx, bserrno);
return;
}
_spdk_bs_blob_list_remove(_blob);
_spdk_blob_remove_xattr(_blob, BLOB_SNAPSHOT, true);
/* 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_inflate_blob_sync, ctx);
}
static void
_spdk_bs_inflate_blob_touch_next(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;
uint64_t offset;
if (bserrno != 0) {
_spdk_bs_clone_snapshot_origblob_cleanup(ctx, bserrno);
return;
}
for (; ctx->cluster < _blob->active.num_clusters; ctx->cluster++) {
if (_blob->active.clusters[ctx->cluster] == 0) {
break;
}
}
if (ctx->cluster < _blob->active.num_clusters) {
offset = _spdk_bs_cluster_to_page(_blob->bs, ctx->cluster);
/* We may safely increment a cluster before write */
ctx->cluster++;
/* Use zero length write to touch a cluster */
spdk_blob_io_write(_blob, ctx->channel, NULL, offset, 0,
_spdk_bs_inflate_blob_touch_next, ctx);
} else {
_spdk_bs_inflate_blob_done(cb_arg, bserrno);
}
}
static void
_spdk_bs_inflate_blob_open_cpl(void *cb_arg, struct spdk_blob *_blob, int bserrno)
{
struct spdk_clone_snapshot_ctx *ctx = (struct spdk_clone_snapshot_ctx *)cb_arg;
uint64_t lfc; /* lowest free cluster */
uint64_t i;
if (bserrno != 0) {
_spdk_bs_clone_snapshot_cleanup_finish(ctx, bserrno);
return;
}
ctx->original.blob = _blob;
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);
return;
}
/* Do two passes - one to verify that we can obtain enough clusters
* and another to actually claim them.
*/
lfc = 0;
for (i = 0; i < _blob->active.num_clusters; i++) {
if (_blob->active.clusters[i] == 0) {
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 */
_spdk_bs_clone_snapshot_origblob_cleanup(ctx, -ENOSPC);
return;
}
lfc++;
}
}
ctx->cluster = 0;
_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)
{
struct spdk_clone_snapshot_ctx *ctx = calloc(1, sizeof(*ctx));
if (!ctx) {
cb_fn(cb_arg, -ENOMEM);
return;
}
ctx->cpl.type = SPDK_BS_CPL_TYPE_BLOB_BASIC;
ctx->cpl.u.bs_basic.cb_fn = cb_fn;
ctx->cpl.u.bs_basic.cb_arg = cb_arg;
ctx->bserrno = 0;
ctx->original.id = blobid;
ctx->channel = channel;
spdk_bs_open_blob(bs, ctx->original.id, _spdk_bs_inflate_blob_open_cpl, ctx);
}
/* END spdk_bs_inflate_blob */
/* START spdk_blob_resize */ /* START spdk_blob_resize */
void void
spdk_blob_resize(struct spdk_blob *blob, uint64_t sz, spdk_blob_op_complete cb_fn, void *cb_arg) spdk_blob_resize(struct spdk_blob *blob, uint64_t sz, spdk_blob_op_complete cb_fn, void *cb_arg)

View File

@ -805,6 +805,92 @@ blob_clone(void)
} }
static void
blob_inflate(void)
{
struct spdk_blob_store *bs;
struct spdk_bs_dev *dev;
struct spdk_blob_opts opts;
struct spdk_blob *blob, *snapshot;
spdk_blob_id blobid, snapshotid;
struct spdk_io_channel *channel;
uint64_t free_clusters;
dev = init_dev();
spdk_bs_init(dev, NULL, bs_op_with_handle_complete, NULL);
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_bs != NULL);
bs = g_bs;
channel = spdk_bs_alloc_io_channel(bs);
SPDK_CU_ASSERT_FATAL(channel != NULL);
/* Create blob with 10 clusters */
spdk_blob_opts_init(&opts);
opts.num_clusters = 10;
spdk_bs_create_blob_ext(bs, &opts, blob_op_with_id_complete, NULL);
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID);
blobid = g_blobid;
spdk_bs_open_blob(bs, blobid, blob_op_with_handle_complete, NULL);
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
blob = g_blob;
CU_ASSERT(spdk_blob_get_num_clusters(blob) == 10)
CU_ASSERT(spdk_blob_is_thin_provisioned(blob) == false);
/* Create snapshot */
spdk_bs_create_snapshot(bs, blobid, NULL, blob_op_with_id_complete, NULL);
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID);
snapshotid = g_blobid;
CU_ASSERT(spdk_blob_is_thin_provisioned(blob) == true);
CU_ASSERT(spdk_blob_get_num_clusters(blob) == 10)
spdk_bs_open_blob(bs, snapshotid, blob_op_with_handle_complete, NULL);
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
snapshot = g_blob;
CU_ASSERT(snapshot->data_ro == true)
CU_ASSERT(snapshot->md_ro == true)
CU_ASSERT(spdk_blob_get_num_clusters(snapshot) == 10);
spdk_blob_close(snapshot, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
free_clusters = spdk_bs_free_cluster_count(bs);
/* Inflate blob */
spdk_bs_inflate_blob(bs, channel, blobid, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
/* All 10 clusters should be allocated from blob store */
CU_ASSERT(spdk_bs_free_cluster_count(bs) == free_clusters - 10);
/* Now, it should be possible to delete snapshot */
spdk_bs_delete_blob(bs, snapshotid, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(spdk_blob_get_num_clusters(blob) == 10)
CU_ASSERT(spdk_blob_is_thin_provisioned(blob) == false);
spdk_blob_close(blob, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
spdk_bs_unload(g_bs, bs_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
g_bs = NULL;
spdk_bs_free_io_channel(channel);
}
static void static void
blob_delete(void) blob_delete(void)
{ {
@ -3762,6 +3848,178 @@ blob_snapshot_rw_iov(void)
g_blobid = 0; g_blobid = 0;
} }
static void
blob_inflate_rw(void)
{
static uint8_t *zero;
struct spdk_blob_store *bs;
struct spdk_bs_dev *dev;
struct spdk_blob *blob, *snapshot;
struct spdk_io_channel *channel;
struct spdk_blob_opts opts;
spdk_blob_id blobid, snapshotid;
uint64_t free_clusters;
uint64_t cluster_size;
uint64_t payload_size;
uint8_t *payload_read;
uint8_t *payload_write;
uint8_t *payload_clone;
uint64_t pages_per_cluster;
uint64_t pages_per_payload;
int i;
dev = init_dev();
spdk_bs_init(dev, NULL, bs_op_with_handle_complete, NULL);
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_bs != NULL);
bs = g_bs;
free_clusters = spdk_bs_free_cluster_count(bs);
cluster_size = spdk_bs_get_cluster_size(bs);
pages_per_cluster = cluster_size / spdk_bs_get_page_size(bs);
pages_per_payload = pages_per_cluster * 5;
payload_size = cluster_size * 5;
payload_read = malloc(payload_size);
SPDK_CU_ASSERT_FATAL(payload_read != NULL);
payload_write = malloc(payload_size);
SPDK_CU_ASSERT_FATAL(payload_write != NULL);
payload_clone = malloc(payload_size);
SPDK_CU_ASSERT_FATAL(payload_clone != NULL);
zero = calloc(1, payload_size);
SPDK_CU_ASSERT_FATAL(zero != NULL);
channel = spdk_bs_alloc_io_channel(bs);
SPDK_CU_ASSERT_FATAL(channel != NULL);
/* Create blob */
spdk_blob_opts_init(&opts);
opts.thin_provision = true;
opts.num_clusters = 5;
spdk_bs_create_blob_ext(bs, &opts, blob_op_with_id_complete, NULL);
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID);
CU_ASSERT(free_clusters == spdk_bs_free_cluster_count(bs));
blobid = g_blobid;
spdk_bs_open_blob(bs, blobid, blob_op_with_handle_complete, NULL);
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
blob = g_blob;
CU_ASSERT(spdk_blob_get_num_clusters(blob) == 5);
/* Initial read should return zeroed payload */
memset(payload_read, 0xFF, payload_size);
spdk_blob_io_read(blob, channel, payload_read, 0, pages_per_payload, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(memcmp(zero, payload_read, payload_size) == 0);
/* Fill whole blob with a pattern */
memset(payload_write, 0xE5, payload_size);
spdk_blob_io_write(blob, channel, payload_write, 0, pages_per_payload,
blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(free_clusters != spdk_bs_free_cluster_count(bs));
/* Create snapshot from blob */
spdk_bs_create_snapshot(bs, blobid, NULL, blob_op_with_id_complete, NULL);
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID);
snapshotid = g_blobid;
spdk_bs_open_blob(bs, snapshotid, blob_op_with_handle_complete, NULL);
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
snapshot = g_blob;
CU_ASSERT(snapshot->data_ro == true)
CU_ASSERT(snapshot->md_ro == true)
CU_ASSERT(spdk_blob_get_num_clusters(snapshot) == 5)
/* Write every second cluster with a pattern.
*
* payload_clone stores expected result on "blob" read at the time and
* is used only to check data consistency on clone before and after
* inflation. Initially we fill it with a backing snapshots pattern
* used before.
*/
memset(payload_clone, 0xE5, payload_size);
memset(payload_write, 0xAA, payload_size);
for (i = 1; i < 5; i += 2) {
spdk_blob_io_write(blob, channel, payload_write, i * pages_per_cluster,
pages_per_cluster, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
/* Update expected result */
memcpy(payload_clone + (cluster_size * i), payload_write,
cluster_size);
}
CU_ASSERT(free_clusters != spdk_bs_free_cluster_count(bs));
/* Check data consistency on clone */
memset(payload_read, 0xFF, payload_size);
spdk_blob_io_read(blob, channel, payload_read, 0, pages_per_payload,
blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(memcmp(payload_clone, payload_read, payload_size) == 0);
/* Close all blobs */
spdk_blob_close(blob, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
spdk_blob_close(snapshot, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
/* Inflate blob */
spdk_bs_inflate_blob(bs, channel, blobid, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
/* Try to delete snapshot (should pass) */
spdk_bs_delete_blob(bs, snapshotid, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
/* Reopen blob after snapshot deletion */
spdk_bs_open_blob(bs, blobid, blob_op_with_handle_complete, NULL);
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
blob = g_blob;
CU_ASSERT(spdk_blob_get_num_clusters(blob) == 5);
/* Check data consistency on inflated blob */
memset(payload_read, 0xFF, payload_size);
spdk_blob_io_read(blob, channel, payload_read, 0, pages_per_payload, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
CU_ASSERT(memcmp(payload_clone, payload_read, payload_size) == 0);
spdk_blob_close(blob, blob_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
spdk_bs_free_io_channel(channel);
/* Unload the blob store */
spdk_bs_unload(g_bs, bs_op_complete, NULL);
CU_ASSERT(g_bserrno == 0);
g_bs = NULL;
g_blob = NULL;
g_blobid = 0;
free(payload_read);
free(payload_write);
free(payload_clone);
free(zero);
}
/** /**
* Snapshot-clones relation test * Snapshot-clones relation test
* *
@ -4054,6 +4312,7 @@ int main(int argc, char **argv)
CU_add_test(suite, "blob_thin_provision", blob_thin_provision) == NULL || CU_add_test(suite, "blob_thin_provision", blob_thin_provision) == NULL ||
CU_add_test(suite, "blob_snapshot", blob_snapshot) == NULL || CU_add_test(suite, "blob_snapshot", blob_snapshot) == NULL ||
CU_add_test(suite, "blob_clone", blob_clone) == NULL || CU_add_test(suite, "blob_clone", blob_clone) == NULL ||
CU_add_test(suite, "blob_inflate", blob_inflate) == NULL ||
CU_add_test(suite, "blob_delete", blob_delete) == NULL || CU_add_test(suite, "blob_delete", blob_delete) == NULL ||
CU_add_test(suite, "blob_resize", blob_resize) == NULL || CU_add_test(suite, "blob_resize", blob_resize) == NULL ||
CU_add_test(suite, "blob_read_only", blob_read_only) == NULL || CU_add_test(suite, "blob_read_only", blob_read_only) == NULL ||
@ -4090,6 +4349,7 @@ int main(int argc, char **argv)
CU_add_test(suite, "bs_load_iter", bs_load_iter) == NULL || CU_add_test(suite, "bs_load_iter", bs_load_iter) == NULL ||
CU_add_test(suite, "blob_snapshot_rw", blob_snapshot_rw) == NULL || CU_add_test(suite, "blob_snapshot_rw", blob_snapshot_rw) == NULL ||
CU_add_test(suite, "blob_snapshot_rw_iov", blob_snapshot_rw_iov) == NULL || CU_add_test(suite, "blob_snapshot_rw_iov", blob_snapshot_rw_iov) == NULL ||
CU_add_test(suite, "blob_inflate_rw", blob_inflate_rw) == NULL ||
CU_add_test(suite, "blob_relations", blob_relations) == NULL CU_add_test(suite, "blob_relations", blob_relations) == NULL
) { ) {
CU_cleanup_registry(); CU_cleanup_registry();