]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: fix deep copy clone to destination with larger object size
authorMykola Golub <mgolub@suse.com>
Tue, 8 May 2018 12:29:38 +0000 (15:29 +0300)
committerJason Dillaman <dillaman@redhat.com>
Wed, 16 May 2018 18:05:55 +0000 (14:05 -0400)
When assembling a destination object from source objects we need to
read from parent if some (but not all) of assembles do not exist.

Signed-off-by: Mykola Golub <mgolub@suse.com>
(cherry picked from commit 947993a8d36f60bde05bc7b9a49c333dd51da4c5)

Conflicts:
src/test/librbd/deep_copy/test_mock_ImageCopyRequest.cc: trivial resolution

src/librbd/deep_copy/ImageCopyRequest.cc
src/librbd/deep_copy/ImageCopyRequest.h
src/librbd/deep_copy/ObjectCopyRequest.cc
src/librbd/deep_copy/ObjectCopyRequest.h
src/test/librbd/deep_copy/test_mock_ImageCopyRequest.cc
src/test/librbd/deep_copy/test_mock_ObjectCopyRequest.cc
src/test/librbd/mock/MockImageCtx.h

index 4a315f9231bcd2f89a70454330cd27d3b72db7ab..d101aa8d17d62e1587ee8ca716d759d366004786 100644 (file)
@@ -6,6 +6,9 @@
 #include "common/errno.h"
 #include "librbd/Utils.h"
 #include "librbd/deep_copy/Utils.h"
+#include "librbd/image/CloseRequest.h"
+#include "librbd/image/OpenRequest.h"
+#include "librbd/image/SetSnapRequest.h"
 #include "osdc/Striper.h"
 
 #define dout_subsys ceph_subsys_rbd
@@ -16,6 +19,7 @@
 namespace librbd {
 namespace deep_copy {
 
+using librbd::util::create_context_callback;
 using librbd::util::unique_lock_name;
 
 template <typename I>
@@ -44,7 +48,7 @@ void ImageCopyRequest<I>::send() {
     return;
   }
 
-  send_object_copies();
+  send_open_parent();
 }
 
 template <typename I>
@@ -55,6 +59,90 @@ void ImageCopyRequest<I>::cancel() {
   m_canceled = true;
 }
 
+template <typename I>
+void ImageCopyRequest<I>::send_open_parent() {
+  {
+    RWLock::RLocker snap_locker(m_src_image_ctx->snap_lock);
+    RWLock::RLocker parent_locker(m_src_image_ctx->parent_lock);
+
+    auto snap_id = m_snap_map.begin()->first;
+    auto parent_info = m_src_image_ctx->get_parent_info(snap_id);
+    if (parent_info == nullptr) {
+        ldout(m_cct, 20) << "could not find parent info for snap id " << snap_id
+                         << dendl;
+    } else {
+      m_parent_spec = parent_info->spec;
+    }
+  }
+
+  if (m_parent_spec.pool_id == -1) {
+    send_object_copies();
+    return;
+  }
+
+  ldout(m_cct, 20) << "pool_id=" << m_parent_spec.pool_id << ", image_id="
+                   << m_parent_spec.image_id << ", snap_id="
+                   << m_parent_spec.snap_id << dendl;
+
+  librados::Rados rados(m_src_image_ctx->md_ctx);
+  librados::IoCtx parent_io_ctx;
+  int r = rados.ioctx_create2(m_parent_spec.pool_id, parent_io_ctx);
+  if (r < 0) {
+    lderr(m_cct) << "failed to access parent pool (id=" << m_parent_spec.pool_id
+                 << "): " << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  m_src_parent_image_ctx = I::create("", m_parent_spec.image_id, nullptr, parent_io_ctx, true);
+
+  auto ctx = create_context_callback<
+    ImageCopyRequest<I>, &ImageCopyRequest<I>::handle_open_parent>(this);
+
+  auto req = image::OpenRequest<I>::create(m_src_parent_image_ctx, false, ctx);
+  req->send();
+}
+
+template <typename I>
+void ImageCopyRequest<I>::handle_open_parent(int r) {
+  ldout(m_cct, 20) << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(m_cct) << "failed to open parent: " << cpp_strerror(r) << dendl;
+    m_src_parent_image_ctx->destroy();
+    m_src_parent_image_ctx = nullptr;
+    finish(r);
+    return;
+  }
+
+  send_set_parent_snap();
+}
+
+template <typename I>
+void ImageCopyRequest<I>::send_set_parent_snap() {
+  ldout(m_cct, 20) << dendl;
+
+  auto ctx = create_context_callback<
+    ImageCopyRequest<I>, &ImageCopyRequest<I>::handle_set_parent_snap>(this);
+  auto req = image::SetSnapRequest<I>::create(*m_src_parent_image_ctx,
+                                              m_parent_spec.snap_id, ctx);
+  req->send();
+}
+
+template <typename I>
+void ImageCopyRequest<I>::handle_set_parent_snap(int r) {
+  ldout(m_cct, 20) << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(m_cct) << "failed to set parent snap: " << cpp_strerror(r) << dendl;
+    m_ret_val = r;
+    send_close_parent();
+    return;
+  }
+
+  send_object_copies();
+}
+
 template <typename I>
 void ImageCopyRequest<I>::send_object_copies() {
   m_object_no = 0;
@@ -90,7 +178,7 @@ void ImageCopyRequest<I>::send_object_copies() {
   }
 
   if (complete) {
-    finish(m_ret_val);
+    send_close_parent();
   }
 }
 
