From 3eb5130de669d5a63dab12f4565a2104d8d3ea0d Mon Sep 17 00:00:00 2001 From: Piotr Pelplinski Date: Mon, 29 Jan 2018 10:25:45 +0100 Subject: [PATCH] blobstore: allow creating clones out of read-only snapshots Signed-off-by: Piotr Pelplinski Signed-off-by: Tomasz Kulasek Change-Id: Iad67be79d0ddd8c498950c4f7b1b3203e47a7a41 Reviewed-on: https://review.gerrithub.io/393936 Reviewed-by: Tomasz Zawadzki Reviewed-by: Jim Harris Reviewed-by: Maciej Szwed Tested-by: SPDK Automated Test System Reviewed-by: Ben Walker --- include/spdk/blob.h | 16 ++++ lib/blob/blobstore.c | 86 ++++++++++++++++++ test/unit/lib/blob/blob.c/blob_ut.c | 131 ++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+) diff --git a/include/spdk/blob.h b/include/spdk/blob.h index 31ca345d7..4a1b0aa86 100644 --- a/include/spdk/blob.h +++ b/include/spdk/blob.h @@ -374,6 +374,22 @@ void spdk_bs_create_snapshot(struct spdk_blob_store *bs, spdk_blob_id blobid, const struct spdk_blob_xattr_opts *snapshot_xattrs, spdk_blob_op_with_id_complete cb_fn, void *cb_arg); +/** + * Create a clone of specified read-only blob. + * + * Structure clone_xattrs as well as anything it references (like e.g. names + * array) must be valid until the completion is called. + * + * \param bs blobstore. + * \param blobid Id of the read only blob used as a snapshot for new clone. + * \param clone_xattrs xattrs specified for clone. + * \param cb_fn Called when the operation is complete. + * \param cb_arg Argument passed to function cb_fn. + */ +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); + /** * Delete an existing blob from the given blobstore. * diff --git a/lib/blob/blobstore.c b/lib/blob/blobstore.c index 11e35918b..5dee2faa0 100644 --- a/lib/blob/blobstore.c +++ b/lib/blob/blobstore.c @@ -3843,6 +3843,92 @@ void spdk_bs_create_snapshot(struct spdk_blob_store *bs, spdk_blob_id blobid, } /* END spdk_bs_create_snapshot */ +/* START spdk_bs_create_clone */ + +static void +_spdk_bs_xattr_clone(void *arg, const char *name, + const void **value, size_t *value_len) +{ + assert(strncmp(name, BLOB_SNAPSHOT, sizeof(BLOB_SNAPSHOT)) == 0); + + struct spdk_blob *blob = (struct spdk_blob *)arg; + *value = &blob->id; + *value_len = sizeof(blob->id); +} + +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); +} + +static void +_spdk_bs_clone_origblob_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_opts opts; + struct spdk_blob_xattr_opts internal_xattrs; + char *xattr_names[] = { BLOB_SNAPSHOT }; + + if (bserrno != 0) { + _spdk_bs_clone_snapshot_cleanup_finish(ctx, bserrno); + return; + } + + ctx->original.blob = _blob; + + if (!_blob->data_ro || !_blob->md_ro) { + SPDK_DEBUGLOG(SPDK_LOG_BLOB, "Clone not from read-only blob\n"); + _spdk_bs_clone_snapshot_origblob_cleanup(ctx, -EINVAL); + return; + } + + spdk_blob_opts_init(&opts); + _spdk_blob_xattrs_init(&internal_xattrs); + + opts.thin_provision = true; + opts.num_clusters = spdk_blob_get_num_clusters(_blob); + if (ctx->xattrs) { + memcpy(&opts.xattrs, ctx->xattrs, sizeof(*ctx->xattrs)); + } + + /* Set internal xattr BLOB_SNAPSHOT */ + internal_xattrs.count = 1; + internal_xattrs.ctx = _blob; + internal_xattrs.names = xattr_names; + internal_xattrs.get_value = _spdk_bs_xattr_clone; + + _spdk_bs_create_blob(_blob->bs, &opts, &internal_xattrs, + _spdk_bs_clone_newblob_create_cpl, ctx); +} + +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) +{ + struct spdk_clone_snapshot_ctx *ctx = calloc(1, sizeof(*ctx)); + + if (!ctx) { + cb_fn(cb_arg, SPDK_BLOBID_INVALID, -ENOMEM); + return; + } + + ctx->cpl.type = SPDK_BS_CPL_TYPE_BLOBID; + ctx->cpl.u.blobid.cb_fn = cb_fn; + ctx->cpl.u.blobid.cb_arg = cb_arg; + ctx->cpl.u.blobid.blobid = SPDK_BLOBID_INVALID; + ctx->bserrno = 0; + ctx->xattrs = clone_xattrs; + ctx->original.id = blobid; + + spdk_bs_open_blob(bs, ctx->original.id, _spdk_bs_clone_origblob_open_cpl, ctx); +} + +/* END spdk_bs_create_clone */ + /* START spdk_blob_resize */ void spdk_blob_resize(struct spdk_blob *blob, uint64_t sz, spdk_blob_op_complete cb_fn, void *cb_arg) diff --git a/test/unit/lib/blob/blob.c/blob_ut.c b/test/unit/lib/blob/blob.c/blob_ut.c index 48976b2b9..25849e4dd 100644 --- a/test/unit/lib/blob/blob.c/blob_ut.c +++ b/test/unit/lib/blob/blob.c/blob_ut.c @@ -674,6 +674,136 @@ blob_snapshot(void) g_bs = NULL; } +static void +blob_clone(void) +{ + struct spdk_blob_store *bs; + struct spdk_bs_dev *dev; + struct spdk_blob_opts opts; + struct spdk_blob *blob, *snapshot, *clone; + spdk_blob_id blobid, cloneid, snapshotid; + struct spdk_blob_xattr_opts xattrs; + const void *value; + size_t value_len; + int rc; + + 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; + + /* 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) + + /* 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; + + 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); + + /* Create clone from snapshot with xattrs */ + xattrs.names = g_xattr_names; + xattrs.get_value = _get_xattr_value; + xattrs.count = 3; + xattrs.ctx = &g_ctx; + + spdk_bs_create_clone(bs, snapshotid, &xattrs, blob_op_with_id_complete, NULL); + CU_ASSERT(g_bserrno == 0); + CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID); + cloneid = g_blobid; + + spdk_bs_open_blob(bs, cloneid, blob_op_with_handle_complete, NULL); + CU_ASSERT(g_bserrno == 0); + SPDK_CU_ASSERT_FATAL(g_blob != NULL); + clone = g_blob; + CU_ASSERT(clone->data_ro == false) + CU_ASSERT(clone->md_ro == false) + CU_ASSERT(spdk_blob_get_num_clusters(clone) == 10); + + rc = spdk_blob_get_xattr_value(clone, g_xattr_names[0], &value, &value_len); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(value_len == strlen(g_xattr_values[0])); + CU_ASSERT_NSTRING_EQUAL_FATAL(value, g_xattr_values[0], value_len); + + rc = spdk_blob_get_xattr_value(clone, g_xattr_names[1], &value, &value_len); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(value_len == strlen(g_xattr_values[1])); + CU_ASSERT_NSTRING_EQUAL((char *)value, g_xattr_values[1], value_len); + + rc = spdk_blob_get_xattr_value(clone, g_xattr_names[2], &value, &value_len); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(value_len == strlen(g_xattr_values[2])); + CU_ASSERT_NSTRING_EQUAL((char *)value, g_xattr_values[2], value_len); + + + spdk_blob_close(clone, blob_op_complete, NULL); + CU_ASSERT(g_bserrno == 0); + + /* Try to create clone from not read only blob */ + spdk_bs_create_clone(bs, blobid, NULL, blob_op_with_id_complete, NULL); + CU_ASSERT(g_bserrno == -EINVAL); + CU_ASSERT(g_blobid == SPDK_BLOBID_INVALID); + + /* Mark blob as read only */ + spdk_blob_set_read_only(blob); + spdk_blob_sync_md(blob, blob_op_complete, NULL); + CU_ASSERT(g_bserrno == 0); + + /* Create clone from read only blob */ + spdk_bs_create_clone(bs, blobid, NULL, blob_op_with_id_complete, NULL); + CU_ASSERT(g_bserrno == 0); + CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID); + cloneid = g_blobid; + + spdk_bs_open_blob(bs, cloneid, blob_op_with_handle_complete, NULL); + CU_ASSERT(g_bserrno == 0); + SPDK_CU_ASSERT_FATAL(g_blob != NULL); + clone = g_blob; + CU_ASSERT(clone->data_ro == false) + CU_ASSERT(clone->md_ro == false) + CU_ASSERT(spdk_blob_get_num_clusters(clone) == 10); + + spdk_blob_close(clone, blob_op_complete, NULL); + CU_ASSERT(g_bserrno == 0); + + 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; + +} + static void blob_delete(void) { @@ -3653,6 +3783,7 @@ int main(int argc, char **argv) CU_add_test(suite, "blob_create_internal", blob_create_internal) == 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_clone", blob_clone) == NULL || CU_add_test(suite, "blob_delete", blob_delete) == NULL || CU_add_test(suite, "blob_resize", blob_resize) == NULL || CU_add_test(suite, "blob_read_only", blob_read_only) == NULL ||