#include "include/stringify.h"
#include "librbd/ExclusiveLock.h"
#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
#include "librbd/ImageWatcher.h"
#include "librbd/Journal.h"
#include "librbd/ObjectMap.h"
ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl;
if (*ret_val == 0) {
- return send_open_object_map();
+ return send_refresh();
} else if (*ret_val != -EBUSY) {
lderr(cct) << "failed to lock: " << cpp_strerror(*ret_val) << dendl;
return m_on_finish;
return nullptr;
}
+template <typename I>
+Context *AcquireRequest<I>::send_refresh() {
+ if (!m_image_ctx.state->is_refresh_required()) {
+ return send_open_object_map();
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << __func__ << dendl;
+
+ using klass = AcquireRequest<I>;
+ Context *ctx = create_context_callback<klass, &klass::handle_refresh>(this);
+ m_image_ctx.state->refresh(ctx);
+ return nullptr;
+}
+
+template <typename I>
+Context *AcquireRequest<I>::handle_refresh(int *ret_val) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl;
+
+ if (*ret_val < 0) {
+ lderr(cct) << "failed to refresh image: " << cpp_strerror(*ret_val)
+ << dendl;
+ m_error_result = *ret_val;
+ send_unlock();
+ return nullptr;
+ }
+
+ return send_open_object_map();
+}
+
template <typename I>
Context *AcquireRequest<I>::send_open_journal() {
// alert caller that we now own the exclusive lock
* | | . . . . . . . . . . . . . . . . . . . . . . |
* | | . . |
* | v v (EBUSY) . |
- * \--> LOCK_IMAGE * * * * * * * > GET_LOCKERS . . . . |
- * . | | |
- * . . . . | | |
- * . v v |
- * . OPEN_OBJECT_MAP (skip if GET_WATCHERS . . . |
- * . | disabled) | . |
- * . v v . |
- * . . > OPEN_JOURNAL (skip if BLACKLIST . (blacklist |
- * . | * disabled) | . disabled) |
- * . | * v . |
- * . | * * * * * * * * BREAK_LOCK < . . . |
- * . v * | |
- * . ALLOCATE_JOURNAL_TAG * \-----------------------------/
- * . | * *
- * . | * *
- * . | v v
- * . | CLOSE_JOURNAL
- * . | |
- * . | v
- * . | CLOSE_OBJECT_MAP
- * . | |
- * . | v
- * . | UNLOCK_IMAGE
- * . | |
- * . v |
- * . . > <finish> <----------/
+ * \--> LOCK_IMAGE * * * * * * * * > GET_LOCKERS . . . . |
+ * | | |
+ * v v |
+ * REFRESH (skip if not GET_WATCHERS |
+ * | needed) | |
+ * v v |
+ * OPEN_OBJECT_MAP (skip if BLACKLIST (skip if blacklist |
+ * | disabled) | disabled) |
+ * v v |
+ * OPEN_JOURNAL (skip if BREAK_LOCK |
+ * | * disabled) | |
+ * | * \-----------------------------/
+ * | * * * * * * * *
+ * v *
+ * ALLOCATE_JOURNAL_TAG *
+ * | * *
+ * | * *
+ * | v v
+ * | CLOSE_JOURNAL
+ * | |
+ * | v
+ * | CLOSE_OBJECT_MAP
+ * | |
+ * | v
+ * | UNLOCK_IMAGE
+ * | |
+ * v |
+ * <finish> <----------/
*
* @endverbatim
*/
void send_lock();
Context *handle_lock(int *ret_val);
+ Context *send_refresh();
+ Context *handle_refresh(int *ret_val);
+
Context *send_open_journal();
Context *handle_open_journal(int *ret_val);
#include "test/librbd/test_mock_fixture.h"
#include "test/librbd/test_support.h"
#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockImageState.h"
#include "test/librbd/mock/MockJournal.h"
#include "test/librbd/mock/MockJournalPolicy.h"
#include "test/librbd/mock/MockObjectMap.h"
.WillOnce(Return(r));
}
+ void expect_is_refresh_required(MockImageCtx &mock_image_ctx, bool required) {
+ EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+ .WillOnce(Return(required));
+ }
+
+ void expect_refresh(MockImageCtx &mock_image_ctx, int r) {
+ EXPECT_CALL(*mock_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
void expect_create_object_map(MockImageCtx &mock_image_ctx,
MockObjectMap *mock_object_map) {
EXPECT_CALL(mock_image_ctx, create_object_map(_))
InSequence seq;
expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, 0);
+ expect_is_refresh_required(mock_image_ctx, false);
MockObjectMap mock_object_map;
expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
ASSERT_EQ(0, ctx.wait());
}
+TEST_F(TestMockExclusiveLockAcquireRequest, SuccessRefresh) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
+ expect_lock(mock_image_ctx, 0);
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh(mock_image_ctx, 0);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false);
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx,
+ TEST_COOKIE,
+ &acquire_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(0, acquire_ctx.wait());
+ ASSERT_EQ(0, ctx.wait());
+}
+
TEST_F(TestMockExclusiveLockAcquireRequest, SuccessJournalDisabled) {
REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
InSequence seq;
expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, 0);
+ expect_is_refresh_required(mock_image_ctx, false);
MockObjectMap mock_object_map;
expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
InSequence seq;
expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, 0);
+ expect_is_refresh_required(mock_image_ctx, false);
expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false);
ASSERT_EQ(0, ctx.wait());
}
+TEST_F(TestMockExclusiveLockAcquireRequest, RefreshError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
+ expect_lock(mock_image_ctx, 0);
+ expect_is_refresh_required(mock_image_ctx, true);
+ expect_refresh(mock_image_ctx, -EINVAL);
+ expect_unlock(mock_image_ctx, 0);
+
+ C_SaferCond *acquire_ctx = new C_SaferCond();
+ C_SaferCond ctx;
+ MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx,
+ TEST_COOKIE,
+ acquire_ctx, &ctx);
+ req->send();
+ ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
TEST_F(TestMockExclusiveLockAcquireRequest, JournalError) {
REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
InSequence seq;
expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, 0);
+ expect_is_refresh_required(mock_image_ctx, false);
MockObjectMap *mock_object_map = new MockObjectMap();
expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
InSequence seq;
expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, 0);
+ expect_is_refresh_required(mock_image_ctx, false);
MockObjectMap *mock_object_map = new MockObjectMap();
expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
InSequence seq;
expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, 0);
+ expect_is_refresh_required(mock_image_ctx, false);
MockObjectMap *mock_object_map = new MockObjectMap();
expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);