diff --git a/lib/blob/blobstore.c b/lib/blob/blobstore.c index 011995aa9..0d7a925da 100644 --- a/lib/blob/blobstore.c +++ b/lib/blob/blobstore.c @@ -4296,6 +4296,8 @@ _spdk_bs_snapshot_unfreeze_cpl(void *cb_arg, int bserrno) } ctx->original.id = origblob->id; + origblob->locked_operation_in_progress = false; + spdk_blob_close(origblob, _spdk_bs_clone_snapshot_cleanup_finish, ctx); } @@ -4533,10 +4535,20 @@ _spdk_bs_snapshot_origblob_open_cpl(void *cb_arg, struct spdk_blob *_blob, int b if (_blob->data_ro || _blob->md_ro) { SPDK_DEBUGLOG(SPDK_LOG_BLOB, "Cannot create snapshot from read only blob with id %lu\n", _blob->id); - _spdk_bs_clone_snapshot_origblob_cleanup(ctx, -EINVAL); + ctx->bserrno = -EINVAL; + spdk_blob_close(_blob, _spdk_bs_clone_snapshot_cleanup_finish, ctx); return; } + if (_blob->locked_operation_in_progress) { + SPDK_DEBUGLOG(SPDK_LOG_BLOB, "Cannot create snapshot - another operation in progress\n"); + ctx->bserrno = -EBUSY; + spdk_blob_close(_blob, _spdk_bs_clone_snapshot_cleanup_finish, ctx); + return; + } + + _blob->locked_operation_in_progress = true; + spdk_blob_opts_init(&opts); _spdk_blob_xattrs_init(&internal_xattrs); @@ -4633,10 +4645,20 @@ _spdk_bs_clone_origblob_open_cpl(void *cb_arg, struct spdk_blob *_blob, int bser 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); + ctx->bserrno = -EINVAL; + spdk_blob_close(_blob, _spdk_bs_clone_snapshot_cleanup_finish, ctx); return; } + if (_blob->locked_operation_in_progress) { + SPDK_DEBUGLOG(SPDK_LOG_BLOB, "Cannot create clone - another operation in progress\n"); + ctx->bserrno = -EBUSY; + spdk_blob_close(_blob, _spdk_bs_clone_snapshot_cleanup_finish, ctx); + return; + } + + _blob->locked_operation_in_progress = true; + spdk_blob_opts_init(&opts); _spdk_blob_xattrs_init(&internal_xattrs); @@ -4812,8 +4834,18 @@ _spdk_bs_inflate_blob_open_cpl(void *cb_arg, struct spdk_blob *_blob, int bserrn _spdk_bs_clone_snapshot_cleanup_finish(ctx, bserrno); return; } + ctx->original.blob = _blob; + if (_blob->locked_operation_in_progress) { + SPDK_DEBUGLOG(SPDK_LOG_BLOB, "Cannot inflate blob - another operation in progress\n"); + ctx->bserrno = -EBUSY; + spdk_blob_close(_blob, _spdk_bs_clone_snapshot_cleanup_finish, ctx); + return; + } + + _blob->locked_operation_in_progress = true; + if (!ctx->allocate_all && _blob->parent_id == SPDK_BLOBID_INVALID) { /* This blob have no parent, so we cannot decouple it. */ SPDK_ERRLOG("Cannot decouple parent of blob with no parent.\n"); @@ -5060,6 +5092,14 @@ _spdk_bs_delete_open_cpl(void *cb_arg, struct spdk_blob *blob, int bserrno) return; } + if (blob->locked_operation_in_progress) { + SPDK_DEBUGLOG(SPDK_LOG_BLOB, "Cannot remove blob - another operation in progress\n"); + spdk_blob_close(blob, _spdk_bs_delete_ebusy_close_cpl, seq); + return; + } + + blob->locked_operation_in_progress = true; + /* * Remove the blob from the blob_store list now, to ensure it does not * get returned after this point by _spdk_blob_lookup(). diff --git a/test/unit/lib/blob/blob.c/blob_ut.c b/test/unit/lib/blob/blob.c/blob_ut.c index 2d653c96d..50ed771cf 100644 --- a/test/unit/lib/blob/blob.c/blob_ut.c +++ b/test/unit/lib/blob/blob.c/blob_ut.c @@ -6463,6 +6463,130 @@ blob_io_unit_compatiblity(void) g_blobid = 0; } +static void +blob_simultaneous_operations(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; + struct spdk_io_channel *channel; + + 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; + + channel = spdk_bs_alloc_io_channel(bs); + SPDK_CU_ASSERT_FATAL(channel != NULL); + + 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 and try to remove blob in the same time: + * - snapshot should be created successfully + * - delete operation should fail w -EBUSY */ + CU_ASSERT(blob->locked_operation_in_progress == false); + spdk_bs_create_snapshot(bs, blobid, NULL, blob_op_with_id_complete, NULL); + CU_ASSERT(blob->locked_operation_in_progress == true); + spdk_bs_delete_blob(bs, blobid, blob_op_complete, NULL); + CU_ASSERT(blob->locked_operation_in_progress == true); + /* Deletion failure */ + CU_ASSERT(g_bserrno == -EBUSY); + poll_threads(); + CU_ASSERT(blob->locked_operation_in_progress == false); + /* Snapshot creation success */ + 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; + + /* Inflate blob and try to remove blob in the same time: + * - blob should be inflated successfully + * - delete operation should fail w -EBUSY */ + CU_ASSERT(blob->locked_operation_in_progress == false); + spdk_bs_inflate_blob(bs, channel, blobid, blob_op_complete, NULL); + CU_ASSERT(blob->locked_operation_in_progress == true); + spdk_bs_delete_blob(bs, blobid, blob_op_complete, NULL); + CU_ASSERT(blob->locked_operation_in_progress == true); + /* Deletion failure */ + CU_ASSERT(g_bserrno == -EBUSY); + poll_threads(); + CU_ASSERT(blob->locked_operation_in_progress == false); + /* Inflation success */ + CU_ASSERT(g_bserrno == 0); + + /* Clone snapshot and try to remove snapshot in the same time: + * - snapshot should be cloned successfully + * - delete operation should fail w -EBUSY */ + CU_ASSERT(blob->locked_operation_in_progress == false); + spdk_bs_create_clone(bs, snapshotid, NULL, blob_op_with_id_complete, NULL); + spdk_bs_delete_blob(bs, snapshotid, blob_op_complete, NULL); + /* Deletion failure */ + CU_ASSERT(g_bserrno == -EBUSY); + poll_threads(); + CU_ASSERT(blob->locked_operation_in_progress == false); + /* Clone created */ + CU_ASSERT(g_bserrno == 0); + + /* Resize blob and try to remove blob in the same time: + * - blob should be resized successfully + * - delete operation should fail w -EBUSY */ + CU_ASSERT(blob->locked_operation_in_progress == false); + spdk_blob_resize(blob, 50, blob_op_complete, NULL); + CU_ASSERT(blob->locked_operation_in_progress == true); + spdk_bs_delete_blob(bs, blobid, blob_op_complete, NULL); + CU_ASSERT(blob->locked_operation_in_progress == true); + /* Deletion failure */ + CU_ASSERT(g_bserrno == -EBUSY); + poll_threads(); + CU_ASSERT(blob->locked_operation_in_progress == false); + /* Blob resized successfully */ + CU_ASSERT(g_bserrno == 0); + + spdk_blob_close(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_bs_delete_blob(bs, blobid, blob_op_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; + + spdk_bs_free_io_channel(channel); + poll_threads(); +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -6530,7 +6654,8 @@ int main(int argc, char **argv) CU_add_test(suite, "blob_operation_split_rw", blob_operation_split_rw) == NULL || CU_add_test(suite, "blob_operation_split_rw_iov", blob_operation_split_rw_iov) == NULL || CU_add_test(suite, "blob_io_unit", blob_io_unit) == NULL || - CU_add_test(suite, "blob_io_unit_compatiblity", blob_io_unit_compatiblity) == NULL + CU_add_test(suite, "blob_io_unit_compatiblity", blob_io_unit_compatiblity) == NULL || + CU_add_test(suite, "blob_simultaneous_operations", blob_simultaneous_operations) == NULL ) { CU_cleanup_registry(); return CU_get_error();