From 7560f2b2a38bfa4ffe37bf485076b485e9aac415 Mon Sep 17 00:00:00 2001 From: Jim Harris Date: Thu, 15 Feb 2018 13:46:41 -0700 Subject: [PATCH] blob: add option to iterate all blobs during spdk_bs_load blobfs and lvol can now use this to automatically iterate all existing blobs during spdk_bs_load. Changes to blobfs and lvol will come in future patches. This will also be used in some upcoming patches which need to iterate through blobs during load to determine snapshot/clone relationships. Signed-off-by: Jim Harris Change-Id: Ic7c5fac4535ceaa926217a105dda532517e3e251 Reviewed-on: https://review.gerrithub.io/400177 Tested-by: SPDK Automated Test System Reviewed-by: Maciej Szwed Reviewed-by: Daniel Verkamp Reviewed-by: Changpeng Liu Reviewed-by: Shuhei Matsumoto --- include/spdk/blob.h | 6 ++ lib/blob/blobstore.c | 44 ++++++++++++ test/unit/lib/blob/blob.c/blob_ut.c | 100 +++++++++++++++++++++++++++- 3 files changed, 149 insertions(+), 1 deletion(-) diff --git a/include/spdk/blob.h b/include/spdk/blob.h index 9148846bf..e791e1e2d 100644 --- a/include/spdk/blob.h +++ b/include/spdk/blob.h @@ -161,6 +161,12 @@ struct spdk_bs_opts { /** Blobstore type */ struct spdk_bs_type bstype; + + /** Callback function to invoke for each blob. */ + spdk_blob_op_with_handle_complete iter_cb_fn; + + /** Argument passed to iter_cb_fn for each blob. */ + void *iter_cb_arg; }; /* Initialize an spdk_bs_opts structure to the default blobstore option values. */ diff --git a/lib/blob/blobstore.c b/lib/blob/blobstore.c index 84a6b4a50..156e507ef 100644 --- a/lib/blob/blobstore.c +++ b/lib/blob/blobstore.c @@ -1966,6 +1966,8 @@ spdk_bs_opts_init(struct spdk_bs_opts *opts) opts->max_md_ops = SPDK_BLOB_OPTS_MAX_MD_OPS; opts->max_channel_ops = SPDK_BLOB_OPTS_DEFAULT_CHANNEL_OPS; memset(&opts->bstype, 0, sizeof(opts->bstype)); + opts->iter_cb_fn = NULL; + opts->iter_cb_arg = NULL; } static int @@ -2062,6 +2064,10 @@ struct spdk_bs_load_ctx { uint32_t cur_page; struct spdk_blob_md_page *page; bool is_load; + + spdk_bs_sequence_t *seq; + spdk_blob_op_with_handle_complete iter_cb_fn; + void *iter_cb_arg; }; static void @@ -2188,9 +2194,45 @@ _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); + spdk_bs_iter_next(ctx->bs, blob, _spdk_bs_load_iter, ctx); + return; + } + + if (bserrno == -ENOENT) { + bserrno = 0; + } else { + /* + * This case needs to be looked at further. Same problem + * exists with applications that rely on explicit blob + * iteration. We should just skip the blob that failed + * to load and coontinue on to the next one. + */ + SPDK_ERRLOG("Error in iterating blobs\n"); + } + + ctx->iter_cb_fn = NULL; + _spdk_bs_load_complete(ctx->seq, ctx, bserrno); +} + 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); @@ -2678,6 +2720,8 @@ spdk_bs_load(struct spdk_bs_dev *dev, struct spdk_bs_opts *o, ctx->bs = bs; ctx->is_load = true; + ctx->iter_cb_fn = opts.iter_cb_fn; + ctx->iter_cb_arg = opts.iter_cb_arg; /* Allocate memory for the super block */ ctx->super = spdk_dma_zmalloc(sizeof(*ctx->super), 0x1000, NULL); diff --git a/test/unit/lib/blob/blob.c/blob_ut.c b/test/unit/lib/blob/blob.c/blob_ut.c index 90b2cb209..7c7725c6f 100644 --- a/test/unit/lib/blob/blob.c/blob_ut.c +++ b/test/unit/lib/blob/blob.c/blob_ut.c @@ -3087,6 +3087,103 @@ blob_thin_prov_rw_iov(void) g_blobid = 0; } +struct iter_ctx { + int current_iter; + spdk_blob_id blobid[4]; +}; + +static void +test_iter(void *arg, struct spdk_blob *blob, int bserrno) +{ + struct iter_ctx *iter_ctx = arg; + spdk_blob_id blobid; + + CU_ASSERT(bserrno == 0); + blobid = spdk_blob_get_id(blob); + CU_ASSERT(blobid == iter_ctx->blobid[iter_ctx->current_iter++]); +} + +static void +bs_load_iter(void) +{ + struct spdk_bs_dev *dev; + struct iter_ctx iter_ctx = { 0 }; + struct spdk_blob *blob; + int i, rc; + struct spdk_bs_opts opts; + + dev = init_dev(); + spdk_bs_opts_init(&opts); + strncpy(opts.bstype.bstype, "TESTTYPE", SPDK_BLOBSTORE_TYPE_LENGTH); + + /* Initialize a new blob store */ + spdk_bs_init(dev, &opts, bs_op_with_handle_complete, NULL); + CU_ASSERT(g_bserrno == 0); + SPDK_CU_ASSERT_FATAL(g_bs != NULL); + + for (i = 0; i < 4; i++) { + g_bserrno = -1; + g_blobid = SPDK_BLOBID_INVALID; + spdk_bs_create_blob(g_bs, blob_op_with_id_complete, NULL); + CU_ASSERT(g_bserrno == 0); + CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID); + iter_ctx.blobid[i] = g_blobid; + + g_bserrno = -1; + g_blob = NULL; + spdk_bs_open_blob(g_bs, g_blobid, blob_op_with_handle_complete, NULL); + CU_ASSERT(g_bserrno == 0); + CU_ASSERT(g_blob != NULL); + blob = g_blob; + + /* Just save the blobid as an xattr for testing purposes. */ + rc = spdk_blob_set_xattr(blob, "blobid", &g_blobid, sizeof(g_blobid)); + CU_ASSERT(rc == 0); + + /* Resize the blob */ + rc = spdk_blob_resize(blob, i); + CU_ASSERT(rc == 0); + + spdk_blob_close(blob, blob_op_complete, NULL); + CU_ASSERT(g_bserrno == 0); + } + + g_bserrno = -1; + spdk_bs_unload(g_bs, bs_op_complete, NULL); + CU_ASSERT(g_bserrno == 0); + + dev = init_dev(); + spdk_bs_opts_init(&opts); + strncpy(opts.bstype.bstype, "TESTTYPE", SPDK_BLOBSTORE_TYPE_LENGTH); + opts.iter_cb_fn = test_iter; + opts.iter_cb_arg = &iter_ctx; + + /* Test blob iteration during load after a clean shutdown. */ + spdk_bs_load(dev, &opts, bs_op_with_handle_complete, NULL); + CU_ASSERT(g_bserrno == 0); + SPDK_CU_ASSERT_FATAL(g_bs != NULL); + + /* Dirty shutdown */ + _spdk_bs_free(g_bs); + + dev = init_dev(); + spdk_bs_opts_init(&opts); + strncpy(opts.bstype.bstype, "TESTTYPE", SPDK_BLOBSTORE_TYPE_LENGTH); + opts.iter_cb_fn = test_iter; + iter_ctx.current_iter = 0; + opts.iter_cb_arg = &iter_ctx; + + /* Test blob iteration during load after a dirty shutdown. */ + spdk_bs_load(dev, &opts, bs_op_with_handle_complete, NULL); + CU_ASSERT(g_bserrno == 0); + SPDK_CU_ASSERT_FATAL(g_bs != NULL); + + spdk_bs_unload(g_bs, bs_op_complete, NULL); + CU_ASSERT(g_bserrno == 0); + g_bs = NULL; + +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -3139,7 +3236,8 @@ int main(int argc, char **argv) CU_add_test(suite, "blob_thin_prov_alloc", blob_thin_prov_alloc) == NULL || CU_add_test(suite, "blob_insert_cluster_msg", blob_insert_cluster_msg) == NULL || CU_add_test(suite, "blob_thin_prov_rw", blob_thin_prov_rw) == NULL || - CU_add_test(suite, "blob_thin_prov_rw_iov", blob_thin_prov_rw_iov) == NULL + CU_add_test(suite, "blob_thin_prov_rw_iov", blob_thin_prov_rw_iov) == NULL || + CU_add_test(suite, "bs_load_iter", bs_load_iter) == NULL ) { CU_cleanup_registry(); return CU_get_error();