From 6801e905c99de98daef648e967ec0e504a92a21a Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Thu, 3 Sep 2020 15:32:23 -0400 Subject: [PATCH] librbd: list and merge parent snapshot deltas If a given object does not exist, optionally map the object's extents to the parent image and request the snapshot deltas from the parent chain. Signed-off-by: Jason Dillaman --- src/librbd/io/ObjectRequest.cc | 103 +++++++++++++++++- src/librbd/io/ObjectRequest.h | 6 +- src/librbd/io/Types.h | 4 + src/test/librbd/io/test_mock_ObjectRequest.cc | 86 +++++++++++++++ 4 files changed, 197 insertions(+), 2 deletions(-) diff --git a/src/librbd/io/ObjectRequest.cc b/src/librbd/io/ObjectRequest.cc index ac6d1b992432c..243c61461725a 100644 --- a/src/librbd/io/ObjectRequest.cc +++ b/src/librbd/io/ObjectRequest.cc @@ -19,6 +19,7 @@ #include "librbd/asio/Utils.h" #include "librbd/io/AioCompletion.h" #include "librbd/io/CopyupRequest.h" +#include "librbd/io/ImageRequest.h" #include "librbd/io/Utils.h" #include @@ -802,7 +803,7 @@ void ObjectListSnapsRequest::handle_list_snaps(int r) { if (r == -ENOENT) { // the object does not exist -- mark the missing extents zero_initial_extent(true); - this->finish(0); + list_from_parent(); return; } else if (r < 0) { lderr(cct) << "failed to retrieve object snapshot list: " << cpp_strerror(r) @@ -938,6 +939,106 @@ void ObjectListSnapsRequest::handle_list_snaps(int r) { ldout(cct, 20) << "snapshot_delta=" << snapshot_delta << dendl; + if (snapshot_delta_empty) { + list_from_parent(); + return; + } + + this->finish(0); +} + +template +void ObjectListSnapsRequest::list_from_parent() { + I *image_ctx = this->m_ictx; + auto cct = image_ctx->cct; + + ceph_assert(!m_snap_ids.empty()); + librados::snap_t snap_id_start = *m_snap_ids.begin(); + librados::snap_t snap_id_end = *m_snap_ids.rbegin(); + + std::unique_lock image_locker{image_ctx->image_lock}; + if ((snap_id_start > 0) || (image_ctx->parent == nullptr) || + ((m_list_snaps_flags & LIST_SNAPS_FLAG_DISABLE_LIST_FROM_PARENT) != 0)) { + image_locker.unlock(); + + this->finish(0); + return; + } + + // calculate reverse mapping onto the parent image + Extents parent_image_extents; + for (auto [object_off, object_len]: m_object_extents) { + Striper::extent_to_file(cct, &image_ctx->layout, this->m_object_no, + object_off, object_len, parent_image_extents); + } + + uint64_t parent_overlap = 0; + uint64_t object_overlap = 0; + int r = image_ctx->get_parent_overlap(snap_id_end, &parent_overlap); + if (r == 0) { + object_overlap = image_ctx->prune_parent_extents(parent_image_extents, + parent_overlap); + } + + if (object_overlap == 0) { + image_locker.unlock(); + + this->finish(0); + return; + } + + auto ctx = create_context_callback< + ObjectListSnapsRequest, + &ObjectListSnapsRequest::handle_list_from_parent>(this); + auto aio_comp = AioCompletion::create_and_start( + ctx, librbd::util::get_image_ctx(image_ctx->parent), AIO_TYPE_GENERIC); + ldout(cct, 20) << "aio_comp=" << aio_comp<< ", " + << "parent_image_extents " << parent_image_extents << dendl; + + ImageListSnapsRequest req( + *image_ctx->parent, aio_comp, std::move(parent_image_extents), {0, + image_ctx->parent->snap_id}, 0, &m_parent_snapshot_delta, this->m_trace); + req.send(); +} + +template +void ObjectListSnapsRequest::handle_list_from_parent(int r) { + I *image_ctx = this->m_ictx; + auto cct = image_ctx->cct; + + ldout(cct, 20) << "r=" << r << ", " + << "parent_snapshot_delta=" << m_parent_snapshot_delta + << dendl; + + // ignore special-case of fully empty dataset + m_parent_snapshot_delta.erase(INITIAL_WRITE_READ_SNAP_IDS); + if (m_parent_snapshot_delta.empty()) { + this->finish(0); + return; + } + + // the write/read snapshot id key is not useful for parent images so + // map the the special-case INITIAL_WRITE_READ_SNAP_IDS key + *m_snapshot_delta = {}; + auto& intervals = (*m_snapshot_delta)[INITIAL_WRITE_READ_SNAP_IDS]; + for (auto& [key, image_extents] : m_parent_snapshot_delta) { + for (auto image_extent : image_extents) { + auto state = image_extent.get_val().state; + + // map image-extents back to this object + striper::LightweightObjectExtents object_extents; + Striper::file_to_extents(cct, &image_ctx->layout, image_extent.get_off(), + image_extent.get_len(), 0, 0, &object_extents); + for (auto& object_extent : object_extents) { + ceph_assert(object_extent.object_no == this->m_object_no); + intervals.insert( + object_extent.offset, object_extent.length, + {state, object_extent.length}); + } + } + } + + ldout(cct, 20) << "snapshot_delta=" << *m_snapshot_delta << dendl; this->finish(0); } diff --git a/src/librbd/io/ObjectRequest.h b/src/librbd/io/ObjectRequest.h index b917322402847..dab819f637a73 100644 --- a/src/librbd/io/ObjectRequest.h +++ b/src/librbd/io/ObjectRequest.h @@ -475,11 +475,15 @@ private: neorados::SnapSet m_snap_set; boost::system::error_code m_ec; + SnapshotDelta m_parent_snapshot_delta; + void list_snaps(); void handle_list_snaps(int r); - void zero_initial_extent(bool dne); + void list_from_parent(); + void handle_list_from_parent(int r); + void zero_initial_extent(bool dne); }; } // namespace io diff --git a/src/librbd/io/Types.h b/src/librbd/io/Types.h index 9d03b33215209..ba2f64c9d1566 100644 --- a/src/librbd/io/Types.h +++ b/src/librbd/io/Types.h @@ -120,6 +120,10 @@ enum { OBJECT_DISPATCH_FLAG_WILL_RETRY_ON_ERROR = 1UL << 1 }; +enum { + LIST_SNAPS_FLAG_DISABLE_LIST_FROM_PARENT = 1UL << 0, +}; + enum SnapshotExtentState { SNAPSHOT_EXTENT_STATE_DNE, /* does not exist */ SNAPSHOT_EXTENT_STATE_ZEROED, diff --git a/src/test/librbd/io/test_mock_ObjectRequest.cc b/src/test/librbd/io/test_mock_ObjectRequest.cc index e6262c2a57809..064e1c3d1b0ac 100644 --- a/src/test/librbd/io/test_mock_ObjectRequest.cc +++ b/src/test/librbd/io/test_mock_ObjectRequest.cc @@ -10,6 +10,7 @@ #include "test/librados_test_stub/MockTestMemRadosClient.h" #include "include/rbd/librbd.hpp" #include "librbd/io/CopyupRequest.h" +#include "librbd/io/ImageRequest.h" #include "librbd/io/ObjectRequest.h" #include "librbd/io/Utils.h" @@ -55,6 +56,37 @@ struct CopyupRequest : public CopyupRequest* CopyupRequest::s_instance = nullptr; +template <> +struct ImageListSnapsRequest { + static ImageListSnapsRequest* s_instance; + + AioCompletion* aio_comp; + Extents image_extents; + SnapshotDelta* snapshot_delta; + + ImageListSnapsRequest() { + s_instance = this; + } + ImageListSnapsRequest( + librbd::MockImageCtx& image_ctx, AioCompletion* aio_comp, + Extents&& image_extents, SnapIds&& snap_ids, int list_snaps_flags, + SnapshotDelta* snapshot_delta, const ZTracer::Trace& parent_trace) { + ceph_assert(s_instance != nullptr); + s_instance->aio_comp = aio_comp; + s_instance->image_extents = image_extents; + s_instance->snapshot_delta = snapshot_delta; + } + + + MOCK_METHOD0(execute_send, void()); + void send() { + ceph_assert(s_instance != nullptr); + s_instance->execute_send(); + } +}; + +ImageListSnapsRequest* ImageListSnapsRequest::s_instance = nullptr; + namespace util { namespace { @@ -113,6 +145,7 @@ struct TestMockIoObjectRequest : public TestMockFixture { typedef ObjectListSnapsRequest MockObjectListSnapsRequest; typedef AbstractObjectWriteRequest MockAbstractObjectWriteRequest; typedef CopyupRequest MockCopyupRequest; + typedef ImageListSnapsRequest MockImageListSnapsRequest; typedef util::Mock MockUtils; void expect_object_may_exist(MockTestImageCtx &mock_image_ctx, @@ -349,6 +382,23 @@ struct TestMockIoObjectRequest : public TestMockFixture { return r; }))); } + + void expect_image_list_snaps(MockImageListSnapsRequest& req, + const Extents& image_extents, + const SnapshotDelta& image_snapshot_delta, + int r) { + EXPECT_CALL(req, execute_send()) + .WillOnce(Invoke( + [&req, image_extents, image_snapshot_delta, r]() { + ASSERT_EQ(image_extents, req.image_extents); + *req.snapshot_delta = image_snapshot_delta; + + auto aio_comp = req.aio_comp; + aio_comp->set_request_count(1); + aio_comp->add_request(); + aio_comp->complete_request(r); + })); + } }; TEST_F(TestMockIoObjectRequest, Read) { @@ -1740,6 +1790,42 @@ TEST_F(TestMockIoObjectRequest, ListSnapsError) { ASSERT_EQ(-EPERM, ctx.wait()); } +TEST_F(TestMockIoObjectRequest, ListSnapsParent) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + mock_image_ctx.parent = &mock_image_ctx; + + InSequence seq; + + expect_list_snaps(mock_image_ctx, {}, -ENOENT); + + expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0); + expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096); + + MockImageListSnapsRequest mock_image_list_snaps_request; + SnapshotDelta image_snapshot_delta; + image_snapshot_delta[{1,6}].insert( + 0, 1024, {SNAPSHOT_EXTENT_STATE_DATA, 1024}); + expect_image_list_snaps(mock_image_list_snaps_request, + {{0, 4096}}, image_snapshot_delta, 0); + + SnapshotDelta snapshot_delta; + C_SaferCond ctx; + auto req = MockObjectListSnapsRequest::create( + &mock_image_ctx, 0, + {{440320, 1024}, {2122728, 1024}, {2220032, 2048}, {3072000, 4096}}, + {0, CEPH_NOSNAP}, 0, {}, &snapshot_delta, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + SnapshotDelta expected_snapshot_delta; + expected_snapshot_delta[{0,0}].insert( + 0, 1024, {SNAPSHOT_EXTENT_STATE_DATA, 1024}); + ASSERT_EQ(expected_snapshot_delta, snapshot_delta); +} + } // namespace io } // namespace librbd -- 2.39.5