]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/test_mock_Journal.cc
b57c66e8907cdfc43f003f46248cc5c3a814a4d0
[ceph.git] / ceph / src / test / librbd / test_mock_Journal.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "test/librbd/test_mock_fixture.h"
5 #include "test/journal/mock/MockJournaler.h"
6 #include "test/librbd/test_support.h"
7 #include "test/librbd/mock/MockImageCtx.h"
8 #include "test/librbd/mock/MockJournalPolicy.h"
9 #include "test/librbd/mock/io/MockObjectDispatch.h"
10 #include "common/Cond.h"
11 #include "common/ceph_mutex.h"
12 #include "common/WorkQueue.h"
13 #include "cls/journal/cls_journal_types.h"
14 #include "journal/Journaler.h"
15 #include "librbd/Journal.h"
16 #include "librbd/Utils.h"
17 #include "librbd/io/AioCompletion.h"
18 #include "librbd/io/ObjectDispatchSpec.h"
19 #include "librbd/journal/Replay.h"
20 #include "librbd/journal/RemoveRequest.h"
21 #include "librbd/journal/CreateRequest.h"
22 #include "librbd/journal/ObjectDispatch.h"
23 #include "librbd/journal/OpenRequest.h"
24 #include "librbd/journal/Types.h"
25 #include "librbd/journal/TypeTraits.h"
26 #include "librbd/journal/PromoteRequest.h"
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 #include <functional>
30 #include <list>
31 #include <boost/scope_exit.hpp>
32
33 #define dout_context g_ceph_context
34 #define dout_subsys ceph_subsys_rbd
35
36 namespace librbd {
37
38 namespace {
39
40 struct MockJournalImageCtx : public MockImageCtx {
41 MockJournalImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
42 }
43 };
44
45 } // anonymous namespace
46
47 namespace journal {
48
49 template <>
50 struct TypeTraits<MockJournalImageCtx> {
51 typedef ::journal::MockJournalerProxy Journaler;
52 typedef ::journal::MockFutureProxy Future;
53 typedef ::journal::MockReplayEntryProxy ReplayEntry;
54 };
55
56 struct MockReplay {
57 static MockReplay *s_instance;
58 static MockReplay &get_instance() {
59 ceph_assert(s_instance != nullptr);
60 return *s_instance;
61 }
62
63 MockReplay() {
64 s_instance = this;
65 }
66
67 MOCK_METHOD2(shut_down, void(bool cancel_ops, Context *));
68 MOCK_METHOD2(decode, int(bufferlist::const_iterator*, EventEntry *));
69 MOCK_METHOD3(process, void(const EventEntry&, Context *, Context *));
70 MOCK_METHOD2(replay_op_ready, void(uint64_t, Context *));
71 };
72
73 template <>
74 class Replay<MockJournalImageCtx> {
75 public:
76 static Replay *create(MockJournalImageCtx &image_ctx) {
77 return new Replay();
78 }
79
80 void shut_down(bool cancel_ops, Context *on_finish) {
81 MockReplay::get_instance().shut_down(cancel_ops, on_finish);
82 }
83
84 int decode(bufferlist::const_iterator *it, EventEntry *event_entry) {
85 return MockReplay::get_instance().decode(it, event_entry);
86 }
87
88 void process(const EventEntry& event_entry, Context *on_ready,
89 Context *on_commit) {
90 MockReplay::get_instance().process(event_entry, on_ready, on_commit);
91 }
92
93 void replay_op_ready(uint64_t op_tid, Context *on_resume) {
94 MockReplay::get_instance().replay_op_ready(op_tid, on_resume);
95 }
96 };
97
98 MockReplay *MockReplay::s_instance = nullptr;
99
100 struct MockRemove {
101 static MockRemove *s_instance;
102 static MockRemove &get_instance() {
103 ceph_assert(s_instance != nullptr);
104 return *s_instance;
105 }
106
107 MockRemove() {
108 s_instance = this;
109 }
110
111 MOCK_METHOD0(send, void());
112 };
113
114 template <>
115 class RemoveRequest<MockJournalImageCtx> {
116 public:
117 static RemoveRequest *create(IoCtx &ioctx, const std::string &imageid,
118 const std::string &client_id,
119 ContextWQ *op_work_queue, Context *on_finish) {
120 return new RemoveRequest();
121 }
122
123 void send() {
124 MockRemove::get_instance().send();
125 }
126 };
127
128 MockRemove *MockRemove::s_instance = nullptr;
129
130 struct MockCreate {
131 static MockCreate *s_instance;
132 static MockCreate &get_instance() {
133 ceph_assert(s_instance != nullptr);
134 return *s_instance;
135 }
136
137 MockCreate() {
138 s_instance = this;
139 }
140
141 MOCK_METHOD0(send, void());
142 };
143
144 template<>
145 class CreateRequest<MockJournalImageCtx> {
146 public:
147 static CreateRequest *create(IoCtx &ioctx, const std::string &imageid,
148 uint8_t order, uint8_t splay_width,
149 const std::string &object_pool,
150 uint64_t tag_class, TagData &tag_data,
151 const std::string &client_id,
152 ContextWQ *op_work_queue, Context *on_finish) {
153 return new CreateRequest();
154 }
155
156 void send() {
157 MockCreate::get_instance().send();
158 }
159 };
160
161 MockCreate *MockCreate::s_instance = nullptr;
162
163 template<>
164 class OpenRequest<MockJournalImageCtx> {
165 public:
166 TagData *tag_data;
167 Context *on_finish;
168 static OpenRequest *s_instance;
169 static OpenRequest *create(MockJournalImageCtx *image_ctx,
170 ::journal::MockJournalerProxy *journaler,
171 ceph::mutex *lock, journal::ImageClientMeta *client_meta,
172 uint64_t *tag_tid, journal::TagData *tag_data,
173 Context *on_finish) {
174 ceph_assert(s_instance != nullptr);
175 s_instance->tag_data = tag_data;
176 s_instance->on_finish = on_finish;
177 return s_instance;
178 }
179
180 OpenRequest() {
181 s_instance = this;
182 }
183
184 MOCK_METHOD0(send, void());
185 };
186
187 OpenRequest<MockJournalImageCtx> *OpenRequest<MockJournalImageCtx>::s_instance = nullptr;
188
189
190 template <>
191 class PromoteRequest<MockJournalImageCtx> {
192 public:
193 static PromoteRequest s_instance;
194 static PromoteRequest *create(MockJournalImageCtx *image_ctx, bool force,
195 Context *on_finish) {
196 return &s_instance;
197 }
198
199 MOCK_METHOD0(send, void());
200 };
201
202 PromoteRequest<MockJournalImageCtx> PromoteRequest<MockJournalImageCtx>::s_instance;
203
204 template <>
205 struct ObjectDispatch<MockJournalImageCtx> : public io::MockObjectDispatch {
206 static ObjectDispatch* s_instance;
207
208 static ObjectDispatch* create(MockJournalImageCtx* image_ctx,
209 Journal<MockJournalImageCtx>* journal) {
210 ceph_assert(s_instance != nullptr);
211 return s_instance;
212 }
213
214 ObjectDispatch() {
215 s_instance = this;
216 }
217 };
218
219 ObjectDispatch<MockJournalImageCtx>* ObjectDispatch<MockJournalImageCtx>::s_instance = nullptr;
220
221 } // namespace journal
222 } // namespace librbd
223
224 // template definitions
225 #include "librbd/Journal.cc"
226
227 using ::testing::_;
228 using ::testing::DoAll;
229 using ::testing::InSequence;
230 using ::testing::Invoke;
231 using ::testing::InvokeWithoutArgs;
232 using ::testing::MatcherCast;
233 using ::testing::Return;
234 using ::testing::SaveArg;
235 using ::testing::SetArgPointee;
236 using ::testing::WithArg;
237 using namespace std::placeholders;
238
239 ACTION_P2(StartReplay, wq, ctx) {
240 wq->queue(ctx, 0);
241 }
242
243 namespace librbd {
244
245 class TestMockJournal : public TestMockFixture {
246 public:
247 typedef journal::MockReplay MockJournalReplay;
248 typedef Journal<MockJournalImageCtx> MockJournal;
249 typedef journal::OpenRequest<MockJournalImageCtx> MockJournalOpenRequest;
250 typedef journal::ObjectDispatch<MockJournalImageCtx> MockObjectDispatch;
251 typedef std::function<void(::journal::ReplayHandler*)> ReplayAction;
252 typedef std::list<Context *> Contexts;
253
254 TestMockJournal() = default;
255 ~TestMockJournal() override {
256 ceph_assert(m_commit_contexts.empty());
257 }
258
259 ceph::mutex m_lock = ceph::make_mutex("lock");
260 ceph::condition_variable m_cond;
261 Contexts m_commit_contexts;
262
263 struct C_ReplayAction : public Context {
264 ::journal::ReplayHandler **replay_handler;
265 ReplayAction replay_action;
266
267 C_ReplayAction(::journal::ReplayHandler **replay_handler,
268 const ReplayAction &replay_action)
269 : replay_handler(replay_handler), replay_action(replay_action) {
270 }
271 void finish(int r) override {
272 if (replay_action) {
273 replay_action(*replay_handler);
274 }
275 }
276 };
277
278 void expect_construct_journaler(::journal::MockJournaler &mock_journaler) {
279 EXPECT_CALL(mock_journaler, construct());
280 }
281
282 void expect_open_journaler(MockImageCtx &mock_image_ctx,
283 ::journal::MockJournaler &mock_journaler,
284 MockJournalOpenRequest &mock_open_request,
285 bool primary, int r) {
286 EXPECT_CALL(mock_journaler, add_listener(_))
287 .WillOnce(SaveArg<0>(&m_listener));
288 EXPECT_CALL(mock_open_request, send())
289 .WillOnce(DoAll(Invoke([&mock_open_request, primary]() {
290 if (!primary) {
291 mock_open_request.tag_data->mirror_uuid = "remote mirror uuid";
292 }
293 }),
294 FinishRequest(&mock_open_request, r,
295 &mock_image_ctx)));
296 }
297
298 void expect_shut_down_journaler(::journal::MockJournaler &mock_journaler) {
299 EXPECT_CALL(mock_journaler, remove_listener(_));
300 EXPECT_CALL(mock_journaler, shut_down(_))
301 .WillOnce(CompleteContext(0, static_cast<asio::ContextWQ*>(NULL)));
302 }
303
304 void expect_register_dispatch(MockImageCtx& mock_image_ctx,
305 MockObjectDispatch& mock_object_dispatch) {
306 EXPECT_CALL(*mock_image_ctx.io_object_dispatcher,
307 register_dispatch(&mock_object_dispatch));
308 }
309
310 void expect_shut_down_dispatch(MockImageCtx& mock_image_ctx) {
311 EXPECT_CALL(*mock_image_ctx.io_object_dispatcher,
312 shut_down_dispatch(io::OBJECT_DISPATCH_LAYER_JOURNAL, _))
313 .WillOnce(WithArg<1>(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)));
314 }
315
316 void expect_get_max_append_size(::journal::MockJournaler &mock_journaler,
317 uint32_t max_size) {
318 EXPECT_CALL(mock_journaler, get_max_append_size())
319 .WillOnce(Return(max_size));
320 }
321
322 void expect_get_journaler_cached_client(::journal::MockJournaler &mock_journaler,
323 const journal::ImageClientMeta &client_meta,
324 int r) {
325 journal::ClientData client_data;
326 client_data.client_meta = client_meta;
327
328 cls::journal::Client client;
329 encode(client_data, client.data);
330
331 EXPECT_CALL(mock_journaler, get_cached_client("", _))
332 .WillOnce(DoAll(SetArgPointee<1>(client),
333 Return(r)));
334 }
335
336 void expect_get_journaler_tags(MockImageCtx &mock_image_ctx,
337 ::journal::MockJournaler &mock_journaler,
338 uint64_t start_after_tag_tid,
339 ::journal::Journaler::Tags &&tags, int r) {
340 EXPECT_CALL(mock_journaler, get_tags(start_after_tag_tid, 0, _, _))
341 .WillOnce(DoAll(SetArgPointee<2>(tags),
342 WithArg<3>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))));
343 }
344
345 void expect_start_replay(MockJournalImageCtx &mock_image_ctx,
346 ::journal::MockJournaler &mock_journaler,
347 const ReplayAction &action) {
348 EXPECT_CALL(mock_journaler, start_replay(_))
349 .WillOnce(DoAll(SaveArg<0>(&m_replay_handler),
350 StartReplay(mock_image_ctx.image_ctx->op_work_queue,
351 new C_ReplayAction(&m_replay_handler,
352 action))));
353 }
354
355 void expect_stop_replay(::journal::MockJournaler &mock_journaler) {
356 EXPECT_CALL(mock_journaler, stop_replay(_))
357 .WillOnce(CompleteContext(0, static_cast<asio::ContextWQ*>(NULL)));
358 }
359
360 void expect_shut_down_replay(MockJournalImageCtx &mock_image_ctx,
361 MockJournalReplay &mock_journal_replay, int r,
362 bool cancel_ops = false) {
363 EXPECT_CALL(mock_journal_replay, shut_down(cancel_ops, _))
364 .WillOnce(WithArg<1>(Invoke([this, &mock_image_ctx, r](Context *on_flush) {
365 this->commit_replay(mock_image_ctx, on_flush, r);})));
366 }
367
368 void expect_get_data(::journal::MockReplayEntry &mock_replay_entry) {
369 EXPECT_CALL(mock_replay_entry, get_data())
370 .WillOnce(Return(bufferlist()));
371 }
372
373 void expect_try_pop_front(MockJournalImageCtx &mock_image_ctx,
374 ::journal::MockJournaler &mock_journaler,
375 bool entries_available,
376 ::journal::MockReplayEntry &mock_replay_entry,
377 const ReplayAction &action = {}) {
378 EXPECT_CALL(mock_journaler, try_pop_front(_))
379 .WillOnce(DoAll(SetArgPointee<0>(::journal::MockReplayEntryProxy()),
380 StartReplay(mock_image_ctx.image_ctx->op_work_queue,
381 new C_ReplayAction(&m_replay_handler,
382 action)),
383 Return(entries_available)));
384 if (entries_available) {
385 expect_get_data(mock_replay_entry);
386 }
387 }
388
389 void expect_replay_process(MockJournalReplay &mock_journal_replay) {
390 EXPECT_CALL(mock_journal_replay, decode(_, _))
391 .WillOnce(Return(0));
392 EXPECT_CALL(mock_journal_replay, process(_, _, _))
393 .WillOnce(DoAll(WithArg<1>(CompleteContext(0, static_cast<asio::ContextWQ*>(NULL))),
394 WithArg<2>(Invoke(this, &TestMockJournal::save_commit_context))));
395 }
396
397 void expect_start_append(::journal::MockJournaler &mock_journaler) {
398 EXPECT_CALL(mock_journaler, start_append(_));
399 }
400
401 void expect_set_append_batch_options(MockJournalImageCtx &mock_image_ctx,
402 ::journal::MockJournaler &mock_journaler,
403 bool user_flushed) {
404 if (mock_image_ctx.image_ctx->config.get_val<bool>("rbd_journal_object_writethrough_until_flush") ==
405 user_flushed) {
406 EXPECT_CALL(mock_journaler, set_append_batch_options(_, _, _));
407 }
408 }
409
410 void expect_stop_append(::journal::MockJournaler &mock_journaler, int r) {
411 EXPECT_CALL(mock_journaler, stop_append(_))
412 .WillOnce(CompleteContext(r, static_cast<asio::ContextWQ*>(NULL)));
413 }
414
415 void expect_committed(::journal::MockJournaler &mock_journaler,
416 size_t events) {
417 EXPECT_CALL(mock_journaler, committed(MatcherCast<const ::journal::MockReplayEntryProxy&>(_)))
418 .Times(events);
419 }
420
421 void expect_append_journaler(::journal::MockJournaler &mock_journaler) {
422 EXPECT_CALL(mock_journaler, append(_, _))
423 .WillOnce(Return(::journal::MockFutureProxy()));
424 }
425
426 void expect_wait_future(::journal::MockFuture &mock_future,
427 Context **on_safe) {
428 EXPECT_CALL(mock_future, wait(_))
429 .WillOnce(SaveArg<0>(on_safe));
430 }
431
432 void expect_future_committed(::journal::MockJournaler &mock_journaler) {
433 EXPECT_CALL(mock_journaler, committed(MatcherCast<const ::journal::MockFutureProxy&>(_)));
434 }
435
436 void expect_future_is_valid(::journal::MockFuture &mock_future) {
437 EXPECT_CALL(mock_future, is_valid()).WillOnce(Return(false));
438 }
439
440 void expect_flush_commit_position(::journal::MockJournaler &mock_journaler) {
441 EXPECT_CALL(mock_journaler, flush_commit_position(_))
442 .WillOnce(CompleteContext(0, static_cast<asio::ContextWQ*>(NULL)));
443 }
444
445 int when_open(MockJournal *mock_journal) {
446 C_SaferCond ctx;
447 mock_journal->open(&ctx);
448 return ctx.wait();
449 }
450
451 int when_close(MockJournal *mock_journal) {
452 C_SaferCond ctx;
453 mock_journal->close(&ctx);
454 return ctx.wait();
455 }
456
457 uint64_t when_append_write_event(MockJournalImageCtx &mock_image_ctx,
458 MockJournal *mock_journal, uint64_t length) {
459 bufferlist bl;
460 bl.append_zero(length);
461
462 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
463 return mock_journal->append_write_event(0, length, bl, false);
464 }
465
466 uint64_t when_append_io_event(MockJournalImageCtx &mock_image_ctx,
467 MockJournal *mock_journal,
468 int filter_ret_val) {
469 std::shared_lock owner_locker{mock_image_ctx.owner_lock};
470 return mock_journal->append_io_event(
471 journal::EventEntry{journal::AioFlushEvent{}}, 0, 0, false,
472 filter_ret_val);
473 }
474
475 void save_commit_context(Context *ctx) {
476 std::lock_guard locker{m_lock};
477 m_commit_contexts.push_back(ctx);
478 m_cond.notify_all();
479 }
480
481 void wake_up() {
482 std::lock_guard locker{m_lock};
483 m_cond.notify_all();
484 }
485
486 void commit_replay(MockJournalImageCtx &mock_image_ctx, Context *on_flush,
487 int r) {
488 Contexts commit_contexts;
489 std::swap(commit_contexts, m_commit_contexts);
490
491 derr << "SHUT DOWN REPLAY START" << dendl;
492 for (auto ctx : commit_contexts) {
493 mock_image_ctx.image_ctx->op_work_queue->queue(ctx, r);
494 }
495
496 on_flush = new LambdaContext([on_flush](int r) {
497 derr << "FLUSH START" << dendl;
498 on_flush->complete(r);
499 derr << "FLUSH FINISH" << dendl;
500 });
501 mock_image_ctx.image_ctx->op_work_queue->queue(on_flush, 0);
502 derr << "SHUT DOWN REPLAY FINISH" << dendl;
503 }
504
505 void open_journal(MockJournalImageCtx &mock_image_ctx,
506 MockJournal *mock_journal,
507 MockObjectDispatch& mock_object_dispatch,
508 ::journal::MockJournaler &mock_journaler,
509 MockJournalOpenRequest &mock_open_request,
510 bool primary = true) {
511 expect_op_work_queue(mock_image_ctx);
512
513 InSequence seq;
514 expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
515 expect_construct_journaler(mock_journaler);
516 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
517 primary, 0);
518 expect_get_max_append_size(mock_journaler, 1 << 16);
519 expect_start_replay(
520 mock_image_ctx, mock_journaler,
521 std::bind(&invoke_replay_complete, _1, 0));
522
523 MockJournalReplay mock_journal_replay;
524 expect_stop_replay(mock_journaler);
525 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
526 expect_committed(mock_journaler, 0);
527 expect_flush_commit_position(mock_journaler);
528 expect_start_append(mock_journaler);
529 expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
530 ASSERT_EQ(0, when_open(mock_journal));
531 }
532
533 void close_journal(MockJournalImageCtx& mock_image_ctx,
534 MockJournal *mock_journal,
535 ::journal::MockJournaler &mock_journaler) {
536 expect_stop_append(mock_journaler, 0);
537 expect_shut_down_dispatch(mock_image_ctx);
538 ASSERT_EQ(0, when_close(mock_journal));
539 }
540
541 static void invoke_replay_ready(::journal::ReplayHandler *handler) {
542 handler->handle_entries_available();
543 }
544
545 static void invoke_replay_complete(::journal::ReplayHandler *handler, int r) {
546 handler->handle_complete(r);
547 }
548
549 ::journal::ReplayHandler *m_replay_handler = nullptr;
550 ::journal::JournalMetadataListener *m_listener = nullptr;
551 };
552
553 TEST_F(TestMockJournal, StateTransitions) {
554 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
555
556 librbd::ImageCtx *ictx;
557 ASSERT_EQ(0, open_image(m_image_name, &ictx));
558
559 MockJournalImageCtx mock_image_ctx(*ictx);
560 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
561 expect_op_work_queue(mock_image_ctx);
562
563 BOOST_SCOPE_EXIT(&mock_journal) {
564 mock_journal->put();
565 } BOOST_SCOPE_EXIT_END
566
567 InSequence seq;
568
569 MockObjectDispatch mock_object_dispatch;
570 expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
571
572 ::journal::MockJournaler mock_journaler;
573 MockJournalOpenRequest mock_open_request;
574 expect_construct_journaler(mock_journaler);
575 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
576 true, 0);
577 expect_get_max_append_size(mock_journaler, 1 << 16);
578 expect_start_replay(
579 mock_image_ctx, mock_journaler,
580 std::bind(&invoke_replay_ready, _1));
581
582 ::journal::MockReplayEntry mock_replay_entry;
583 MockJournalReplay mock_journal_replay;
584 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
585 expect_replay_process(mock_journal_replay);
586 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
587 expect_replay_process(mock_journal_replay);
588 expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
589 std::bind(&invoke_replay_ready, _1));
590 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
591 expect_replay_process(mock_journal_replay);
592 expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
593 std::bind(&invoke_replay_complete, _1, 0));
594
595 expect_stop_replay(mock_journaler);
596 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
597 expect_committed(mock_journaler, 3);
598 expect_flush_commit_position(mock_journaler);
599
600 expect_start_append(mock_journaler);
601 expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
602
603 ASSERT_EQ(0, when_open(mock_journal));
604
605 expect_stop_append(mock_journaler, 0);
606 expect_shut_down_journaler(mock_journaler);
607 expect_shut_down_dispatch(mock_image_ctx);
608 ASSERT_EQ(0, when_close(mock_journal));
609 }
610
611 TEST_F(TestMockJournal, InitError) {
612 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
613
614 librbd::ImageCtx *ictx;
615 ASSERT_EQ(0, open_image(m_image_name, &ictx));
616
617 MockJournalImageCtx mock_image_ctx(*ictx);
618 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
619 expect_op_work_queue(mock_image_ctx);
620
621 BOOST_SCOPE_EXIT(&mock_journal) {
622 mock_journal->put();
623 } BOOST_SCOPE_EXIT_END
624
625 InSequence seq;
626
627 MockObjectDispatch mock_object_dispatch;
628 expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
629
630 ::journal::MockJournaler mock_journaler;
631 MockJournalOpenRequest mock_open_request;
632 expect_construct_journaler(mock_journaler);
633 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
634 true, -EINVAL);
635 expect_shut_down_journaler(mock_journaler);
636 ASSERT_EQ(-EINVAL, when_open(mock_journal));
637 }
638
639 TEST_F(TestMockJournal, ReplayCompleteError) {
640 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
641
642 librbd::ImageCtx *ictx;
643 ASSERT_EQ(0, open_image(m_image_name, &ictx));
644
645 MockJournalImageCtx mock_image_ctx(*ictx);
646 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
647 expect_op_work_queue(mock_image_ctx);
648
649 BOOST_SCOPE_EXIT(&mock_journal) {
650 mock_journal->put();
651 } BOOST_SCOPE_EXIT_END
652
653 InSequence seq;
654
655 MockObjectDispatch mock_object_dispatch;
656 expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
657
658 ::journal::MockJournaler mock_journaler;
659 MockJournalOpenRequest mock_open_request;
660 expect_construct_journaler(mock_journaler);
661 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
662 true, 0);
663 expect_get_max_append_size(mock_journaler, 1 << 16);
664 expect_start_replay(
665 mock_image_ctx, mock_journaler,
666 std::bind(&invoke_replay_complete, _1, -EINVAL));
667
668 MockJournalReplay mock_journal_replay;
669 expect_stop_replay(mock_journaler);
670 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0, true);
671 expect_flush_commit_position(mock_journaler);
672 expect_shut_down_journaler(mock_journaler);
673
674 // replay failure should result in replay-restart
675 expect_construct_journaler(mock_journaler);
676 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
677 true, 0);
678 expect_get_max_append_size(mock_journaler, 1 << 16);
679 expect_start_replay(
680 mock_image_ctx, mock_journaler,
681 std::bind(&invoke_replay_complete, _1, 0));
682
683 expect_stop_replay(mock_journaler);
684 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
685 expect_flush_commit_position(mock_journaler);
686 expect_start_append(mock_journaler);
687 expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
688 ASSERT_EQ(0, when_open(mock_journal));
689
690 expect_stop_append(mock_journaler, 0);
691 expect_shut_down_journaler(mock_journaler);
692 expect_shut_down_dispatch(mock_image_ctx);
693 ASSERT_EQ(0, when_close(mock_journal));
694 }
695
696 TEST_F(TestMockJournal, FlushReplayError) {
697 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
698
699 librbd::ImageCtx *ictx;
700 ASSERT_EQ(0, open_image(m_image_name, &ictx));
701
702 MockJournalImageCtx mock_image_ctx(*ictx);
703 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
704 expect_op_work_queue(mock_image_ctx);
705
706 BOOST_SCOPE_EXIT(&mock_journal) {
707 mock_journal->put();
708 } BOOST_SCOPE_EXIT_END
709
710 InSequence seq;
711
712 MockObjectDispatch mock_object_dispatch;
713 expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
714
715 ::journal::MockJournaler mock_journaler;
716 MockJournalOpenRequest mock_open_request;
717 expect_construct_journaler(mock_journaler);
718 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
719 true, 0);
720 expect_get_max_append_size(mock_journaler, 1 << 16);
721 expect_start_replay(
722 mock_image_ctx, mock_journaler,
723 std::bind(&invoke_replay_ready, _1));
724
725 ::journal::MockReplayEntry mock_replay_entry;
726 MockJournalReplay mock_journal_replay;
727 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
728 expect_replay_process(mock_journal_replay);
729 expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
730 std::bind(&invoke_replay_complete, _1, 0));
731 expect_stop_replay(mock_journaler);
732 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, -EINVAL);
733 expect_flush_commit_position(mock_journaler);
734 expect_shut_down_journaler(mock_journaler);
735
736 // replay flush failure should result in replay-restart
737 expect_construct_journaler(mock_journaler);
738 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
739 true, 0);
740 expect_get_max_append_size(mock_journaler, 1 << 16);
741 expect_start_replay(
742 mock_image_ctx, mock_journaler,
743 std::bind(&invoke_replay_complete, _1, 0));
744
745 expect_stop_replay(mock_journaler);
746 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
747 expect_flush_commit_position(mock_journaler);
748 expect_start_append(mock_journaler);
749 expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
750 ASSERT_EQ(0, when_open(mock_journal));
751
752 expect_stop_append(mock_journaler, 0);
753 expect_shut_down_journaler(mock_journaler);
754 expect_shut_down_dispatch(mock_image_ctx);
755 ASSERT_EQ(0, when_close(mock_journal));
756 }
757
758 TEST_F(TestMockJournal, CorruptEntry) {
759 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
760
761 librbd::ImageCtx *ictx;
762 ASSERT_EQ(0, open_image(m_image_name, &ictx));
763
764 MockJournalImageCtx mock_image_ctx(*ictx);
765 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
766 expect_op_work_queue(mock_image_ctx);
767
768 BOOST_SCOPE_EXIT(&mock_journal) {
769 mock_journal->put();
770 } BOOST_SCOPE_EXIT_END
771
772 InSequence seq;
773
774 MockObjectDispatch mock_object_dispatch;
775 expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
776
777 ::journal::MockJournaler mock_journaler;
778 MockJournalOpenRequest mock_open_request;
779 expect_construct_journaler(mock_journaler);
780 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
781 true, 0);
782 expect_get_max_append_size(mock_journaler, 1 << 16);
783 expect_start_replay(
784 mock_image_ctx, mock_journaler,
785 std::bind(&invoke_replay_ready, _1));
786
787 ::journal::MockReplayEntry mock_replay_entry;
788 MockJournalReplay mock_journal_replay;
789 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
790 EXPECT_CALL(mock_journal_replay, decode(_, _)).WillOnce(Return(-EBADMSG));
791 expect_stop_replay(mock_journaler);
792 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0, true);
793 expect_flush_commit_position(mock_journaler);
794 expect_shut_down_journaler(mock_journaler);
795
796 // replay failure should result in replay-restart
797 expect_construct_journaler(mock_journaler);
798 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
799 true, 0);
800 expect_get_max_append_size(mock_journaler, 1 << 16);
801 expect_start_replay(
802 mock_image_ctx, mock_journaler,
803 std::bind(&invoke_replay_complete, _1, 0));
804 expect_stop_replay(mock_journaler);
805 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
806 expect_flush_commit_position(mock_journaler);
807 expect_start_append(mock_journaler);
808 expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
809 ASSERT_EQ(0, when_open(mock_journal));
810
811 expect_stop_append(mock_journaler, -EINVAL);
812 expect_shut_down_journaler(mock_journaler);
813 expect_shut_down_dispatch(mock_image_ctx);
814 ASSERT_EQ(-EINVAL, when_close(mock_journal));
815 }
816
817 TEST_F(TestMockJournal, StopError) {
818 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
819
820 librbd::ImageCtx *ictx;
821 ASSERT_EQ(0, open_image(m_image_name, &ictx));
822
823 MockJournalImageCtx mock_image_ctx(*ictx);
824 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
825 expect_op_work_queue(mock_image_ctx);
826
827 BOOST_SCOPE_EXIT(&mock_journal) {
828 mock_journal->put();
829 } BOOST_SCOPE_EXIT_END
830
831 InSequence seq;
832
833 MockObjectDispatch mock_object_dispatch;
834 expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
835
836 ::journal::MockJournaler mock_journaler;
837 MockJournalOpenRequest mock_open_request;
838 expect_construct_journaler(mock_journaler);
839 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
840 true, 0);
841 expect_get_max_append_size(mock_journaler, 1 << 16);
842 expect_start_replay(
843 mock_image_ctx, mock_journaler,
844 std::bind(&invoke_replay_complete, _1, 0));
845
846 MockJournalReplay mock_journal_replay;
847 expect_stop_replay(mock_journaler);
848 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
849 expect_flush_commit_position(mock_journaler);
850 expect_start_append(mock_journaler);
851 expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
852 ASSERT_EQ(0, when_open(mock_journal));
853
854 expect_stop_append(mock_journaler, -EINVAL);
855 expect_shut_down_journaler(mock_journaler);
856 expect_shut_down_dispatch(mock_image_ctx);
857 ASSERT_EQ(-EINVAL, when_close(mock_journal));
858 }
859
860 TEST_F(TestMockJournal, ReplayOnDiskPreFlushError) {
861 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
862
863 librbd::ImageCtx *ictx;
864 ASSERT_EQ(0, open_image(m_image_name, &ictx));
865
866 MockJournalImageCtx mock_image_ctx(*ictx);
867 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
868 expect_op_work_queue(mock_image_ctx);
869
870 BOOST_SCOPE_EXIT(&mock_journal) {
871 mock_journal->put();
872 } BOOST_SCOPE_EXIT_END
873
874 InSequence seq;
875 MockObjectDispatch mock_object_dispatch;
876 expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
877
878 ::journal::MockJournaler mock_journaler;
879 MockJournalOpenRequest mock_open_request;
880 expect_construct_journaler(mock_journaler);
881 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
882 true, 0);
883 expect_get_max_append_size(mock_journaler, 1 << 16);
884
885 expect_start_replay(
886 mock_image_ctx, mock_journaler,
887 std::bind(&invoke_replay_ready, _1));
888
889 ::journal::MockReplayEntry mock_replay_entry;
890 MockJournalReplay mock_journal_replay;
891 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
892
893 EXPECT_CALL(mock_journal_replay, decode(_, _))
894 .WillOnce(Return(0));
895 Context *on_ready;
896 EXPECT_CALL(mock_journal_replay, process(_, _, _))
897 .WillOnce(DoAll(SaveArg<1>(&on_ready),
898 WithArg<2>(Invoke(this, &TestMockJournal::save_commit_context))));
899
900 expect_try_pop_front(mock_image_ctx, mock_journaler, false,
901 mock_replay_entry);
902 expect_stop_replay(mock_journaler);
903 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0, true);
904 expect_flush_commit_position(mock_journaler);
905 expect_shut_down_journaler(mock_journaler);
906
907 // replay write-to-disk failure should result in replay-restart
908 expect_construct_journaler(mock_journaler);
909 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
910 true, 0);
911 expect_get_max_append_size(mock_journaler, 1 << 16);
912 expect_start_replay(
913 mock_image_ctx, mock_journaler, {
914 std::bind(&invoke_replay_complete, _1, 0)
915 });
916
917 expect_stop_replay(mock_journaler);
918 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
919 expect_flush_commit_position(mock_journaler);
920 expect_start_append(mock_journaler);
921 expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
922
923 C_SaferCond ctx;
924 mock_journal->open(&ctx);
925
926 // wait for the process callback
927 {
928 std::unique_lock locker{m_lock};
929 m_cond.wait(locker, [this] { return !m_commit_contexts.empty(); });
930 }
931 on_ready->complete(0);
932
933 // inject RADOS error in the middle of replay
934 Context *on_safe = m_commit_contexts.front();
935 m_commit_contexts.clear();
936 on_safe->complete(-EINVAL);
937
938 // flag the replay as complete
939 m_replay_handler->handle_complete(0);
940
941 ASSERT_EQ(0, ctx.wait());
942
943 expect_stop_append(mock_journaler, 0);
944 expect_shut_down_journaler(mock_journaler);
945 expect_shut_down_dispatch(mock_image_ctx);
946 ASSERT_EQ(0, when_close(mock_journal));
947 }
948
949 TEST_F(TestMockJournal, ReplayOnDiskPostFlushError) {
950 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
951
952 librbd::ImageCtx *ictx;
953 ASSERT_EQ(0, open_image(m_image_name, &ictx));
954
955 MockJournalImageCtx mock_image_ctx(*ictx);
956 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
957 expect_op_work_queue(mock_image_ctx);
958
959 BOOST_SCOPE_EXIT(&mock_journal) {
960 mock_journal->put();
961 } BOOST_SCOPE_EXIT_END
962
963 InSequence seq;
964
965 MockObjectDispatch mock_object_dispatch;
966 expect_register_dispatch(mock_image_ctx, mock_object_dispatch);
967
968 ::journal::MockJournaler mock_journaler;
969 MockJournalOpenRequest mock_open_request;
970 expect_construct_journaler(mock_journaler);
971 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
972 true, 0);
973 expect_get_max_append_size(mock_journaler, 1 << 16);
974 expect_start_replay(
975 mock_image_ctx, mock_journaler,
976 std::bind(&invoke_replay_ready, _1));
977
978 ::journal::MockReplayEntry mock_replay_entry;
979 MockJournalReplay mock_journal_replay;
980 expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
981 expect_replay_process(mock_journal_replay);
982 expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
983 std::bind(&invoke_replay_complete, _1, 0));
984 expect_stop_replay(mock_journaler);
985
986 Context *on_flush = nullptr;
987 EXPECT_CALL(mock_journal_replay, shut_down(false, _))
988 .WillOnce(DoAll(SaveArg<1>(&on_flush),
989 InvokeWithoutArgs(this, &TestMockJournal::wake_up)));
990 expect_flush_commit_position(mock_journaler);
991
992 // replay write-to-disk failure should result in replay-restart
993 expect_shut_down_journaler(mock_journaler);
994 expect_construct_journaler(mock_journaler);
995 expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request,
996 true, 0);
997 expect_get_max_append_size(mock_journaler, 1 << 16);
998 expect_start_replay(
999 mock_image_ctx, mock_journaler,
1000 std::bind(&invoke_replay_complete, _1, 0));
1001
1002 expect_stop_replay(mock_journaler);
1003 expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
1004 expect_flush_commit_position(mock_journaler);
1005 expect_start_append(mock_journaler);
1006 expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
1007
1008 C_SaferCond ctx;
1009 mock_journal->open(&ctx);
1010
1011 // proceed with the flush
1012 {
1013 // wait for on_flush callback
1014 std::unique_lock locker{m_lock};
1015 m_cond.wait(locker, [&] {return on_flush != nullptr;});
1016 }
1017
1018 {
1019 // wait for the on_safe process callback
1020 std::unique_lock locker{m_lock};
1021 m_cond.wait(locker, [this] {return !m_commit_contexts.empty();});
1022 }
1023 m_commit_contexts.front()->complete(-EINVAL);
1024 m_commit_contexts.clear();
1025 on_flush->complete(0);
1026
1027 ASSERT_EQ(0, ctx.wait());
1028
1029 expect_stop_append(mock_journaler, 0);
1030 expect_shut_down_journaler(mock_journaler);
1031 expect_shut_down_dispatch(mock_image_ctx);
1032 ASSERT_EQ(0, when_close(mock_journal));
1033 }
1034
1035 TEST_F(TestMockJournal, EventAndIOCommitOrder) {
1036 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1037
1038 librbd::ImageCtx *ictx;
1039 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1040
1041 MockJournalImageCtx mock_image_ctx(*ictx);
1042 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1043 MockObjectDispatch mock_object_dispatch;
1044 ::journal::MockJournaler mock_journaler;
1045 MockJournalOpenRequest mock_open_request;
1046 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1047 mock_journaler, mock_open_request);
1048 BOOST_SCOPE_EXIT_ALL(&) {
1049 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1050 mock_journal->put();
1051 };
1052
1053 ::journal::MockFuture mock_future;
1054 Context *on_journal_safe1;
1055 expect_append_journaler(mock_journaler);
1056 expect_wait_future(mock_future, &on_journal_safe1);
1057 ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, 0));
1058 mock_journal->get_work_queue()->drain();
1059
1060 Context *on_journal_safe2;
1061 expect_append_journaler(mock_journaler);
1062 expect_wait_future(mock_future, &on_journal_safe2);
1063 ASSERT_EQ(2U, when_append_io_event(mock_image_ctx, mock_journal, 0));
1064 mock_journal->get_work_queue()->drain();
1065
1066 // commit journal event followed by IO event (standard)
1067 on_journal_safe1->complete(0);
1068 ictx->op_work_queue->drain();
1069 expect_future_committed(mock_journaler);
1070 mock_journal->commit_io_event(1U, 0);
1071
1072 // commit IO event followed by journal event (cache overwrite)
1073 mock_journal->commit_io_event(2U, 0);
1074 expect_future_committed(mock_journaler);
1075
1076 C_SaferCond event_ctx;
1077 mock_journal->wait_event(2U, &event_ctx);
1078 on_journal_safe2->complete(0);
1079 ictx->op_work_queue->drain();
1080 ASSERT_EQ(0, event_ctx.wait());
1081
1082 expect_shut_down_journaler(mock_journaler);
1083 }
1084
1085 TEST_F(TestMockJournal, AppendWriteEvent) {
1086 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1087
1088 librbd::ImageCtx *ictx;
1089 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1090
1091 MockJournalImageCtx mock_image_ctx(*ictx);
1092 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1093 MockObjectDispatch mock_object_dispatch;
1094 ::journal::MockJournaler mock_journaler;
1095 MockJournalOpenRequest mock_open_request;
1096 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1097 mock_journaler, mock_open_request);
1098 BOOST_SCOPE_EXIT_ALL(&) {
1099 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1100 mock_journal->put();
1101 };
1102
1103 InSequence seq;
1104
1105 ::journal::MockFuture mock_future;
1106 Context *on_journal_safe = nullptr;
1107 expect_append_journaler(mock_journaler);
1108 expect_append_journaler(mock_journaler);
1109 expect_append_journaler(mock_journaler);
1110 expect_wait_future(mock_future, &on_journal_safe);
1111 ASSERT_EQ(1U, when_append_write_event(mock_image_ctx, mock_journal, 1 << 17));
1112 mock_journal->get_work_queue()->drain();
1113
1114 on_journal_safe->complete(0);
1115 C_SaferCond event_ctx;
1116 mock_journal->wait_event(1U, &event_ctx);
1117 ASSERT_EQ(0, event_ctx.wait());
1118
1119 expect_future_committed(mock_journaler);
1120 expect_future_committed(mock_journaler);
1121 expect_future_committed(mock_journaler);
1122 mock_journal->commit_io_event(1U, 0);
1123 ictx->op_work_queue->drain();
1124
1125 expect_shut_down_journaler(mock_journaler);
1126 }
1127
1128 TEST_F(TestMockJournal, EventCommitError) {
1129 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1130
1131 librbd::ImageCtx *ictx;
1132 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1133
1134 MockJournalImageCtx mock_image_ctx(*ictx);
1135 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1136 MockObjectDispatch mock_object_dispatch;
1137 ::journal::MockJournaler mock_journaler;
1138 MockJournalOpenRequest mock_open_request;
1139 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1140 mock_journaler, mock_open_request);
1141 BOOST_SCOPE_EXIT_ALL(&) {
1142 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1143 mock_journal->put();
1144 };
1145
1146 ::journal::MockFuture mock_future;
1147 Context *on_journal_safe;
1148 expect_append_journaler(mock_journaler);
1149 expect_wait_future(mock_future, &on_journal_safe);
1150 ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, 0));
1151 mock_journal->get_work_queue()->drain();
1152
1153 // commit the event in the journal w/o waiting writeback
1154 expect_future_committed(mock_journaler);
1155 C_SaferCond object_request_ctx;
1156 mock_journal->wait_event(1U, &object_request_ctx);
1157 on_journal_safe->complete(-EINVAL);
1158 ASSERT_EQ(-EINVAL, object_request_ctx.wait());
1159
1160 // cache should receive the error after attempting writeback
1161 expect_future_is_valid(mock_future);
1162 C_SaferCond flush_ctx;
1163 mock_journal->flush_event(1U, &flush_ctx);
1164 ASSERT_EQ(-EINVAL, flush_ctx.wait());
1165
1166 expect_shut_down_journaler(mock_journaler);
1167 }
1168
1169 TEST_F(TestMockJournal, EventCommitErrorWithPendingWriteback) {
1170 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1171
1172 librbd::ImageCtx *ictx;
1173 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1174
1175 MockJournalImageCtx mock_image_ctx(*ictx);
1176 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1177 MockObjectDispatch mock_object_dispatch;
1178 ::journal::MockJournaler mock_journaler;
1179 MockJournalOpenRequest mock_open_request;
1180 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1181 mock_journaler, mock_open_request);
1182 BOOST_SCOPE_EXIT_ALL(&) {
1183 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1184 mock_journal->put();
1185 };
1186
1187 ::journal::MockFuture mock_future;
1188 Context *on_journal_safe;
1189 expect_append_journaler(mock_journaler);
1190 expect_wait_future(mock_future, &on_journal_safe);
1191 ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, 0));
1192 mock_journal->get_work_queue()->drain();
1193
1194 expect_future_is_valid(mock_future);
1195 C_SaferCond flush_ctx;
1196 mock_journal->flush_event(1U, &flush_ctx);
1197
1198 // commit the event in the journal w/ waiting cache writeback
1199 expect_future_committed(mock_journaler);
1200 C_SaferCond object_request_ctx;
1201 mock_journal->wait_event(1U, &object_request_ctx);
1202 on_journal_safe->complete(-EINVAL);
1203 ASSERT_EQ(-EINVAL, object_request_ctx.wait());
1204
1205 // cache should receive the error if waiting
1206 ASSERT_EQ(-EINVAL, flush_ctx.wait());
1207
1208 expect_shut_down_journaler(mock_journaler);
1209 }
1210
1211 TEST_F(TestMockJournal, IOCommitError) {
1212 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1213
1214 librbd::ImageCtx *ictx;
1215 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1216
1217 MockJournalImageCtx mock_image_ctx(*ictx);
1218 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1219 MockObjectDispatch mock_object_dispatch;
1220 ::journal::MockJournaler mock_journaler;
1221 MockJournalOpenRequest mock_open_request;
1222 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1223 mock_journaler, mock_open_request);
1224 BOOST_SCOPE_EXIT_ALL(&) {
1225 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1226 mock_journal->put();
1227 };
1228
1229 ::journal::MockFuture mock_future;
1230 Context *on_journal_safe;
1231 expect_append_journaler(mock_journaler);
1232 expect_wait_future(mock_future, &on_journal_safe);
1233 ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, 0));
1234 mock_journal->get_work_queue()->drain();
1235
1236 // failed IO remains uncommitted in journal
1237 on_journal_safe->complete(0);
1238 ictx->op_work_queue->drain();
1239 mock_journal->commit_io_event(1U, -EINVAL);
1240
1241 expect_shut_down_journaler(mock_journaler);
1242 }
1243
1244 TEST_F(TestMockJournal, IOCommitErrorFiltered) {
1245 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1246
1247 librbd::ImageCtx *ictx;
1248 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1249
1250 MockJournalImageCtx mock_image_ctx(*ictx);
1251 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1252 MockObjectDispatch mock_object_dispatch;
1253 ::journal::MockJournaler mock_journaler;
1254 MockJournalOpenRequest mock_open_request;
1255 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1256 mock_journaler, mock_open_request);
1257 BOOST_SCOPE_EXIT_ALL(&) {
1258 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1259 mock_journal->put();
1260 };
1261
1262 ::journal::MockFuture mock_future;
1263 Context *on_journal_safe;
1264 expect_append_journaler(mock_journaler);
1265 expect_wait_future(mock_future, &on_journal_safe);
1266 ASSERT_EQ(1U, when_append_io_event(mock_image_ctx, mock_journal, -EILSEQ));
1267 mock_journal->get_work_queue()->drain();
1268
1269 // filter failed IO committed in journal
1270 on_journal_safe->complete(0);
1271 ictx->op_work_queue->drain();
1272 expect_future_committed(mock_journaler);
1273 mock_journal->commit_io_event(1U, -EILSEQ);
1274
1275 expect_shut_down_journaler(mock_journaler);
1276 }
1277
1278 TEST_F(TestMockJournal, FlushCommitPosition) {
1279 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1280
1281 librbd::ImageCtx *ictx;
1282 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1283
1284 MockJournalImageCtx mock_image_ctx(*ictx);
1285 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1286 MockObjectDispatch mock_object_dispatch;
1287 ::journal::MockJournaler mock_journaler;
1288 MockJournalOpenRequest mock_open_request;
1289 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1290 mock_journaler, mock_open_request);
1291 BOOST_SCOPE_EXIT_ALL(&) {
1292 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1293 mock_journal->put();
1294 };
1295
1296 expect_flush_commit_position(mock_journaler);
1297 C_SaferCond ctx;
1298 mock_journal->flush_commit_position(&ctx);
1299 ASSERT_EQ(0, ctx.wait());
1300
1301 expect_shut_down_journaler(mock_journaler);
1302 }
1303
1304 TEST_F(TestMockJournal, ExternalReplay) {
1305 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1306
1307 librbd::ImageCtx *ictx;
1308 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1309
1310 MockJournalImageCtx mock_image_ctx(*ictx);
1311 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1312 MockObjectDispatch mock_object_dispatch;
1313 ::journal::MockJournaler mock_journaler;
1314 MockJournalOpenRequest mock_open_request;
1315 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1316 mock_journaler, mock_open_request);
1317 BOOST_SCOPE_EXIT_ALL(&) {
1318 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1319 mock_journal->put();
1320 };
1321
1322 InSequence seq;
1323 expect_stop_append(mock_journaler, 0);
1324 expect_start_append(mock_journaler);
1325 expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
1326 expect_shut_down_journaler(mock_journaler);
1327
1328 C_SaferCond start_ctx;
1329
1330 journal::Replay<MockJournalImageCtx> *journal_replay = nullptr;
1331 mock_journal->start_external_replay(&journal_replay, &start_ctx);
1332 ASSERT_EQ(0, start_ctx.wait());
1333
1334 mock_journal->stop_external_replay();
1335 }
1336
1337 TEST_F(TestMockJournal, ExternalReplayFailure) {
1338 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1339
1340 librbd::ImageCtx *ictx;
1341 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1342
1343 MockJournalImageCtx mock_image_ctx(*ictx);
1344 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1345 MockObjectDispatch mock_object_dispatch;
1346 ::journal::MockJournaler mock_journaler;
1347 MockJournalOpenRequest mock_open_request;
1348 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1349 mock_journaler, mock_open_request);
1350 BOOST_SCOPE_EXIT_ALL(&) {
1351 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1352 mock_journal->put();
1353 };
1354
1355 InSequence seq;
1356 expect_stop_append(mock_journaler, -EINVAL);
1357 expect_start_append(mock_journaler);
1358 expect_set_append_batch_options(mock_image_ctx, mock_journaler, false);
1359 expect_shut_down_journaler(mock_journaler);
1360
1361 C_SaferCond start_ctx;
1362
1363 journal::Replay<MockJournalImageCtx> *journal_replay = nullptr;
1364 mock_journal->start_external_replay(&journal_replay, &start_ctx);
1365 ASSERT_EQ(-EINVAL, start_ctx.wait());
1366 }
1367
1368 TEST_F(TestMockJournal, AppendDisabled) {
1369 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1370
1371 librbd::ImageCtx *ictx;
1372 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1373
1374 MockJournalImageCtx mock_image_ctx(*ictx);
1375 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1376 MockObjectDispatch mock_object_dispatch;
1377 MockJournalPolicy mock_journal_policy;
1378
1379 ::journal::MockJournaler mock_journaler;
1380 MockJournalOpenRequest mock_open_request;
1381 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1382 mock_journaler, mock_open_request);
1383 BOOST_SCOPE_EXIT_ALL(&) {
1384 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1385 mock_journal->put();
1386 };
1387
1388 InSequence seq;
1389 std::shared_lock image_locker{mock_image_ctx.image_lock};
1390 EXPECT_CALL(mock_image_ctx, get_journal_policy()).WillOnce(
1391 Return(ictx->get_journal_policy()));
1392 ASSERT_TRUE(mock_journal->is_journal_appending());
1393
1394 EXPECT_CALL(mock_image_ctx, get_journal_policy()).WillOnce(
1395 Return(&mock_journal_policy));
1396 EXPECT_CALL(mock_journal_policy, append_disabled()).WillOnce(Return(true));
1397 ASSERT_FALSE(mock_journal->is_journal_appending());
1398
1399 expect_shut_down_journaler(mock_journaler);
1400 }
1401
1402 TEST_F(TestMockJournal, CloseListenerEvent) {
1403 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1404
1405 librbd::ImageCtx *ictx;
1406 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1407
1408 MockJournalImageCtx mock_image_ctx(*ictx);
1409 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1410
1411 BOOST_SCOPE_EXIT(&mock_journal) {
1412 mock_journal->put();
1413 } BOOST_SCOPE_EXIT_END
1414
1415 MockObjectDispatch mock_object_dispatch;
1416 ::journal::MockJournaler mock_journaler;
1417 MockJournalOpenRequest mock_open_request;
1418 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1419 mock_journaler, mock_open_request);
1420
1421 struct Listener : public journal::Listener {
1422 C_SaferCond ctx;
1423 void handle_close() override {
1424 ctx.complete(0);
1425 }
1426 void handle_resync() override {
1427 ADD_FAILURE() << "unexpected resync request";
1428 }
1429 void handle_promoted() override {
1430 ADD_FAILURE() << "unexpected promotion event";
1431 }
1432 } listener;
1433 mock_journal->add_listener(&listener);
1434
1435 expect_shut_down_journaler(mock_journaler);
1436 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1437
1438 ASSERT_EQ(0, listener.ctx.wait());
1439 mock_journal->remove_listener(&listener);
1440 }
1441
1442 TEST_F(TestMockJournal, ResyncRequested) {
1443 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1444
1445 librbd::ImageCtx *ictx;
1446 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1447
1448 MockJournalImageCtx mock_image_ctx(*ictx);
1449 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1450 MockObjectDispatch mock_object_dispatch;
1451 ::journal::MockJournaler mock_journaler;
1452 MockJournalOpenRequest mock_open_request;
1453 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1454 mock_journaler, mock_open_request,
1455 false);
1456
1457 struct Listener : public journal::Listener {
1458 C_SaferCond ctx;
1459 void handle_close() override {
1460 ADD_FAILURE() << "unexpected close action";
1461 }
1462 void handle_resync() override {
1463 ctx.complete(0);
1464 }
1465 void handle_promoted() override {
1466 ADD_FAILURE() << "unexpected promotion event";
1467 }
1468 } listener;
1469 mock_journal->add_listener(&listener);
1470
1471 BOOST_SCOPE_EXIT_ALL(&) {
1472 mock_journal->remove_listener(&listener);
1473 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1474 mock_journal->put();
1475 };
1476
1477 InSequence seq;
1478
1479 journal::TagData tag_data;
1480 tag_data.mirror_uuid = Journal<>::LOCAL_MIRROR_UUID;
1481
1482 bufferlist tag_data_bl;
1483 encode(tag_data, tag_data_bl);
1484 expect_get_journaler_tags(mock_image_ctx, mock_journaler, 0,
1485 {{0, 0, tag_data_bl}}, 0);
1486
1487 journal::ImageClientMeta image_client_meta;
1488 image_client_meta.tag_class = 0;
1489 image_client_meta.resync_requested = true;
1490 expect_get_journaler_cached_client(mock_journaler, image_client_meta, 0);
1491 expect_shut_down_journaler(mock_journaler);
1492
1493 m_listener->handle_update(nullptr);
1494 ASSERT_EQ(0, listener.ctx.wait());
1495 }
1496
1497 TEST_F(TestMockJournal, ForcePromoted) {
1498 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1499
1500 librbd::ImageCtx *ictx;
1501 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1502
1503 MockJournalImageCtx mock_image_ctx(*ictx);
1504 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1505 MockObjectDispatch mock_object_dispatch;
1506 ::journal::MockJournaler mock_journaler;
1507 MockJournalOpenRequest mock_open_request;
1508 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1509 mock_journaler, mock_open_request, false);
1510
1511 struct Listener : public journal::Listener {
1512 C_SaferCond ctx;
1513 void handle_close() override {
1514 ADD_FAILURE() << "unexpected close action";
1515 }
1516 void handle_resync() override {
1517 ADD_FAILURE() << "unexpected resync event";
1518 }
1519 void handle_promoted() override {
1520 ctx.complete(0);
1521 }
1522 } listener;
1523 mock_journal->add_listener(&listener);
1524
1525 BOOST_SCOPE_EXIT_ALL(&) {
1526 mock_journal->remove_listener(&listener);
1527 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1528 mock_journal->put();
1529 };
1530
1531 InSequence seq;
1532
1533 journal::TagData tag_data;
1534 tag_data.mirror_uuid = Journal<>::LOCAL_MIRROR_UUID;
1535
1536 bufferlist tag_data_bl;
1537 encode(tag_data, tag_data_bl);
1538 expect_get_journaler_tags(mock_image_ctx, mock_journaler, 0,
1539 {{100, 0, tag_data_bl}}, 0);
1540
1541 journal::ImageClientMeta image_client_meta;
1542 image_client_meta.tag_class = 0;
1543 expect_get_journaler_cached_client(mock_journaler, image_client_meta, 0);
1544 expect_shut_down_journaler(mock_journaler);
1545
1546 m_listener->handle_update(nullptr);
1547 ASSERT_EQ(0, listener.ctx.wait());
1548 }
1549
1550 TEST_F(TestMockJournal, UserFlushed) {
1551 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
1552
1553 librbd::ImageCtx *ictx;
1554 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1555
1556 MockJournalImageCtx mock_image_ctx(*ictx);
1557 MockJournal *mock_journal = new MockJournal(mock_image_ctx);
1558 MockObjectDispatch mock_object_dispatch;
1559 ::journal::MockJournaler mock_journaler;
1560 MockJournalOpenRequest mock_open_request;
1561 open_journal(mock_image_ctx, mock_journal, mock_object_dispatch,
1562 mock_journaler, mock_open_request);
1563 BOOST_SCOPE_EXIT_ALL(&) {
1564 close_journal(mock_image_ctx, mock_journal, mock_journaler);
1565 mock_journal->put();
1566 };
1567
1568 expect_set_append_batch_options(mock_image_ctx, mock_journaler, true);
1569 mock_journal->user_flushed();
1570
1571 expect_shut_down_journaler(mock_journaler);
1572 }
1573
1574 } // namespace librbd