summaryrefslogtreecommitdiffstats
path: root/src/test/librbd/cache/pwl
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/librbd/cache/pwl')
-rw-r--r--src/test/librbd/cache/pwl/test_WriteLogMap.cc338
-rw-r--r--src/test/librbd/cache/pwl/test_mock_ReplicatedWriteLog.cc743
-rw-r--r--src/test/librbd/cache/pwl/test_mock_SSDWriteLog.cc761
3 files changed, 1842 insertions, 0 deletions
diff --git a/src/test/librbd/cache/pwl/test_WriteLogMap.cc b/src/test/librbd/cache/pwl/test_WriteLogMap.cc
new file mode 100644
index 000000000..1cafb00ff
--- /dev/null
+++ b/src/test/librbd/cache/pwl/test_WriteLogMap.cc
@@ -0,0 +1,338 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+
+#include "librbd/cache/pwl/LogMap.cc"
+
+void register_test_write_log_map() {
+}
+
+namespace librbd {
+namespace cache {
+namespace pwl {
+
+using namespace std;
+
+struct TestLogEntry {
+ uint64_t image_offset_bytes;
+ uint64_t write_bytes;
+ uint32_t referring_map_entries = 0;
+ TestLogEntry(const uint64_t image_offset_bytes, const uint64_t write_bytes)
+ : image_offset_bytes(image_offset_bytes), write_bytes(write_bytes) {
+ }
+ uint64_t get_offset_bytes() {
+ return image_offset_bytes;
+ }
+ uint64_t get_write_bytes() {
+ return write_bytes;
+ }
+ BlockExtent block_extent() {
+ return BlockExtent(image_offset_bytes, image_offset_bytes + write_bytes);
+ }
+ uint32_t get_map_ref() {
+ return referring_map_entries;
+ }
+ void inc_map_ref() {
+ referring_map_entries++;
+ }
+ void dec_map_ref() {
+ referring_map_entries--;
+ }
+ friend std::ostream &operator<<(std::ostream &os,
+ const TestLogEntry &entry) {
+ os << "referring_map_entries=" << entry.referring_map_entries << ", "
+ << "image_offset_bytes=" << entry.image_offset_bytes << ", "
+ << "write_bytes=" << entry.write_bytes;
+ return os;
+ };
+};
+
+typedef std::list<std::shared_ptr<TestLogEntry>> TestLogEntries;
+typedef LogMapEntry<TestLogEntry> TestMapEntry;
+typedef LogMapEntries<TestLogEntry> TestLogMapEntries;
+typedef LogMap<TestLogEntry> TestLogMap;
+
+class TestWriteLogMap : public TestFixture {
+public:
+ void SetUp() override {
+ TestFixture::SetUp();
+ m_cct = reinterpret_cast<CephContext*>(m_ioctx.cct());
+ }
+
+ CephContext *m_cct;
+};
+
+TEST_F(TestWriteLogMap, Simple) {
+ TestLogEntries es;
+ TestLogMapEntries lme;
+ TestLogMap map(m_cct);
+
+ /* LogEntry takes offset, length, in bytes */
+ auto e1 = make_shared<TestLogEntry>(4, 8);
+ TestLogEntry *e1_ptr = e1.get();
+ ASSERT_EQ(4, e1_ptr->get_offset_bytes());
+ ASSERT_EQ(8, e1_ptr->get_write_bytes());
+ map.add_log_entry(e1);
+
+ /* BlockExtent takes first, last, in blocks */
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(0, 100));
+ int numfound = found0.size();
+ /* Written range includes the single write above */
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+
+ /* Nothing before that */
+ found0 = map.find_map_entries(BlockExtent(0, 3));
+ numfound = found0.size();
+ ASSERT_EQ(0, numfound);
+
+ /* Nothing after that */
+ found0 = map.find_map_entries(BlockExtent(12, 99));
+ numfound = found0.size();
+ ASSERT_EQ(0, numfound);
+
+ /* 4-11 will be e1 */
+ for (int i=4; i<12; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ }
+
+ map.remove_log_entry(e1);
+ /* Nothing should be found */
+ for (int i=4; i<12; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(0, numfound);
+ }
+}
+
+TEST_F(TestWriteLogMap, OverlapFront) {
+ TestLogMap map(m_cct);
+
+ auto e0 = make_shared<TestLogEntry>(4, 8);
+ map.add_log_entry(e0);
+ /* replaces block 4-7 of e0 */
+ auto e1 = make_shared<TestLogEntry>(0, 8);
+ map.add_log_entry(e1);
+
+ /* Written range includes the two writes above */
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(0, 100));
+ int numfound = found0.size();
+ ASSERT_EQ(2, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ ASSERT_EQ(0, found0.front().block_extent.block_start);
+ ASSERT_EQ(8, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e0, found0.front().log_entry);
+ ASSERT_EQ(8, found0.front().block_extent.block_start);
+ ASSERT_EQ(12, found0.front().block_extent.block_end);
+
+ /* 0-7 will be e1 */
+ for (int i=0; i<8; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ }
+
+ /* 8-11 will be e0 */
+ for (int i=8; i<12; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ }
+}
+
+TEST_F(TestWriteLogMap, OverlapBack) {
+ TestLogMap map(m_cct);
+
+ auto e0 = make_shared<TestLogEntry>(0, 8);
+ map.add_log_entry(e0);
+ /* replaces block 4-7 of e0 */
+ auto e1 = make_shared<TestLogEntry>(4, 8);
+ map.add_log_entry(e1);
+
+ /* Written range includes the two writes above */
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(0, 100));
+ int numfound = found0.size();
+ ASSERT_EQ(2, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ ASSERT_EQ(0, found0.front().block_extent.block_start);
+ ASSERT_EQ(4, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e1, found0.front().log_entry);
+ ASSERT_EQ(4, found0.front().block_extent.block_start);
+ ASSERT_EQ(12, found0.front().block_extent.block_end);
+
+ /* 0-3 will be e0 */
+ for (int i=0; i<4; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ }
+
+ /* 4-11 will be e1 */
+ for (int i=4; i<12; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ }
+
+ map.remove_log_entry(e0);
+
+ /* 0-3 will find nothing */
+ for (int i=0; i<4; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(0, numfound);
+ }
+
+ /* 4-11 will still be e1 */
+ for (int i=4; i<12; i++) {
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(i, i + 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ }
+
+}
+
+TEST_F(TestWriteLogMap, OverlapMiddle) {
+ TestLogMap map(m_cct);
+
+ auto e0 = make_shared<TestLogEntry>(0, 1);
+ map.add_log_entry(e0);
+
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(0, 1));
+ int numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ TestLogEntries entries = map.find_log_entries(BlockExtent(0, 1));
+ int entriesfound = entries.size();
+ ASSERT_EQ(1, entriesfound);
+ ASSERT_EQ(e0, entries.front());
+
+ auto e1 = make_shared<TestLogEntry>(1, 1);
+ map.add_log_entry(e1);
+
+ found0 = map.find_map_entries(BlockExtent(1, 2));
+ numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e1, found0.front().log_entry);
+ entries = map.find_log_entries(BlockExtent(1, 2));
+ entriesfound = entries.size();
+ ASSERT_EQ(1, entriesfound);
+ ASSERT_EQ(e1, entries.front());
+
+ auto e2 = make_shared<TestLogEntry>(2, 1);
+ map.add_log_entry(e2);
+
+ found0 = map.find_map_entries(BlockExtent(2, 3));
+ numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e2, found0.front().log_entry);
+ entries = map.find_log_entries(BlockExtent(2, 3));
+ entriesfound = entries.size();
+ ASSERT_EQ(1, entriesfound);
+ ASSERT_EQ(e2, entries.front());
+
+ /* replaces e1 */
+ auto e3 = make_shared<TestLogEntry>(1, 1);
+ map.add_log_entry(e3);
+
+ found0 = map.find_map_entries(BlockExtent(1, 2));
+ numfound = found0.size();
+ ASSERT_EQ(1, numfound);
+ ASSERT_EQ(e3, found0.front().log_entry);
+ entries = map.find_log_entries(BlockExtent(1, 2));
+ entriesfound = entries.size();
+ ASSERT_EQ(1, entriesfound);
+ ASSERT_EQ(e3, entries.front());
+
+ found0 = map.find_map_entries(BlockExtent(0, 100));
+ numfound = found0.size();
+ ASSERT_EQ(3, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ found0.pop_front();
+ ASSERT_EQ(e3, found0.front().log_entry);
+ found0.pop_front();
+ ASSERT_EQ(e2, found0.front().log_entry);
+ entries = map.find_log_entries(BlockExtent(0, 100));
+ entriesfound = entries.size();
+ ASSERT_EQ(3, entriesfound);
+ ASSERT_EQ(e0, entries.front());
+ entries.pop_front();
+ ASSERT_EQ(e3, entries.front());
+ entries.pop_front();
+ ASSERT_EQ(e2, entries.front());
+
+ entries.clear();
+ entries.emplace_back(e0);
+ entries.emplace_back(e1);
+ map.remove_log_entries(entries);
+
+ found0 = map.find_map_entries(BlockExtent(0, 100));
+ numfound = found0.size();
+ ASSERT_EQ(2, numfound);
+ ASSERT_EQ(e3, found0.front().log_entry);
+ found0.pop_front();
+ ASSERT_EQ(e2, found0.front().log_entry);
+}
+
+TEST_F(TestWriteLogMap, OverlapSplit) {
+ TestLogMap map(m_cct);
+
+ auto e0 = make_shared<TestLogEntry>(0, 8);
+ map.add_log_entry(e0);
+
+ /* Splits e0 at 1 */
+ auto e1 = make_shared<TestLogEntry>(1, 1);
+ map.add_log_entry(e1);
+
+ /* Splits e0 again at 4 */
+ auto e2 = make_shared<TestLogEntry>(4, 2);
+ map.add_log_entry(e2);
+
+ /* Replaces one block of e2, and one of e0 */
+ auto e3 = make_shared<TestLogEntry>(5, 2);
+ map.add_log_entry(e3);
+
+ /* Expecting: 0:e0, 1:e1, 2..3:e0, 4:e2, 5..6:e3, 7:e0 */
+ TestLogMapEntries found0 = map.find_map_entries(BlockExtent(0, 100));
+ int numfound = found0.size();
+ ASSERT_EQ(6, numfound);
+ ASSERT_EQ(e0, found0.front().log_entry);
+ ASSERT_EQ(0, found0.front().block_extent.block_start);
+ ASSERT_EQ(1, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e1, found0.front().log_entry);
+ ASSERT_EQ(1, found0.front().block_extent.block_start);
+ ASSERT_EQ(2, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e0, found0.front().log_entry);
+ ASSERT_EQ(2, found0.front().block_extent.block_start);
+ ASSERT_EQ(4, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e2, found0.front().log_entry);
+ ASSERT_EQ(4, found0.front().block_extent.block_start);
+ ASSERT_EQ(5, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e3, found0.front().log_entry);
+ ASSERT_EQ(5, found0.front().block_extent.block_start);
+ ASSERT_EQ(7, found0.front().block_extent.block_end);
+ found0.pop_front();
+ ASSERT_EQ(e0, found0.front().log_entry);
+ ASSERT_EQ(7, found0.front().block_extent.block_start);
+ ASSERT_EQ(8, found0.front().block_extent.block_end);
+}
+
+} // namespace pwl
+} // namespace cache
+} // namespace librbd
diff --git a/src/test/librbd/cache/pwl/test_mock_ReplicatedWriteLog.cc b/src/test/librbd/cache/pwl/test_mock_ReplicatedWriteLog.cc
new file mode 100644
index 000000000..a37f58038
--- /dev/null
+++ b/src/test/librbd/cache/pwl/test_mock_ReplicatedWriteLog.cc
@@ -0,0 +1,743 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+#include "common/hostname.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/cache/pwl/ImageCacheState.h"
+#include "librbd/cache/pwl/Types.h"
+#include "librbd/cache/ImageWriteback.h"
+#include "librbd/plugin/Api.h"
+
+namespace librbd {
+namespace {
+
+struct MockContextRWL : public C_SaferCond {
+ MOCK_METHOD1(complete, void(int));
+ MOCK_METHOD1(finish, void(int));
+
+ void do_complete(int r) {
+ C_SaferCond::complete(r);
+ }
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/cache/pwl/AbstractWriteLog.cc"
+#include "librbd/cache/pwl/rwl/WriteLog.cc"
+template class librbd::cache::pwl::rwl::WriteLog<librbd::MockImageCtx>;
+
+// template definitions
+#include "librbd/cache/ImageWriteback.cc"
+#include "librbd/cache/pwl/ImageCacheState.cc"
+#include "librbd/cache/pwl/Request.cc"
+#include "librbd/cache/pwl/rwl/Request.cc"
+#include "librbd/plugin/Api.cc"
+
+namespace librbd {
+namespace cache {
+namespace pwl {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+
+typedef io::Extent Extent;
+typedef io::Extents Extents;
+
+struct TestMockCacheReplicatedWriteLog : public TestMockFixture {
+ typedef librbd::cache::pwl::rwl::WriteLog<librbd::MockImageCtx> MockReplicatedWriteLog;
+ typedef librbd::cache::pwl::ImageCacheState<librbd::MockImageCtx> MockImageCacheStateRWL;
+ typedef librbd::cache::ImageWriteback<librbd::MockImageCtx> MockImageWriteback;
+ typedef librbd::plugin::Api<librbd::MockImageCtx> MockApi;
+
+ MockImageCacheStateRWL *get_cache_state(
+ MockImageCtx& mock_image_ctx, MockApi& mock_api) {
+ MockImageCacheStateRWL *rwl_state = new MockImageCacheStateRWL(&mock_image_ctx, mock_api);
+ return rwl_state;
+ }
+
+ void validate_cache_state(librbd::ImageCtx *image_ctx,
+ MockImageCacheStateRWL &state,
+ bool present, bool empty, bool clean,
+ string host, string path,
+ uint64_t size) {
+ ASSERT_EQ(present, state.present);
+ ASSERT_EQ(empty, state.empty);
+ ASSERT_EQ(clean, state.clean);
+
+ ASSERT_EQ(host, state.host);
+ ASSERT_EQ(path, state.path);
+ ASSERT_EQ(size, state.size);
+ }
+
+ void expect_context_complete(MockContextRWL& mock_context, int r) {
+ EXPECT_CALL(mock_context, complete(r))
+ .WillRepeatedly(Invoke([&mock_context](int r) {
+ mock_context.do_complete(r);
+ }));
+ }
+
+ void expect_metadata_set(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_set(_, _, _))
+ .WillRepeatedly(Invoke([](std::string key, std::string val, Context* ctx) {
+ ctx->complete(0);
+ }));
+ }
+
+ void expect_metadata_remove(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_remove(_, _))
+ .WillRepeatedly(Invoke([](std::string key, Context* ctx) {
+ ctx->complete(0);
+ }));
+ }
+};
+
+TEST_F(TestMockCacheReplicatedWriteLog, init_state_write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockApi mock_api;
+ MockImageCacheStateRWL image_cache_state(&mock_image_ctx, mock_api);
+
+ validate_cache_state(ictx, image_cache_state, false, true, true, "", "", 0);
+
+ image_cache_state.empty = false;
+ image_cache_state.clean = false;
+ ceph::mutex lock = ceph::make_mutex("MockImageCacheStateRWL lock");
+ MockContextRWL finish_ctx;
+ expect_metadata_set(mock_image_ctx);
+ expect_context_complete(finish_ctx, 0);
+ std::unique_lock locker(lock);
+ image_cache_state.write_image_cache_state(locker, &finish_ctx);
+ ASSERT_FALSE(locker.owns_lock());
+ ASSERT_EQ(0, finish_ctx.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, init_state_json_write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockApi mock_api;
+ MockImageCacheStateRWL image_cache_state(&mock_image_ctx, mock_api);
+
+ string strf = "{ \"present\": true, \"empty\": false, \"clean\": false, \
+ \"host\": \"testhost\", \
+ \"path\": \"/tmp\", \
+ \"mode\": \"rwl\", \
+ \"size\": 1024 }";
+ json_spirit::mValue json_root;
+ ASSERT_TRUE(json_spirit::read(strf.c_str(), json_root));
+ ASSERT_TRUE(image_cache_state.init_from_metadata(json_root));
+ validate_cache_state(ictx, image_cache_state, true, false, false,
+ "testhost", "/tmp", 1024);
+
+ MockContextRWL finish_ctx;
+ expect_metadata_remove(mock_image_ctx);
+ expect_context_complete(finish_ctx, 0);
+ image_cache_state.clear_image_cache_state(&finish_ctx);
+ ASSERT_EQ(0, finish_ctx.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, init_shutdown) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ MockContextRWL finish_ctx1;
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ rwl.shut_down(&finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ MockContextRWL finish_ctx1;
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(&finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush_source_shutdown) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(io::FLUSH_SOURCE_SHUTDOWN, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush_source_internal) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(io::FLUSH_SOURCE_INTERNAL, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush_source_user) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ usleep(10000);
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(io::FLUSH_SOURCE_USER, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, read_hit_rwl_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ expect_context_complete(finish_ctx_read, 0);
+ Extents image_extents_read{{0, 4096}};
+ bufferlist read_bl;
+ rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl_copy.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, read_hit_part_rwl_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ Extents image_extents_read{{512, 4096}};
+ bufferlist hit_bl;
+ bl_copy.begin(511).copy(4096-512, hit_bl);
+ expect_context_complete(finish_ctx_read, 512);
+ bufferlist read_bl;
+ rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(512, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ bufferlist read_bl_hit;
+ read_bl.begin(0).copy(4096-512, read_bl_hit);
+ ASSERT_TRUE(hit_bl.contents_equal(read_bl_hit));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, read_miss_rwl_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ Extents image_extents_read{{4096, 4096}};
+ expect_context_complete(finish_ctx_read, 4096);
+ bufferlist read_bl;
+ ASSERT_EQ(0, read_bl.length());
+ rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(4096, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, discard) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_discard;
+ expect_context_complete(finish_ctx_discard, 0);
+ rwl.discard(0, 4096, 1, &finish_ctx_discard);
+ ASSERT_EQ(0, finish_ctx_discard.wait());
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(read_bl.is_zero());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, writesame) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ bufferlist bl, test_bl;
+ bl.append(std::string(512, '1'));
+ test_bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.writesame(0, 4096, std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(test_bl.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, invalidate) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_invalidate;
+ expect_context_complete(finish_ctx_invalidate, 0);
+ rwl.invalidate(&finish_ctx_invalidate);
+ ASSERT_EQ(0, finish_ctx_invalidate.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, compare_and_write_compare_matched) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl1;
+ bl1.append(std::string(4096, '1'));
+ bufferlist com_bl = bl1;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_cw;
+ bufferlist bl2;
+ bl2.append(std::string(4096, '2'));
+ bufferlist bl2_copy = bl2;
+ uint64_t mismatch_offset = -1;
+ expect_context_complete(finish_ctx_cw, 0);
+ rwl.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
+ &mismatch_offset, fadvise_flags, &finish_ctx_cw);
+ ASSERT_EQ(0, finish_ctx_cw.wait());
+ ASSERT_EQ(0, mismatch_offset);
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl2_copy.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, compare_and_write_compare_failed) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockReplicatedWriteLog rwl(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl1;
+ bl1.append(std::string(4096, '1'));
+ bufferlist bl1_copy = bl1;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_cw;
+ bufferlist bl2;
+ bl2.append(std::string(4096, '2'));
+ bufferlist com_bl = bl2;
+ uint64_t mismatch_offset = -1;
+ expect_context_complete(finish_ctx_cw, -EILSEQ);
+ rwl.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
+ &mismatch_offset, fadvise_flags, &finish_ctx_cw);
+ ASSERT_EQ(-EILSEQ, finish_ctx_cw.wait());
+ ASSERT_EQ(0, mismatch_offset);
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl1_copy.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+} // namespace pwl
+} // namespace cache
+} // namespace librbd
diff --git a/src/test/librbd/cache/pwl/test_mock_SSDWriteLog.cc b/src/test/librbd/cache/pwl/test_mock_SSDWriteLog.cc
new file mode 100644
index 000000000..72a44dcc9
--- /dev/null
+++ b/src/test/librbd/cache/pwl/test_mock_SSDWriteLog.cc
@@ -0,0 +1,761 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+#include "common/hostname.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/cache/pwl/AbstractWriteLog.h"
+#include "librbd/cache/pwl/ImageCacheState.h"
+#include "librbd/cache/pwl/Types.h"
+#include "librbd/cache/ImageWriteback.h"
+#include "librbd/plugin/Api.h"
+
+namespace librbd {
+namespace {
+
+struct MockContextSSD : public C_SaferCond {
+ MOCK_METHOD1(complete, void(int));
+ MOCK_METHOD1(finish, void(int));
+
+ void do_complete(int r) {
+ C_SaferCond::complete(r);
+ }
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/cache/pwl/AbstractWriteLog.cc"
+#include "librbd/cache/pwl/ssd/WriteLog.cc"
+template class librbd::cache::pwl::ssd::WriteLog<librbd::MockImageCtx>;
+
+// template definitions
+#include "librbd/cache/ImageWriteback.cc"
+#include "librbd/cache/pwl/ImageCacheState.cc"
+#include "librbd/cache/pwl/Request.cc"
+#include "librbd/plugin/Api.cc"
+#include "librbd/cache/pwl/ssd/Request.cc"
+
+namespace librbd {
+namespace cache {
+namespace pwl {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+
+typedef io::Extent Extent;
+typedef io::Extents Extents;
+
+struct TestMockCacheSSDWriteLog : public TestMockFixture {
+ typedef librbd::cache::pwl::ssd::WriteLog<librbd::MockImageCtx> MockSSDWriteLog;
+ typedef librbd::cache::pwl::ImageCacheState<librbd::MockImageCtx> MockImageCacheStateSSD;
+ typedef librbd::cache::ImageWriteback<librbd::MockImageCtx> MockImageWriteback;
+ typedef librbd::plugin::Api<librbd::MockImageCtx> MockApi;
+
+ MockImageCacheStateSSD *get_cache_state(
+ MockImageCtx& mock_image_ctx, MockApi& mock_api) {
+ MockImageCacheStateSSD *ssd_state = new MockImageCacheStateSSD(
+ &mock_image_ctx, mock_api);
+ return ssd_state;
+ }
+
+ void validate_cache_state(librbd::ImageCtx *image_ctx,
+ MockImageCacheStateSSD &state,
+ bool present, bool empty, bool clean,
+ string host, string path,
+ uint64_t size) {
+ ASSERT_EQ(present, state.present);
+ ASSERT_EQ(empty, state.empty);
+ ASSERT_EQ(clean, state.clean);
+
+ ASSERT_EQ(host, state.host);
+ ASSERT_EQ(path, state.path);
+ ASSERT_EQ(size, state.size);
+ }
+
+ void expect_context_complete(MockContextSSD& mock_context, int r) {
+ EXPECT_CALL(mock_context, complete(r))
+ .WillRepeatedly(Invoke([&mock_context](int r) {
+ mock_context.do_complete(r);
+ }));
+ }
+
+ void expect_metadata_set(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_set(_, _, _))
+ .WillRepeatedly(Invoke([](std::string key, std::string val, Context* ctx) {
+ ctx->complete(0);
+ }));
+ }
+
+ void expect_metadata_remove(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_remove(_, _))
+ .WillRepeatedly(Invoke([](std::string key, Context* ctx) {
+ ctx->complete(0);
+ }));
+ }
+};
+
+TEST_F(TestMockCacheSSDWriteLog, init_state_write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockApi mock_api;
+ MockImageCacheStateSSD image_cache_state(&mock_image_ctx, mock_api);
+
+ validate_cache_state(ictx, image_cache_state, false, true, true, "", "", 0);
+
+ image_cache_state.empty = false;
+ image_cache_state.clean = false;
+ ceph::mutex lock = ceph::make_mutex("MockImageCacheStateSSD lock");
+ MockContextSSD finish_ctx;
+ expect_metadata_set(mock_image_ctx);
+ expect_context_complete(finish_ctx, 0);
+ std::unique_lock locker(lock);
+ image_cache_state.write_image_cache_state(locker, &finish_ctx);
+ ASSERT_FALSE(locker.owns_lock());
+ ASSERT_EQ(0, finish_ctx.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, init_state_json_write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockApi mock_api;
+ MockImageCacheStateSSD image_cache_state(&mock_image_ctx, mock_api);
+
+ string strf = "{ \"present\": true, \"empty\": false, \"clean\": false, \
+ \"host\": \"testhost\", \
+ \"path\": \"/tmp\", \
+ \"mode\": \"ssd\", \
+ \"size\": 1024 }";
+ json_spirit::mValue json_root;
+ ASSERT_TRUE(json_spirit::read(strf.c_str(), json_root));
+ ASSERT_TRUE(image_cache_state.init_from_metadata(json_root));
+ validate_cache_state(ictx, image_cache_state, true, false, false,
+ "testhost", "/tmp", 1024);
+
+ MockContextSSD finish_ctx;
+ expect_metadata_remove(mock_image_ctx);
+ expect_context_complete(finish_ctx, 0);
+ image_cache_state.clear_image_cache_state(&finish_ctx);
+ ASSERT_EQ(0, finish_ctx.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, init_shutdown) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ MockContextSSD finish_ctx1;
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ ssd.shut_down(&finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ MockContextSSD finish_ctx1;
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, read_hit_ssd_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl),
+ fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_read;
+ expect_context_complete(finish_ctx_read, 0);
+ Extents image_extents_read{{0, 4096}};
+ bufferlist read_bl;
+ ssd.read(std::move(image_extents_read), &read_bl,
+ fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl_copy.contents_equal(read_bl));
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, read_hit_part_ssd_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 8192}};
+ bufferlist bl;
+ bl.append(std::string(8192, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl),
+ fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_read;
+ Extents image_extents_read{{4096, 4096}};
+ bufferlist hit_bl;
+ bl_copy.begin(4095).copy(4096, hit_bl);
+ expect_context_complete(finish_ctx_read, 0);
+ bufferlist read_bl;
+ ssd.read(std::move(image_extents_read), &read_bl,
+ fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ bufferlist read_bl_hit;
+ read_bl.begin(0).copy(4096, read_bl_hit);
+ ASSERT_TRUE(hit_bl.contents_equal(read_bl_hit));
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, read_miss_ssd_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl),
+ fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_read;
+ Extents image_extents_read{{4096, 4096}};
+ expect_context_complete(finish_ctx_read, 4096);
+ bufferlist read_bl;
+ ASSERT_EQ(0, read_bl.length());
+ ssd.read(std::move(image_extents_read), &read_bl,
+ fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(4096, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, compare_and_write_compare_matched) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl1;
+ bl1.append(std::string(4096, '1'));
+ bufferlist com_bl = bl1;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_cw;
+ bufferlist bl2;
+ bl2.append(std::string(4096, '2'));
+ bufferlist bl2_copy = bl2;
+ uint64_t mismatch_offset = -1;
+ expect_context_complete(finish_ctx_cw, 0);
+ ssd.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
+ &mismatch_offset, fadvise_flags, &finish_ctx_cw);
+ ASSERT_EQ(0, finish_ctx_cw.wait());
+ ASSERT_EQ(0, mismatch_offset);
+
+ MockContextSSD finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ ssd.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl2_copy.contents_equal(read_bl));
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, compare_and_write_compare_failed) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl1;
+ bl1.append(std::string(4096, '1'));
+ bufferlist bl1_copy = bl1;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_cw;
+ bufferlist bl2;
+ bl2.append(std::string(4096, '2'));
+ bufferlist com_bl = bl2;
+ uint64_t mismatch_offset = -1;
+ expect_context_complete(finish_ctx_cw, -EILSEQ);
+ ssd.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
+ &mismatch_offset, fadvise_flags, &finish_ctx_cw);
+ ASSERT_EQ(-EILSEQ, finish_ctx_cw.wait());
+ ASSERT_EQ(0, mismatch_offset);
+
+ MockContextSSD finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ ssd.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl1_copy.contents_equal(read_bl));
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, writesame) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ bufferlist bl, test_bl;
+ bl.append(std::string(512, '1'));
+ test_bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.writesame(0, 4096, std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ ssd.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(test_bl.contents_equal(read_bl));
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, discard) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_discard;
+ expect_context_complete(finish_ctx_discard, 0);
+ ssd.discard(0, 4096, 1, &finish_ctx_discard);
+ ASSERT_EQ(0, finish_ctx_discard.wait());
+
+ MockContextSSD finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ ssd.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(read_bl.is_zero());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, invalidate) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_invalidate;
+ expect_context_complete(finish_ctx_invalidate, 0);
+ ssd.invalidate(&finish_ctx_invalidate);
+ ASSERT_EQ(0, finish_ctx_invalidate.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, flush) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ ssd.flush(&finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, flush_source_shutdown) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ ssd.flush(io::FLUSH_SOURCE_SHUTDOWN, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+
+TEST_F(TestMockCacheSSDWriteLog, flush_source_internal) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextSSD finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ ssd.flush(io::FLUSH_SOURCE_INTERNAL, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ ssd.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheSSDWriteLog, flush_source_user) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageWriteback mock_image_writeback(mock_image_ctx);
+ MockApi mock_api;
+ MockSSDWriteLog ssd(
+ mock_image_ctx, get_cache_state(mock_image_ctx, mock_api),
+ mock_image_writeback, mock_api);
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextSSD finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ ssd.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextSSD finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ ssd.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ usleep(10000);
+ MockContextSSD finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ ssd.flush(io::FLUSH_SOURCE_USER, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextSSD finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ Extents image_extents2{{0, 4096}};
+ bufferlist bl2;
+ bl2.append(std::string(4096, '1'));
+ int fadvise_flags2 = 0;
+ ssd.write(std::move(image_extents2), std::move(bl2), fadvise_flags2, &finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+
+ MockContextSSD finish_ctx4;
+ expect_context_complete(finish_ctx4, 0);
+ ssd.shut_down(&finish_ctx4);
+ ASSERT_EQ(0, finish_ctx4.wait());
+}
+
+} // namespace pwl
+} // namespace cache
+} // namespace librbd