diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc index f53a81edc259e..0ae8e012185ab 100644 --- a/src/librbd/internal.cc +++ b/src/librbd/internal.cc @@ -2478,7 +2478,7 @@ int mirror_image_disable_internal(ImageCtx *ictx, bool force, return cls_client::metadata_list(&ictx->md_ctx, ictx->header_oid, start, max, pairs); } - int mirror_image_enable(ImageCtx *ictx) { + int mirror_image_enable(ImageCtx *ictx, bool relax_same_pool_parent_check) { CephContext *cct = ictx->cct; ldout(cct, 20) << "mirror_image_enable " << ictx << dendl; @@ -2501,16 +2501,25 @@ int mirror_image_disable_internal(ImageCtx *ictx, bool force, return -EINVAL; } - // is mirroring not enabled for the parent? + // is mirroring not enabled for the parent? { RWLock::RLocker l(ictx->parent_lock); ImageCtx *parent = ictx->parent; - if(parent) { - cls::rbd::MirrorImage mirror_image_internal; - r = cls_client::mirror_image_get(&(parent->md_ctx), parent->id, &mirror_image_internal); - if (r == -ENOENT) { - lderr(cct) << "mirroring is not enabled for the parent" << dendl; - return -EINVAL; + if (parent) { + if (relax_same_pool_parent_check && + parent->md_ctx.get_id() == ictx->md_ctx.get_id()) { + if (!parent->test_features(RBD_FEATURE_JOURNALING)) { + lderr(cct) << "journaling is not enabled for the parent" << dendl; + return -EINVAL; + } + } else { + cls::rbd::MirrorImage mirror_image_internal; + r = cls_client::mirror_image_get(&(parent->md_ctx), parent->id, + &mirror_image_internal); + if (r == -ENOENT) { + lderr(cct) << "mirroring is not enabled for the parent" << dendl; + return -EINVAL; + } } } } @@ -3025,7 +3034,7 @@ int mirror_image_disable_internal(ImageCtx *ictx, bool force, return r; } - r = mirror_image_enable(img_ctx); + r = mirror_image_enable(img_ctx, true); if (r < 0) { lderr(cct) << "error enabling mirroring for image " << img_pair.first << ": " << cpp_strerror(r) << dendl; diff --git a/src/librbd/internal.h b/src/librbd/internal.h index 550eecf9ac5c7..912da6d1dbf42 100644 --- a/src/librbd/internal.h +++ b/src/librbd/internal.h @@ -214,7 +214,7 @@ namespace librbd { int mirror_image_status_summary(IoCtx& io_ctx, std::map *states); - int mirror_image_enable(ImageCtx *ictx); + int mirror_image_enable(ImageCtx *ictx, bool relax_same_pool_parent_check); int mirror_image_disable(ImageCtx *ictx, bool force); int mirror_image_promote(ImageCtx *ictx, bool force); int mirror_image_demote(ImageCtx *ictx); diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index 3b18628690c36..5545e69391d54 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -1380,7 +1380,7 @@ namespace librbd { int Image::mirror_image_enable() { ImageCtx *ictx = (ImageCtx *)ctx; - return librbd::mirror_image_enable(ictx); + return librbd::mirror_image_enable(ictx, false); } int Image::mirror_image_disable(bool force) { @@ -2967,7 +2967,7 @@ extern "C" int rbd_metadata_list(rbd_image_t image, const char *start, uint64_t extern "C" int rbd_mirror_image_enable(rbd_image_t image) { librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; - return librbd::mirror_image_enable(ictx); + return librbd::mirror_image_enable(ictx, false); } extern "C" int rbd_mirror_image_disable(rbd_image_t image, bool force) diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index 7344b76e5b1cb..37d0eb32b8bc3 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -4979,6 +4979,28 @@ TEST_F(TestLibRBD, Mirror) { ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE)); ASSERT_EQ(0, rbd.mirror_mode_get(ioctx, &mirror_mode)); + + // Add some images to the pool + int order = 0; + std::string parent_name = get_temp_image_name(); + std::string child_name = get_temp_image_name(); + ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(), 2 << 20, + &order)); + bool old_format; + uint64_t features; + ASSERT_EQ(0, get_features(&old_format, &features)); + if ((features & RBD_FEATURE_LAYERING) != 0) { + librbd::Image parent; + ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), NULL)); + ASSERT_EQ(0, parent.snap_create("parent_snap")); + ASSERT_EQ(0, parent.close()); + ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), "parent_snap")); + ASSERT_EQ(0, parent.snap_protect("parent_snap")); + ASSERT_EQ(0, parent.close()); + ASSERT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "parent_snap", ioctx, + child_name.c_str(), features, &order)); + } + ASSERT_EQ(RBD_MIRROR_MODE_IMAGE, mirror_mode); ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)); diff --git a/src/test/rbd_mirror/image_replayer/test_mock_BootstrapRequest.cc b/src/test/rbd_mirror/image_replayer/test_mock_BootstrapRequest.cc index 56bb21f2df279..542b78c54289c 100644 --- a/src/test/rbd_mirror/image_replayer/test_mock_BootstrapRequest.cc +++ b/src/test/rbd_mirror/image_replayer/test_mock_BootstrapRequest.cc @@ -9,6 +9,7 @@ #include "tools/rbd_mirror/image_replayer/BootstrapRequest.h" #include "tools/rbd_mirror/image_replayer/CloseImageRequest.h" #include "tools/rbd_mirror/image_replayer/CreateImageRequest.h" +#include "tools/rbd_mirror/image_replayer/IsPrimaryRequest.h" #include "tools/rbd_mirror/image_replayer/OpenImageRequest.h" #include "tools/rbd_mirror/image_replayer/OpenLocalImageRequest.h" #include "test/journal/mock/MockJournaler.h" @@ -149,6 +150,31 @@ struct CreateImageRequest { MOCK_METHOD0(send, void()); }; +template<> +struct IsPrimaryRequest { + static IsPrimaryRequest* s_instance; + bool *primary = nullptr; + Context *on_finish = nullptr; + + static IsPrimaryRequest* create(librbd::MockTestImageCtx *image_ctx, + bool *primary, Context *on_finish) { + assert(s_instance != nullptr); + s_instance->primary = primary; + s_instance->on_finish = on_finish; + return s_instance; + } + + IsPrimaryRequest() { + assert(s_instance == nullptr); + s_instance = this; + } + ~IsPrimaryRequest() { + s_instance = nullptr; + } + + MOCK_METHOD0(send, void()); +}; + template<> struct OpenImageRequest { static OpenImageRequest* s_instance; @@ -216,6 +242,8 @@ CloseImageRequest* CloseImageRequest::s_instance = nullptr; CreateImageRequest* CreateImageRequest::s_instance = nullptr; +IsPrimaryRequest* + IsPrimaryRequest::s_instance = nullptr; OpenImageRequest* OpenImageRequest::s_instance = nullptr; OpenLocalImageRequest* @@ -251,6 +279,7 @@ class TestMockImageReplayerBootstrapRequest : public TestMockFixture { typedef ImageSyncThrottlerRef MockImageSyncThrottler; typedef BootstrapRequest MockBootstrapRequest; typedef CloseImageRequest MockCloseImageRequest; + typedef IsPrimaryRequest MockIsPrimaryRequest; typedef OpenImageRequest MockOpenImageRequest; typedef OpenLocalImageRequest MockOpenLocalImageRequest; typedef std::list Tags; @@ -358,11 +387,13 @@ class TestMockImageReplayerBootstrapRequest : public TestMockFixture { })); } - void expect_journal_is_tag_owner(librbd::MockJournal &mock_journal, - bool is_owner, int r) { - EXPECT_CALL(mock_journal, is_tag_owner(_)) - .WillOnce(DoAll(SetArgPointee<0>(is_owner), - Return(r))); + void expect_is_primary(MockIsPrimaryRequest &mock_is_primary_request, + bool primary, int r) { + EXPECT_CALL(mock_is_primary_request, send()) + .WillOnce(Invoke([this, &mock_is_primary_request, primary, r]() { + *mock_is_primary_request.primary = primary; + m_threads->work_queue->queue(mock_is_primary_request.on_finish, r); + })); } void expect_journal_get_tag_tid(librbd::MockJournal &mock_journal, @@ -455,7 +486,8 @@ TEST_F(TestMockImageReplayerBootstrapRequest, NonPrimaryRemoteSyncingState) { MockOpenImageRequest mock_open_image_request; expect_open_image(mock_open_image_request, m_remote_io_ctx, mock_remote_image_ctx.id, mock_remote_image_ctx, 0); - expect_journal_is_tag_owner(mock_journal, false, 0); + MockIsPrimaryRequest mock_is_primary_request; + expect_is_primary(mock_is_primary_request, false, 0); // switch the state to replaying mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING; @@ -512,7 +544,8 @@ TEST_F(TestMockImageReplayerBootstrapRequest, RemoteDemotePromote) { MockOpenImageRequest mock_open_image_request; expect_open_image(mock_open_image_request, m_remote_io_ctx, mock_remote_image_ctx.id, mock_remote_image_ctx, 0); - expect_journal_is_tag_owner(mock_journal, true, 0); + MockIsPrimaryRequest mock_is_primary_request; + expect_is_primary(mock_is_primary_request, true, 0); // open the local image mock_local_image_ctx.journal = &mock_journal; @@ -590,7 +623,8 @@ TEST_F(TestMockImageReplayerBootstrapRequest, MultipleRemoteDemotePromotes) { MockOpenImageRequest mock_open_image_request; expect_open_image(mock_open_image_request, m_remote_io_ctx, mock_remote_image_ctx.id, mock_remote_image_ctx, 0); - expect_journal_is_tag_owner(mock_journal, true, 0); + MockIsPrimaryRequest mock_is_primary_request; + expect_is_primary(mock_is_primary_request, true, 0); // open the local image mock_local_image_ctx.journal = &mock_journal; @@ -678,7 +712,8 @@ TEST_F(TestMockImageReplayerBootstrapRequest, LocalDemoteRemotePromote) { MockOpenImageRequest mock_open_image_request; expect_open_image(mock_open_image_request, m_remote_io_ctx, mock_remote_image_ctx.id, mock_remote_image_ctx, 0); - expect_journal_is_tag_owner(mock_journal, true, 0); + MockIsPrimaryRequest mock_is_primary_request; + expect_is_primary(mock_is_primary_request, true, 0); // open the local image mock_local_image_ctx.journal = &mock_journal; @@ -754,7 +789,8 @@ TEST_F(TestMockImageReplayerBootstrapRequest, SplitBrainForcePromote) { MockOpenImageRequest mock_open_image_request; expect_open_image(mock_open_image_request, m_remote_io_ctx, mock_remote_image_ctx.id, mock_remote_image_ctx, 0); - expect_journal_is_tag_owner(mock_journal, true, 0); + MockIsPrimaryRequest mock_is_primary_request; + expect_is_primary(mock_is_primary_request, true, 0); // open the local image mock_local_image_ctx.journal = &mock_journal; diff --git a/src/test/rbd_mirror/test_ImageReplayer.cc b/src/test/rbd_mirror/test_ImageReplayer.cc index 9916e362e57c3..ed4dc16a00cb3 100644 --- a/src/test/rbd_mirror/test_ImageReplayer.cc +++ b/src/test/rbd_mirror/test_ImageReplayer.cc @@ -99,6 +99,7 @@ class TestImageReplayer : public ::rbd::mirror::TestFixture { EXPECT_EQ(0, m_remote_cluster.ioctx_create(m_remote_pool_name.c_str(), m_remote_ioctx)); + EXPECT_EQ(0, librbd::mirror_mode_set(m_remote_ioctx, RBD_MIRROR_MODE_POOL)); m_image_name = get_temp_image_name(); uint64_t features = librbd::util::get_rbd_default_features(g_ceph_context); @@ -392,7 +393,7 @@ TEST_F(TestImageReplayer, BootstrapErrorLocalImageExists) TEST_F(TestImageReplayer, BootstrapErrorNoJournal) { - // disable remote journal journaling + // disable remote image journaling librbd::ImageCtx *ictx; open_remote_image(&ictx); uint64_t features; @@ -407,6 +408,58 @@ TEST_F(TestImageReplayer, BootstrapErrorNoJournal) ASSERT_EQ(-ENOENT, cond.wait()); } +TEST_F(TestImageReplayer, BootstrapErrorMirrorDisabled) +{ + // disable remote image mirroring + ASSERT_EQ(0, librbd::mirror_mode_set(m_remote_ioctx, RBD_MIRROR_MODE_IMAGE)); + librbd::ImageCtx *ictx; + open_remote_image(&ictx); + ASSERT_EQ(0, librbd::mirror_image_disable(ictx, true)); + close_image(ictx); + + create_replayer<>(); + C_SaferCond cond; + m_replayer->start(&cond); + ASSERT_EQ(-ENOENT, cond.wait()); +} + +TEST_F(TestImageReplayer, BootstrapMirrorDisabling) +{ + // set remote image mirroring state to DISABLING + ASSERT_EQ(0, librbd::mirror_mode_set(m_remote_ioctx, RBD_MIRROR_MODE_IMAGE)); + librbd::ImageCtx *ictx; + open_remote_image(&ictx); + ASSERT_EQ(0, librbd::mirror_image_enable(ictx, false)); + cls::rbd::MirrorImage mirror_image; + ASSERT_EQ(0, librbd::cls_client::mirror_image_get(&m_remote_ioctx, ictx->id, + &mirror_image)); + mirror_image.state = cls::rbd::MirrorImageState::MIRROR_IMAGE_STATE_DISABLING; + ASSERT_EQ(0, librbd::cls_client::mirror_image_set(&m_remote_ioctx, ictx->id, + mirror_image)); + close_image(ictx); + + create_replayer<>(); + C_SaferCond cond; + m_replayer->start(&cond); + ASSERT_EQ(0, cond.wait()); + ASSERT_TRUE(m_replayer->is_stopped()); +} + +TEST_F(TestImageReplayer, BootstrapDemoted) +{ + // demote remote image + librbd::ImageCtx *ictx; + open_remote_image(&ictx); + ASSERT_EQ(0, librbd::mirror_image_demote(ictx)); + close_image(ictx); + + create_replayer<>(); + C_SaferCond cond; + m_replayer->start(&cond); + ASSERT_EQ(0, cond.wait()); + ASSERT_TRUE(m_replayer->is_stopped()); +} + TEST_F(TestImageReplayer, StartInterrupted) { create_replayer<>(); diff --git a/src/tools/rbd_mirror/CMakeLists.txt b/src/tools/rbd_mirror/CMakeLists.txt index fc7b504c51e2d..fff32f7168dd5 100644 --- a/src/tools/rbd_mirror/CMakeLists.txt +++ b/src/tools/rbd_mirror/CMakeLists.txt @@ -13,6 +13,7 @@ set(rbd_mirror_internal image_replayer/CloseImageRequest.cc image_replayer/CreateImageRequest.cc image_replayer/EventPreprocessor.cc + image_replayer/IsPrimaryRequest.cc image_replayer/OpenImageRequest.cc image_replayer/OpenLocalImageRequest.cc image_replayer/ReplayStatusFormatter.cc diff --git a/src/tools/rbd_mirror/image_replayer/BootstrapRequest.cc b/src/tools/rbd_mirror/image_replayer/BootstrapRequest.cc index 20ddf90da613b..54c3818e93aad 100644 --- a/src/tools/rbd_mirror/image_replayer/BootstrapRequest.cc +++ b/src/tools/rbd_mirror/image_replayer/BootstrapRequest.cc @@ -5,6 +5,7 @@ #include "BootstrapRequest.h" #include "CloseImageRequest.h" #include "CreateImageRequest.h" +#include "IsPrimaryRequest.h" #include "OpenImageRequest.h" #include "OpenLocalImageRequest.h" #include "common/debug.h" @@ -257,11 +258,6 @@ void BootstrapRequest::open_remote_image() { template void BootstrapRequest::handle_open_remote_image(int r) { - // deduce the class type for the journal to support unit tests - using Journal = typename std::decay< - typename std::remove_pointer().journal)> - ::type>::type; - dout(20) << ": r=" << r << dendl; if (r < 0) { @@ -271,18 +267,36 @@ void BootstrapRequest::handle_open_remote_image(int r) { return; } - // TODO: make async - bool tag_owner; - r = Journal::is_tag_owner(m_remote_image_ctx, &tag_owner); + is_primary(); +} + +template +void BootstrapRequest::is_primary() { + dout(20) << dendl; + + update_progress("OPEN_REMOTE_IMAGE"); + + Context *ctx = create_context_callback< + BootstrapRequest, &BootstrapRequest::handle_is_primary>( + this); + IsPrimaryRequest *request = IsPrimaryRequest::create(m_remote_image_ctx, + &m_primary, ctx); + request->send(); +} + +template +void BootstrapRequest::handle_is_primary(int r) { + dout(20) << ": r=" << r << dendl; + if (r < 0) { - derr << ": failed to query remote image primary status: " << cpp_strerror(r) + derr << ": error querying remote image primary status: " << cpp_strerror(r) << dendl; m_ret_val = r; close_remote_image(); return; } - if (!tag_owner) { + if (!m_primary) { dout(5) << ": remote image is not primary -- skipping image replay" << dendl; m_ret_val = -EREMOTEIO; diff --git a/src/tools/rbd_mirror/image_replayer/BootstrapRequest.h b/src/tools/rbd_mirror/image_replayer/BootstrapRequest.h index 51d394e3d1582..6b4ed215a0f26 100644 --- a/src/tools/rbd_mirror/image_replayer/BootstrapRequest.h +++ b/src/tools/rbd_mirror/image_replayer/BootstrapRequest.h @@ -101,6 +101,9 @@ class BootstrapRequest : public BaseRequest { * v * * OPEN_REMOTE_IMAGE * * * * * * * * * * * * * * * * * * | * + * v * + * IS_PRIMARY * * * * * * * * * * * * * * * * * * * * * + * | * * | (remote image primary) * * \----> OPEN_LOCAL_IMAGE * * * * * * * * * * * * * * | | . ^ * @@ -167,6 +170,7 @@ class BootstrapRequest : public BaseRequest { cls::journal::Client m_client; uint64_t m_remote_tag_class = 0; ImageCtxT *m_remote_image_ctx = nullptr; + bool m_primary = false; bool m_created_local_image = false; int m_ret_val = 0; @@ -187,6 +191,9 @@ class BootstrapRequest : public BaseRequest { void open_remote_image(); void handle_open_remote_image(int r); + void is_primary(); + void handle_is_primary(int r); + void update_client_state(); void handle_update_client_state(int r); diff --git a/src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.cc b/src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.cc new file mode 100644 index 0000000000000..8a45a95081bef --- /dev/null +++ b/src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.cc @@ -0,0 +1,122 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "IsPrimaryRequest.h" +#include "common/errno.h" +#include "common/WorkQueue.h" +#include "cls/rbd/cls_rbd_client.h" +#include "librbd/ImageCtx.h" +#include "librbd/Journal.h" +#include "librbd/Utils.h" +#include + +#define dout_context g_ceph_context +#define dout_subsys ceph_subsys_rbd_mirror +#undef dout_prefix +#define dout_prefix *_dout << "rbd::mirror::image_replayer::IsPrimaryRequest: " \ + << this << " " << __func__ << " " + +namespace rbd { +namespace mirror { +namespace image_replayer { + +using librbd::util::create_context_callback; +using librbd::util::create_rados_ack_callback; + +template +IsPrimaryRequest::IsPrimaryRequest(I *image_ctx, bool *primary, + Context *on_finish) + : m_image_ctx(image_ctx), m_primary(primary), m_on_finish(on_finish) { +} + +template +void IsPrimaryRequest::send() { + send_get_mirror_state(); +} + +template +void IsPrimaryRequest::send_get_mirror_state() { + dout(20) << dendl; + + librados::ObjectReadOperation op; + librbd::cls_client::mirror_image_get_start(&op, m_image_ctx->id); + + librados::AioCompletion *aio_comp = create_rados_ack_callback< + IsPrimaryRequest, &IsPrimaryRequest::handle_get_mirror_state>(this); + int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, aio_comp, &op, + &m_out_bl); + assert(r == 0); + aio_comp->release(); +} + +template +void IsPrimaryRequest::handle_get_mirror_state(int r) { + dout(20) << ": r=" << r << dendl; + + cls::rbd::MirrorImage mirror_image; + if (r == 0) { + bufferlist::iterator iter = m_out_bl.begin(); + r = librbd::cls_client::mirror_image_get_finish(&iter, &mirror_image); + if (r == 0) { + if (mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_ENABLED) { + send_is_tag_owner(); + return; + } else if (mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_DISABLING) { + dout(5) << ": image mirroring is being disabled" << dendl; + *m_primary = false; + } else { + derr << ": image mirroring is disabled" << dendl; + r = -EINVAL; + } + } else { + derr << ": failed to decode image mirror state: " << cpp_strerror(r) + << dendl; + } + } else { + derr << ": failed to retrieve image mirror state: " << cpp_strerror(r) + << dendl; + } + + finish(r); +} + +template +void IsPrimaryRequest::send_is_tag_owner() { + // deduce the class type for the journal to support unit tests + using Journal = typename std::decay< + typename std::remove_pointer().journal)> + ::type>::type; + + dout(20) << dendl; + + Context *ctx = create_context_callback< + IsPrimaryRequest, &IsPrimaryRequest::handle_is_tag_owner>(this); + + Journal::is_tag_owner(m_image_ctx, m_primary, ctx); +} + +template +void IsPrimaryRequest::handle_is_tag_owner(int r) { + dout(20) << ": r=" << r << dendl; + + if (r < 0) { + derr << ": failed to query remote image tag owner: " << cpp_strerror(r) + << dendl; + } + + finish(r); +} + +template +void IsPrimaryRequest::finish(int r) { + dout(20) << ": r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace image_replayer +} // namespace mirror +} // namespace rbd + +template class rbd::mirror::image_replayer::IsPrimaryRequest; diff --git a/src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.h b/src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.h new file mode 100644 index 0000000000000..ddb332cbfe69c --- /dev/null +++ b/src/tools/rbd_mirror/image_replayer/IsPrimaryRequest.h @@ -0,0 +1,67 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef RBD_MIRROR_IMAGE_REPLAYER_IS_PRIMARY_REQUEST_H +#define RBD_MIRROR_IMAGE_REPLAYER_IS_PRIMARY_REQUEST_H + +#include "include/buffer.h" + +class Context; +class ContextWQ; +namespace librbd { class ImageCtx; } + +namespace rbd { +namespace mirror { +namespace image_replayer { + +template +class IsPrimaryRequest { +public: + static IsPrimaryRequest* create(ImageCtxT *image_ctx, bool *primary, + Context *on_finish) { + return new IsPrimaryRequest(image_ctx, primary, on_finish); + } + + IsPrimaryRequest(ImageCtxT *image_ctx, bool *primary, Context *on_finish); + + void send(); + +private: + /** + * @verbatim + * + * + * | + * v + * GET_MIRROR_STATE * * * * * + * | * + * v * + * IS_TAG_OWNER * * * * * * * (error) + * | * + * v * + * < * * * * * * * * + * + * @endverbatim + */ + ImageCtxT *m_image_ctx; + bool *m_primary; + Context *m_on_finish; + + bufferlist m_out_bl; + + void send_get_mirror_state(); + void handle_get_mirror_state(int r); + + void send_is_tag_owner(); + void handle_is_tag_owner(int r); + + void finish(int r); +}; + +} // namespace image_replayer +} // namespace mirror +} // namespace rbd + +extern template class rbd::mirror::image_replayer::IsPrimaryRequest; + +#endif // RBD_MIRROR_IMAGE_REPLAYER_IS_PRIMARY_REQUEST_H diff --git a/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.cc b/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.cc index 5f7689fa96f15..a17e8b7df4072 100644 --- a/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.cc +++ b/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.cc @@ -2,8 +2,9 @@ // vim: ts=8 sw=2 smarttab #include "include/compat.h" -#include "OpenLocalImageRequest.h" #include "CloseImageRequest.h" +#include "IsPrimaryRequest.h" +#include "OpenLocalImageRequest.h" #include "common/errno.h" #include "common/WorkQueue.h" #include "librbd/ExclusiveLock.h" @@ -114,42 +115,54 @@ void OpenLocalImageRequest::handle_open_image(int r) { return; } - send_lock_image(); + send_is_primary(); } template -void OpenLocalImageRequest::send_lock_image() { - // deduce the class type for the journal to support unit tests - using Journal = typename std::decay< - typename std::remove_pointer().journal)> - ::type>::type; - +void OpenLocalImageRequest::send_is_primary() { dout(20) << dendl; - RWLock::RLocker owner_locker((*m_local_image_ctx)->owner_lock); - if ((*m_local_image_ctx)->exclusive_lock == nullptr) { - derr << ": image does not support exclusive lock" << dendl; - send_close_image(false, -EINVAL); - return; - } + Context *ctx = create_context_callback< + OpenLocalImageRequest, &OpenLocalImageRequest::handle_is_primary>( + this); + IsPrimaryRequest *request = IsPrimaryRequest::create(*m_local_image_ctx, + &m_primary, ctx); + request->send(); +} + +template +void OpenLocalImageRequest::handle_is_primary(int r) { + dout(20) << ": r=" << r << dendl; - // TODO: make an async version - bool tag_owner; - int r = Journal::is_tag_owner(*m_local_image_ctx, &tag_owner); if (r < 0) { - derr << ": failed to query journal: " << cpp_strerror(r) << dendl; + derr << ": error querying local image primary status: " << cpp_strerror(r) + << dendl; send_close_image(false, r); return; } // if the local image owns the tag -- don't steal the lock since // we aren't going to mirror peer data into this image anyway - if (tag_owner) { + if (m_primary) { dout(10) << ": local image is primary -- skipping image replay" << dendl; send_close_image(false, -EREMOTEIO); return; } + send_lock_image(); +} + +template +void OpenLocalImageRequest::send_lock_image() { + dout(20) << dendl; + + RWLock::RLocker owner_locker((*m_local_image_ctx)->owner_lock); + if ((*m_local_image_ctx)->exclusive_lock == nullptr) { + derr << ": image does not support exclusive lock" << dendl; + send_close_image(false, -EINVAL); + return; + } + // disallow any proxied maintenance operations before grabbing lock (*m_local_image_ctx)->exclusive_lock->block_requests(-EROFS); @@ -169,11 +182,16 @@ void OpenLocalImageRequest::handle_lock_image(int r) { << cpp_strerror(r) << dendl; send_close_image(false, r); return; - } else if ((*m_local_image_ctx)->exclusive_lock == nullptr || - !(*m_local_image_ctx)->exclusive_lock->is_lock_owner()) { - derr << ": image is not locked" << dendl; - send_close_image(false, -EBUSY); - return; + } + + { + RWLock::RLocker owner_locker((*m_local_image_ctx)->owner_lock); + if ((*m_local_image_ctx)->exclusive_lock == nullptr || + !(*m_local_image_ctx)->exclusive_lock->is_lock_owner()) { + derr << ": image is not locked" << dendl; + send_close_image(false, -EBUSY); + return; + } } finish(0); diff --git a/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.h b/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.h index e40b1c2a06226..2a1bbb2a8e2e8 100644 --- a/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.h +++ b/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.h @@ -48,6 +48,9 @@ class OpenLocalImageRequest { * v * OPEN_IMAGE * * * * * * * * * | * + * v * + * IS_PRIMARY * * * * * * * * + * | * * v (skip if primary) v * LOCK_IMAGE * * * > CLOSE_IMAGE * | | @@ -63,11 +66,15 @@ class OpenLocalImageRequest { ContextWQ *m_work_queue; Context *m_on_finish; + bool m_primary = false; int m_ret_val = 0; void send_open_image(); void handle_open_image(int r); + void send_is_primary(); + void handle_is_primary(int r); + void send_lock_image(); void handle_lock_image(int r);