From: Jason Dillaman Date: Mon, 13 Jul 2020 20:11:06 +0000 (-0400) Subject: librbd: fix parent cache races and error handling X-Git-Tag: v16.1.0~1702^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F36077%2Fhead;p=ceph.git librbd: fix parent cache races and error handling If the plugin fails to connect to the daemon at start-up it will crash the process due to a resource deadlock exception being thrown as the client is destroyed. Additionally, librbd will support concurrent IO thread processing in the future so the client needs to be protected by a lock. Signed-off-by: Jason Dillaman --- diff --git a/src/librbd/cache/ParentCacheObjectDispatch.cc b/src/librbd/cache/ParentCacheObjectDispatch.cc index 088384b1525d..73d372743101 100644 --- a/src/librbd/cache/ParentCacheObjectDispatch.cc +++ b/src/librbd/cache/ParentCacheObjectDispatch.cc @@ -26,17 +26,20 @@ namespace cache { template ParentCacheObjectDispatch::ParentCacheObjectDispatch( - I* image_ctx) : m_image_ctx(image_ctx), m_cache_client(nullptr), - m_initialized(false), m_connecting(false) { + I* image_ctx) + : m_image_ctx(image_ctx), + m_lock(ceph::make_mutex( + "librbd::cache::ParentCacheObjectDispatch::lock", true, 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"); + auto controller_path = image_ctx->cct->_conf.template get_val( + "immutable_object_cache_sock"); m_cache_client = new CacheClient(controller_path.c_str(), m_image_ctx->cct); } template ParentCacheObjectDispatch::~ParentCacheObjectDispatch() { - delete m_cache_client; + delete m_cache_client; + m_cache_client = nullptr; } template @@ -52,18 +55,10 @@ void ParentCacheObjectDispatch::init(Context* on_finish) { return; } - Context* create_session_ctx = new LambdaContext([this, on_finish](int ret) { - m_connecting.store(false); - if (on_finish != nullptr) { - on_finish->complete(ret); - } - }); - - m_connecting.store(true); - create_cache_session(create_session_ctx, false); - m_image_ctx->io_object_dispatcher->register_dispatch(this); - m_initialized = true; + + std::unique_lock locker{m_lock}; + create_cache_session(on_finish, false); } template @@ -77,36 +72,18 @@ bool ParentCacheObjectDispatch::read( auto cct = m_image_ctx->cct; ldout(cct, 20) << "object_no=" << object_no << " " << object_off << "~" << object_len << dendl; - ceph_assert(m_initialized); string oid = data_object_name(m_image_ctx, object_no); /* if RO daemon still don't startup, or RO daemon crash, * or session occur any error, try to re-connect daemon.*/ + std::unique_lock locker{m_lock}; if (!m_cache_client->is_session_work()) { - if (!m_connecting.exchange(true)) { - /* Since we don't have a giant lock protecting the full re-connect process, - * if thread A first passes the if (!m_cache_client->is_session_work()), - * thread B could have also passed it and reconnected - * before thread A resumes and executes if (!m_connecting.exchange(true)). - * This will result in thread A re-connecting a working session. - * So, we need to check if session is normal again. If session work, - * we need set m_connecting to false. */ - if (!m_cache_client->is_session_work()) { - Context* on_finish = new LambdaContext([this](int ret) { - m_connecting.store(false); - }); - create_cache_session(on_finish, true); - } else { - m_connecting.store(false); - } - } + create_cache_session(nullptr, true); ldout(cct, 5) << "Parent cache try to re-connect to RO daemon. " << "dispatch current request to lower object layer" << dendl; return false; } - ceph_assert(m_cache_client->is_session_work()); - CacheGenContextURef ctx = make_gen_lambda_context> ([this, read_data, dispatch_result, on_dispatched, @@ -165,18 +142,29 @@ int ParentCacheObjectDispatch::handle_register_client(bool reg) { } template -int ParentCacheObjectDispatch::create_cache_session(Context* on_finish, bool is_reconnect) { +void ParentCacheObjectDispatch::create_cache_session(Context* on_finish, + bool is_reconnect) { + ceph_assert(ceph_mutex_is_locked_by_me(m_lock)); + if (m_connecting) { + return; + } + m_connecting = true; + auto cct = m_image_ctx->cct; ldout(cct, 20) << dendl; Context* register_ctx = new LambdaContext([this, cct, on_finish](int ret) { if (ret < 0) { lderr(cct) << "Parent cache fail to register client." << dendl; - } else { - ceph_assert(m_cache_client->is_session_work()); } handle_register_client(ret < 0 ? false : true); - on_finish->complete(ret); + + ceph_assert(m_connecting); + m_connecting = false; + + if (on_finish != nullptr) { + on_finish->complete(0); + } }); Context* connect_ctx = new LambdaContext( @@ -197,21 +185,19 @@ int ParentCacheObjectDispatch::create_cache_session(Context* on_finish, bool delete m_cache_client; // create new CacheClient to connect RO daemon. - std::string controller_path = - ((CephContext*)(m_image_ctx->cct))->_conf.get_val("immutable_object_cache_sock"); + auto controller_path = cct->_conf.template get_val( + "immutable_object_cache_sock"); m_cache_client = new CacheClient(controller_path.c_str(), m_image_ctx->cct); } m_cache_client->run(); - m_cache_client->connect(connect_ctx); - return 0; } template int ParentCacheObjectDispatch::read_object( - std::string file_path, ceph::bufferlist* read_data, uint64_t offset, - uint64_t length, Context *on_finish) { + std::string file_path, ceph::bufferlist* read_data, uint64_t offset, + uint64_t length, Context *on_finish) { auto *cct = m_image_ctx->cct; ldout(cct, 20) << "file path: " << file_path << dendl; diff --git a/src/librbd/cache/ParentCacheObjectDispatch.h b/src/librbd/cache/ParentCacheObjectDispatch.h index 42222dd9d2b8..2c7b52341415 100644 --- a/src/librbd/cache/ParentCacheObjectDispatch.h +++ b/src/librbd/cache/ParentCacheObjectDispatch.h @@ -5,8 +5,9 @@ #define CEPH_LIBRBD_CACHE_PARENT_CACHER_OBJECT_DISPATCH_H #include "librbd/io/ObjectDispatchInterface.h" -#include "tools/immutable_object_cache/CacheClient.h" +#include "common/ceph_mutex.h" #include "librbd/cache/TypeTraits.h" +#include "tools/immutable_object_cache/CacheClient.h" #include "tools/immutable_object_cache/Types.h" namespace librbd { @@ -104,10 +105,6 @@ public: uint64_t journal_tid, uint64_t new_journal_tid) { } - bool get_state() { - return m_initialized; - } - ImageCtxT* get_image_ctx() { return m_image_ctx; } @@ -127,12 +124,13 @@ private: io::DispatchResult* dispatch_result, Context* on_dispatched); int handle_register_client(bool reg); - int create_cache_session(Context* on_finish, bool is_reconnect); + void create_cache_session(Context* on_finish, bool is_reconnect); ImageCtxT* m_image_ctx; - CacheClient *m_cache_client; - bool m_initialized; - std::atomic m_connecting; + + ceph::mutex m_lock; + CacheClient *m_cache_client = nullptr; + bool m_connecting = false; }; } // namespace cache diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index 2cfd785935e0..e57842ef9826 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -51,7 +51,7 @@ set(unittest_librbd_srcs test_mock_TrashWatcher.cc test_mock_Watcher.cc cache/test_mock_WriteAroundObjectDispatch.cc - cache/test_mock_ParentImageCache.cc + cache/test_mock_ParentCacheObjectDispatch.cc deep_copy/test_mock_ImageCopyRequest.cc deep_copy/test_mock_MetadataCopyRequest.cc deep_copy/test_mock_ObjectCopyRequest.cc diff --git a/src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc b/src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc new file mode 100644 index 000000000000..3b36e44f4ebe --- /dev/null +++ b/src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc @@ -0,0 +1,341 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "include/Context.h" +#include "tools/immutable_object_cache/CacheClient.h" +#include "test/immutable_object_cache/MockCacheDaemon.h" +#include "librbd/cache/ParentCacheObjectDispatch.h" +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/mock/MockImageCtx.h" + +using namespace ceph::immutable_obj_cache; + +namespace librbd { + +namespace { + +struct MockParentImageCacheImageCtx : public MockImageCtx { + MockParentImageCacheImageCtx(ImageCtx& image_ctx) + : MockImageCtx(image_ctx), shared_cache_path("/tmp/socket/path"){ + } + ~MockParentImageCacheImageCtx() {} + + std::string shared_cache_path; +}; + +}; // anonymous namespace + +namespace cache { + +template<> +struct TypeTraits { + typedef ceph::immutable_obj_cache::MockCacheClient CacheClient; +}; + +}; // namespace cache + +}; // namespace librbd + +#include "librbd/cache/ParentCacheObjectDispatch.cc" +template class librbd::cache::ParentCacheObjectDispatch; + +namespace librbd { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::WithArg; +using ::testing::WithArgs; + +class TestMockParentCacheObjectDispatch : public TestMockFixture { +public : + typedef cache::ParentCacheObjectDispatch MockParentImageCache; + + // ====== mock cache client ==== + void expect_cache_run(MockParentImageCache& mparent_image_cache, bool ret_val) { + auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), run()); + + expect.WillOnce((Invoke([]() { + }))); + } + + void expect_cache_session_state(MockParentImageCache& mparent_image_cache, bool ret_val) { + auto & expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), is_session_work()); + + expect.WillOnce((Invoke([ret_val]() { + return ret_val; + }))); + } + + void expect_cache_connect(MockParentImageCache& mparent_image_cache, int ret_val) { + auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), connect()); + + expect.WillOnce((Invoke([ret_val]() { + return ret_val; + }))); + } + + void expect_cache_async_connect(MockParentImageCache& mparent_image_cache, int ret_val, + Context* on_finish) { + auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), connect(_)); + + expect.WillOnce(WithArg<0>(Invoke([on_finish, ret_val](Context* ctx) { + ctx->complete(ret_val); + on_finish->complete(ret_val); + }))); + } + + void expect_cache_lookup_object(MockParentImageCache& mparent_image_cache, + Context* on_finish) { + auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), + internal_lookup(_, _, _, _)); + + expect.WillOnce(WithArg<3>(Invoke([on_finish](std::string oid) { + on_finish->complete(0); + }))); + } + + void expect_cache_close(MockParentImageCache& mparent_image_cache, int ret_val) { + auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), close()); + + expect.WillOnce((Invoke([]() { + }))); + } + + void expect_cache_stop(MockParentImageCache& mparent_image_cache, int ret_val) { + auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), stop()); + + expect.WillOnce((Invoke([]() { + }))); + } + + void expect_cache_register(MockParentImageCache& mparent_image_cache, Context* mock_handle_register, int ret_val) { + auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), register_client(_)); + + expect.WillOnce(WithArg<0>(Invoke([mock_handle_register, ret_val](Context* ctx) { + if(ret_val == 0) { + mock_handle_register->complete(true); + } else { + mock_handle_register->complete(false); + } + ctx->complete(true); + return ret_val; + }))); + } + + void expect_io_object_dispatcher_register_state(MockParentImageCache& mparent_image_cache, + int ret_val) { + auto& expect = EXPECT_CALL((*(mparent_image_cache.get_image_ctx()->io_object_dispatcher)), + register_dispatch(_)); + + expect.WillOnce(WithArg<0>(Invoke([&mparent_image_cache] + (io::ObjectDispatchInterface* object_dispatch) { + ASSERT_EQ(object_dispatch, &mparent_image_cache); + }))); + } +}; + +TEST_F(TestMockParentCacheObjectDispatch, test_initialization_success) { + librbd::ImageCtx* ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + MockParentImageCacheImageCtx mock_image_ctx(*ictx); + mock_image_ctx.child = &mock_image_ctx; + + auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx); + + expect_cache_run(*mock_parent_image_cache, 0); + C_SaferCond cond; + Context* handle_connect = new LambdaContext([&cond](int ret) { + ASSERT_EQ(ret, 0); + cond.complete(0); + }); + expect_cache_async_connect(*mock_parent_image_cache, 0, handle_connect); + Context* ctx = new LambdaContext([](bool reg) { + ASSERT_EQ(reg, true); + }); + expect_cache_register(*mock_parent_image_cache, ctx, 0); + expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0); + expect_cache_close(*mock_parent_image_cache, 0); + expect_cache_stop(*mock_parent_image_cache, 0); + + mock_parent_image_cache->init(); + cond.wait(); + + ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(), + io::OBJECT_DISPATCH_LAYER_PARENT_CACHE); + expect_cache_session_state(*mock_parent_image_cache, true); + ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), true); + + mock_parent_image_cache->get_cache_client()->close(); + mock_parent_image_cache->get_cache_client()->stop(); + + delete mock_parent_image_cache; +} + +TEST_F(TestMockParentCacheObjectDispatch, test_initialization_fail_at_connect) { + librbd::ImageCtx* ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + MockParentImageCacheImageCtx mock_image_ctx(*ictx); + mock_image_ctx.child = &mock_image_ctx; + + auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx); + + expect_cache_run(*mock_parent_image_cache, 0); + C_SaferCond cond; + Context* handle_connect = new LambdaContext([&cond](int ret) { + ASSERT_EQ(ret, -1); + cond.complete(0); + }); + expect_cache_async_connect(*mock_parent_image_cache, -1, handle_connect); + expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0); + expect_cache_session_state(*mock_parent_image_cache, false); + expect_cache_close(*mock_parent_image_cache, 0); + expect_cache_stop(*mock_parent_image_cache, 0); + + mock_parent_image_cache->init(); + + // initialization fails. + ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(), + io::OBJECT_DISPATCH_LAYER_PARENT_CACHE); + ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), false); + + mock_parent_image_cache->get_cache_client()->close(); + mock_parent_image_cache->get_cache_client()->stop(); + + delete mock_parent_image_cache; + +} + +TEST_F(TestMockParentCacheObjectDispatch, test_initialization_fail_at_register) { + librbd::ImageCtx* ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + MockParentImageCacheImageCtx mock_image_ctx(*ictx); + mock_image_ctx.child = &mock_image_ctx; + + auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx); + + expect_cache_run(*mock_parent_image_cache, 0); + C_SaferCond cond; + Context* handle_connect = new LambdaContext([&cond](int ret) { + ASSERT_EQ(ret, 0); + cond.complete(0); + }); + expect_cache_async_connect(*mock_parent_image_cache, 0, handle_connect); + Context* ctx = new LambdaContext([](bool reg) { + ASSERT_EQ(reg, false); + }); + expect_cache_register(*mock_parent_image_cache, ctx, -1); + expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0); + expect_cache_close(*mock_parent_image_cache, 0); + expect_cache_stop(*mock_parent_image_cache, 0); + + mock_parent_image_cache->init(); + cond.wait(); + + ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(), + io::OBJECT_DISPATCH_LAYER_PARENT_CACHE); + expect_cache_session_state(*mock_parent_image_cache, true); + ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), true); + + mock_parent_image_cache->get_cache_client()->close(); + mock_parent_image_cache->get_cache_client()->stop(); + + delete mock_parent_image_cache; +} + +TEST_F(TestMockParentCacheObjectDispatch, test_disble_interface) { + librbd::ImageCtx* ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + MockParentImageCacheImageCtx mock_image_ctx(*ictx); + mock_image_ctx.child = &mock_image_ctx; + + auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx); + + std::string temp_oid("12345"); + ceph::bufferlist temp_bl; + ::SnapContext *temp_snapc = nullptr; + io::DispatchResult* temp_dispatch_result = nullptr; + io::Extents temp_buffer_extents; + int* temp_op_flags = nullptr; + uint64_t* temp_journal_tid = nullptr; + Context** temp_on_finish = nullptr; + Context* temp_on_dispatched = nullptr; + ZTracer::Trace* temp_trace = nullptr; + io::LightweightBufferExtents buffer_extents; + + ASSERT_EQ(mock_parent_image_cache->discard(0, 0, 0, *temp_snapc, 0, *temp_trace, temp_op_flags, + temp_journal_tid, temp_dispatch_result, temp_on_finish, temp_on_dispatched), false); + ASSERT_EQ(mock_parent_image_cache->write(0, 0, std::move(temp_bl), *temp_snapc, 0, + *temp_trace, temp_op_flags, temp_journal_tid, temp_dispatch_result, + temp_on_finish, temp_on_dispatched), false); + ASSERT_EQ(mock_parent_image_cache->write_same(0, 0, 0, std::move(buffer_extents), + std::move(temp_bl), *temp_snapc, 0, *temp_trace, temp_op_flags, + temp_journal_tid, temp_dispatch_result, temp_on_finish, temp_on_dispatched), false ); + ASSERT_EQ(mock_parent_image_cache->compare_and_write(0, 0, std::move(temp_bl), std::move(temp_bl), + *temp_snapc, 0, *temp_trace, temp_journal_tid, temp_op_flags, temp_journal_tid, + temp_dispatch_result, temp_on_finish, temp_on_dispatched), false); + ASSERT_EQ(mock_parent_image_cache->flush(io::FLUSH_SOURCE_USER, *temp_trace, temp_journal_tid, + temp_dispatch_result, temp_on_finish, temp_on_dispatched), false); + ASSERT_EQ(mock_parent_image_cache->invalidate_cache(nullptr), false); + ASSERT_EQ(mock_parent_image_cache->reset_existence_cache(nullptr), false); + + delete mock_parent_image_cache; + +} + +TEST_F(TestMockParentCacheObjectDispatch, test_read) { + librbd::ImageCtx* ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + MockParentImageCacheImageCtx mock_image_ctx(*ictx); + mock_image_ctx.child = &mock_image_ctx; + + auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx); + + expect_cache_run(*mock_parent_image_cache, 0); + C_SaferCond conn_cond; + Context* handle_connect = new LambdaContext([&conn_cond](int ret) { + ASSERT_EQ(ret, 0); + conn_cond.complete(0); + }); + expect_cache_async_connect(*mock_parent_image_cache, 0, handle_connect); + Context* ctx = new LambdaContext([](bool reg) { + ASSERT_EQ(reg, true); + }); + expect_cache_register(*mock_parent_image_cache, ctx, 0); + expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0); + expect_cache_close(*mock_parent_image_cache, 0); + expect_cache_stop(*mock_parent_image_cache, 0); + + mock_parent_image_cache->init(); + conn_cond.wait(); + + ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(), + io::OBJECT_DISPATCH_LAYER_PARENT_CACHE); + expect_cache_session_state(*mock_parent_image_cache, true); + ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), true); + + C_SaferCond cond; + Context* on_finish = &cond; + + auto& expect = EXPECT_CALL(*(mock_parent_image_cache->get_cache_client()), is_session_work()); + expect.WillOnce(Return(true)); + + expect_cache_lookup_object(*mock_parent_image_cache, on_finish); + + mock_parent_image_cache->read(0, 0, 4096, CEPH_NOSNAP, 0, {}, + nullptr, nullptr, nullptr, nullptr, &on_finish, nullptr); + ASSERT_EQ(0, cond.wait()); + + mock_parent_image_cache->get_cache_client()->close(); + mock_parent_image_cache->get_cache_client()->stop(); + delete mock_parent_image_cache; +} + +} // namespace librbd diff --git a/src/test/librbd/cache/test_mock_ParentImageCache.cc b/src/test/librbd/cache/test_mock_ParentImageCache.cc deleted file mode 100644 index 64394ef3113e..000000000000 --- a/src/test/librbd/cache/test_mock_ParentImageCache.cc +++ /dev/null @@ -1,348 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#include "test/librbd/test_mock_fixture.h" -#include "test/librbd/test_support.h" -#include "test/librbd/mock/MockImageCtx.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "include/Context.h" -#include "tools/immutable_object_cache/CacheClient.h" -#include "test/immutable_object_cache/MockCacheDaemon.h" -#include "librbd/cache/ParentCacheObjectDispatch.h" -#include "test/librbd/test_mock_fixture.h" -#include "test/librbd/mock/MockImageCtx.h" - -using namespace ceph::immutable_obj_cache; - -namespace librbd { - -namespace { - -struct MockParentImageCacheImageCtx : public MockImageCtx { - MockParentImageCacheImageCtx(ImageCtx& image_ctx) - : MockImageCtx(image_ctx), shared_cache_path("/tmp/socket/path"){ - } - ~MockParentImageCacheImageCtx() {} - - std::string shared_cache_path; -}; - -}; // anonymous namespace - -namespace cache { - -template<> -struct TypeTraits { - typedef ceph::immutable_obj_cache::MockCacheClient CacheClient; -}; - -}; // namespace cache - -}; // namespace librbd - -#include "librbd/cache/ParentCacheObjectDispatch.cc" -template class librbd::cache::ParentCacheObjectDispatch; - -namespace librbd { - -using ::testing::_; -using ::testing::DoAll; -using ::testing::Invoke; -using ::testing::InSequence; -using ::testing::Return; -using ::testing::WithArg; -using ::testing::WithArgs; - -class TestMockParentImageCache : public TestMockFixture { -public : - typedef cache::ParentCacheObjectDispatch MockParentImageCache; - - // ====== mock cache client ==== - void expect_cache_run(MockParentImageCache& mparent_image_cache, bool ret_val) { - auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), run()); - - expect.WillOnce((Invoke([]() { - }))); - } - - void expect_cache_session_state(MockParentImageCache& mparent_image_cache, bool ret_val) { - auto & expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), is_session_work()); - - expect.WillOnce((Invoke([ret_val]() { - return ret_val; - }))); - } - - void expect_cache_connect(MockParentImageCache& mparent_image_cache, int ret_val) { - auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), connect()); - - expect.WillOnce((Invoke([ret_val]() { - return ret_val; - }))); - } - - void expect_cache_async_connect(MockParentImageCache& mparent_image_cache, int ret_val, - Context* on_finish) { - auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), connect(_)); - - expect.WillOnce(WithArg<0>(Invoke([on_finish, ret_val](Context* ctx) { - ctx->complete(ret_val); - on_finish->complete(ret_val); - }))); - } - - void expect_cache_lookup_object(MockParentImageCache& mparent_image_cache, - Context* on_finish) { - auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), - internal_lookup(_, _, _, _)); - - expect.WillOnce(WithArg<3>(Invoke([on_finish](std::string oid) { - on_finish->complete(0); - }))); - } - - void expect_cache_close(MockParentImageCache& mparent_image_cache, int ret_val) { - auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), close()); - - expect.WillOnce((Invoke([]() { - }))); - } - - void expect_cache_stop(MockParentImageCache& mparent_image_cache, int ret_val) { - auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), stop()); - - expect.WillOnce((Invoke([]() { - }))); - } - - void expect_cache_register(MockParentImageCache& mparent_image_cache, Context* mock_handle_register, int ret_val) { - auto& expect = EXPECT_CALL(*(mparent_image_cache.get_cache_client()), register_client(_)); - - expect.WillOnce(WithArg<0>(Invoke([mock_handle_register, ret_val](Context* ctx) { - if(ret_val == 0) { - mock_handle_register->complete(true); - } else { - mock_handle_register->complete(false); - } - ctx->complete(true); - return ret_val; - }))); - } - - void expect_io_object_dispatcher_register_state(MockParentImageCache& mparent_image_cache, - int ret_val) { - auto& expect = EXPECT_CALL((*(mparent_image_cache.get_image_ctx()->io_object_dispatcher)), - register_dispatch(_)); - - expect.WillOnce(WithArg<0>(Invoke([&mparent_image_cache] - (io::ObjectDispatchInterface* object_dispatch) { - ASSERT_EQ(object_dispatch, &mparent_image_cache); - }))); - } -}; - -TEST_F(TestMockParentImageCache, test_initialization_success) { - librbd::ImageCtx* ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockParentImageCacheImageCtx mock_image_ctx(*ictx); - mock_image_ctx.child = &mock_image_ctx; - - auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx); - - expect_cache_run(*mock_parent_image_cache, 0); - C_SaferCond cond; - Context* handle_connect = new LambdaContext([&cond](int ret) { - ASSERT_EQ(ret, 0); - cond.complete(0); - }); - expect_cache_async_connect(*mock_parent_image_cache, 0, handle_connect); - Context* ctx = new LambdaContext([](bool reg) { - ASSERT_EQ(reg, true); - }); - expect_cache_register(*mock_parent_image_cache, ctx, 0); - expect_cache_session_state(*mock_parent_image_cache, true); - expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0); - expect_cache_close(*mock_parent_image_cache, 0); - expect_cache_stop(*mock_parent_image_cache, 0); - - mock_parent_image_cache->init(); - cond.wait(); - - ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(), - io::OBJECT_DISPATCH_LAYER_PARENT_CACHE); - ASSERT_EQ(mock_parent_image_cache->get_state(), true); - expect_cache_session_state(*mock_parent_image_cache, true); - ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), true); - - mock_parent_image_cache->get_cache_client()->close(); - mock_parent_image_cache->get_cache_client()->stop(); - - delete mock_parent_image_cache; -} - -TEST_F(TestMockParentImageCache, test_initialization_fail_at_connect) { - librbd::ImageCtx* ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockParentImageCacheImageCtx mock_image_ctx(*ictx); - mock_image_ctx.child = &mock_image_ctx; - - auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx); - - expect_cache_run(*mock_parent_image_cache, 0); - C_SaferCond cond; - Context* handle_connect = new LambdaContext([&cond](int ret) { - ASSERT_EQ(ret, -1); - cond.complete(0); - }); - expect_cache_async_connect(*mock_parent_image_cache, -1, handle_connect); - expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0); - expect_cache_session_state(*mock_parent_image_cache, false); - expect_cache_close(*mock_parent_image_cache, 0); - expect_cache_stop(*mock_parent_image_cache, 0); - - mock_parent_image_cache->init(); - - // initialization fails. - ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(), - io::OBJECT_DISPATCH_LAYER_PARENT_CACHE); - ASSERT_EQ(mock_parent_image_cache->get_state(), true); - ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), false); - - mock_parent_image_cache->get_cache_client()->close(); - mock_parent_image_cache->get_cache_client()->stop(); - - delete mock_parent_image_cache; - -} - -TEST_F(TestMockParentImageCache, test_initialization_fail_at_register) { - librbd::ImageCtx* ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockParentImageCacheImageCtx mock_image_ctx(*ictx); - mock_image_ctx.child = &mock_image_ctx; - - auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx); - - expect_cache_run(*mock_parent_image_cache, 0); - C_SaferCond cond; - Context* handle_connect = new LambdaContext([&cond](int ret) { - ASSERT_EQ(ret, 0); - cond.complete(0); - }); - expect_cache_async_connect(*mock_parent_image_cache, 0, handle_connect); - Context* ctx = new LambdaContext([](bool reg) { - ASSERT_EQ(reg, false); - }); - expect_cache_register(*mock_parent_image_cache, ctx, -1); - expect_cache_session_state(*mock_parent_image_cache, true); - expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0); - expect_cache_close(*mock_parent_image_cache, 0); - expect_cache_stop(*mock_parent_image_cache, 0); - - mock_parent_image_cache->init(); - cond.wait(); - - ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(), - io::OBJECT_DISPATCH_LAYER_PARENT_CACHE); - ASSERT_EQ(mock_parent_image_cache->get_state(), true); - expect_cache_session_state(*mock_parent_image_cache, true); - ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), true); - - mock_parent_image_cache->get_cache_client()->close(); - mock_parent_image_cache->get_cache_client()->stop(); - - delete mock_parent_image_cache; -} - -TEST_F(TestMockParentImageCache, test_disble_interface) { - librbd::ImageCtx* ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockParentImageCacheImageCtx mock_image_ctx(*ictx); - mock_image_ctx.child = &mock_image_ctx; - - auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx); - - std::string temp_oid("12345"); - ceph::bufferlist temp_bl; - ::SnapContext *temp_snapc = nullptr; - io::DispatchResult* temp_dispatch_result = nullptr; - io::Extents temp_buffer_extents; - int* temp_op_flags = nullptr; - uint64_t* temp_journal_tid = nullptr; - Context** temp_on_finish = nullptr; - Context* temp_on_dispatched = nullptr; - ZTracer::Trace* temp_trace = nullptr; - io::LightweightBufferExtents buffer_extents; - - ASSERT_EQ(mock_parent_image_cache->discard(0, 0, 0, *temp_snapc, 0, *temp_trace, temp_op_flags, - temp_journal_tid, temp_dispatch_result, temp_on_finish, temp_on_dispatched), false); - ASSERT_EQ(mock_parent_image_cache->write(0, 0, std::move(temp_bl), *temp_snapc, 0, - *temp_trace, temp_op_flags, temp_journal_tid, temp_dispatch_result, - temp_on_finish, temp_on_dispatched), false); - ASSERT_EQ(mock_parent_image_cache->write_same(0, 0, 0, std::move(buffer_extents), - std::move(temp_bl), *temp_snapc, 0, *temp_trace, temp_op_flags, - temp_journal_tid, temp_dispatch_result, temp_on_finish, temp_on_dispatched), false ); - ASSERT_EQ(mock_parent_image_cache->compare_and_write(0, 0, std::move(temp_bl), std::move(temp_bl), - *temp_snapc, 0, *temp_trace, temp_journal_tid, temp_op_flags, temp_journal_tid, - temp_dispatch_result, temp_on_finish, temp_on_dispatched), false); - ASSERT_EQ(mock_parent_image_cache->flush(io::FLUSH_SOURCE_USER, *temp_trace, temp_journal_tid, - temp_dispatch_result, temp_on_finish, temp_on_dispatched), false); - ASSERT_EQ(mock_parent_image_cache->invalidate_cache(nullptr), false); - ASSERT_EQ(mock_parent_image_cache->reset_existence_cache(nullptr), false); - - delete mock_parent_image_cache; - -} - -TEST_F(TestMockParentImageCache, test_read) { - librbd::ImageCtx* ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockParentImageCacheImageCtx mock_image_ctx(*ictx); - mock_image_ctx.child = &mock_image_ctx; - - auto mock_parent_image_cache = MockParentImageCache::create(&mock_image_ctx); - - expect_cache_run(*mock_parent_image_cache, 0); - C_SaferCond conn_cond; - Context* handle_connect = new LambdaContext([&conn_cond](int ret) { - ASSERT_EQ(ret, 0); - conn_cond.complete(0); - }); - expect_cache_async_connect(*mock_parent_image_cache, 0, handle_connect); - Context* ctx = new LambdaContext([](bool reg) { - ASSERT_EQ(reg, true); - }); - expect_cache_register(*mock_parent_image_cache, ctx, 0); - expect_cache_session_state(*mock_parent_image_cache, true); - expect_io_object_dispatcher_register_state(*mock_parent_image_cache, 0); - expect_cache_close(*mock_parent_image_cache, 0); - expect_cache_stop(*mock_parent_image_cache, 0); - - mock_parent_image_cache->init(); - conn_cond.wait(); - - ASSERT_EQ(mock_parent_image_cache->get_dispatch_layer(), - io::OBJECT_DISPATCH_LAYER_PARENT_CACHE); - ASSERT_EQ(mock_parent_image_cache->get_state(), true); - expect_cache_session_state(*mock_parent_image_cache, true); - ASSERT_EQ(mock_parent_image_cache->get_cache_client()->is_session_work(), true); - - C_SaferCond cond; - Context* on_finish = &cond; - - auto& expect = EXPECT_CALL(*(mock_parent_image_cache->get_cache_client()), is_session_work()); - expect.WillOnce(Return(true)).WillOnce(Return(true)); - - expect_cache_lookup_object(*mock_parent_image_cache, on_finish); - - mock_parent_image_cache->read(0, 0, 4096, CEPH_NOSNAP, 0, {}, - nullptr, nullptr, nullptr, nullptr, &on_finish, nullptr); - ASSERT_EQ(0, cond.wait()); - - mock_parent_image_cache->get_cache_client()->close(); - mock_parent_image_cache->get_cache_client()->stop(); - delete mock_parent_image_cache; -} - -} // namespace librbd