From 00b2c86f7c486fbd82e6ce27edbd90f866de88af Mon Sep 17 00:00:00 2001 From: Mykola Golub Date: Mon, 12 Aug 2019 14:42:51 +0100 Subject: [PATCH] librbd: behave more gracefully when data pool removed allowing to open the image and do some maintenance operations, and returning -ENODEV for ops that require data pool. Fixes: https://tracker.ceph.com/issues/41206 Signed-off-by: Mykola Golub --- src/librbd/DeepCopyRequest.cc | 12 ++++ src/librbd/ImageCtx.cc | 18 +++-- src/librbd/ImageCtx.h | 2 +- src/librbd/api/DiffIterate.cc | 6 ++ src/librbd/api/Image.cc | 17 +++++ src/librbd/api/Image.h | 2 + src/librbd/api/Snapshot.cc | 2 +- .../cache/ObjectCacherObjectDispatch.cc | 1 + src/librbd/cache/ParentCacheObjectDispatch.cc | 1 + src/librbd/deep_copy/ObjectCopyRequest.cc | 2 + src/librbd/image/OpenRequest.cc | 14 ++-- src/librbd/image/RefreshRequest.cc | 8 ++- src/librbd/image/RemoveRequest.cc | 5 ++ src/librbd/image/RemoveRequest.h | 2 +- src/librbd/io/CopyupRequest.cc | 1 + src/librbd/io/ImageRequestWQ.cc | 8 +++ src/librbd/io/ObjectRequest.cc | 1 + src/librbd/librbd.cc | 4 +- src/librbd/operation/ObjectMapIterate.cc | 11 +++ src/librbd/operation/SnapshotCreateRequest.cc | 9 +++ src/librbd/operation/SnapshotRemoveRequest.cc | 5 ++ src/librbd/operation/SparsifyRequest.cc | 5 ++ src/librbd/operation/TrimRequest.cc | 9 +++ .../librbd/image/test_mock_RefreshRequest.cc | 2 +- src/test/librbd/mock/MockImageCtx.h | 2 +- src/test/librbd/test_internal.cc | 69 +++++++++++++++++++ 26 files changed, 198 insertions(+), 20 deletions(-) diff --git a/src/librbd/DeepCopyRequest.cc b/src/librbd/DeepCopyRequest.cc index ddbd34a761187..64615685202a8 100644 --- a/src/librbd/DeepCopyRequest.cc +++ b/src/librbd/DeepCopyRequest.cc @@ -50,6 +50,18 @@ DeepCopyRequest::~DeepCopyRequest() { template void DeepCopyRequest::send() { + if (!m_src_image_ctx->data_ctx.is_valid()) { + lderr(m_cct) << "missing data pool for source image" << dendl; + finish(-ENODEV); + return; + } + + if (!m_dst_image_ctx->data_ctx.is_valid()) { + lderr(m_cct) << "missing data pool for destination image" << dendl; + finish(-ENODEV); + return; + } + int r = validate_copy_points(); if (r < 0) { finish(r); diff --git a/src/librbd/ImageCtx.cc b/src/librbd/ImageCtx.cc index b5e0390b3654a..6c70df9a53564 100644 --- a/src/librbd/ImageCtx.cc +++ b/src/librbd/ImageCtx.cc @@ -168,7 +168,9 @@ public: delete[] format_string; md_ctx.aio_flush(); - data_ctx.aio_flush(); + if (data_ctx.is_valid()) { + data_ctx.aio_flush(); + } io_work_queue->drain(); delete io_object_dispatcher; @@ -187,7 +189,7 @@ public: asok_hook = new LibrbdAdminSocketHook(this); string pname = string("librbd-") + id + string("-") + - data_ctx.get_pool_name() + string("-") + name; + md_ctx.get_pool_name() + string("-") + name; if (!snap_name.empty()) { pname += "-"; pname += snap_name; @@ -208,7 +210,7 @@ public: asok_hook = nullptr; } - void ImageCtx::init_layout() + void ImageCtx::init_layout(int64_t pool_id) { if (stripe_unit == 0 || stripe_count == 0) { stripe_unit = 1ull << order; @@ -225,7 +227,7 @@ public: layout.stripe_unit = stripe_unit; layout.stripe_count = stripe_count; layout.object_size = 1ull << order; - layout.pool_id = data_ctx.get_id(); // FIXME: pool id overflow? + layout.pool_id = pool_id; // FIXME: pool id overflow? delete[] format_string; size_t len = object_prefix.length() + 16; @@ -325,7 +327,9 @@ public: snap_namespace = it->second.snap_namespace; snap_name = it->second.name; snap_exists = true; - data_ctx.snap_set_read(snap_id); + if (data_ctx.is_valid()) { + data_ctx.snap_set_read(snap_id); + } return 0; } return -ENOENT; @@ -338,7 +342,9 @@ public: snap_namespace = {}; snap_name = ""; snap_exists = true; - data_ctx.snap_set_read(snap_id); + if (data_ctx.is_valid()) { + data_ctx.snap_set_read(snap_id); + } } snap_t ImageCtx::get_snap_id(const cls::rbd::SnapshotNamespace& in_snap_namespace, diff --git a/src/librbd/ImageCtx.h b/src/librbd/ImageCtx.h index 84a5dbec2edf1..b15b1506716ea 100644 --- a/src/librbd/ImageCtx.h +++ b/src/librbd/ImageCtx.h @@ -234,7 +234,7 @@ namespace librbd { ~ImageCtx(); void init(); void shutdown(); - void init_layout(); + void init_layout(int64_t pool_id); void perf_start(std::string name); void perf_stop(); void set_read_flag(unsigned flag); diff --git a/src/librbd/api/DiffIterate.cc b/src/librbd/api/DiffIterate.cc index 86be5b4e948a2..e4c850f39d0a7 100644 --- a/src/librbd/api/DiffIterate.cc +++ b/src/librbd/api/DiffIterate.cc @@ -239,6 +239,10 @@ int DiffIterate::diff_iterate(I *ictx, ldout(ictx->cct, 20) << "diff_iterate " << ictx << " off = " << off << " len = " << len << dendl; + if (!ictx->data_ctx.is_valid()) { + return -ENODEV; + } + // ensure previous writes are visible to listsnaps C_SaferCond flush_ctx; { @@ -277,6 +281,8 @@ template int DiffIterate::execute() { CephContext* cct = m_image_ctx.cct; + ceph_assert(m_image_ctx.data_ctx.is_valid()); + librados::IoCtx head_ctx; librados::snap_t from_snap_id = 0; librados::snap_t end_snap_id; diff --git a/src/librbd/api/Image.cc b/src/librbd/api/Image.cc index 69e9f80b29c38..2737039d52545 100644 --- a/src/librbd/api/Image.cc +++ b/src/librbd/api/Image.cc @@ -76,6 +76,23 @@ int pre_remove_image(librados::IoCtx& io_ctx, const std::string& image_id) { } // anonymous namespace +template +int64_t Image::get_data_pool_id(I *ictx) { + if (ictx->data_ctx.is_valid()) { + return ictx->data_ctx.get_id(); + } + + int64_t pool_id; + int r = cls_client::get_data_pool(&ictx->md_ctx, ictx->header_oid, &pool_id); + if (r < 0) { + CephContext *cct = ictx->cct; + lderr(cct) << "error getting data pool ID: " << cpp_strerror(r) << dendl; + return r; + } + + return pool_id; +} + template int Image::get_op_features(I *ictx, uint64_t *op_features) { CephContext *cct = ictx->cct; diff --git a/src/librbd/api/Image.h b/src/librbd/api/Image.h index 8bfadae70de93..af928c84c06c9 100644 --- a/src/librbd/api/Image.h +++ b/src/librbd/api/Image.h @@ -24,6 +24,8 @@ template struct Image { typedef std::map ImageNameToIds; + static int64_t get_data_pool_id(ImageCtxT *ictx); + static int get_op_features(ImageCtxT *ictx, uint64_t *op_features); static int list_images(librados::IoCtx& io_ctx, diff --git a/src/librbd/api/Snapshot.cc b/src/librbd/api/Snapshot.cc index 4d24f91a61594..8ca4c52be3c6a 100644 --- a/src/librbd/api/Snapshot.cc +++ b/src/librbd/api/Snapshot.cc @@ -111,7 +111,7 @@ int Snapshot::get_group_namespace(I *ictx, uint64_t snap_id, return -ENOENT; } - GetGroupVisitor ggv = GetGroupVisitor(ictx->cct, &ictx->data_ctx, group_snap); + GetGroupVisitor ggv = GetGroupVisitor(ictx->cct, &ictx->md_ctx, group_snap); r = boost::apply_visitor(ggv, snap_info->snap_namespace); if (r < 0) { return r; diff --git a/src/librbd/cache/ObjectCacherObjectDispatch.cc b/src/librbd/cache/ObjectCacherObjectDispatch.cc index 0b57201bbbb88..8343dca5d5a72 100644 --- a/src/librbd/cache/ObjectCacherObjectDispatch.cc +++ b/src/librbd/cache/ObjectCacherObjectDispatch.cc @@ -82,6 +82,7 @@ ObjectCacherObjectDispatch::ObjectCacherObjectDispatch( m_writethrough_until_flush(writethrough_until_flush), m_cache_lock(ceph::make_mutex(util::unique_lock_name( "librbd::cache::ObjectCacherObjectDispatch::cache_lock", this))) { + ceph_assert(m_image_ctx->data_ctx.is_valid()); } template diff --git a/src/librbd/cache/ParentCacheObjectDispatch.cc b/src/librbd/cache/ParentCacheObjectDispatch.cc index f1a1b5edabc88..87fba3c560b46 100644 --- a/src/librbd/cache/ParentCacheObjectDispatch.cc +++ b/src/librbd/cache/ParentCacheObjectDispatch.cc @@ -29,6 +29,7 @@ template ParentCacheObjectDispatch::ParentCacheObjectDispatch( I* image_ctx) : m_image_ctx(image_ctx), m_cache_client(nullptr), m_initialized(false), m_connecting(false) { + ceph_assert(m_image_ctx->data_ctx.is_valid()); std::string controller_path = ((CephContext*)(m_image_ctx->cct))->_conf.get_val("immutable_object_cache_sock"); m_cache_client = new CacheClient(controller_path.c_str(), m_image_ctx->cct); diff --git a/src/librbd/deep_copy/ObjectCopyRequest.cc b/src/librbd/deep_copy/ObjectCopyRequest.cc index 15c647975f30c..d630c03a83999 100644 --- a/src/librbd/deep_copy/ObjectCopyRequest.cc +++ b/src/librbd/deep_copy/ObjectCopyRequest.cc @@ -50,6 +50,8 @@ ObjectCopyRequest::ObjectCopyRequest(I *src_image_ctx, m_dst_image_ctx(dst_image_ctx), m_cct(dst_image_ctx->cct), m_snap_map(snap_map), m_dst_object_number(dst_object_number), m_flatten(flatten), m_on_finish(on_finish) { + ceph_assert(src_image_ctx->data_ctx.is_valid()); + ceph_assert(dst_image_ctx->data_ctx.is_valid()); ceph_assert(!m_snap_map.empty()); m_src_async_op = new io::AsyncOperation(); diff --git a/src/librbd/image/OpenRequest.cc b/src/librbd/image/OpenRequest.cc index 69269f59f4616..0188230c9f4ac 100644 --- a/src/librbd/image/OpenRequest.cc +++ b/src/librbd/image/OpenRequest.cc @@ -476,13 +476,19 @@ Context *OpenRequest::handle_v2_get_data_pool(int *result) { *result = util::create_ioctx(m_image_ctx->md_ctx, "data pool", data_pool_id, {}, &m_image_ctx->data_ctx); if (*result < 0) { - send_close_image(*result); - return nullptr; + if (*result != -ENOENT) { + send_close_image(*result); + return nullptr; + } + m_image_ctx->data_ctx.close(); + } else { + m_image_ctx->data_ctx.set_namespace(m_image_ctx->md_ctx.get_namespace()); } - m_image_ctx->data_ctx.set_namespace(m_image_ctx->md_ctx.get_namespace()); + } else { + data_pool_id = m_image_ctx->md_ctx.get_id(); } - m_image_ctx->init_layout(); + m_image_ctx->init_layout(data_pool_id); send_refresh(); return nullptr; } diff --git a/src/librbd/image/RefreshRequest.cc b/src/librbd/image/RefreshRequest.cc index d521e33bff617..8f0400a1f47d4 100644 --- a/src/librbd/image/RefreshRequest.cc +++ b/src/librbd/image/RefreshRequest.cc @@ -1296,7 +1296,7 @@ void RefreshRequest::apply() { m_image_ctx.op_features = 0; m_image_ctx.operations_disabled = false; m_image_ctx.object_prefix = std::move(m_object_prefix); - m_image_ctx.init_layout(); + m_image_ctx.init_layout(m_image_ctx.md_ctx.get_id()); } else { // HEAD revision doesn't have a defined overlap so it's only // applicable to snapshots @@ -1386,8 +1386,10 @@ void RefreshRequest::apply() { if (m_refresh_parent != nullptr) { m_refresh_parent->apply(); } - m_image_ctx.data_ctx.selfmanaged_snap_set_write_ctx(m_image_ctx.snapc.seq, - m_image_ctx.snaps); + if (m_image_ctx.data_ctx.is_valid()) { + m_image_ctx.data_ctx.selfmanaged_snap_set_write_ctx(m_image_ctx.snapc.seq, + m_image_ctx.snaps); + } // handle dynamically enabled / disabled features if (m_image_ctx.exclusive_lock != nullptr && diff --git a/src/librbd/image/RemoveRequest.cc b/src/librbd/image/RemoveRequest.cc index f1fe0d96cd7b9..f5d9c227fda95 100644 --- a/src/librbd/image/RemoveRequest.cc +++ b/src/librbd/image/RemoveRequest.cc @@ -129,6 +129,11 @@ void RemoveRequest::handle_pre_remove_image(int r) { return; } + if (!m_image_ctx->data_ctx.is_valid()) { + detach_child(); + return; + } + trim_image(); } diff --git a/src/librbd/image/RemoveRequest.h b/src/librbd/image/RemoveRequest.h index 40a571e5aab85..98d597645a491 100644 --- a/src/librbd/image/RemoveRequest.h +++ b/src/librbd/image/RemoveRequest.h @@ -63,7 +63,7 @@ private: * PRE REMOVE IMAGE * * * | * | * | * v * | - * TRIM IMAGE * * * * * | + * (skip if invalid data pool) TRIM IMAGE * * * * * | * | * | * v * | * DETACH CHILD * | diff --git a/src/librbd/io/CopyupRequest.cc b/src/librbd/io/CopyupRequest.cc index e9f5d2487ff89..b65ab475e14db 100644 --- a/src/librbd/io/CopyupRequest.cc +++ b/src/librbd/io/CopyupRequest.cc @@ -117,6 +117,7 @@ CopyupRequest::CopyupRequest(I *ictx, uint64_t objectno, : m_image_ctx(ictx), m_object_no(objectno), m_image_extents(image_extents), m_trace(util::create_trace(*m_image_ctx, "copy-up", parent_trace)) { + ceph_assert(m_image_ctx->data_ctx.is_valid()); m_async_op.start_op(*util::get_image_ctx(m_image_ctx)); } diff --git a/src/librbd/io/ImageRequestWQ.cc b/src/librbd/io/ImageRequestWQ.cc index 5bb0f1664f9de..a3a70be4cb986 100644 --- a/src/librbd/io/ImageRequestWQ.cc +++ b/src/librbd/io/ImageRequestWQ.cc @@ -844,6 +844,14 @@ int ImageRequestWQ::start_in_flight_io(AioCompletion *c) { return false; } + if (!m_image_ctx.data_ctx.is_valid()) { + CephContext *cct = m_image_ctx.cct; + lderr(cct) << "missing data pool" << dendl; + + c->fail(-ENODEV); + return false; + } + m_in_flight_ios++; return true; } diff --git a/src/librbd/io/ObjectRequest.cc b/src/librbd/io/ObjectRequest.cc index b8845f6b7f4e2..a8eb54e0be0df 100644 --- a/src/librbd/io/ObjectRequest.cc +++ b/src/librbd/io/ObjectRequest.cc @@ -103,6 +103,7 @@ ObjectRequest::ObjectRequest( : m_ictx(ictx), m_object_no(objectno), m_object_off(off), m_object_len(len), m_snap_id(snap_id), m_completion(completion), m_trace(util::create_trace(*ictx, "", trace)) { + ceph_assert(m_ictx->data_ctx.is_valid()); if (m_trace.valid()) { m_trace.copy_name(trace_name + std::string(" ") + data_object_name(ictx, objectno)); diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index 6c303c963fa49..58eb056b49364 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -1487,7 +1487,7 @@ namespace librbd { int64_t Image::get_data_pool_id() { ImageCtx *ictx = reinterpret_cast(ctx); - return ictx->data_ctx.get_id(); + return librbd::api::Image<>::get_data_pool_id(ictx); } int Image::parent_info(string *parent_pool_name, string *parent_name, @@ -4261,7 +4261,7 @@ extern "C" int rbd_get_block_name_prefix(rbd_image_t image, char *prefix, extern "C" int64_t rbd_get_data_pool_id(rbd_image_t image) { librbd::ImageCtx *ictx = reinterpret_cast(image); - return ictx->data_ctx.get_id(); + return librbd::api::Image<>::get_data_pool_id(ictx); } extern "C" int rbd_get_parent_info(rbd_image_t image, diff --git a/src/librbd/operation/ObjectMapIterate.cc b/src/librbd/operation/ObjectMapIterate.cc index 37f9303fc3ba4..50db3df851fb7 100644 --- a/src/librbd/operation/ObjectMapIterate.cc +++ b/src/librbd/operation/ObjectMapIterate.cc @@ -192,6 +192,11 @@ private: template void ObjectMapIterateRequest::send() { + if (!m_image_ctx.data_ctx.is_valid()) { + this->async_complete(-ENODEV); + return; + } + send_verify_objects(); } @@ -199,6 +204,12 @@ template bool ObjectMapIterateRequest::should_complete(int r) { CephContext *cct = m_image_ctx.cct; ldout(cct, 5) << this << " should_complete: " << " r=" << r << dendl; + + if (r == -ENODEV) { + lderr(cct) << "missing data pool" << dendl; + return true; + } + if (r < 0) { lderr(cct) << "object map operation encountered an error: " << cpp_strerror(r) << dendl; diff --git a/src/librbd/operation/SnapshotCreateRequest.cc b/src/librbd/operation/SnapshotCreateRequest.cc index 763625b4ac917..c949a7958fc0e 100644 --- a/src/librbd/operation/SnapshotCreateRequest.cc +++ b/src/librbd/operation/SnapshotCreateRequest.cc @@ -36,6 +36,15 @@ SnapshotCreateRequest::SnapshotCreateRequest(I &image_ctx, template void SnapshotCreateRequest::send_op() { + I &image_ctx = this->m_image_ctx; + CephContext *cct = image_ctx.cct; + + if (!image_ctx.data_ctx.is_valid()) { + lderr(cct) << "missing data pool" << dendl; + this->async_complete(-ENODEV); + return; + } + send_suspend_requests(); } diff --git a/src/librbd/operation/SnapshotRemoveRequest.cc b/src/librbd/operation/SnapshotRemoveRequest.cc index e120aea4ae801..cbfc938760a1a 100644 --- a/src/librbd/operation/SnapshotRemoveRequest.cc +++ b/src/librbd/operation/SnapshotRemoveRequest.cc @@ -261,6 +261,11 @@ template void SnapshotRemoveRequest::release_snap_id() { I &image_ctx = this->m_image_ctx; + if (!image_ctx.data_ctx.is_valid()) { + remove_snap(); + return; + } + CephContext *cct = image_ctx.cct; ldout(cct, 5) << "snap_name=" << m_snap_name << ", " << "snap_id=" << m_snap_id << dendl; diff --git a/src/librbd/operation/SparsifyRequest.cc b/src/librbd/operation/SparsifyRequest.cc index 0c5a4efab4b67..5d9837c3eab25 100644 --- a/src/librbd/operation/SparsifyRequest.cc +++ b/src/librbd/operation/SparsifyRequest.cc @@ -124,6 +124,11 @@ public: ldout(m_cct, 20) << dendl; + if (!image_ctx.data_ctx.is_valid()) { + lderr(m_cct) << "missing data pool" << dendl; + return -ENODEV; + } + if (image_ctx.exclusive_lock != nullptr && !image_ctx.exclusive_lock->is_lock_owner()) { ldout(m_cct, 1) << "lost exclusive lock during sparsify" << dendl; diff --git a/src/librbd/operation/TrimRequest.cc b/src/librbd/operation/TrimRequest.cc index f1a34a5592bf0..3c2f58f64af82 100644 --- a/src/librbd/operation/TrimRequest.cc +++ b/src/librbd/operation/TrimRequest.cc @@ -174,6 +174,15 @@ bool TrimRequest::should_complete(int r) template void TrimRequest::send() { + I &image_ctx = this->m_image_ctx; + CephContext *cct = image_ctx.cct; + + if (!image_ctx.data_ctx.is_valid()) { + lderr(cct) << "missing data pool" << dendl; + send_finish(-ENODEV); + return; + } + send_pre_trim(); } diff --git a/src/test/librbd/image/test_mock_RefreshRequest.cc b/src/test/librbd/image/test_mock_RefreshRequest.cc index 8274e04b1086d..0f89928818c21 100644 --- a/src/test/librbd/image/test_mock_RefreshRequest.cc +++ b/src/test/librbd/image/test_mock_RefreshRequest.cc @@ -375,7 +375,7 @@ public: } void expect_init_layout(MockRefreshImageCtx &mock_image_ctx) { - EXPECT_CALL(mock_image_ctx, init_layout()); + EXPECT_CALL(mock_image_ctx, init_layout(_)); } void expect_test_features(MockRefreshImageCtx &mock_image_ctx) { diff --git a/src/test/librbd/mock/MockImageCtx.h b/src/test/librbd/mock/MockImageCtx.h index 0b8a9345c27ae..092b90f40a49a 100644 --- a/src/test/librbd/mock/MockImageCtx.h +++ b/src/test/librbd/mock/MockImageCtx.h @@ -139,7 +139,7 @@ struct MockImageCtx { ctx.wait(); } - MOCK_METHOD0(init_layout, void()); + MOCK_METHOD1(init_layout, void(int64_t)); MOCK_CONST_METHOD1(get_object_name, std::string(uint64_t)); MOCK_CONST_METHOD0(get_object_size, uint64_t()); diff --git a/src/test/librbd/test_internal.cc b/src/test/librbd/test_internal.cc index 84b4d1c3c29dd..7e9bd9eb939ae 100644 --- a/src/test/librbd/test_internal.cc +++ b/src/test/librbd/test_internal.cc @@ -1739,3 +1739,72 @@ TEST_F(TestInternal, SparsifyClone) { ASSERT_EQ(0, ictx->data_ctx.stat(oid, &size, NULL)); ASSERT_EQ(0, ictx->data_ctx.read(oid, read_bl, 4096, 0)); } + +TEST_F(TestInternal, MissingDataPool) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, snap_create(*ictx, "snap1")); + std::string header_oid = ictx->header_oid; + close_image(ictx); + + // emulate non-existent data pool + int64_t pool_id = 1234; + std::string pool_name; + int r; + while ((r = _rados.pool_reverse_lookup(pool_id, &pool_name)) == 0) { + pool_id++; + } + ASSERT_EQ(r, -ENOENT); + bufferlist bl; + using ceph::encode; + encode(pool_id, bl); + ASSERT_EQ(0, m_ioctx.omap_set(header_oid, {{"data_pool_id", bl}})); + + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + ASSERT_FALSE(ictx->data_ctx.is_valid()); + ASSERT_EQ(pool_id, librbd::api::Image<>::get_data_pool_id(ictx)); + + librbd::image_info_t info; + ASSERT_EQ(0, librbd::info(ictx, info, sizeof(info))); + + vector snaps; + EXPECT_EQ(0, librbd::snap_list(ictx, snaps)); + EXPECT_EQ(1U, snaps.size()); + EXPECT_EQ("snap1", snaps[0].name); + + bufferptr read_ptr(256); + bufferlist read_bl; + read_bl.push_back(read_ptr); + librbd::io::ReadResult read_result{&read_bl}; + ASSERT_EQ(-ENODEV, + ictx->io_work_queue->read(0, 256, + librbd::io::ReadResult{read_result}, 0)); + ASSERT_EQ(-ENODEV, + ictx->io_work_queue->write(0, bl.length(), bufferlist{bl}, 0)); + ASSERT_EQ(-ENODEV, ictx->io_work_queue->discard(0, 1, 256)); + ASSERT_EQ(-ENODEV, + ictx->io_work_queue->writesame(0, bl.length(), bufferlist{bl}, 0)); + uint64_t mismatch_off; + ASSERT_EQ(-ENODEV, + ictx->io_work_queue->compare_and_write(0, bl.length(), + bufferlist{bl}, + bufferlist{bl}, + &mismatch_off, 0)); + ASSERT_EQ(-ENODEV, ictx->io_work_queue->flush()); + + ASSERT_EQ(-ENODEV, snap_create(*ictx, "snap2")); + ASSERT_EQ(0, ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(), + "snap1")); + + librbd::NoOpProgressContext no_op; + ASSERT_EQ(-ENODEV, ictx->operations->resize(0, true, no_op)); + + close_image(ictx); + + ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, m_image_name, no_op)); + + ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, m_image_name, m_image_size)); +} -- 2.39.5