From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- src/test/librbd/io/test_mock_ImageRequest.cc | 738 +++++++++++++++++++++++++++ 1 file changed, 738 insertions(+) create mode 100644 src/test/librbd/io/test_mock_ImageRequest.cc (limited to 'src/test/librbd/io/test_mock_ImageRequest.cc') diff --git a/src/test/librbd/io/test_mock_ImageRequest.cc b/src/test/librbd/io/test_mock_ImageRequest.cc new file mode 100644 index 000000000..9d6423d66 --- /dev/null +++ b/src/test/librbd/io/test_mock_ImageRequest.cc @@ -0,0 +1,738 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/mock/MockJournal.h" +#include "test/librbd/mock/cache/MockImageCache.h" +#include "librbd/io/ImageRequest.h" +#include "librbd/io/ObjectDispatchSpec.h" +#include "librbd/io/Utils.h" + +namespace librbd { +namespace { + +struct MockTestImageCtx; + +struct MockTestJournal : public MockJournal { + MOCK_METHOD4(append_write_event, uint64_t(uint64_t, size_t, + const bufferlist &, bool)); + MOCK_METHOD5(append_compare_and_write_event, uint64_t(uint64_t, size_t, + const bufferlist &, + const bufferlist &, + bool)); + MOCK_METHOD5(append_io_event_mock, uint64_t(const journal::EventEntry&, + uint64_t, size_t, bool, int)); + uint64_t append_io_event(journal::EventEntry &&event_entry, + uint64_t offset, size_t length, + bool flush_entry, int filter_ret_val) { + // googlemock doesn't support move semantics + return append_io_event_mock(event_entry, offset, length, flush_entry, + filter_ret_val); + } + + MOCK_METHOD2(commit_io_event, void(uint64_t, int)); +}; + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { + } + + MockTestJournal* journal; +}; + +} // anonymous namespace + +namespace util { + +inline ImageCtx *get_image_ctx(MockTestImageCtx *image_ctx) { + return image_ctx->image_ctx; +} + +} // namespace util +} // namespace librbd + +#include "librbd/io/ImageRequest.cc" + +namespace librbd { +namespace io { + +namespace util { + +template <> +void area_to_object_extents(MockTestImageCtx* image_ctx, uint64_t offset, + uint64_t length, ImageArea area, + uint64_t buffer_offset, + striper::LightweightObjectExtents* object_extents) { + Striper::file_to_extents(image_ctx->cct, &image_ctx->layout, offset, length, + 0, buffer_offset, object_extents); +} + +template <> +std::pair object_to_area_extents( + MockTestImageCtx* image_ctx, uint64_t object_no, + const Extents& object_extents) { + Extents extents; + for (auto [off, len] : object_extents) { + Striper::extent_to_file(image_ctx->cct, &image_ctx->layout, object_no, off, + len, extents); + } + return {std::move(extents), ImageArea::DATA}; +} + +} // namespace util + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::WithArg; +using ::testing::WithoutArgs; +using ::testing::Exactly; + +struct TestMockIoImageRequest : public TestMockFixture { + typedef ImageRequest MockImageRequest; + typedef ImageReadRequest MockImageReadRequest; + typedef ImageWriteRequest MockImageWriteRequest; + typedef ImageDiscardRequest MockImageDiscardRequest; + typedef ImageFlushRequest MockImageFlushRequest; + typedef ImageWriteSameRequest MockImageWriteSameRequest; + typedef ImageCompareAndWriteRequest MockImageCompareAndWriteRequest; + typedef ImageListSnapsRequest MockImageListSnapsRequest; + + void expect_is_journal_appending(MockTestJournal &mock_journal, bool appending) { + EXPECT_CALL(mock_journal, is_journal_appending()) + .WillOnce(Return(appending)); + } + + void expect_get_modify_timestamp(MockTestImageCtx &mock_image_ctx, + bool needs_update) { + if (needs_update) { + mock_image_ctx.mtime_update_interval = 5; + EXPECT_CALL(mock_image_ctx, get_modify_timestamp()) + .WillOnce(Return(ceph_clock_now() - utime_t(10,0))); + } else { + mock_image_ctx.mtime_update_interval = 600; + EXPECT_CALL(mock_image_ctx, get_modify_timestamp()) + .WillOnce(Return(ceph_clock_now())); + } + } + + void expect_journal_append_io_event(MockTestJournal &mock_journal, uint64_t journal_tid, + uint64_t offset, size_t length) { + EXPECT_CALL(mock_journal, append_io_event_mock(_, offset, length, _, _)) + .WillOnce(Return(journal_tid)); + } + + void expect_object_discard_request(MockTestImageCtx &mock_image_ctx, + uint64_t object_no, uint64_t offset, + uint32_t length, int r) { + EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_)) + .WillOnce(Invoke([&mock_image_ctx, object_no, offset, length, r] + (ObjectDispatchSpec* spec) { + auto* discard_spec = boost::get(&spec->request); + ASSERT_TRUE(discard_spec != nullptr); + ASSERT_EQ(object_no, discard_spec->object_no); + ASSERT_EQ(offset, discard_spec->object_off); + ASSERT_EQ(length, discard_spec->object_len); + + spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE; + mock_image_ctx.image_ctx->op_work_queue->queue(&spec->dispatcher_ctx, r); + })); + } + + void expect_object_request_send(MockTestImageCtx &mock_image_ctx, + int r) { + EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_)) + .WillOnce(Invoke([&mock_image_ctx, r](ObjectDispatchSpec* spec) { + spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE; + mock_image_ctx.image_ctx->op_work_queue->queue(&spec->dispatcher_ctx, r); + })); + } + + void expect_object_list_snaps_request(MockTestImageCtx &mock_image_ctx, + uint64_t object_no, + const SnapshotDelta& snap_delta, + int r) { + EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_)) + .WillOnce( + Invoke([&mock_image_ctx, object_no, snap_delta, r] + (ObjectDispatchSpec* spec) { + auto request = boost::get< + librbd::io::ObjectDispatchSpec::ListSnapsRequest>( + &spec->request); + ASSERT_TRUE(request != nullptr); + ASSERT_EQ(object_no, request->object_no); + + *request->snapshot_delta = snap_delta; + spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE; + mock_image_ctx.image_ctx->op_work_queue->queue(&spec->dispatcher_ctx, r); + })); + } +}; + +TEST_F(TestMockIoImageRequest, AioWriteModifyTimestamp) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + mock_image_ctx.mtime_update_interval = 5; + + utime_t dummy = ceph_clock_now(); + dummy -= utime_t(10,0); + + EXPECT_CALL(mock_image_ctx, get_modify_timestamp()) + .Times(Exactly(3)) + .WillOnce(Return(dummy)) + .WillOnce(Return(dummy)) + .WillOnce(Return(dummy + utime_t(10,0))); + + EXPECT_CALL(mock_image_ctx, set_modify_timestamp(_)) + .Times(Exactly(1)); + + InSequence seq; + expect_is_journal_appending(mock_journal, false); + expect_object_request_send(mock_image_ctx, 0); + + C_SaferCond aio_comp_ctx_1, aio_comp_ctx_2; + AioCompletion *aio_comp_1 = AioCompletion::create_and_start( + &aio_comp_ctx_1, ictx, AIO_TYPE_WRITE); + + AioCompletion *aio_comp_2 = AioCompletion::create_and_start( + &aio_comp_ctx_2, ictx, AIO_TYPE_WRITE); + + bufferlist bl; + bl.append("1"); + MockImageWriteRequest mock_aio_image_write_1( + mock_image_ctx, aio_comp_1, {{0, 1}}, ImageArea::DATA, std::move(bl), + 0, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_write_1.send(); + } + ASSERT_EQ(0, aio_comp_ctx_1.wait()); + + expect_is_journal_appending(mock_journal, false); + expect_object_request_send(mock_image_ctx, 0); + + bl.append("1"); + MockImageWriteRequest mock_aio_image_write_2( + mock_image_ctx, aio_comp_2, {{0, 1}}, ImageArea::DATA, std::move(bl), + 0, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_write_2.send(); + } + ASSERT_EQ(0, aio_comp_ctx_2.wait()); +} + +TEST_F(TestMockIoImageRequest, AioReadAccessTimestamp) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + mock_image_ctx.atime_update_interval = 5; + + utime_t dummy = ceph_clock_now(); + dummy -= utime_t(10,0); + + EXPECT_CALL(mock_image_ctx, get_access_timestamp()) + .Times(Exactly(3)) + .WillOnce(Return(dummy)) + .WillOnce(Return(dummy)) + .WillOnce(Return(dummy + utime_t(10,0))); + + EXPECT_CALL(mock_image_ctx, set_access_timestamp(_)) + .Times(Exactly(1)); + + InSequence seq; + expect_object_request_send(mock_image_ctx, 0); + + C_SaferCond aio_comp_ctx_1, aio_comp_ctx_2; + AioCompletion *aio_comp_1 = AioCompletion::create_and_start( + &aio_comp_ctx_1, ictx, AIO_TYPE_READ); + + + ReadResult rr; + MockImageReadRequest mock_aio_image_read_1( + mock_image_ctx, aio_comp_1, {{0, 1}}, ImageArea::DATA, std::move(rr), + mock_image_ctx.get_data_io_context(), 0, 0, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_read_1.send(); + } + ASSERT_EQ(1, aio_comp_ctx_1.wait()); + + AioCompletion *aio_comp_2 = AioCompletion::create_and_start( + &aio_comp_ctx_2, ictx, AIO_TYPE_READ); + expect_object_request_send(mock_image_ctx, 0); + + MockImageReadRequest mock_aio_image_read_2( + mock_image_ctx, aio_comp_2, {{0, 1}}, ImageArea::DATA, std::move(rr), + mock_image_ctx.get_data_io_context(), 0, 0, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_read_2.send(); + } + ASSERT_EQ(1, aio_comp_ctx_2.wait()); +} + +TEST_F(TestMockIoImageRequest, PartialDiscard) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ictx->discard_granularity_bytes = 0; + + MockTestImageCtx mock_image_ctx(*ictx); + mock_image_ctx.journal = nullptr; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_object_discard_request(mock_image_ctx, 0, 16, 63, 0); + expect_object_discard_request(mock_image_ctx, 0, 84, 100, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); + MockImageDiscardRequest mock_aio_image_discard( + mock_image_ctx, aio_comp, {{16, 63}, {84, 100}}, ImageArea::DATA, + ictx->discard_granularity_bytes, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_discard.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, TailDiscard) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, resize(ictx, ictx->layout.object_size)); + ictx->discard_granularity_bytes = 2 * ictx->layout.object_size; + + MockTestImageCtx mock_image_ctx(*ictx); + mock_image_ctx.journal = nullptr; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_object_discard_request( + mock_image_ctx, 0, ictx->layout.object_size - 1024, 1024, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); + MockImageDiscardRequest mock_aio_image_discard( + mock_image_ctx, aio_comp, + {{ictx->layout.object_size - 1024, 1024}}, ImageArea::DATA, + ictx->discard_granularity_bytes, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_discard.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, DiscardGranularity) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, resize(ictx, ictx->layout.object_size)); + ictx->discard_granularity_bytes = 32; + + MockTestImageCtx mock_image_ctx(*ictx); + mock_image_ctx.journal = nullptr; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_object_discard_request(mock_image_ctx, 0, 32, 32, 0); + expect_object_discard_request(mock_image_ctx, 0, 96, 64, 0); + expect_object_discard_request( + mock_image_ctx, 0, ictx->layout.object_size - 32, 32, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); + MockImageDiscardRequest mock_aio_image_discard( + mock_image_ctx, aio_comp, + {{16, 63}, {96, 31}, {84, 100}, {ictx->layout.object_size - 33, 33}}, + ImageArea::DATA, ictx->discard_granularity_bytes, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_discard.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, PartialDiscardJournalAppendEnabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ictx->discard_granularity_bytes = 0; + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_is_journal_appending(mock_journal, true); + expect_journal_append_io_event(mock_journal, 0, 16, 63); + expect_journal_append_io_event(mock_journal, 1, 84, 100); + expect_object_discard_request(mock_image_ctx, 0, 16, 63, 0); + expect_object_discard_request(mock_image_ctx, 0, 84, 100, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); + MockImageDiscardRequest mock_aio_image_discard( + mock_image_ctx, aio_comp, {{16, 63}, {84, 100}}, ImageArea::DATA, + ictx->discard_granularity_bytes, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_discard.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, TailDiscardJournalAppendEnabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, resize(ictx, ictx->layout.object_size)); + ictx->discard_granularity_bytes = 2 * ictx->layout.object_size; + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_is_journal_appending(mock_journal, true); + expect_journal_append_io_event( + mock_journal, 0, ictx->layout.object_size - 1024, 1024); + expect_object_discard_request( + mock_image_ctx, 0, ictx->layout.object_size - 1024, 1024, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); + MockImageDiscardRequest mock_aio_image_discard( + mock_image_ctx, aio_comp, + {{ictx->layout.object_size - 1024, 1024}}, ImageArea::DATA, + ictx->discard_granularity_bytes, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_discard.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, PruneRequiredDiscardJournalAppendEnabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ictx->discard_granularity_bytes = 32; + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_is_journal_appending(mock_journal, true); + EXPECT_CALL(mock_journal, append_io_event_mock(_, _, _, _, _)).Times(0); + EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_)).Times(0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); + MockImageDiscardRequest mock_aio_image_discard( + mock_image_ctx, aio_comp, {{96, 31}}, ImageArea::DATA, + ictx->discard_granularity_bytes, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_discard.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, LengthModifiedDiscardJournalAppendEnabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ictx->discard_granularity_bytes = 32; + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_is_journal_appending(mock_journal, true); + expect_journal_append_io_event(mock_journal, 0, 32, 32); + expect_object_discard_request(mock_image_ctx, 0, 32, 32, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); + MockImageDiscardRequest mock_aio_image_discard( + mock_image_ctx, aio_comp, {{16, 63}}, ImageArea::DATA, + ictx->discard_granularity_bytes, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_discard.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, DiscardGranularityJournalAppendEnabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, resize(ictx, ictx->layout.object_size)); + ictx->discard_granularity_bytes = 32; + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_is_journal_appending(mock_journal, true); + expect_journal_append_io_event(mock_journal, 0, 32, 32); + expect_journal_append_io_event(mock_journal, 1, 96, 64); + expect_journal_append_io_event( + mock_journal, 2, ictx->layout.object_size - 32, 32); + expect_object_discard_request(mock_image_ctx, 0, 32, 32, 0); + expect_object_discard_request(mock_image_ctx, 0, 96, 64, 0); + expect_object_discard_request( + mock_image_ctx, 0, ictx->layout.object_size - 32, 32, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); + MockImageDiscardRequest mock_aio_image_discard( + mock_image_ctx, aio_comp, + {{16, 63}, {96, 31}, {84, 100}, {ictx->layout.object_size - 33, 33}}, + ImageArea::DATA, ictx->discard_granularity_bytes, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_discard.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, AioWriteJournalAppendDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_is_journal_appending(mock_journal, false); + expect_object_request_send(mock_image_ctx, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_WRITE); + + bufferlist bl; + bl.append("1"); + MockImageWriteRequest mock_aio_image_write( + mock_image_ctx, aio_comp, {{0, 1}}, ImageArea::DATA, std::move(bl), 0, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_write.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, AioDiscardJournalAppendDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ictx->discard_granularity_bytes = 0; + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_is_journal_appending(mock_journal, false); + expect_object_request_send(mock_image_ctx, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); + MockImageDiscardRequest mock_aio_image_discard( + mock_image_ctx, aio_comp, {{0, 1}}, ImageArea::DATA, + ictx->discard_granularity_bytes, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_discard.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, AioFlushJournalAppendDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_is_journal_appending(mock_journal, false); + expect_object_request_send(mock_image_ctx, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_FLUSH); + MockImageFlushRequest mock_aio_image_flush(mock_image_ctx, aio_comp, + FLUSH_SOURCE_USER, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_flush.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, AioWriteSameJournalAppendDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_is_journal_appending(mock_journal, false); + expect_object_request_send(mock_image_ctx, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_WRITESAME); + + bufferlist bl; + bl.append("1"); + MockImageWriteSameRequest mock_aio_image_writesame( + mock_image_ctx, aio_comp, {{0, 1}}, ImageArea::DATA, std::move(bl), 0, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_writesame.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, AioCompareAndWriteJournalAppendDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_get_modify_timestamp(mock_image_ctx, false); + expect_is_journal_appending(mock_journal, false); + expect_object_request_send(mock_image_ctx, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_COMPARE_AND_WRITE); + + bufferlist cmp_bl; + cmp_bl.append("1"); + bufferlist write_bl; + write_bl.append("1"); + uint64_t mismatch_offset; + MockImageCompareAndWriteRequest mock_aio_image_write( + mock_image_ctx, aio_comp, {{0, 1}}, ImageArea::DATA, + std::move(cmp_bl), std::move(write_bl), &mismatch_offset, 0, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_aio_image_write.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockIoImageRequest, ListSnaps) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + mock_image_ctx.layout.object_size = 16384; + mock_image_ctx.layout.stripe_unit = 4096; + mock_image_ctx.layout.stripe_count = 2; + + InSequence seq; + + SnapshotDelta object_snapshot_delta; + object_snapshot_delta[{5,6}].insert( + 0, 1024, {SPARSE_EXTENT_STATE_DATA, 1024}); + object_snapshot_delta[{5,5}].insert( + 4096, 4096, {SPARSE_EXTENT_STATE_ZEROED, 4096}); + expect_object_list_snaps_request(mock_image_ctx, 0, object_snapshot_delta, 0); + object_snapshot_delta = {}; + object_snapshot_delta[{5,6}].insert( + 1024, 3072, {SPARSE_EXTENT_STATE_DATA, 3072}); + object_snapshot_delta[{5,5}].insert( + 2048, 2048, {SPARSE_EXTENT_STATE_ZEROED, 2048}); + expect_object_list_snaps_request(mock_image_ctx, 1, object_snapshot_delta, 0); + + SnapshotDelta snapshot_delta; + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create_and_start( + &aio_comp_ctx, ictx, AIO_TYPE_GENERIC); + MockImageListSnapsRequest mock_image_list_snaps_request( + mock_image_ctx, aio_comp, {{0, 16384}, {16384, 16384}}, ImageArea::DATA, + {0, CEPH_NOSNAP}, 0, &snapshot_delta, {}); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + mock_image_list_snaps_request.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); + + SnapshotDelta expected_snapshot_delta; + expected_snapshot_delta[{5,6}].insert( + 0, 1024, {SPARSE_EXTENT_STATE_DATA, 1024}); + expected_snapshot_delta[{5,6}].insert( + 5120, 3072, {SPARSE_EXTENT_STATE_DATA, 3072}); + expected_snapshot_delta[{5,5}].insert( + 6144, 6144, {SPARSE_EXTENT_STATE_ZEROED, 6144}); + ASSERT_EQ(expected_snapshot_delta, snapshot_delta); +} + +} // namespace io +} // namespace librbd -- cgit v1.2.3