@@ -118,7 +206,8 @@ void ImageCopyRequest<I>::send_next_object_copy() {
       handle_object_copy(ono, r);
     });
   ObjectCopyRequest<I> *req = ObjectCopyRequest<I>::create(
-      m_src_image_ctx, m_dst_image_ctx, m_snap_map, ono, ctx);
+      m_src_image_ctx, m_src_parent_image_ctx, m_dst_image_ctx, m_snap_map, ono,
+      ctx);
   req->send();
 }
 
@@ -158,8 +247,40 @@ void ImageCopyRequest<I>::handle_object_copy(uint64_t object_no, int r) {
   }
 
   if (complete) {
+    send_close_parent();
+  }
+}
+
+template <typename I>
+void ImageCopyRequest<I>::send_close_parent() {
+  if (m_src_parent_image_ctx == nullptr) {
     finish(m_ret_val);
+    return;
   }
+
+  ldout(m_cct, 20) << dendl;
+
+  auto ctx = create_context_callback<
+    ImageCopyRequest<I>, &ImageCopyRequest<I>::handle_close_parent>(this);
+  auto req = image::CloseRequest<I>::create(m_src_parent_image_ctx, ctx);
+  req->send();
+}
+
+template <typename I>
+void ImageCopyRequest<I>::handle_close_parent(int r) {
+  ldout(m_cct, 20) << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(m_cct) << "failed to close parent: " << cpp_strerror(r) << dendl;
+    if (m_ret_val == 0) {
+      m_ret_val = r;
+    }
+  }
+
+  m_src_parent_image_ctx->destroy();
+  m_src_parent_image_ctx = nullptr;
+
+  finish(m_ret_val);
 }
 
 template <typename I>
