diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ee2fa16e..24f536459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,9 @@ makes it explicit that the default is being used. ### Blobstore +spdk_bs_destroy() was added to allow destroying blobstore on device +with an initialized blobstore. + spdk_bs_io_readv_blob() and spdk_bs_io_writev_blob() were added to enable scattered payloads. diff --git a/include/spdk/blob.h b/include/spdk/blob.h index 7e9f38d3f..20ee91348 100644 --- a/include/spdk/blob.h +++ b/include/spdk/blob.h @@ -163,6 +163,13 @@ void spdk_bs_load(struct spdk_bs_dev *dev, struct spdk_bs_opts *opts, void spdk_bs_init(struct spdk_bs_dev *dev, struct spdk_bs_opts *opts, spdk_bs_op_with_handle_complete cb_fn, void *cb_arg); +/* Destroy a blob store by unmapping super block and destroying in-memory structures. + * If unmap_device is set to true, entire device will be unmapped. Otherwise only + * super block will be unmapped. + */ +void spdk_bs_destroy(struct spdk_blob_store *bs, bool unmap_device, spdk_bs_op_complete cb_fn, + void *cb_arg); + /* Flush all volatile data to disk and destroy in-memory structures. */ void spdk_bs_unload(struct spdk_blob_store *bs, spdk_bs_op_complete cb_fn, void *cb_arg); diff --git a/lib/blob/blobstore.c b/lib/blob/blobstore.c index 60f61d7ed..e90bdabfe 100644 --- a/lib/blob/blobstore.c +++ b/lib/blob/blobstore.c @@ -2183,6 +2183,84 @@ spdk_bs_init(struct spdk_bs_dev *dev, struct spdk_bs_opts *o, /* END spdk_bs_init */ +/* START spdk_bs_destroy */ + +static void +_spdk_bs_destroy_trim_cpl(spdk_bs_sequence_t *seq, void *cb_arg, int bserrno) +{ + struct spdk_bs_init_ctx *ctx = cb_arg; + struct spdk_blob_store *bs = ctx->bs; + + /* + * We need to defer calling spdk_bs_call_cpl() until after + * dev destruction, so tuck these away for later use. + */ + bs->unload_err = bserrno; + memcpy(&bs->unload_cpl, &seq->cpl, sizeof(struct spdk_bs_cpl)); + seq->cpl.type = SPDK_BS_CPL_TYPE_NONE; + + spdk_bs_sequence_finish(seq, bserrno); + + _spdk_bs_free(bs); + spdk_dma_free(ctx->super); + free(ctx); +} + +void +spdk_bs_destroy(struct spdk_blob_store *bs, bool unmap_device, spdk_bs_op_complete cb_fn, + void *cb_arg) +{ + struct spdk_bs_cpl cpl; + spdk_bs_sequence_t *seq; + struct spdk_bs_init_ctx *ctx; + + SPDK_DEBUGLOG(SPDK_TRACE_BLOB, "Destroying blobstore\n"); + + if (!TAILQ_EMPTY(&bs->blobs)) { + SPDK_ERRLOG("Blobstore still has open blobs\n"); + cb_fn(cb_arg, -EBUSY); + return; + } + + cpl.type = SPDK_BS_CPL_TYPE_BS_BASIC; + cpl.u.bs_basic.cb_fn = cb_fn; + cpl.u.bs_basic.cb_arg = cb_arg; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + cb_fn(cb_arg, -ENOMEM); + return; + } + + ctx->super = spdk_dma_zmalloc(sizeof(*ctx->super), 0x1000, NULL); + if (!ctx->super) { + free(ctx); + cb_fn(cb_arg, -ENOMEM); + return; + } + + ctx->bs = bs; + + seq = spdk_bs_sequence_start(bs->md_target.md_channel, &cpl); + if (!seq) { + spdk_dma_free(ctx->super); + free(ctx); + cb_fn(cb_arg, -ENOMEM); + return; + } + + if (unmap_device) { + /* TRIM the entire device */ + spdk_bs_sequence_unmap(seq, 0, bs->dev->blockcnt, _spdk_bs_destroy_trim_cpl, ctx); + } else { + /* Write zeroes to the super block */ + spdk_bs_sequence_write(seq, ctx->super, _spdk_bs_page_to_lba(bs, 0), _spdk_bs_byte_to_lba(bs, + sizeof(*ctx->super)), _spdk_bs_destroy_trim_cpl, ctx); + } +} + +/* END spdk_bs_destroy */ + /* START spdk_bs_unload */ static void diff --git a/test/unit/lib/blob/blob.c/blob_ut.c b/test/unit/lib/blob/blob.c/blob_ut.c index f873adb84..5c3aa937d 100644 --- a/test/unit/lib/blob/blob.c/blob_ut.c +++ b/test/unit/lib/blob/blob.c/blob_ut.c @@ -1263,6 +1263,42 @@ bs_resize_md(void) g_bs = NULL; } +static void +bs_destroy(void) +{ + struct spdk_bs_dev *dev; + struct spdk_bs_opts opts; + + g_scheduler_delay = true; + + _bs_flush_scheduler(); + CU_ASSERT(TAILQ_EMPTY(&g_scheduled_ops)); + + /* Initialize a new blob store */ + dev = init_dev(); + spdk_bs_opts_init(&opts); + spdk_bs_init(dev, &opts, bs_op_with_handle_complete, NULL); + CU_ASSERT(g_bserrno == 0); + SPDK_CU_ASSERT_FATAL(g_bs != NULL); + + /* Destroy the blob store */ + g_bserrno = -1; + spdk_bs_destroy(g_bs, 0, bs_op_complete, NULL); + /* Callback is called after device is destroyed in next scheduler run. */ + _bs_flush_scheduler(); + CU_ASSERT(TAILQ_EMPTY(&g_scheduled_ops)); + CU_ASSERT(g_bserrno == 0); + + /* Loading an non-existent blob store should fail. */ + g_bserrno = -1; + g_bs = NULL; + dev = init_dev(); + + spdk_bs_load(dev, &opts, bs_op_with_handle_complete, NULL); + CU_ASSERT(g_bserrno != 0); + g_scheduler_delay = false; +} + /* Try to hit all of the corner cases associated with serializing * a blob to disk */ @@ -1768,6 +1804,7 @@ int main(int argc, char **argv) CU_add_test(suite, "bs_unload", bs_unload) == NULL || CU_add_test(suite, "bs_cluster_sz", bs_cluster_sz) == NULL || CU_add_test(suite, "bs_resize_md", bs_resize_md) == NULL || + CU_add_test(suite, "bs_destroy", bs_destroy) == NULL || CU_add_test(suite, "bs_type", bs_type) == NULL || CU_add_test(suite, "bs_super_block", bs_super_block) == NULL || CU_add_test(suite, "blob_serialize", blob_serialize) == NULL ||