}
};
+template <typename I>
+struct C_RefreshIfRequired : public Context {
+ I &image_ctx;
+ Context *on_finish;
+
+ C_RefreshIfRequired(I &image_ctx, Context *on_finish)
+ : image_ctx(image_ctx), on_finish(on_finish) {
+ }
+
+ virtual void finish(int r) override {
+ if (image_ctx.state->is_refresh_required()) {
+ image_ctx.state->refresh(on_finish);
+ return;
+ }
+
+ on_finish->complete(0);
+ }
+};
+
} // anonymous namespace
template <typename I>
op_event->ignore_error_codes = {-EEXIST};
// avoid lock cycles
- m_image_ctx.op_work_queue->queue(
- new ExecuteOp<I, journal::SnapCreateEvent>(m_image_ctx, event,
- on_op_complete), 0);
+ m_image_ctx.op_work_queue->queue(new C_RefreshIfRequired<I>(
+ m_image_ctx, new ExecuteOp<I, journal::SnapCreateEvent>(m_image_ctx, event,
+ on_op_complete)),
+ 0);
// do not process more events until the state machine is ready
// since it will affect IO
OpEvent *op_event;
Context *on_op_complete = create_op_context_callback(event.op_tid, on_safe,
&op_event);
- op_event->on_op_finish_event = new FunctionContext(
- [this, event, on_op_complete](int r) {
- m_image_ctx.operations->snap_remove(event.snap_name.c_str(),
- on_op_complete);
- });
+ op_event->on_op_finish_event = new C_RefreshIfRequired<I>(
+ m_image_ctx, new FunctionContext(
+ [this, event, on_op_complete](int r) {
+ m_image_ctx.operations->snap_remove(event.snap_name.c_str(),
+ on_op_complete);
+ }));
// ignore errors caused due to replay
op_event->ignore_error_codes = {-ENOENT};
OpEvent *op_event;
Context *on_op_complete = create_op_context_callback(event.op_tid, on_safe,
&op_event);
- op_event->on_op_finish_event = new FunctionContext(
- [this, event, on_op_complete](int r) {
- m_image_ctx.operations->snap_rename(event.snap_id,
- event.snap_name.c_str(),
- on_op_complete);
- });
+ op_event->on_op_finish_event = new C_RefreshIfRequired<I>(
+ m_image_ctx, new FunctionContext(
+ [this, event, on_op_complete](int r) {
+ m_image_ctx.operations->snap_rename(event.snap_id,
+ event.snap_name.c_str(),
+ on_op_complete);
+ }));
// ignore errors caused due to replay
op_event->ignore_error_codes = {-EEXIST};
OpEvent *op_event;
Context *on_op_complete = create_op_context_callback(event.op_tid, on_safe,
&op_event);
- op_event->on_op_finish_event = new FunctionContext(
- [this, event, on_op_complete](int r) {
- m_image_ctx.operations->snap_protect(event.snap_name.c_str(),
- on_op_complete);
- });
+ op_event->on_op_finish_event = new C_RefreshIfRequired<I>(
+ m_image_ctx, new FunctionContext(
+ [this, event, on_op_complete](int r) {
+ m_image_ctx.operations->snap_protect(event.snap_name.c_str(),
+ on_op_complete);
+ }));
// ignore errors caused due to replay
op_event->ignore_error_codes = {-EBUSY};
OpEvent *op_event;
Context *on_op_complete = create_op_context_callback(event.op_tid, on_safe,
&op_event);
- op_event->on_op_finish_event = new FunctionContext(
- [this, event, on_op_complete](int r) {
- m_image_ctx.operations->snap_unprotect(event.snap_name.c_str(),
- on_op_complete);
- });
+ op_event->on_op_finish_event = new C_RefreshIfRequired<I>(
+ m_image_ctx, new FunctionContext(
+ [this, event, on_op_complete](int r) {
+ m_image_ctx.operations->snap_unprotect(event.snap_name.c_str(),
+ on_op_complete);
+ }));
// ignore errors caused due to replay
op_event->ignore_error_codes = {-EINVAL};
OpEvent *op_event;
Context *on_op_complete = create_op_context_callback(event.op_tid, on_safe,
&op_event);
- op_event->on_op_finish_event = new FunctionContext(
- [this, event, on_op_complete](int r) {
- m_image_ctx.operations->snap_rollback(event.snap_name.c_str(),
- no_op_progress_callback,
- on_op_complete);
- });
+ op_event->on_op_finish_event = new C_RefreshIfRequired<I>(
+ m_image_ctx, new FunctionContext(
+ [this, event, on_op_complete](int r) {
+ m_image_ctx.operations->snap_rollback(event.snap_name.c_str(),
+ no_op_progress_callback,
+ on_op_complete);
+ }));
on_ready->complete(0);
}
OpEvent *op_event;
Context *on_op_complete = create_op_context_callback(event.op_tid, on_safe,
&op_event);
- op_event->on_op_finish_event = new FunctionContext(
- [this, event, on_op_complete](int r) {
- m_image_ctx.operations->rename(event.image_name.c_str(), on_op_complete);
- });
+ op_event->on_op_finish_event = new C_RefreshIfRequired<I>(
+ m_image_ctx, new FunctionContext(
+ [this, event, on_op_complete](int r) {
+ m_image_ctx.operations->rename(event.image_name.c_str(),
+ on_op_complete);
+ }));
// ignore errors caused due to replay
op_event->ignore_error_codes = {-EEXIST};
&op_event);
// avoid lock cycles
- m_image_ctx.op_work_queue->queue(
- new ExecuteOp<I, journal::ResizeEvent>(m_image_ctx, event,
- on_op_complete), 0);
+ m_image_ctx.op_work_queue->queue(new C_RefreshIfRequired<I>(
+ m_image_ctx, new ExecuteOp<I, journal::ResizeEvent>(m_image_ctx, event,
+ on_op_complete)), 0);
// do not process more events until the state machine is ready
// since it will affect IO
OpEvent *op_event;
Context *on_op_complete = create_op_context_callback(event.op_tid, on_safe,
&op_event);
- op_event->on_op_finish_event = new FunctionContext(
- [this, event, on_op_complete](int r) {
- m_image_ctx.operations->flatten(no_op_progress_callback, on_op_complete);
- });
+ op_event->on_op_finish_event = new C_RefreshIfRequired<I>(
+ m_image_ctx, new FunctionContext(
+ [this, event, on_op_complete](int r) {
+ m_image_ctx.operations->flatten(no_op_progress_callback,
+ on_op_complete);
+ }));
// ignore errors caused due to replay
op_event->ignore_error_codes = {-EINVAL};
test/librbd/mock/MockContextWQ.h \
test/librbd/mock/MockExclusiveLock.h \
test/librbd/mock/MockImageCtx.h \
+ test/librbd/mock/MockImageState.h \
test/librbd/mock/MockImageWatcher.h \
test/librbd/mock/MockJournal.h \
test/librbd/mock/MockObjectMap.h \
NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
+ void expect_refresh_image(MockReplayImageCtx &mock_image_ctx, bool required,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+ .WillOnce(Return(required));
+ if (required) {
+ EXPECT_CALL(*mock_image_ctx.state, refresh(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+ }
+ }
+
void when_process(MockJournalReplay &mock_journal_replay,
EventEntry &&event_entry, Context *on_ready,
Context *on_safe) {
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_create(mock_image_ctx, &on_finish, "snap", 123);
C_SaferCond on_start_ready;
InSequence seq;
Context *on_snap_create_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_create(mock_image_ctx, &on_snap_create_finish, "snap", 123);
Context *on_snap_remove_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_remove(mock_image_ctx, &on_snap_remove_finish, "snap");
C_SaferCond on_snap_remove_ready;
InSequence seq;
Context *on_snap_create_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_create(mock_image_ctx, &on_snap_create_finish, "snap", 123);
C_SaferCond on_snap_remove_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_remove(mock_image_ctx, &on_finish, "snap");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_create(mock_image_ctx, &on_finish, "snap", 123);
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish = nullptr;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_create(mock_image_ctx, &on_finish, "snap", 123);
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_remove(mock_image_ctx, &on_finish, "snap");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_remove(mock_image_ctx, &on_finish, "snap");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_rename(mock_image_ctx, &on_finish, 234, "snap");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_rename(mock_image_ctx, &on_finish, 234, "snap");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_protect(mock_image_ctx, &on_finish, "snap");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_protect(mock_image_ctx, &on_finish, "snap");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_unprotect(mock_image_ctx, &on_finish, "snap");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_unprotect(mock_image_ctx, &on_finish, "snap");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_snap_rollback(mock_image_ctx, &on_finish, "snap");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_rename(mock_image_ctx, &on_finish, "image");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_rename(mock_image_ctx, &on_finish, "image");
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_resize(mock_image_ctx, &on_finish, 234, 123);
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_flatten(mock_image_ctx, &on_finish);
C_SaferCond on_start_ready;
InSequence seq;
Context *on_finish;
+ expect_refresh_image(mock_image_ctx, false, 0);
expect_flatten(mock_image_ctx, &on_finish);
C_SaferCond on_start_ready;
ASSERT_EQ(0, on_ready.wait());
}
+TEST_F(TestMockJournalReplay, RefreshImageBeforeOpStart) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockReplayImageCtx mock_image_ctx(*ictx);
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish;
+ expect_refresh_image(mock_image_ctx, true, 0);
+ expect_resize(mock_image_ctx, &on_finish, 234, 123);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{ResizeEvent(123, 234)},
+ &on_start_ready, &on_start_safe);
+
+ C_SaferCond on_resume;
+ when_replay_op_ready(mock_journal_replay, 123, &on_resume);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(0, on_resume.wait());
+ on_finish->complete(0);
+
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
} // namespace journal
} // namespace librbd
#include "test/librbd/mock/MockAioImageRequestWQ.h"
#include "test/librbd/mock/MockContextWQ.h"
#include "test/librbd/mock/MockExclusiveLock.h"
+#include "test/librbd/mock/MockImageState.h"
#include "test/librbd/mock/MockImageWatcher.h"
#include "test/librbd/mock/MockJournal.h"
#include "test/librbd/mock/MockObjectMap.h"
aio_work_queue(new MockAioImageRequestWQ()),
op_work_queue(new MockContextWQ()),
parent(NULL), operations(new MockOperations()),
+ state(new MockImageState()),
image_watcher(NULL), object_map(NULL),
exclusive_lock(NULL), journal(NULL),
concurrent_management_ops(image_ctx.concurrent_management_ops),
image_ctx->md_ctx.aio_flush();
image_ctx->data_ctx.aio_flush();
image_ctx->op_work_queue->drain();
+ delete state;
delete operations;
delete image_watcher;
delete op_work_queue;
MockImageCtx *parent;
MockOperations *operations;
+ MockImageState *state;
MockImageWatcher *image_watcher;
MockObjectMap *object_map;
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IMAGE_STATE_H
+#define CEPH_TEST_LIBRBD_MOCK_IMAGE_STATE_H
+
+#include <gmock/gmock.h>
+
+class Context;
+
+namespace librbd {
+
+struct MockImageState {
+ MOCK_CONST_METHOD0(is_refresh_required, bool());
+ MOCK_METHOD1(refresh, void(Context*));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IMAGE_STATE_H