From b5caf4e26f3be2fac9a92103632b0588ab093e11 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Tue, 1 Dec 2020 12:34:34 -0500 Subject: [PATCH] librbd/migration: re-use RawSnapshot for RawFormat's HEAD revision Signed-off-by: Jason Dillaman --- src/librbd/migration/RawFormat.cc | 172 ++++++------------ src/librbd/migration/RawFormat.h | 9 +- .../librbd/migration/test_mock_RawFormat.cc | 152 +++++++++------- .../mock/migration/MockSnapshotInterface.h | 44 +++++ 4 files changed, 195 insertions(+), 182 deletions(-) create mode 100644 src/test/librbd/mock/migration/MockSnapshotInterface.h diff --git a/src/librbd/migration/RawFormat.cc b/src/librbd/migration/RawFormat.cc index 87f8b21c41991..8e8ecd1b03b41 100644 --- a/src/librbd/migration/RawFormat.cc +++ b/src/librbd/migration/RawFormat.cc @@ -9,101 +9,13 @@ #include "librbd/Utils.h" #include "librbd/io/AioCompletion.h" #include "librbd/io/ReadResult.h" -#include "librbd/migration/FileStream.h" +#include "librbd/migration/SnapshotInterface.h" #include "librbd/migration/SourceSpecBuilder.h" -#include "librbd/migration/StreamInterface.h" - -#define dout_subsys ceph_subsys_rbd -#undef dout_prefix -#define dout_prefix *_dout << "librbd::migration::RawFormat: " << this \ - << " " << __func__ << ": " namespace librbd { namespace migration { #define dout_subsys ceph_subsys_rbd -#undef dout_prefix -#define dout_prefix *_dout << "librbd::migration::RawFormat::OpenRequest " \ - << this << " " << __func__ << ": " - -template -struct RawFormat::OpenRequest { - RawFormat* raw_format; - Context* on_finish; - - uint64_t image_size = 0; - - OpenRequest(RawFormat* raw_format, Context* on_finish) - : raw_format(raw_format), on_finish(on_finish) { - } - - void send() { - open_stream(); - } - - void open_stream() { - auto cct = raw_format->m_image_ctx->cct; - ldout(cct, 10) << dendl; - - auto ctx = util::create_context_callback< - OpenRequest, &OpenRequest::handle_open_stream>(this); - raw_format->m_stream->open(ctx); - } - - void handle_open_stream(int r) { - auto cct = raw_format->m_image_ctx->cct; - ldout(cct, 10) << "r=" << r << dendl; - - if (r < 0) { - lderr(cct) << "failed to open stream: " << cpp_strerror(r) << dendl; - finish(r); - return; - } - - get_image_size(); - } - - void get_image_size() { - auto cct = raw_format->m_image_ctx->cct; - ldout(cct, 10) << dendl; - - auto ctx = util::create_context_callback< - OpenRequest, &OpenRequest::handle_get_image_size>(this); - raw_format->get_image_size(CEPH_NOSNAP, &image_size, ctx); - } - - void handle_get_image_size(int r) { - auto cct = raw_format->m_image_ctx->cct; - ldout(cct, 10) << "r=" << r << dendl; - - if (r < 0) { - lderr(cct) << "failed to open stream: " << cpp_strerror(r) << dendl; - finish(r); - return; - } - - raw_format->m_image_ctx->image_lock.lock(); - raw_format->m_image_ctx->size = image_size; - raw_format->m_image_ctx->image_lock.unlock(); - - finish(0); - } - - void finish(int r) { - auto cct = raw_format->m_image_ctx->cct; - ldout(cct, 10) << "r=" << r << dendl; - - if (r < 0) { - raw_format->m_image_ctx->state->close(new LambdaContext( - [r, on_finish=on_finish](int _) { on_finish->complete(r); })); - } else { - on_finish->complete(0); - } - - delete this; - } -}; - #undef dout_prefix #define dout_prefix *_dout << "librbd::migration::RawFormat: " << this \ << " " << __func__ << ": " @@ -121,17 +33,48 @@ void RawFormat::open(Context* on_finish) { auto cct = m_image_ctx->cct; ldout(cct, 10) << dendl; - int r = m_source_spec_builder->build_stream(m_json_object, &m_stream); + on_finish = new LambdaContext([this, on_finish](int r) { + handle_open(r, on_finish); }); + + // treat the base image as a HEAD-revision snapshot + int r = m_source_spec_builder->build_snapshot(m_json_object, CEPH_NOSNAP, + &m_snapshots[CEPH_NOSNAP]); + if (r < 0) { + lderr(cct) << "failed to build HEAD revision handler: " << cpp_strerror(r) + << dendl; + on_finish->complete(r); + return; + } + + auto gather_ctx = new C_Gather(cct, on_finish); + SnapshotInterface* previous_snapshot = nullptr; + for (auto& [_, snapshot] : m_snapshots) { + snapshot->open(previous_snapshot, gather_ctx->new_sub()); + previous_snapshot = snapshot.get(); + } + gather_ctx->activate(); +} + +template +void RawFormat::handle_open(int r, Context* on_finish) { + auto cct = m_image_ctx->cct; + ldout(cct, 10) << "r=" << r << dendl; + if (r < 0) { - lderr(cct) << "failed to build migration stream handler" << cpp_strerror(r) + lderr(cct) << "failed to open raw image: " << cpp_strerror(r) << dendl; - m_image_ctx->state->close( - new LambdaContext([r, on_finish](int _) { on_finish->complete(r); })); + m_image_ctx->state->close(new LambdaContext( + [r, on_finish=on_finish](int _) { on_finish->complete(r); })); return; } - auto req = new OpenRequest(this, on_finish); - req->send(); + auto head_snapshot = m_snapshots[CEPH_NOSNAP]; + ceph_assert(head_snapshot); + + m_image_ctx->image_lock.lock(); + m_image_ctx->size = head_snapshot->get_snap_info().size; + m_image_ctx->image_lock.unlock(); + on_finish->complete(0); } template @@ -139,12 +82,12 @@ void RawFormat::close(Context* on_finish) { auto cct = m_image_ctx->cct; ldout(cct, 10) << dendl; - if (!m_stream) { - on_finish->complete(0); - return; + auto gather_ctx = new C_Gather(cct, on_finish); + for (auto& [snap_id, snapshot] : m_snapshots) { + snapshot->close(gather_ctx->new_sub()); } - m_stream->close(on_finish); + gather_ctx->activate(); } template @@ -153,6 +96,12 @@ void RawFormat::get_snapshots(SnapInfos* snap_infos, Context* on_finish) { ldout(cct, 10) << dendl; snap_infos->clear(); + for (auto& [snap_id, snapshot] : m_snapshots) { + if (snap_id == CEPH_NOSNAP) { + continue; + } + snap_infos->emplace(snap_id, snapshot->get_snap_info()); + } on_finish->complete(0); } @@ -162,12 +111,14 @@ void RawFormat::get_image_size(uint64_t snap_id, uint64_t* size, auto cct = m_image_ctx->cct; ldout(cct, 10) << dendl; - if (snap_id != CEPH_NOSNAP) { - on_finish->complete(-EINVAL); + auto snapshot_it = m_snapshots.find(snap_id); + if (snapshot_it == m_snapshots.end()) { + on_finish->complete(-ENOENT); return; } - m_stream->get_size(size, on_finish); + *size = snapshot_it->second->get_snap_info().size; + on_finish->complete(0); } template @@ -178,20 +129,15 @@ bool RawFormat::read( auto cct = m_image_ctx->cct; ldout(cct, 20) << "image_extents=" << image_extents << dendl; - if (snap_id != CEPH_NOSNAP) { - aio_comp->fail(-EINVAL); + auto snapshot_it = m_snapshots.find(snap_id); + if (snapshot_it == m_snapshots.end()) { + aio_comp->fail(-ENOENT); return true; } - aio_comp->read_result = std::move(read_result); - aio_comp->read_result.set_image_extents(image_extents); - - aio_comp->set_request_count(1); - auto ctx = new io::ReadResult::C_ImageReadRequest(aio_comp, - 0, image_extents); - - // raw directly maps the image-extent IO down to a byte IO extent - m_stream->read(std::move(image_extents), &ctx->bl, ctx); + snapshot_it->second->read(aio_comp, std::move(image_extents), + std::move(read_result), op_flags, read_flags, + parent_trace); return true; } diff --git a/src/librbd/migration/RawFormat.h b/src/librbd/migration/RawFormat.h index 450fd7d44064a..0febfe3fb1d36 100644 --- a/src/librbd/migration/RawFormat.h +++ b/src/librbd/migration/RawFormat.h @@ -8,6 +8,7 @@ #include "librbd/Types.h" #include "librbd/migration/FormatInterface.h" #include "json_spirit/json_spirit.h" +#include #include struct Context; @@ -20,7 +21,7 @@ struct ImageCtx; namespace migration { template struct SourceSpecBuilder; -struct StreamInterface; +struct SnapshotInterface; template class RawFormat : public FormatInterface { @@ -54,14 +55,16 @@ public: Context* on_finish) override; private: - struct OpenRequest; + typedef std::shared_ptr Snapshot; + typedef std::map Snapshots; ImageCtxT* m_image_ctx; json_spirit::mObject m_json_object; const SourceSpecBuilder* m_source_spec_builder; - std::shared_ptr m_stream; + Snapshots m_snapshots; + void handle_open(int r, Context* on_finish); }; } // namespace migration diff --git a/src/test/librbd/migration/test_mock_RawFormat.cc b/src/test/librbd/migration/test_mock_RawFormat.cc index 023a50b93f433..0adf6b9395cf6 100644 --- a/src/test/librbd/migration/test_mock_RawFormat.cc +++ b/src/test/librbd/migration/test_mock_RawFormat.cc @@ -3,10 +3,9 @@ #include "test/librbd/test_mock_fixture.h" #include "test/librbd/test_support.h" -#include "test/librbd/mock/migration/MockStreamInterface.h" +#include "test/librbd/mock/migration/MockSnapshotInterface.h" #include "include/rbd_types.h" #include "common/ceph_mutex.h" -#include "librbd/migration/FileStream.h" #include "librbd/migration/RawFormat.h" #include "librbd/migration/SourceSpecBuilder.h" #include "gtest/gtest.h" @@ -28,8 +27,8 @@ namespace migration { template<> struct SourceSpecBuilder { - MOCK_CONST_METHOD2(build_stream, int(const json_spirit::mObject&, - std::shared_ptr*)); + MOCK_CONST_METHOD3(build_snapshot, int(const json_spirit::mObject&, uint64_t, + std::shared_ptr*)); }; @@ -41,6 +40,8 @@ struct SourceSpecBuilder { using ::testing::_; using ::testing::InSequence; using ::testing::Invoke; +using ::testing::ReturnRef; +using ::testing::WithArg; using ::testing::WithArgs; namespace librbd { @@ -65,42 +66,48 @@ public: json_object["stream"] = stream_obj; } - void expect_build_stream(MockSourceSpecBuilder& mock_source_spec_builder, - MockStreamInterface* mock_stream_interface, int r) { - EXPECT_CALL(mock_source_spec_builder, build_stream(_, _)) - .WillOnce(WithArgs<1>(Invoke([mock_stream_interface, r] - (std::shared_ptr* ptr) { - ptr->reset(mock_stream_interface); + void expect_build_snapshot(MockSourceSpecBuilder& mock_source_spec_builder, + uint64_t index, + MockSnapshotInterface* mock_snapshot_interface, + int r) { + EXPECT_CALL(mock_source_spec_builder, build_snapshot(_, index, _)) + .WillOnce(WithArgs<2>(Invoke([mock_snapshot_interface, r] + (std::shared_ptr* ptr) { + ptr->reset(mock_snapshot_interface); return r; }))); } - void expect_stream_open(MockStreamInterface& mock_stream_interface, int r) { - EXPECT_CALL(mock_stream_interface, open(_)) - .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); })); + void expect_snapshot_open(MockSnapshotInterface& mock_snapshot_interface, + int r) { + EXPECT_CALL(mock_snapshot_interface, open(_, _)) + .WillOnce(WithArg<1>(Invoke([r](Context* ctx) { ctx->complete(r); }))); } - void expect_stream_close(MockStreamInterface& mock_stream_interface, int r) { - EXPECT_CALL(mock_stream_interface, close(_)) + void expect_snapshot_close(MockSnapshotInterface& mock_snapshot_interface, + int r) { + EXPECT_CALL(mock_snapshot_interface, close(_)) .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); })); } - void expect_stream_get_size(MockStreamInterface& mock_stream_interface, - uint64_t size, int r) { - EXPECT_CALL(mock_stream_interface, get_size(_, _)) - .WillOnce(Invoke([size, r](uint64_t* out_size, Context* ctx) { - *out_size = size; - ctx->complete(r); - })); + void expect_snapshot_get_info(MockSnapshotInterface& mock_snapshot_interface, + const SnapInfo& snap_info) { + EXPECT_CALL(mock_snapshot_interface, get_snap_info()) + .WillOnce(ReturnRef(snap_info)); } - void expect_stream_read(MockStreamInterface& mock_stream_interface, - const io::Extents& byte_extents, - const bufferlist& bl, int r) { - EXPECT_CALL(mock_stream_interface, read(byte_extents, _, _)) - .WillOnce(WithArgs<1, 2>(Invoke([bl, r] - (bufferlist* out_bl, Context* ctx) { - *out_bl = bl; + void expect_snapshot_read(MockSnapshotInterface& mock_snapshot_interface, + const io::Extents& image_extents, + const bufferlist& bl, int r) { + EXPECT_CALL(mock_snapshot_interface, read(_, image_extents, _)) + .WillOnce(WithArgs<0, 2>(Invoke([bl, image_extents, r] + (io::AioCompletion* aio_comp, io::ReadResult& read_result) { + aio_comp->read_result = std::move(read_result); + aio_comp->read_result.set_image_extents(image_extents); + aio_comp->set_request_count(1); + auto ctx = new io::ReadResult::C_ImageReadRequest(aio_comp, 0, + image_extents); + ctx->bl = std::move(bl); ctx->complete(r); }))); } @@ -121,13 +128,15 @@ TEST_F(TestMockMigrationRawFormat, OpenClose) { InSequence seq; MockSourceSpecBuilder mock_source_spec_builder; - auto mock_stream_interface = new MockStreamInterface(); - expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0); + auto mock_snapshot_interface = new MockSnapshotInterface(); + expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP, + mock_snapshot_interface, 0); - expect_stream_open(*mock_stream_interface, 0); - expect_stream_get_size(*mock_stream_interface, 0, 0); + expect_snapshot_open(*mock_snapshot_interface, 0); + SnapInfo snap_info{{}, {}, 123, {}, 0, 0, {}}; + expect_snapshot_get_info(*mock_snapshot_interface, snap_info); - expect_stream_close(*mock_stream_interface, 0); + expect_snapshot_close(*mock_snapshot_interface, 0); MockRawFormat mock_raw_format(&mock_image_ctx, json_object, &mock_source_spec_builder); @@ -147,10 +156,11 @@ TEST_F(TestMockMigrationRawFormat, OpenError) { InSequence seq; MockSourceSpecBuilder mock_source_spec_builder; - auto mock_stream_interface = new MockStreamInterface(); - expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0); + auto mock_snapshot_interface = new MockSnapshotInterface(); + expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP, + mock_snapshot_interface, 0); - expect_stream_open(*mock_stream_interface, -ENOENT); + expect_snapshot_open(*mock_snapshot_interface, -ENOENT); expect_close(mock_image_ctx, 0); @@ -168,13 +178,15 @@ TEST_F(TestMockMigrationRawFormat, GetSnapshots) { InSequence seq; MockSourceSpecBuilder mock_source_spec_builder; - auto mock_stream_interface = new MockStreamInterface(); - expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0); + auto mock_snapshot_interface = new MockSnapshotInterface(); + expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP, + mock_snapshot_interface, 0); - expect_stream_open(*mock_stream_interface, 0); - expect_stream_get_size(*mock_stream_interface, 0, 0); + expect_snapshot_open(*mock_snapshot_interface, 0); + SnapInfo snap_info{{}, {}, 123, {}, 0, 0, {}}; + expect_snapshot_get_info(*mock_snapshot_interface, snap_info); - expect_stream_close(*mock_stream_interface, 0); + expect_snapshot_close(*mock_snapshot_interface, 0); MockRawFormat mock_raw_format(&mock_image_ctx, json_object, &mock_source_spec_builder); @@ -200,15 +212,17 @@ TEST_F(TestMockMigrationRawFormat, GetImageSize) { InSequence seq; MockSourceSpecBuilder mock_source_spec_builder; - auto mock_stream_interface = new MockStreamInterface(); - expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0); + auto mock_snapshot_interface = new MockSnapshotInterface(); + expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP, + mock_snapshot_interface, 0); - expect_stream_open(*mock_stream_interface, 0); - expect_stream_get_size(*mock_stream_interface, 0, 0); + expect_snapshot_open(*mock_snapshot_interface, 0); + SnapInfo snap_info{{}, {}, 123, {}, 0, 0, {}}; + expect_snapshot_get_info(*mock_snapshot_interface, snap_info); - expect_stream_get_size(*mock_stream_interface, 123, 0); + expect_snapshot_get_info(*mock_snapshot_interface, snap_info); - expect_stream_close(*mock_stream_interface, 0); + expect_snapshot_close(*mock_snapshot_interface, 0); MockRawFormat mock_raw_format(&mock_image_ctx, json_object, &mock_source_spec_builder); @@ -228,19 +242,21 @@ TEST_F(TestMockMigrationRawFormat, GetImageSize) { ASSERT_EQ(0, ctx3.wait()); } -TEST_F(TestMockMigrationRawFormat, GetImageSizeSnapshot) { +TEST_F(TestMockMigrationRawFormat, GetImageSizeSnapshotDNE) { MockTestImageCtx mock_image_ctx(*m_image_ctx); InSequence seq; MockSourceSpecBuilder mock_source_spec_builder; - auto mock_stream_interface = new MockStreamInterface(); - expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0); + auto mock_snapshot_interface = new MockSnapshotInterface(); + expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP, + mock_snapshot_interface, 0); - expect_stream_open(*mock_stream_interface, 0); - expect_stream_get_size(*mock_stream_interface, 0, 0); + expect_snapshot_open(*mock_snapshot_interface, 0); + SnapInfo snap_info{{}, {}, 123, {}, 0, 0, {}}; + expect_snapshot_get_info(*mock_snapshot_interface, snap_info); - expect_stream_close(*mock_stream_interface, 0); + expect_snapshot_close(*mock_snapshot_interface, 0); MockRawFormat mock_raw_format(&mock_image_ctx, json_object, &mock_source_spec_builder); @@ -252,7 +268,7 @@ TEST_F(TestMockMigrationRawFormat, GetImageSizeSnapshot) { C_SaferCond ctx2; uint64_t size; mock_raw_format.get_image_size(0, &size, &ctx2); - ASSERT_EQ(-EINVAL, ctx2.wait()); + ASSERT_EQ(-ENOENT, ctx2.wait()); C_SaferCond ctx3; mock_raw_format.close(&ctx3); @@ -265,17 +281,19 @@ TEST_F(TestMockMigrationRawFormat, Read) { InSequence seq; MockSourceSpecBuilder mock_source_spec_builder; - auto mock_stream_interface = new MockStreamInterface(); - expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0); + auto mock_snapshot_interface = new MockSnapshotInterface(); + expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP, + mock_snapshot_interface, 0); - expect_stream_open(*mock_stream_interface, 0); - expect_stream_get_size(*mock_stream_interface, 0, 0); + expect_snapshot_open(*mock_snapshot_interface, 0); + SnapInfo snap_info{{}, {}, 123, {}, 0, 0, {}}; + expect_snapshot_get_info(*mock_snapshot_interface, snap_info); bufferlist expect_bl; expect_bl.append(std::string(123, '1')); - expect_stream_read(*mock_stream_interface, {{123, 123}}, expect_bl, 0); + expect_snapshot_read(*mock_snapshot_interface, {{123, 123}}, expect_bl, 0); - expect_stream_close(*mock_stream_interface, 0); + expect_snapshot_close(*mock_snapshot_interface, 0); MockRawFormat mock_raw_format(&mock_image_ctx, json_object, &mock_source_spec_builder); @@ -305,13 +323,15 @@ TEST_F(TestMockMigrationRawFormat, ListSnaps) { InSequence seq; MockSourceSpecBuilder mock_source_spec_builder; - auto mock_stream_interface = new MockStreamInterface(); - expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0); + auto mock_snapshot_interface = new MockSnapshotInterface(); + expect_build_snapshot(mock_source_spec_builder, CEPH_NOSNAP, + mock_snapshot_interface, 0); - expect_stream_open(*mock_stream_interface, 0); - expect_stream_get_size(*mock_stream_interface, 0, 0); + expect_snapshot_open(*mock_snapshot_interface, 0); + SnapInfo snap_info{{}, {}, 123, {}, 0, 0, {}}; + expect_snapshot_get_info(*mock_snapshot_interface, snap_info); - expect_stream_close(*mock_stream_interface, 0); + expect_snapshot_close(*mock_snapshot_interface, 0); MockRawFormat mock_raw_format(&mock_image_ctx, json_object, &mock_source_spec_builder); diff --git a/src/test/librbd/mock/migration/MockSnapshotInterface.h b/src/test/librbd/mock/migration/MockSnapshotInterface.h new file mode 100644 index 0000000000000..abb6d1a08a76a --- /dev/null +++ b/src/test/librbd/mock/migration/MockSnapshotInterface.h @@ -0,0 +1,44 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_SNAPSHOT_INTERFACE_H +#define CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_SNAPSHOT_INTERFACE_H + +#include "include/buffer.h" +#include "gmock/gmock.h" +#include "librbd/io/AioCompletion.h" +#include "librbd/io/ReadResult.h" +#include "librbd/io/Types.h" +#include "librbd/migration/SnapshotInterface.h" + +namespace librbd { +namespace migration { + +struct MockSnapshotInterface : public SnapshotInterface { + MOCK_METHOD2(open, void(SnapshotInterface*, Context*)); + MOCK_METHOD1(close, void(Context*)); + + MOCK_CONST_METHOD0(get_snap_info, const SnapInfo&()); + + MOCK_METHOD3(read, void(io::AioCompletion*, const io::Extents&, + io::ReadResult&)); + void read(io::AioCompletion* aio_comp, io::Extents&& image_extents, + io::ReadResult&& read_result, int op_flags, int read_flags, + const ZTracer::Trace &parent_trace) override { + read(aio_comp, image_extents, read_result); + } + + MOCK_METHOD3(list_snap, void(const io::Extents&, io::SparseExtents*, + Context*)); + void list_snap(io::Extents&& image_extents, int list_snaps_flags, + io::SparseExtents* sparse_extents, + const ZTracer::Trace &parent_trace, + Context* on_finish) override { + list_snap(image_extents, sparse_extents, on_finish); + } +}; + +} // namespace migration +} // namespace librbd + +#endif // CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_SNAPSHOT_INTERFACE_H -- 2.39.5