index bdba8fc59409ea00b8f86295ced8b8c988eeb9c2..8c395a34c9fd2c9923a5adacb33375c0f5b65bc0 100644 (file)
@@ -53,12 +53,23 @@ private:
   /**
    * @verbatim
    *
-   * <start>   . . . . .
+   * <start>
+   *    |
+   *    v
+   * OPEN_PARENT (skip if not needed)
+   *    |
+   *    v
+   * SET_PARENT_SNAP (skip if not needed)
+   *    |
+   *    |      . . . . .
    *    |      .       .  (parallel execution of
    *    v      v       .   multiple objects at once)
    * COPY_OBJECT . . . .
    *    |
    *    v
+   * CLOSE_PARENT (skip if not needed)
+   *    |
+   *    v
    * <finish>
    *
    * @endverbatim
@@ -85,11 +96,22 @@ private:
   bool m_updating_progress = false;
   SnapMap m_snap_map;
   int m_ret_val = 0;
+  ParentSpec m_parent_spec;
+  ImageCtxT *m_src_parent_image_ctx = nullptr;
+
+  void send_open_parent();
+  void handle_open_parent(int r);
+
+  void send_set_parent_snap();
+  void handle_set_parent_snap(int r);
 
   void send_object_copies();
   void send_next_object_copy();
   void handle_object_copy(uint64_t object_no, int r);
 
+  void send_close_parent();
+  void handle_close_parent(int r);
+
   void finish(int r);
 };
 
index bd83ebfae8ce9b712611cd2c65da717ad91c4328..212d90d0bb790e8c10f1344d3b49f1914041c977 100644 (file)
@@ -7,6 +7,9 @@
 #include "librbd/ExclusiveLock.h"
 #include "librbd/ObjectMap.h"
 #include "librbd/Utils.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageRequest.h"
+#include "librbd/io/ReadResult.h"
 #include "osdc/Striper.h"
 
 #define dout_subsys ceph_subsys_rbd
@@ -37,13 +40,17 @@ using librbd::util::create_context_callback;
 using librbd::util::create_rados_callback;
 
 template <typename I>
-ObjectCopyRequest<I>::ObjectCopyRequest(I *src_image_ctx, I *dst_image_ctx,
+ObjectCopyRequest<I>::ObjectCopyRequest(I *src_image_ctx,
+                                        I *src_parent_image_ctx,
+                                        I *dst_image_ctx,
                                         const SnapMap &snap_map,
                                         uint64_t dst_object_number,
                                         Context *on_finish)
-  : m_src_image_ctx(src_image_ctx), m_dst_image_ctx(dst_image_ctx),
-    m_cct(dst_image_ctx->cct), m_snap_map(snap_map),
-    m_dst_object_number(dst_object_number), m_on_finish(on_finish) {
+  : m_src_image_ctx(src_image_ctx),
+    m_src_parent_image_ctx(src_parent_image_ctx),
+    m_dst_image_ctx(dst_image_ctx), m_cct(dst_image_ctx->cct),
+    m_snap_map(snap_map), m_dst_object_number(dst_object_number),
+    m_on_finish(on_finish) {
   assert(!m_snap_map.empty());
 
   m_src_io_ctx.dup(m_src_image_ctx->data_ctx);
@@ -92,11 +99,7 @@ void ObjectCopyRequest<I>::handle_list_snaps(int r) {
 
   ldout(m_cct, 20) << "r=" << r << dendl;
 
-  if (r == -ENOENT) {
-    r = 0;
-  }
-
-  if (r < 0) {
+  if (r < 0 && r != -ENOENT) {
     lderr(m_cct) << "failed to list snaps: " << cpp_strerror(r) << dendl;
     finish(r);
     return;
@@ -115,7 +118,19 @@ void ObjectCopyRequest<I>::handle_list_snaps(int r) {
     m_retry_snap_set = {};
   }
 
-  compute_read_ops();
+  if (r == -ENOENT) {
+    for (auto &it : m_src_object_extents) {
+      auto &e = it.second;
+      if (e.object_no == m_src_ono) {
+        e.noent = true;
+      }
+    }
+    m_read_ops = {};
+    m_read_snaps = {};
+    m_zero_interval = {};
+  } else {
+    compute_read_ops();
+  }
   send_read_object();
 }
 
@@ -135,16 +150,7 @@ void ObjectCopyRequest<I>::send_read_object() {
     }
 
     // all objects have been read
-
-    compute_zero_ops();
-
-    if (m_write_ops.empty()) {
-      // nothing to copy
-      finish(0);
-      return;
-    }
-
-    send_write_object();
+    send_read_from_parent();
     return;
   }
 
@@ -220,6 +226,71 @@ void ObjectCopyRequest<I>::handle_read_object(int r) {
   send_read_object();
 }
 
+template <typename I>
+void ObjectCopyRequest<I>::send_read_from_parent() {
+  io::Extents image_extents;
+  compute_read_from_parent_ops(&image_extents);
+  if (image_extents.empty()) {
+    handle_read_from_parent(0);
+    return;
+  }
+
+  ldout(m_cct, 20) << dendl;
+
+  assert(m_src_parent_image_ctx != nullptr);
+
+  auto ctx = create_context_callback<
+    ObjectCopyRequest<I>, &ObjectCopyRequest<I>::handle_read_from_parent>(this);
+  auto comp = io::AioCompletion::create_and_start(
+    ctx, util::get_image_ctx(m_src_image_ctx), io::AIO_TYPE_READ);
+  ldout(m_cct, 20) << "completion " << comp << ", extents " << image_extents
+                   << dendl;
+  io::ImageRequest<I>::aio_read(m_src_parent_image_ctx, comp,
+                                std::move(image_extents),
+                                io::ReadResult{&m_read_from_parent_data}, 0,
+                                ZTracer::Trace());
+}
+
+template <typename I>
+void ObjectCopyRequest<I>::handle_read_from_parent(int r) {
+  ldout(m_cct, 20) << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(m_cct) << "failed to read from parent: " << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  if (!m_read_ops.empty()) {
+    assert(m_read_ops.size() == 1);
+    auto src_snap_seq = m_read_ops.begin()->first.first;
+    auto &copy_ops = m_read_ops.begin()->second;
+    uint64_t offset = 0;
+    for (auto it = copy_ops.begin(); it != copy_ops.end(); ) {
+      it->out_bl.substr_of(m_read_from_parent_data, offset, it->length);
+      offset += it->length;
+      if (it->out_bl.is_zero()) {
+        m_zero_interval[src_snap_seq].insert(it->dst_offset, it->length);
+        it = copy_ops.erase(it);
+      } else {
+        it++;
+      }
+    }
+    merge_write_ops();
+  }
+
+  compute_zero_ops();
+
+  if (m_write_ops.empty()) {
+    // nothing to copy
+    finish(0);
+    return;
+  }
+
+  send_write_object();
+  return;
+}
+
 template <typename I>
 void ObjectCopyRequest<I>::send_write_object() {
   assert(!m_write_ops.empty());
@@ -592,6 +663,94 @@ void ObjectCopyRequest<I>::compute_read_ops() {
   }
 }
 
+template <typename I>
+void ObjectCopyRequest<I>::compute_read_from_parent_ops(
+    io::Extents *parent_image_extents) {
+  m_read_ops = {};
+  m_zero_interval = {};
+  parent_image_extents->clear();
+
+  if (m_src_parent_image_ctx == nullptr) {
+    ldout(m_cct, 20) << "no parent" << dendl;
+    return;
+  }
+
+  size_t noent_count = 0;
+  for (auto &it : m_src_object_extents) {
+    if (it.second.noent) {
+      noent_count++;
+    }
+  }
+
+  if (noent_count == 0) {
+    ldout(m_cct, 20) << "no extents need read from parent" << dendl;
+    return;
+  }
+
+  if (noent_count == m_src_object_extents.size()) {
+    ldout(m_cct, 20) << "reading all extents skipped when no flatten"
+                     << dendl;
+    return;
+  }
+
+  ldout(m_cct, 20) << dendl;
+
+  auto src_snap_seq = m_snap_map.begin()->first;
+
+  RWLock::RLocker snap_locker(m_src_image_ctx->snap_lock);
+  RWLock::RLocker parent_locker(m_src_image_ctx->parent_lock);
+
+  uint64_t parent_overlap;
+  int r = m_src_image_ctx->get_parent_overlap(src_snap_seq, &parent_overlap);
+  if (r < 0) {
+    ldout(m_cct, 5) << "failed getting parent overlap for snap_id: "
+                    << src_snap_seq << ": " << cpp_strerror(r) << dendl;
+    return;
+  }
+  if (parent_overlap == 0) {
+    ldout(m_cct, 20) << "no parent overlap" << dendl;
+    return;
+  }
+
+  for (auto &it : m_src_object_extents) {
+    auto dst_object_offset = it.first;
+    auto &e = it.second;
+
+    if (!e.noent) {
+      continue;
+    }
+
+    std::vector<std::pair<uint64_t, uint64_t>> image_extents;
+    Striper::extent_to_file(m_cct, &m_src_image_ctx->layout, e.object_no,
+                            e.offset, e.length, image_extents);
+
+    uint64_t overlap = m_src_image_ctx->prune_parent_extents(image_extents,
+                                                             parent_overlap);
+    if (overlap == 0) {
+      ldout(m_cct, 20) << "no parent overlap for object_no " << e.object_no
+                       << " extent " << e.offset << "~" << e.length << dendl;
+      continue;
+    }
+
+    ldout(m_cct, 20) << "object_no " << e.object_no << " extent " << e.offset
+                     << "~" << e.length << " overlap " << parent_overlap
+                     << " parent extents " << image_extents << dendl;
+
+    assert(image_extents.size() == 1);
+
+    auto src_image_offset = image_extents.begin()->first;
+    auto length = image_extents.begin()->second;
+    m_read_ops[{src_snap_seq, 0}].emplace_back(COPY_OP_TYPE_WRITE, e.offset,
+                                               dst_object_offset, length);
+    m_read_ops[{src_snap_seq, 0}].rbegin()->src_extent_map[e.offset] = length;
+    parent_image_extents->emplace_back(src_image_offset, length);
+  }
+
+  if (!parent_image_extents->empty()) {
+    m_dst_object_state[src_snap_seq] = OBJECT_EXISTS;
+  }
+}
+
 template <typename I>
 void ObjectCopyRequest<I>::merge_write_ops() {
   ldout(m_cct, 20) << dendl;
index d6f3ec3285031bceb87ca73e01f048a9276557a3..d2a0cad623d0799f052c1081ee1332a4e21d5d56 100644 (file)
@@ -10,6 +10,7 @@
 #include "common/snap_types.h"
 #include "librbd/ImageCtx.h"
 #include "librbd/deep_copy/Types.h"
+#include "librbd/io/Types.h"
 #include <list>
 #include <map>
 #include <string>
@@ -24,16 +25,19 @@ template <typename ImageCtxT = librbd::ImageCtx>
 class ObjectCopyRequest {
 public:
   static ObjectCopyRequest* create(ImageCtxT *src_image_ctx,
+                                   ImageCtxT *src_parent_image_ctx,
                                    ImageCtxT *dst_image_ctx,
                                    const SnapMap &snap_map,
-                                   uint64_t object_number, Context *on_finish) {
-    return new ObjectCopyRequest(src_image_ctx, dst_image_ctx, snap_map,
-                                 object_number, on_finish);
+                                   uint64_t object_number,
+                                   Context *on_finish) {
+    return new ObjectCopyRequest(src_image_ctx, src_parent_image_ctx,
+                                 dst_image_ctx, snap_map, object_number,
+                                 on_finish);
   }
 
-  ObjectCopyRequest(ImageCtxT *src_image_ctx, ImageCtxT *dst_image_ctx,
-                    const SnapMap &snap_map, uint64_t object_number,
-                    Context *on_finish);
+  ObjectCopyRequest(ImageCtxT *src_image_ctx, ImageCtxT *src_parent_image_ctx,
+                    ImageCtxT *dst_image_ctx, const SnapMap &snap_map,
+                    uint64_t object_number, Context *on_finish);
 
   void send();
 
@@ -62,6 +66,8 @@ private:
    * READ_OBJECT ---------/          |
    *    |     |                      |
    *    |     \----------------------/
+   *    v
+   * READ_FROM_PARENT (skip if not needed)
    *    |
    *    |     /-----------\
    *    |     |           | (repeat for each snapshot)
@@ -83,6 +89,7 @@ private:
     uint64_t object_no = 0;
     uint64_t offset = 0;
     uint64_t length = 0;
+    bool noent = false;
 
     SrcObjectExtent() {
     }
@@ -125,6 +132,7 @@ private:
   typedef std::map<librados::snap_t, std::map<uint64_t, uint64_t>> SnapObjectSizes;
 
   ImageCtxT *m_src_image_ctx;
+  ImageCtxT *m_src_parent_image_ctx;
   ImageCtxT *m_dst_image_ctx;
   CephContext *m_cct;
   const SnapMap &m_snap_map;
@@ -151,6 +159,7 @@ private:
   std::map<librados::snap_t, interval_set<uint64_t>> m_zero_interval;
   std::map<librados::snap_t, interval_set<uint64_t>> m_dst_zero_interval;
   std::map<librados::snap_t, uint8_t> m_dst_object_state;
+  bufferlist m_read_from_parent_data;
 
   void send_list_snaps();
   void handle_list_snaps(int r);
@@ -158,6 +167,9 @@ private:
   void send_read_object();
   void handle_read_object(int r);
 
+  void send_read_from_parent();
+  void handle_read_from_parent(int r);
+
   void send_write_object();
   void handle_write_object(int r);
 
@@ -170,6 +182,7 @@ private:
 
   void compute_src_object_extents();
   void compute_read_ops();
+  void compute_read_from_parent_ops(io::Extents *image_extents);
   void merge_write_ops();
   void compute_zero_ops();
 
index bd8932e788bb148390c1af21813224284f282209..bd4f897249c99a0fc43ab423a8b422891ab003d4 100644 (file)
@@ -8,6 +8,9 @@
 #include "librbd/Operations.h"
 #include "librbd/deep_copy/ImageCopyRequest.h"
 #include "librbd/deep_copy/ObjectCopyRequest.h"
+#include "librbd/image/CloseRequest.h"
+#include "librbd/image/OpenRequest.h"
+#include "librbd/image/SetSnapRequest.h"
 #include "librbd/internal.h"
 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
 #include "test/librbd/mock/MockImageCtx.h"
@@ -19,11 +22,25 @@ namespace librbd {
 namespace {
 
 struct MockTestImageCtx : public librbd::MockImageCtx {
-  MockTestImageCtx(librbd::ImageCtx &image_ctx)
+  static MockTestImageCtx* s_instance;
+  static MockTestImageCtx* create(const std::string &image_name,
+                                  const std::string &image_id,
+                                  const char *snap, librados::IoCtx& p,
+                                  bool read_only) {
+    assert(s_instance != nullptr);
+    return s_instance;
+  }
+
+  explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
     : librbd::MockImageCtx(image_ctx) {
+    s_instance = this;
   }
+
+  MOCK_METHOD0(destroy, void());
 };
 
+MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
+
 } // anonymous namespace
 
 namespace deep_copy {
@@ -33,6 +50,7 @@ struct ObjectCopyRequest<librbd::MockTestImageCtx> {
   static ObjectCopyRequest* s_instance;
   static ObjectCopyRequest* create(
       librbd::MockTestImageCtx *src_image_ctx,
+      librbd::MockTestImageCtx *src_parent_image_ctx,
       librbd::MockTestImageCtx *dst_image_ctx, const SnapMap &snap_map,
       uint64_t object_number, Context *on_finish) {
     assert(s_instance != nullptr);
@@ -59,6 +77,70 @@ struct ObjectCopyRequest<librbd::MockTestImageCtx> {
 ObjectCopyRequest<librbd::MockTestImageCtx>* ObjectCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
 
 } // namespace deep_copy
+
+namespace image {
+
+template <>
+struct CloseRequest<MockTestImageCtx> {
+  Context* on_finish = nullptr;
+  static CloseRequest* s_instance;
+  static CloseRequest* create(MockTestImageCtx *image_ctx, Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+
+  CloseRequest() {
+    s_instance = this;
+  }
+};
+
+CloseRequest<MockTestImageCtx>* CloseRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct OpenRequest<MockTestImageCtx> {
+  Context* on_finish = nullptr;
+  static OpenRequest* s_instance;
+  static OpenRequest* create(MockTestImageCtx *image_ctx,
+                             bool skip_open_parent, Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+
+  OpenRequest() {
+    s_instance = this;
+  }
+};
+
+OpenRequest<MockTestImageCtx>* OpenRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct SetSnapRequest<MockTestImageCtx> {
+  Context* on_finish = nullptr;
+  static SetSnapRequest* s_instance;
+  static SetSnapRequest* create(MockTestImageCtx &image_ctx, uint64_t snap_id,
+                                Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+
+  SetSnapRequest() {
+    s_instance = this;
+  }
+};
+
+SetSnapRequest<MockTestImageCtx>* SetSnapRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+
 } // namespace librbd
 
 // template definitions
@@ -104,6 +186,10 @@ public:
       .WillOnce(Return(size)).RetiresOnSaturation();
   }
 
+  void expect_get_parent_info(librbd::MockTestImageCtx &mock_image_ctx) {
+    EXPECT_CALL(mock_image_ctx, get_parent_info(_)).WillOnce(Return(nullptr));
+  }
+
   void expect_object_copy_send(MockObjectCopyRequest &mock_object_copy_request) {
     EXPECT_CALL(mock_object_copy_request, send());
   }
@@ -203,6 +289,7 @@ TEST_F(TestMockDeepCopyImageCopyRequest, SimpleImage) {
   MockObjectCopyRequest mock_object_copy_request;
 
   InSequence seq;
+  expect_get_parent_info(mock_src_image_ctx);
   expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
   expect_get_image_size(mock_src_image_ctx, 0);
   expect_object_copy_send(mock_object_copy_request);
@@ -238,6 +325,7 @@ TEST_F(TestMockDeepCopyImageCopyRequest, OutOfOrder) {
   librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
   MockObjectCopyRequest mock_object_copy_request;
 
+  expect_get_parent_info(mock_src_image_ctx);
   expect_get_image_size(mock_src_image_ctx,
                         object_count * (1 << m_src_image_ctx->order));
   expect_get_image_size(mock_src_image_ctx, 0);
@@ -305,6 +393,7 @@ TEST_F(TestMockDeepCopyImageCopyRequest, SnapshotSubset) {
   MockObjectCopyRequest mock_object_copy_request;
 
   InSequence seq;
+  expect_get_parent_info(mock_src_image_ctx);
   expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
   expect_get_image_size(mock_src_image_ctx, 0);
   expect_get_image_size(mock_src_image_ctx, 0);
@@ -337,6 +426,7 @@ TEST_F(TestMockDeepCopyImageCopyRequest, RestartPartialSync) {
   MockObjectCopyRequest mock_object_copy_request;
 
   InSequence seq;
+  expect_get_parent_info(mock_src_image_ctx);
   expect_get_image_size(mock_src_image_ctx, 2 * (1 << m_src_image_ctx->order));
   expect_get_image_size(mock_src_image_ctx, 0);
   expect_object_copy_send(mock_object_copy_request);
@@ -371,6 +461,7 @@ TEST_F(TestMockDeepCopyImageCopyRequest, Cancel) {
   MockObjectCopyRequest mock_object_copy_request;
 
   InSequence seq;
+  expect_get_parent_info(mock_src_image_ctx);
   expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
   expect_get_image_size(mock_src_image_ctx, 0);
   expect_object_copy_send(mock_object_copy_request);
@@ -407,6 +498,7 @@ TEST_F(TestMockDeepCopyImageCopyRequest, Cancel_Inflight_Sync) {
   MockObjectCopyRequest mock_object_copy_request;
 
   InSequence seq;
+  expect_get_parent_info(mock_src_image_ctx);
   expect_get_image_size(mock_src_image_ctx, 6 * (1 << m_src_image_ctx->order));
   expect_get_image_size(mock_src_image_ctx, m_image_size);
 
index 448e5dda89c4a8729732fe0ea31216ed2c2970cf..cc86b800aaa8019844fee80451f49da36cce9761 100644 (file)
@@ -11,6 +11,7 @@
 #include "librbd/Operations.h"
 #include "librbd/api/Image.h"
 #include "librbd/deep_copy/ObjectCopyRequest.h"
+#include "librbd/io/ImageRequest.h"
 #include "librbd/io/ImageRequestWQ.h"
 #include "librbd/io/ReadResult.h"
 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
@@ -27,6 +28,34 @@ struct MockTestImageCtx : public librbd::MockImageCtx {
 };
 
 } // anonymous namespace
+
+namespace util {
+
+inline ImageCtx* get_image_ctx(MockTestImageCtx* image_ctx) {
+  return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+namespace io {
+
+template <>
+struct ImageRequest<MockTestImageCtx> {
+  static ImageRequest *s_instance;
+
+  static void aio_read(MockTestImageCtx *ictx, AioCompletion *c,
+                       Extents &&image_extents, ReadResult &&read_result,
+                       int op_flags, const ZTracer::Trace &parent_trace) {
+    assert(s_instance != nullptr);
+    s_instance->aio_read(c, image_extents);
+  }
+  MOCK_METHOD2(aio_read, void(AioCompletion *, const Extents&));
+};
+
+ImageRequest<MockTestImageCtx> *ImageRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace io
+
 } // namespace librbd
 
 // template definitions
@@ -169,8 +198,9 @@ public:
       librbd::MockTestImageCtx &mock_src_image_ctx,
       librbd::MockTestImageCtx &mock_dst_image_ctx, Context *on_finish) {
     expect_get_object_name(mock_dst_image_ctx);
-    return new MockObjectCopyRequest(&mock_src_image_ctx, &mock_dst_image_ctx,
-                                     m_snap_map, 0, on_finish);
+    return new MockObjectCopyRequest(&mock_src_image_ctx, nullptr,
+                                     &mock_dst_image_ctx, m_snap_map, 0,
+                                     on_finish);
   }
 
   void expect_set_snap_read(librados::MockTestMemIoCtxImpl &mock_io_ctx,
index 40215bc3e0b77c8ac18801dc77357dd149f75d74..8c77ca16ffb84c73bb19beae7456a5361b93b11d 100644 (file)
@@ -165,6 +165,7 @@ struct MockImageCtx {
                                             cls::rbd::SnapshotNamespace *out_snap_namespace));
   MOCK_CONST_METHOD2(get_parent_spec, int(librados::snap_t in_snap_id,
                                           ParentSpec *pspec));
+  MOCK_CONST_METHOD1(get_parent_info, const ParentInfo*(librados::snap_t));
   MOCK_CONST_METHOD2(get_parent_overlap, int(librados::snap_t in_snap_id,
                                              uint64_t *overlap));
   MOCK_CONST_METHOD2(prune_parent_extents, uint64_t(vector<pair<uint64_t,uint64_t> >& ,