blobstore: clone-snapshot blobstore relations

This commit provides an API to obtain an information about
snapshot and clone relations.

The main objective is:

 1) Determinate if we can delete snapshot (if have some created
    clones),

 2) Provide an information about parent/children nodes to the upper
    layer (e.g. lvol)

Realization:

 1) Structure parent-children is stored in the blob store object
    and updated on:

     a) blob store load,

     b) blob create/delete,

 2) Full information about parent-children is provided via new API:
    spdk_blob_get_parent() and spdk_blob_get_children(),

Note:

    While we don't store an information about these relations in the
    blob store, we need to open all blobs on blob store load to create
    it. It should be considered that it have an impact on the blobstore
    loading performance.

Change-Id: Ie0237fa5b93af01aa73d1f68ac1694e653fb75e5
Signed-off-by: Tomasz Kulasek <tomaszx.kulasek@intel.com>
Reviewed-on: https://review.gerrithub.io/405025
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Maciej Szwed <maciej.szwed@intel.com>
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
Tomasz Kulasek 2018-03-22 16:03:17 +01:00 committed by Daniel Verkamp
parent cbb8f46575
commit d7e065be93
4 changed files with 346 additions and 17 deletions

View File

@ -390,6 +390,36 @@ void spdk_bs_create_clone(struct spdk_blob_store *bs, spdk_blob_id blobid,
const struct spdk_blob_xattr_opts *clone_xattrs,
spdk_blob_op_with_id_complete cb_fn, void *cb_arg);
/**
* Provide table with blob id's of clones are dependent on specified snapshot.
*
* Ids array should be allocated and the count parameter set to the number of
* id's it can store, before calling this function.
*
* If ids is NULL or count parameter is not sufficient to handle ids of all
* clones, -ENOMEM error is returned and count parameter is updated to the
* total number of clones.
*
* \param bs blobstore.
* \param blobid Snapshots blob id.
* \param ids Array of the clone ids or NULL to get required size in count.
* \param count Size of ids. After call it is updated to the number of clones.
*
* \return -ENOMEM if count is not sufficient to store all clones.
*/
int spdk_blob_get_clones(struct spdk_blob_store *bs, spdk_blob_id blobid, spdk_blob_id *ids,
size_t *count);
/**
* Get the blob id for the parent snapshot of this blob.
*
* \param bs blobstore.
* \param blobid Blob id.
*
* \return blob id of parent blob or SPDK_BLOBID_INVALID if have no parent
*/
spdk_blob_id spdk_blob_get_parent_snapshot(struct spdk_blob_store *bs, spdk_blob_id blobid);
/**
* Check if blob is read only.
*
@ -399,6 +429,24 @@ void spdk_bs_create_clone(struct spdk_blob_store *bs, spdk_blob_id blobid,
*/
bool spdk_blob_is_read_only(struct spdk_blob *blob);
/**
* Check if blob is a snapshot.
*
* \param blob Blob.
*
* \return true if blob is a snapshot.
*/
bool spdk_blob_is_snapshot(struct spdk_blob *blob);
/**
* Check if blob is a clone.
*
* \param blob Blob.
*
* \return true if blob is a clone.
*/
bool spdk_blob_is_clone(struct spdk_blob *blob);
/**
* Check if blob is thin-provisioned.
*

View File

@ -171,6 +171,8 @@ _spdk_blob_alloc(struct spdk_blob_store *bs, spdk_blob_id id)
blob->id = id;
blob->bs = bs;
blob->parent_id = SPDK_BLOBID_INVALID;
blob->state = SPDK_BLOB_STATE_DIRTY;
blob->active.num_pages = 1;
blob->active.pages = calloc(1, sizeof(*blob->active.pages));
@ -891,7 +893,8 @@ _spdk_blob_load_cpl(spdk_bs_sequence_t *seq, void *cb_arg, int bserrno)
return;
}
/* open snapshot blob and continue in the callback function */
spdk_bs_open_blob(blob->bs, *(spdk_blob_id *)value,
blob->parent_id = *(spdk_blob_id *)value;
spdk_bs_open_blob(blob->bs, blob->parent_id,
_spdk_blob_load_snapshot_cpl, ctx);
return;
} else {
@ -2069,9 +2072,127 @@ _spdk_bs_dev_destroy(void *io_device)
free(bs);
}
static int
_spdk_bs_blob_list_add(struct spdk_blob *blob)
{
spdk_blob_id snapshot_id;
struct spdk_blob_list *snapshot_entry = NULL;
struct spdk_blob_list *clone_entry = NULL;
assert(blob != NULL);
snapshot_id = blob->parent_id;
if (snapshot_id == SPDK_BLOBID_INVALID) {
return 0;
}
TAILQ_FOREACH(snapshot_entry, &blob->bs->snapshots, link) {
if (snapshot_entry->id == snapshot_id) {
break;
}
}
if (snapshot_entry == NULL) {
/* Snapshot not found */
snapshot_entry = calloc(1, sizeof(struct spdk_blob_list));
if (snapshot_entry == NULL) {
return -ENOMEM;
}
snapshot_entry->id = snapshot_id;
TAILQ_INIT(&snapshot_entry->clones);
TAILQ_INSERT_TAIL(&blob->bs->snapshots, snapshot_entry, link);
} else {
TAILQ_FOREACH(clone_entry, &snapshot_entry->clones, link) {
if (clone_entry->id == blob->id) {
break;
}
}
}
if (clone_entry == NULL) {
/* Clone not found */
clone_entry = calloc(1, sizeof(struct spdk_blob_list));
if (clone_entry == NULL) {
return -ENOMEM;
}
clone_entry->id = blob->id;
TAILQ_INIT(&clone_entry->clones);
TAILQ_INSERT_TAIL(&snapshot_entry->clones, clone_entry, link);
snapshot_entry->clone_count++;
}
return 0;
}
static int
_spdk_bs_blob_list_remove(struct spdk_blob *blob)
{
struct spdk_blob_list *snapshot_entry = NULL;
struct spdk_blob_list *clone_entry = NULL;
spdk_blob_id snapshot_id;
assert(blob != NULL);
snapshot_id = blob->parent_id;
if (snapshot_id == SPDK_BLOBID_INVALID) {
return 0;
}
TAILQ_FOREACH(snapshot_entry, &blob->bs->snapshots, link) {
if (snapshot_entry->id == snapshot_id) {
break;
}
}
assert(snapshot_entry != NULL);
TAILQ_FOREACH(clone_entry, &snapshot_entry->clones, link) {
if (clone_entry->id == blob->id) {
break;
}
}
assert(clone_entry != NULL);
blob->parent_id = SPDK_BLOBID_INVALID;
TAILQ_REMOVE(&snapshot_entry->clones, clone_entry, link);
free(clone_entry);
snapshot_entry->clone_count--;
if (snapshot_entry->clone_count == 0) {
/* Snapshot have no more clones */
TAILQ_REMOVE(&blob->bs->snapshots, snapshot_entry, link);
free(snapshot_entry);
}
return 0;
}
static int
_spdk_bs_blob_list_free(struct spdk_blob_store *bs)
{
struct spdk_blob_list *snapshot_entry;
struct spdk_blob_list *snapshot_entry_tmp;
struct spdk_blob_list *clone_entry;
struct spdk_blob_list *clone_entry_tmp;
TAILQ_FOREACH_SAFE(snapshot_entry, &bs->snapshots, link, snapshot_entry_tmp) {
TAILQ_FOREACH_SAFE(clone_entry, &snapshot_entry->clones, link, clone_entry_tmp) {
TAILQ_REMOVE(&snapshot_entry->clones, clone_entry, link);
free(clone_entry);
}
TAILQ_REMOVE(&bs->snapshots, snapshot_entry, link);
free(snapshot_entry);
}
return 0;
}
static void
_spdk_bs_free(struct spdk_blob_store *bs)
{
_spdk_bs_blob_list_free(bs);
spdk_bs_unregister_md_thread(bs);
spdk_io_device_unregister(bs, _spdk_bs_dev_destroy);
}
@ -2126,6 +2247,7 @@ _spdk_bs_alloc(struct spdk_bs_dev *dev, struct spdk_bs_opts *opts)
}
TAILQ_INIT(&bs->blobs);
TAILQ_INIT(&bs->snapshots);
bs->dev = dev;
bs->md_thread = spdk_get_thread();
assert(bs->md_thread != NULL);
@ -2312,16 +2434,16 @@ _spdk_bs_write_used_blobids(spdk_bs_sequence_t *seq, void *arg, spdk_bs_sequence
spdk_bs_sequence_write_dev(seq, ctx->mask, lba, lba_count, cb_fn, arg);
}
static void _spdk_bs_load_complete(spdk_bs_sequence_t *seq, struct spdk_bs_load_ctx *ctx,
int bserrno);
static void
_spdk_bs_load_iter(void *arg, struct spdk_blob *blob, int bserrno)
{
struct spdk_bs_load_ctx *ctx = arg;
if (bserrno == 0) {
ctx->iter_cb_fn(ctx->iter_cb_arg, blob, 0);
if (ctx->iter_cb_fn) {
ctx->iter_cb_fn(ctx->iter_cb_arg, blob, 0);
}
_spdk_bs_blob_list_add(blob);
spdk_bs_iter_next(ctx->bs, blob, _spdk_bs_load_iter, ctx);
return;
}
@ -2339,22 +2461,18 @@ _spdk_bs_load_iter(void *arg, struct spdk_blob *blob, int bserrno)
}
ctx->iter_cb_fn = NULL;
_spdk_bs_load_complete(ctx->seq, ctx, bserrno);
spdk_dma_free(ctx->super);
spdk_dma_free(ctx->mask);
spdk_bs_sequence_finish(ctx->seq, bserrno);
free(ctx);
}
static void
_spdk_bs_load_complete(spdk_bs_sequence_t *seq, struct spdk_bs_load_ctx *ctx, int bserrno)
{
if (ctx->iter_cb_fn) {
ctx->seq = seq;
spdk_bs_iter_first(ctx->bs, _spdk_bs_load_iter, ctx);
return;
}
spdk_dma_free(ctx->super);
spdk_dma_free(ctx->mask);
free(ctx);
spdk_bs_sequence_finish(seq, bserrno);
ctx->seq = seq;
spdk_bs_iter_first(ctx->bs, _spdk_bs_load_iter, ctx);
}
static void
@ -3678,6 +3796,8 @@ _spdk_bs_snapshot_origblob_sync_cpl(void *cb_arg, int bserrno)
return;
}
_spdk_bs_blob_list_add(ctx->original.blob);
spdk_blob_set_read_only(newblob);
/* sync snapshot metadata */
@ -3702,6 +3822,7 @@ _spdk_bs_snapshot_newblob_sync_cpl(void *cb_arg, int bserrno)
_spdk_bs_clone_snapshot_newblob_cleanup(ctx, bserrno);
return;
}
origblob->parent_id = newblob->id;
/* Create new back_bs_dev for snapshot */
origblob->back_bs_dev = spdk_bs_create_blob_bs_dev(newblob);
@ -3713,6 +3834,8 @@ _spdk_bs_snapshot_newblob_sync_cpl(void *cb_arg, int bserrno)
/* set clone blob as thin provisioned */
_spdk_blob_set_thin_provision(origblob);
_spdk_bs_blob_list_add(newblob);
/* Zero out origblob cluster map */
memset(origblob->active.clusters, 0,
origblob->active.num_clusters * sizeof(origblob->active.clusters));
@ -3856,13 +3979,25 @@ _spdk_bs_xattr_clone(void *arg, const char *name,
*value_len = sizeof(blob->id);
}
static void
_spdk_bs_clone_newblob_open_cpl(void *cb_arg, struct spdk_blob *_blob, int bserrno)
{
struct spdk_clone_snapshot_ctx *ctx = (struct spdk_clone_snapshot_ctx *)cb_arg;
struct spdk_blob *clone = _blob;
ctx->new.blob = clone;
_spdk_bs_blob_list_add(clone);
spdk_blob_close(clone, _spdk_bs_clone_snapshot_origblob_cleanup, ctx);
}
static void
_spdk_bs_clone_newblob_create_cpl(void *cb_arg, spdk_blob_id blobid, int bserrno)
{
struct spdk_clone_snapshot_ctx *ctx = (struct spdk_clone_snapshot_ctx *)cb_arg;
ctx->cpl.u.blobid.blobid = blobid;
_spdk_bs_clone_snapshot_origblob_cleanup(ctx, bserrno);
spdk_bs_open_blob(ctx->original.blob->bs, blobid, _spdk_bs_clone_newblob_open_cpl, ctx);
}
static void
@ -4015,6 +4150,13 @@ _spdk_bs_delete_open_cpl(void *cb_arg, struct spdk_blob *blob, int bserrno)
return;
}
bserrno = _spdk_bs_blob_list_remove(blob);
if (bserrno != 0) {
SPDK_DEBUGLOG(SPDK_LOG_BLOB, "Remove blob #%" PRIu64 " from a list\n", blob->id);
spdk_bs_sequence_finish(seq, bserrno);
return;
}
/*
* Remove the blob from the blob_store list now, to ensure it does not
* get returned after this point by _spdk_blob_lookup().
@ -4035,11 +4177,27 @@ spdk_bs_delete_blob(struct spdk_blob_store *bs, spdk_blob_id blobid,
{
struct spdk_bs_cpl cpl;
spdk_bs_sequence_t *seq;
struct spdk_blob_list *snapshot_entry = NULL;
SPDK_DEBUGLOG(SPDK_LOG_BLOB, "Deleting blob %lu\n", blobid);
assert(spdk_get_thread() == bs->md_thread);
/* Check if this is a snapshot with clones */
TAILQ_FOREACH(snapshot_entry, &bs->snapshots, link) {
if (snapshot_entry->id == blobid) {
break;
}
}
if (snapshot_entry != NULL) {
/* If snapshot have clones, we cannot remove it */
if (!TAILQ_EMPTY(&snapshot_entry->clones)) {
SPDK_ERRLOG("Cannot remove snapshot with clones\n");
cb_fn(cb_arg, -EBUSY);
return;
}
}
cpl.type = SPDK_BS_CPL_TYPE_BLOB_BASIC;
cpl.u.blob_basic.cb_fn = cb_fn;
cpl.u.blob_basic.cb_arg = cb_arg;
@ -4686,6 +4844,39 @@ spdk_blob_is_read_only(struct spdk_blob *blob)
return (blob->data_ro || blob->md_ro);
}
bool
spdk_blob_is_snapshot(struct spdk_blob *blob)
{
struct spdk_blob_list *snapshot_entry;
assert(blob != NULL);
TAILQ_FOREACH(snapshot_entry, &blob->bs->snapshots, link) {
if (snapshot_entry->id == blob->id) {
break;
}
}
if (snapshot_entry == NULL) {
return false;
}
return true;
}
bool
spdk_blob_is_clone(struct spdk_blob *blob)
{
assert(blob != NULL);
if (blob->parent_id != SPDK_BLOBID_INVALID) {
assert(spdk_blob_is_thin_provisioned(blob));
return true;
}
return false;
}
bool
spdk_blob_is_thin_provisioned(struct spdk_blob *blob)
{
@ -4693,4 +4884,53 @@ spdk_blob_is_thin_provisioned(struct spdk_blob *blob)
return !!(blob->invalid_flags & SPDK_BLOB_THIN_PROV);
}
spdk_blob_id
spdk_blob_get_parent_snapshot(struct spdk_blob_store *bs, spdk_blob_id blob_id)
{
struct spdk_blob_list *snapshot_entry = NULL;
struct spdk_blob_list *clone_entry = NULL;
TAILQ_FOREACH(snapshot_entry, &bs->snapshots, link) {
TAILQ_FOREACH(clone_entry, &snapshot_entry->clones, link) {
if (clone_entry->id == blob_id) {
return snapshot_entry->id;
}
}
}
return SPDK_BLOBID_INVALID;
}
int
spdk_blob_get_clones(struct spdk_blob_store *bs, spdk_blob_id blobid, spdk_blob_id *ids,
size_t *count)
{
struct spdk_blob_list *snapshot_entry, *clone_entry;
size_t n;
TAILQ_FOREACH(snapshot_entry, &bs->snapshots, link) {
if (snapshot_entry->id == blobid) {
break;
}
}
if (snapshot_entry == NULL) {
*count = 0;
return 0;
}
if (ids == NULL || *count < snapshot_entry->clone_count) {
SPDK_ERRLOG("Not enough space in array\n");
*count = snapshot_entry->clone_count;
return -ENOMEM;
}
*count = snapshot_entry->clone_count;
n = 0;
TAILQ_FOREACH(clone_entry, &snapshot_entry->clones, link) {
ids[n++] = clone_entry->id;
}
return 0;
}
SPDK_LOG_REGISTER_COMPONENT("blob", SPDK_LOG_BLOB)

