on_released->complete(r);
}
+template <typename I>
+void ExclusiveLock<I>::handle_watch_registered() {
+ Mutex::Locker locker(m_lock);
+ if (m_state != STATE_WAITING_FOR_REGISTER) {
+ return;
+ }
+
+ ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl;
+ Action active_action = get_active_action();
+ assert(active_action == ACTION_TRY_LOCK ||
+ active_action == ACTION_REQUEST_LOCK);
+ execute_next_action();
+}
+
template <typename I>
void ExclusiveLock<I>::handle_lock_released() {
Mutex::Locker locker(m_lock);
case STATE_INITIALIZING:
case STATE_ACQUIRING:
case STATE_WAITING_FOR_PEER:
+ case STATE_WAITING_FOR_REGISTER:
case STATE_POST_ACQUIRING:
case STATE_PRE_RELEASING:
case STATE_RELEASING:
return;
}
- ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl;
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
m_state = STATE_ACQUIRING;
m_watch_handle = m_image_ctx.image_watcher->get_watch_handle();
+ if (m_watch_handle == 0) {
+ lderr(cct) << "image watcher not registered - delaying request" << dendl;
+ m_state = STATE_WAITING_FOR_REGISTER;
+ return;
+ }
using el = ExclusiveLock<I>;
AcquireRequest<I>* req = AcquireRequest<I>::create(
void request_lock(Context *on_locked);
void release_lock(Context *on_released);
+ void handle_watch_registered();
void handle_lock_released();
void assert_header_locked(librados::ObjectWriteOperation *op);
private:
/**
- * <start> WAITING_FOR_PEER -----------------\
- * | ^ |
- * | * (request_lock busy) |
- * | * * * * * * * * * * * * |
+ * <start> * * > WAITING_FOR_REGISTER --------\
+ * | * (watch not registered) |
+ * | * |
+ * | * * > WAITING_FOR_PEER ------------\
+ * | * (request_lock busy) |
+ * | * |
+ * | * * * * * * * * * * * * * * |
* | * |
* v (init) (try_lock/request_lock) * |
* UNINITIALIZED -------> UNLOCKED ------------------------> ACQUIRING <--/
STATE_ACQUIRING,
STATE_POST_ACQUIRING,
STATE_WAITING_FOR_PEER,
+ STATE_WAITING_FOR_REGISTER,
STATE_PRE_RELEASING,
STATE_RELEASING,
STATE_PRE_SHUTTING_DOWN,
m_watch_state = WATCH_STATE_REGISTERED;
}
+
+ // if the exclusive lock state machine was paused waiting for the
+ // watch to be re-registered, wake it up
+ RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
+ RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+ if (m_image_ctx.exclusive_lock != nullptr) {
+ m_image_ctx.exclusive_lock->handle_watch_registered();
+ }
+
handle_payload(HeaderUpdatePayload(), NULL);
}
ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
}
+TEST_F(TestMockExclusiveLock, RequestLockWatchNotRegistered) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockExclusiveLockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock exclusive_lock(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_block_writes(mock_image_ctx);
+ ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+
+ EXPECT_CALL(*mock_image_ctx.image_watcher, get_watch_handle())
+ .WillOnce(DoAll(Invoke([&mock_image_ctx, &exclusive_lock]() {
+ mock_image_ctx.image_ctx->op_work_queue->queue(
+ new FunctionContext([&exclusive_lock](int r) {
+ exclusive_lock.handle_watch_registered();
+ }));
+ }),
+ Return(0)));
+ MockAcquireRequest request_lock_acquire;
+ expect_acquire_lock(mock_image_ctx, request_lock_acquire, 0);
+ ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock));
+ ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
+
+ MockReleaseRequest shutdown_release;
+ expect_release_lock(mock_image_ctx, shutdown_release, 0, true);
+ ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
+ ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
+}
+
} // namespace librbd