blobstore: Remove blob on blobstore load when required

In some cases user may want to flag blob for removal
then do some operations (before removing it) and while
it happens there might be power failure. In such cases
we should remove this blob on next blobstore load.
Example of such usage is delete snapshot functionality
that will be introduced in upcoming patch.

Signed-off-by: Maciej Szwed <maciej.szwed@intel.com>
Change-Id: I85f396b73762d2665ba8aec62528bb224acace74
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/453835
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Maciej Szwed 2019-05-09 12:55:25 +02:00 committed by Jim Harris
parent 543d8b7b67
commit 92cafd1586
3 changed files with 275 additions and 7 deletions

View File

@ -2544,6 +2544,8 @@ struct spdk_bs_load_ctx {
spdk_bs_sequence_t *seq;
spdk_blob_op_with_handle_complete iter_cb_fn;
void *iter_cb_arg;
struct spdk_blob *blob;
spdk_blob_id blobid;
};
static void
@ -2700,21 +2702,147 @@ _spdk_blob_set_thin_provision(struct spdk_blob *blob)
blob->state = SPDK_BLOB_STATE_DIRTY;
}
static void _spdk_bs_load_iter(void *arg, struct spdk_blob *blob, int bserrno);
static void
_spdk_bs_delete_corrupted_blob_cpl(void *cb_arg, int bserrno)
{
struct spdk_bs_load_ctx *ctx = cb_arg;
spdk_blob_id id;
int64_t page_num;
/* Iterate to next blob (we can't use spdk_bs_iter_next function as our
* last blob has been removed */
page_num = _spdk_bs_blobid_to_page(ctx->blobid);
page_num++;
page_num = spdk_bit_array_find_first_set(ctx->bs->used_blobids, page_num);
if (page_num >= spdk_bit_array_capacity(ctx->bs->used_blobids)) {
_spdk_bs_load_iter(ctx, NULL, -ENOENT);
return;
}
id = _spdk_bs_page_to_blobid(page_num);
spdk_bs_open_blob(ctx->bs, id, _spdk_bs_load_iter, ctx);
}
static void
_spdk_bs_delete_corrupted_close_cb(void *cb_arg, int bserrno)
{
struct spdk_bs_load_ctx *ctx = cb_arg;
if (bserrno != 0) {
SPDK_ERRLOG("Failed to close corrupted blob\n");
spdk_bs_iter_next(ctx->bs, ctx->blob, _spdk_bs_load_iter, ctx);
return;
}
spdk_bs_delete_blob(ctx->bs, ctx->blobid, _spdk_bs_delete_corrupted_blob_cpl, ctx);
}
static void
_spdk_bs_delete_corrupted_blob(void *cb_arg, int bserrno)
{
struct spdk_bs_load_ctx *ctx = cb_arg;
uint64_t i;
if (bserrno != 0) {
SPDK_ERRLOG("Failed to close clone of a corrupted blob\n");
spdk_bs_iter_next(ctx->bs, ctx->blob, _spdk_bs_load_iter, ctx);
return;
}
/* Snapshot and clone have the same copy of cluster map at this point.
* Let's clear cluster map for snpashot now so that it won't be cleared
* for clone later when we remove snapshot. Also set thin provision to
* pass data corruption check */
for (i = 0; i < ctx->blob->active.num_clusters; i++) {
ctx->blob->active.clusters[i] = 0;
}
ctx->blob->md_ro = false;
_spdk_blob_set_thin_provision(ctx->blob);
ctx->blobid = ctx->blob->id;
spdk_blob_close(ctx->blob, _spdk_bs_delete_corrupted_close_cb, ctx);
}
static void
_spdk_bs_update_corrupted_blob(void *cb_arg, int bserrno)
{
struct spdk_bs_load_ctx *ctx = cb_arg;
if (bserrno != 0) {
SPDK_ERRLOG("Failed to close clone of a corrupted blob\n");
spdk_bs_iter_next(ctx->bs, ctx->blob, _spdk_bs_load_iter, ctx);
return;
}
ctx->blob->md_ro = false;
_spdk_blob_remove_xattr(ctx->blob, SNAPSHOT_PENDING_REMOVAL, true);
spdk_blob_set_read_only(ctx->blob);
if (ctx->iter_cb_fn) {
ctx->iter_cb_fn(ctx->iter_cb_arg, ctx->blob, 0);
}
_spdk_bs_blob_list_add(ctx->blob);
spdk_bs_iter_next(ctx->bs, ctx->blob, _spdk_bs_load_iter, ctx);
}
static void
_spdk_bs_examine_clone(void *cb_arg, struct spdk_blob *blob, int bserrno)
{
struct spdk_bs_load_ctx *ctx = cb_arg;
if (bserrno != 0) {
SPDK_ERRLOG("Failed to open clone of a corrupted blob\n");
spdk_bs_iter_next(ctx->bs, ctx->blob, _spdk_bs_load_iter, ctx);
return;
}
if (blob->parent_id == ctx->blob->id) {
/* Power failure occured before updating clone - keep snapshot */
spdk_blob_close(blob, _spdk_bs_update_corrupted_blob, ctx);
} else {
/* Power failure occured after updating clone - remove snapshot */
spdk_blob_close(blob, _spdk_bs_delete_corrupted_blob, ctx);
}
}
static void
_spdk_bs_load_iter(void *arg, struct spdk_blob *blob, int bserrno)
{
struct spdk_bs_load_ctx *ctx = arg;
const void *value;
size_t len;
int rc = 0;
if (bserrno == 0) {
if (ctx->iter_cb_fn) {
ctx->iter_cb_fn(ctx->iter_cb_arg, blob, 0);
/* Examine blob if it is corrupted after power failure. Fix
* the ones that can be fixed and remove any other corrupted
* ones. If it is not corrupted just process it */
rc = _spdk_blob_get_xattr_value(blob, SNAPSHOT_PENDING_REMOVAL, &value, &len, true);
if (rc != 0) {
/* Not corrupted - process it and continue with iterating through blobs */
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;
}
_spdk_bs_blob_list_add(blob);
spdk_bs_iter_next(ctx->bs, blob, _spdk_bs_load_iter, ctx);
return;
}
if (bserrno == -ENOENT) {
assert(len == sizeof(spdk_blob_id));
ctx->blob = blob;
/* Open clone to check if we are able to fix this blob or should we remove it */
spdk_bs_open_blob(ctx->bs, *(spdk_blob_id *)value, _spdk_bs_examine_clone, ctx);
return;
} else if (bserrno == -ENOENT) {
bserrno = 0;
} else {
/*

View File

@ -215,6 +215,7 @@ enum spdk_blob_op_type {
#define BLOB_SNAPSHOT "SNAP"
#define SNAPSHOT_IN_PROGRESS "SNAPTMP"
#define SNAPSHOT_PENDING_REMOVAL "SNAPRM"
struct spdk_blob_bs_dev {
struct spdk_bs_dev bs_dev;

View File

@ -2734,6 +2734,144 @@ bs_load(void)
}
static void
bs_load_pending_removal(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;
const void *value;
size_t value_len;
int rc;
dev = init_dev();
spdk_bs_init(dev, NULL, bs_op_with_handle_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_bs != NULL);
bs = g_bs;
/* Create blob */
spdk_blob_opts_init(&opts);
opts.num_clusters = 10;
spdk_bs_create_blob_ext(bs, &opts, blob_op_with_id_complete, NULL);
poll_threads();
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);
poll_threads();
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
blob = g_blob;
/* Create snapshot */
spdk_bs_create_snapshot(bs, blobid, NULL, blob_op_with_id_complete, NULL);
poll_threads();
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);
poll_threads();
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
snapshot = g_blob;
/* Set SNAPSHOT_PENDING_REMOVAL xattr */
snapshot->md_ro = false;
rc = _spdk_blob_set_xattr(snapshot, SNAPSHOT_PENDING_REMOVAL, &blobid, sizeof(spdk_blob_id), true);
CU_ASSERT(rc == 0);
snapshot->md_ro = true;
spdk_blob_close(snapshot, blob_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
spdk_blob_close(blob, blob_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
/* Reload blobstore */
spdk_bs_unload(g_bs, bs_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
g_bs = NULL;
dev = init_dev();
spdk_bs_load(dev, NULL, bs_op_with_handle_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_bs != NULL);
bs = g_bs;
/* Snapshot should not be removed as blob is still pointing to it */
spdk_bs_open_blob(bs, snapshotid, blob_op_with_handle_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
snapshot = g_blob;
/* SNAPSHOT_PENDING_REMOVAL xattr should be removed during load */
rc = spdk_blob_get_xattr_value(snapshot, SNAPSHOT_PENDING_REMOVAL, &value, &value_len);
CU_ASSERT(rc != 0);
/* Set SNAPSHOT_PENDING_REMOVAL xattr again */
snapshot->md_ro = false;
rc = _spdk_blob_set_xattr(snapshot, SNAPSHOT_PENDING_REMOVAL, &blobid, sizeof(spdk_blob_id), true);
CU_ASSERT(rc == 0);
snapshot->md_ro = true;
spdk_bs_open_blob(bs, blobid, blob_op_with_handle_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_blob != NULL);
blob = g_blob;
/* Remove parent_id from blob by removing BLOB_SNAPSHOT xattr */
_spdk_blob_remove_xattr(blob, BLOB_SNAPSHOT, true);
spdk_blob_sync_md(blob, blob_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
spdk_blob_close(snapshot, blob_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
spdk_blob_close(blob, blob_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
/* Reload blobstore */
spdk_bs_unload(g_bs, bs_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
g_bs = NULL;
dev = init_dev();
spdk_bs_load(dev, NULL, bs_op_with_handle_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
SPDK_CU_ASSERT_FATAL(g_bs != NULL);
bs = g_bs;
/* Snapshot should be removed as blob is not pointing to it anymore */
spdk_bs_open_blob(bs, snapshotid, blob_op_with_handle_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno != 0);
spdk_bs_unload(g_bs, bs_op_complete, NULL);
poll_threads();
CU_ASSERT(g_bserrno == 0);
g_bs = NULL;
}
static void
bs_load_custom_cluster_size(void)
{
@ -6638,6 +6776,7 @@ int main(int argc, char **argv)
CU_add_test(suite, "blob_iter", blob_iter) == NULL ||
CU_add_test(suite, "blob_xattr", blob_xattr) == NULL ||
CU_add_test(suite, "bs_load", bs_load) == NULL ||
CU_add_test(suite, "bs_load_pending_removal", bs_load_pending_removal) == NULL ||
CU_add_test(suite, "bs_load_custom_cluster_size", bs_load_custom_cluster_size) == NULL ||
CU_add_test(suite, "bs_unload", bs_unload) == NULL ||
CU_add_test(suite, "bs_cluster_sz", bs_cluster_sz) == NULL ||