View File

@ -105,12 +105,20 @@ enum spdk_blob_state {
TAILQ_HEAD(spdk_xattr_tailq, spdk_xattr);
struct spdk_blob_list {
spdk_blob_id id;
size_t clone_count;
TAILQ_HEAD(, spdk_blob_list) clones;
TAILQ_ENTRY(spdk_blob_list) link;
};
struct spdk_blob {
struct spdk_blob_store *bs;
uint32_t open_ref;
spdk_blob_id id;
spdk_blob_id parent_id;
enum spdk_blob_state state;
@ -171,6 +179,7 @@ struct spdk_blob_store {
int unload_err;
TAILQ_HEAD(, spdk_blob) blobs;
TAILQ_HEAD(, spdk_blob_list) snapshots;
};
struct spdk_bs_channel {

View File

@ -163,6 +163,38 @@ spdk_blob_get_num_clusters(struct spdk_blob *b)
return 0;
}
int
spdk_blob_get_clones(struct spdk_blob_store *bs, spdk_blob_id blobid, spdk_blob_id *ids,
size_t *count)
{
*count = 0;
return 0;
}
spdk_blob_id
spdk_blob_get_parent_snapshot(struct spdk_blob_store *bs, spdk_blob_id blobid)
{
return 0;
}
bool
spdk_blob_is_snapshot(struct spdk_blob *blob)
{
return false;
}
bool
spdk_blob_is_clone(struct spdk_blob *blob)
{
return false;
}
bool
spdk_blob_is_thin_provisioned(struct spdk_blob *blob)
{
return false;
}
static struct spdk_lvol *_lvol_create(struct spdk_lvol_store *lvs);
void