]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: list and merge parent snapshot deltas
authorJason Dillaman <dillaman@redhat.com>
Thu, 3 Sep 2020 19:32:23 +0000 (15:32 -0400)
committerJason Dillaman <dillaman@redhat.com>
Mon, 21 Sep 2020 11:51:55 +0000 (07:51 -0400)
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 <dillaman@redhat.com>
src/librbd/io/ObjectRequest.cc
src/librbd/io/ObjectRequest.h
src/librbd/io/Types.h
src/test/librbd/io/test_mock_ObjectRequest.cc

index ac6d1b992432cbca41e2594edee635bbc8206578..243c61461725a85b80b03175cfac185d922ecc38 100644 (file)
@@ -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 <boost/optional.hpp>
@@ -802,7 +803,7 @@ void ObjectListSnapsRequest<I>::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<I>::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 <typename I>
+void ObjectListSnapsRequest<I>::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<I>,
+    &ObjectListSnapsRequest<I>::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<I> 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 <typename I>
+void ObjectListSnapsRequest<I>::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);
 }
 
index b9173224028472450437b31d24df97a3a0d65357..dab819f637a733c9a58a48fbe8a91f6b1e7ac17c 100644 (file)
@@ -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
index 9d03b332152097e5f199364bb142ad1fca73de6f..ba2f64c9d15667f5bdfdc935cda1a33bd1c33fca 100644 (file)
@@ -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,
index e6262c2a57809d004307269821bdb3b7dc060142..064e1c3d1b0acca0aa43326cc73f04cb8634d943 100644 (file)
@@ -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<librbd::MockTestImageCtx> : public CopyupRequest<librbd::Mo
 
 CopyupRequest<librbd::MockTestImageCtx>* CopyupRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
 
+template <>
+struct ImageListSnapsRequest<librbd::MockTestImageCtx> {
+  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<librbd::MockTestImageCtx>* ImageListSnapsRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
 namespace util {
 
 namespace {
@@ -113,6 +145,7 @@ struct TestMockIoObjectRequest : public TestMockFixture {
   typedef ObjectListSnapsRequest<librbd::MockTestImageCtx> MockObjectListSnapsRequest;
   typedef AbstractObjectWriteRequest<librbd::MockTestImageCtx> MockAbstractObjectWriteRequest;
   typedef CopyupRequest<librbd::MockTestImageCtx> MockCopyupRequest;
+  typedef ImageListSnapsRequest<librbd::MockTestImageCtx> 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