]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: support reading multiple extents in the object dispatch interface 36145/head
authorOr Ozeri <oro@il.ibm.com>
Thu, 16 Jul 2020 11:14:36 +0000 (14:14 +0300)
committerOr Ozeri <oro@il.ibm.com>
Sun, 2 Aug 2020 09:27:45 +0000 (12:27 +0300)
This commit extends the object dispatch read function to support multiple object extents (using a single librados ObjectOperation).
In addition, we add an option to return the version number for that read.
This new function will be used by the new crypto object dispatch layer to support read-modify-write operation required for encrypting and writing unaligned data.

Signed-off-by: Or Ozeri <oro@il.ibm.com>
24 files changed:
src/librbd/cache/ObjectCacherObjectDispatch.cc
src/librbd/cache/ObjectCacherObjectDispatch.h
src/librbd/cache/ObjectCacherWriteback.cc
src/librbd/cache/ParentCacheObjectDispatch.cc
src/librbd/cache/ParentCacheObjectDispatch.h
src/librbd/cache/WriteAroundObjectDispatch.cc
src/librbd/cache/WriteAroundObjectDispatch.h
src/librbd/io/ImageRequest.cc
src/librbd/io/ObjectDispatch.cc
src/librbd/io/ObjectDispatch.h
src/librbd/io/ObjectDispatchInterface.h
src/librbd/io/ObjectDispatchSpec.h
src/librbd/io/ObjectDispatcher.cc
src/librbd/io/ObjectRequest.cc
src/librbd/io/ObjectRequest.h
src/librbd/io/SimpleSchedulerObjectDispatch.cc
src/librbd/io/SimpleSchedulerObjectDispatch.h
src/librbd/io/Utils.cc
src/librbd/io/Utils.h
src/librbd/journal/ObjectDispatch.h
src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc
src/test/librbd/io/test_mock_ObjectRequest.cc
src/test/librbd/io/test_mock_SimpleSchedulerObjectDispatch.cc
src/test/librbd/mock/io/MockObjectDispatch.h

index ffa0fc0a840ea8f4eee0a8492b8772168d0108e8..db0c71f186a428d8705b20967d135d9d0186bad4 100644 (file)
@@ -181,15 +181,21 @@ void ObjectCacherObjectDispatch<I>::shut_down(Context* on_finish) {
 
 template <typename I>
 bool ObjectCacherObjectDispatch<I>::read(
-    uint64_t object_no, uint64_t object_off, uint64_t object_len,
-    librados::snap_t snap_id, int op_flags, const ZTracer::Trace &parent_trace,
-    ceph::bufferlist* read_data, io::Extents* extent_map,
+    uint64_t object_no, const io::Extents &extents, librados::snap_t snap_id,
+    int op_flags, const ZTracer::Trace &parent_trace,
+    ceph::bufferlist* read_data, io::Extents* extent_map, uint64_t* version,
     int* object_dispatch_flags, io::DispatchResult* dispatch_result,
     Context** on_finish, Context* on_dispatched) {
   // IO chained in reverse order
   auto cct = m_image_ctx->cct;
-  ldout(cct, 20) << "object_no=" << object_no << " " << object_off << "~"
-                 << object_len << dendl;
+  ldout(cct, 20) << "object_no=" << object_no << " " << extents << dendl;
+
+  if (version != nullptr || extents.size() != 1) {
+    // we currently don't cache read versions
+    // and don't support reading more than one extent
+    return false;
+  }
+  auto [object_off, object_len] = extents.front();
 
   // ensure we aren't holding the cache lock post-read
   on_dispatched = util::create_async_context_callback(*m_image_ctx,
index cb5e389126446e4017beb60bea5947866fc272ac..6e4ab23ae77e69c29e12e44c627fd790ae2dac21 100644 (file)
@@ -42,10 +42,10 @@ public:
   void shut_down(Context* on_finish) override;
 
   bool read(
-      uint64_t object_no, uint64_t object_off, uint64_t object_len,
-      librados::snap_t snap_id, int op_flags,
-      const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data,
-      io::Extents* extent_map, int* object_dispatch_flags,
+      uint64_t object_no, const io::Extents &extents, librados::snap_t snap_id,
+      int op_flags, const ZTracer::Trace &parent_trace,
+      ceph::bufferlist* read_data, io::Extents* extent_map,
+      uint64_t* version, int* object_dispatch_flags,
       io::DispatchResult* dispatch_result, Context** on_finish,
       Context* on_dispatched) override;
 
index c05d9e737f6514fb77e6bafa2b4ad0a6d4ee1835..529b6c54ad105e3b5c87c26ffc58fb8dd22ad444 100644 (file)
@@ -137,8 +137,8 @@ void ObjectCacherWriteback::read(const object_t& oid, uint64_t object_no,
     aio_comp, off, len, {{0, len}});
 
   auto req = io::ObjectDispatchSpec::create_read(
-    m_ictx, io::OBJECT_DISPATCH_LAYER_CACHE, object_no, off, len, snapid,
-    op_flags, trace, &req_comp->bl, &req_comp->extent_map, req_comp);
+    m_ictx, io::OBJECT_DISPATCH_LAYER_CACHE, object_no, {{off, len}}, snapid,
+    op_flags, trace, &req_comp->bl, &req_comp->extent_map, nullptr, req_comp);
   req->send();
 }
 
index 17a601d7a126c81ab72d3d3dd6b7d216fea3828e..7e3823481fd4609338986c38e09b2991d1c65d8f 100644 (file)
@@ -64,15 +64,22 @@ void ParentCacheObjectDispatch<I>::init(Context* on_finish) {
 
 template <typename I>
 bool ParentCacheObjectDispatch<I>::read(
-    uint64_t object_no, uint64_t object_off,
-    uint64_t object_len, librados::snap_t snap_id, int op_flags,
-    const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data,
-    io::Extents* extent_map, int* object_dispatch_flags,
-    io::DispatchResult* dispatch_result, Context** on_finish,
-    Context* on_dispatched) {
+    uint64_t object_no, const io::Extents &extents, librados::snap_t snap_id,
+    int op_flags, const ZTracer::Trace &parent_trace,
+    ceph::bufferlist* read_data, io::Extents* extent_map, uint64_t* version,
+    int* object_dispatch_flags, io::DispatchResult* dispatch_result,
+    Context** on_finish, Context* on_dispatched) {
   auto cct = m_image_ctx->cct;
-  ldout(cct, 20) << "object_no=" << object_no << " " << object_off << "~"
-                 << object_len << dendl;
+  ldout(cct, 20) << "object_no=" << object_no << " " << extents << dendl;
+
+  if (version != nullptr || extents.size() != 1) {
+    // we currently don't cache read versions
+    // and don't support reading more than one extent
+    return false;
+  }
+
+  auto [object_off, object_len] = extents.front();
+
   string oid = data_object_name(m_image_ctx, object_no);
 
   /* if RO daemon still don't startup, or RO daemon crash,
@@ -128,7 +135,7 @@ void ParentCacheObjectDispatch<I>::handle_read_cache(
         *dispatch_result = io::DISPATCH_RESULT_COMPLETE;
         on_dispatched->complete(r);
       });
-    io::util::read_parent<I>(m_image_ctx, object_no, read_off, read_len,
+    io::util::read_parent<I>(m_image_ctx, object_no, {{read_off, read_len}},
                              snap_id, parent_trace, read_data, ctx);
     return;
   }
index d3265619669b67ea8969d7e79c636cafbcab5996..588d284f2fd1068408cb9f7ed7f483cf4c858e95 100644 (file)
@@ -40,10 +40,10 @@ public:
   }
 
   bool read(
-      uint64_t object_no, uint64_t object_off, uint64_t object_len,
-      librados::snap_t snap_id, int op_flags,
-      const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data,
-      io::Extents* extent_map, int* object_dispatch_flags,
+      uint64_t object_no, const io::Extents &extents, librados::snap_t snap_id,
+      int op_flags, const ZTracer::Trace &parent_trace,
+      ceph::bufferlist* read_data, io::Extents* extent_map,
+      uint64_t* version, int* object_dispatch_flags,
       io::DispatchResult* dispatch_result, Context** on_finish,
       Context* on_dispatched) override;
 
index 3d4fd1fc31e0b52bc230c7ddca5671a8e58fff33..bbcf4a65b24f1f6aca82dab36baa467a47cb30cd 100644 (file)
@@ -57,11 +57,12 @@ void WriteAroundObjectDispatch<I>::shut_down(Context* on_finish) {
 
 template <typename I>
 bool WriteAroundObjectDispatch<I>::read(
-    uint64_t object_no, uint64_t object_off, uint64_t object_len,
-    librados::snap_t snap_id, int op_flags, const ZTracer::Trace &parent_trace,
-    ceph::bufferlist* read_data, io::Extents* extent_map,
+    uint64_t object_no, const io::Extents &extents, librados::snap_t snap_id,
+    int op_flags, const ZTracer::Trace &parent_trace,
+    ceph::bufferlist* read_data, io::Extents* extent_map, uint64_t* version,
     int* object_dispatch_flags, io::DispatchResult* dispatch_result,
     Context** on_finish, Context* on_dispatched) {
+  auto [object_off, object_len] = extents.front();
   return dispatch_unoptimized_io(object_no, object_off, object_len,
                                  dispatch_result, on_dispatched);
 }
index 873feb724493e17e64c998cf5caef1509e126b2b..bf1e1d985513fcf2f55950bc4709ce4fe50fad69 100644 (file)
@@ -42,10 +42,10 @@ public:
   void shut_down(Context* on_finish) override;
 
   bool read(
-      uint64_t object_no, uint64_t object_off, uint64_t object_len,
-      librados::snap_t snap_id, int op_flags,
-      const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data,
-      io::Extents* extent_map, int* object_dispatch_flags,
+      uint64_t object_no, const io::Extents &extents, librados::snap_t snap_id,
+      int op_flags, const ZTracer::Trace &parent_trace,
+      ceph::bufferlist* read_data, io::Extents* extent_map,
+      uint64_t* version, int* object_dispatch_flags,
       io::DispatchResult* dispatch_result, Context** on_finish,
       Context* on_dispatched) override;
 
index e8b824f0ee6f7b7f674c9ae121b843e16c2bea9e..48a3decfcbcafd0f4664c13f2bd62aaee46f97dc 100644 (file)
@@ -101,8 +101,8 @@ void readahead(I *ictx, const Extents& image_extents) {
                                              object_extent.length);
       auto req = io::ObjectDispatchSpec::create_read(
         ictx, io::OBJECT_DISPATCH_LAYER_NONE, object_extent.object_no,
-        object_extent.offset, object_extent.length, snap_id, 0, {},
-        &req_comp->read_data, &req_comp->extent_map, req_comp);
+        {{object_extent.offset, object_extent.length}}, snap_id, 0, {},
+        &req_comp->read_data, &req_comp->extent_map, nullptr, req_comp);
       req->send();
     }
 
@@ -405,9 +405,9 @@ void ImageReadRequest<I>::send_request() {
     auto req_comp = new io::ReadResult::C_ObjectReadRequest(
       aio_comp, oe.offset, oe.length, std::move(oe.buffer_extents));
     auto req = ObjectDispatchSpec::create_read(
-      &image_ctx, OBJECT_DISPATCH_LAYER_NONE, oe.object_no, oe.offset,
-      oe.length, snap_id, m_op_flags, this->m_trace, &req_comp->bl,
-      &req_comp->extent_map, req_comp);
+      &image_ctx, OBJECT_DISPATCH_LAYER_NONE, oe.object_no,
+      {{oe.offset, oe.length}}, snap_id, m_op_flags, this->m_trace,
+      &req_comp->bl, &req_comp->extent_map, nullptr, req_comp);
     req->send();
   }
 
index 4e0e626cfd3cb8856ce51d77b9f93cce41bd7c94..c6099316685a8aa4377320e1906a9f0969450515 100644 (file)
@@ -33,20 +33,18 @@ void ObjectDispatch<I>::shut_down(Context* on_finish) {
 
 template <typename I>
 bool ObjectDispatch<I>::read(
-    uint64_t object_no, uint64_t object_off, uint64_t object_len,
-    librados::snap_t snap_id, int op_flags, const ZTracer::Trace &parent_trace,
-    ceph::bufferlist* read_data, Extents* extent_map,
+    uint64_t object_no, const Extents &extents, librados::snap_t snap_id,
+    int op_flags, const ZTracer::Trace &parent_trace,
+    ceph::bufferlist* read_data, Extents* extent_map, uint64_t* version,
     int* object_dispatch_flags, DispatchResult* dispatch_result,
     Context** on_finish, Context* on_dispatched) {
   auto cct = m_image_ctx->cct;
-  ldout(cct, 20) << data_object_name(m_image_ctx, object_no) << " "
-                 << object_off << "~" << object_len << dendl;
+  ldout(cct, 20) << "object_no=" << object_no << " " << extents << dendl;
 
   *dispatch_result = DISPATCH_RESULT_COMPLETE;
-  auto req = new ObjectReadRequest<I>(m_image_ctx, object_no, object_off,
-                                      object_len, snap_id, op_flags,
-                                      parent_trace, read_data, extent_map,
-                                      on_dispatched);
+  auto req = new ObjectReadRequest<I>(m_image_ctx, object_no, extents, snap_id,
+                                      op_flags, parent_trace, read_data,
+                                      extent_map, version, on_dispatched);
   req->send();
   return true;
 }
index a7945ed0ce0db358347a4a822e98e94695770453..4a65ec417beefee3bd4aa2fd7a9377e6c51ce116 100644 (file)
@@ -34,10 +34,10 @@ public:
   void shut_down(Context* on_finish) override;
 
   bool read(
-      uint64_t object_no, uint64_t object_off, uint64_t object_len,
-      librados::snap_t snap_id, int op_flags,
-      const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data,
-      Extents* extent_map, int* object_dispatch_flags,
+      uint64_t object_no, const Extents &extents, librados::snap_t snap_id,
+      int op_flags, const ZTracer::Trace &parent_trace,
+      ceph::bufferlist* read_data, Extents* extent_map,
+      uint64_t* version, int* object_dispatch_flags,
       DispatchResult* dispatch_result, Context** on_finish,
       Context* on_dispatched) override;
 
index 9397d1ead147162bb0fe546885f0e4b3d4e43901..1ade6adbd518610473315f4b0f975084cb03c893 100644 (file)
@@ -34,9 +34,9 @@ struct ObjectDispatchInterface {
   virtual void shut_down(Context* on_finish) = 0;
 
   virtual bool read(
-      uint64_t object_no, uint64_t object_off, uint64_t object_len,
-      librados::snap_t snap_id, int op_flags,const ZTracer::Trace &parent_trace,
-      ceph::bufferlist* read_data, Extents* extent_map,
+      uint64_t object_no, const Extents &extents,
+      librados::snap_t snap_id, int op_flags, const ZTracer::Trace &parent_trace,
+      ceph::bufferlist* read_data, Extents* extent_map, uint64_t* version,
       int* object_dispatch_flags, DispatchResult* dispatch_result,
       Context** on_finish, Context* on_dispatched) = 0;
 
index 74ff4208f7a302ee5fdd27265a60b9a134b16959..942b1d719cf8620a7650b90359ca45d6bd5d0e9e 100644 (file)
@@ -36,35 +36,35 @@ private:
 public:
   struct RequestBase {
     uint64_t object_no;
-    uint64_t object_off;
 
-    RequestBase(uint64_t object_no, uint64_t object_off)
-      : object_no(object_no), object_off(object_off) {
+    RequestBase(uint64_t object_no)
+      : object_no(object_no) {
     }
   };
 
   struct ReadRequest : public RequestBase {
-    uint64_t object_len;
+    const Extents extents;
     librados::snap_t snap_id;
     ceph::bufferlist* read_data;
     Extents* extent_map;
+    uint64_t* version;
 
-    ReadRequest(uint64_t object_no, uint64_t object_off, uint64_t object_len,
+    ReadRequest(uint64_t object_no, const Extents &extents,
                 librados::snap_t snap_id, ceph::bufferlist* read_data,
-                Extents* extent_map)
-      : RequestBase(object_no, object_off),
-        object_len(object_len), snap_id(snap_id), read_data(read_data),
-        extent_map(extent_map) {
+                Extents* extent_map, uint64_t* version)
+      : RequestBase(object_no), extents(extents), snap_id(snap_id),
+        read_data(read_data), extent_map(extent_map), version(version) {
     }
   };
 
   struct WriteRequestBase : public RequestBase {
+    uint64_t object_off;
     ::SnapContext snapc;
     uint64_t journal_tid;
 
     WriteRequestBase(uint64_t object_no, uint64_t object_off,
                      const ::SnapContext& snapc, uint64_t journal_tid)
-      : RequestBase(object_noobject_off), snapc(snapc),
+      : RequestBase(object_no), object_off(object_off), snapc(snapc),
         journal_tid(journal_tid) {
     }
   };
@@ -153,15 +153,14 @@ public:
   template <typename ImageCtxT>
   static ObjectDispatchSpec* create_read(
       ImageCtxT* image_ctx, ObjectDispatchLayer object_dispatch_layer,
-      uint64_t object_no, uint64_t object_off, uint64_t object_len,
-      librados::snap_t snap_id, int op_flags,
-      const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data,
-      Extents* extent_map, Context* on_finish) {
+      uint64_t object_no, const Extents &extents, librados::snap_t snap_id,
+      int op_flags, const ZTracer::Trace &parent_trace,
+      ceph::bufferlist* read_data, Extents* extent_map, uint64_t* version,
+      Context* on_finish) {
     return new ObjectDispatchSpec(image_ctx->io_object_dispatcher,
                                   object_dispatch_layer,
-                                  ReadRequest{object_no, object_off,
-                                              object_len, snap_id, read_data,
-                                              extent_map},
+                                  ReadRequest{object_no, extents, snap_id,
+                                              read_data, extent_map, version},
                                   op_flags, parent_trace, on_finish);
   }
 
index 7c46fdff75d2d5d3c4eadbf1a55bbafe9080a729..4732aa68e27c8b1a7ccf9513946a4c13f1e05414 100644 (file)
@@ -107,9 +107,9 @@ struct ObjectDispatcher<I>::SendVisitor : public boost::static_visitor<bool> {
 
   bool operator()(ObjectDispatchSpec::ReadRequest& read) const {
     return object_dispatch->read(
-      read.object_no, read.object_off, read.object_len, read.snap_id,
+      read.object_no, read.extents, read.snap_id,
       object_dispatch_spec->op_flags, object_dispatch_spec->parent_trace,
-      read.read_data, read.extent_map,
+      read.read_data, read.extent_map, read.version,
       &object_dispatch_spec->object_dispatch_flags,
       &object_dispatch_spec->dispatch_result,
       &object_dispatch_spec->dispatcher_ctx.on_finish,
index 1bae9099d24cea62a435de7ae2d3bf2a234564d0..87879eb8607a00ec74ac403f988a19c7f71a4ad6 100644 (file)
@@ -99,11 +99,10 @@ ObjectRequest<I>::create_compare_and_write(
 
 template <typename I>
 ObjectRequest<I>::ObjectRequest(
-    I *ictx, uint64_t objectno, uint64_t off, uint64_t len,
-    librados::snap_t snap_id, const char *trace_name,
-    const ZTracer::Trace &trace, Context *completion)
-  : m_ictx(ictx), m_object_no(objectno), m_object_off(off),
-    m_object_len(len), m_snap_id(snap_id), m_completion(completion),
+    I *ictx, uint64_t objectno, librados::snap_t snap_id,
+    const char *trace_name, const ZTracer::Trace &trace, Context *completion)
+  : m_ictx(ictx), m_object_no(objectno), m_snap_id(snap_id),
+    m_completion(completion),
     m_trace(create_trace(*ictx, "", trace)) {
   ceph_assert(m_ictx->data_ctx.is_valid());
   if (m_trace.valid()) {
@@ -182,12 +181,14 @@ void ObjectRequest<I>::finish(int r) {
 
 template <typename I>
 ObjectReadRequest<I>::ObjectReadRequest(
-    I *ictx, uint64_t objectno, uint64_t offset, uint64_t len,
+    I *ictx, uint64_t objectno, const Extents &extents,
     librados::snap_t snap_id, int op_flags, const ZTracer::Trace &parent_trace,
-    bufferlist* read_data, Extents* extent_map, Context *completion)
-  : ObjectRequest<I>(ictx, objectno, offset, len, snap_id, "read",
+    ceph::bufferlist* read_data, Extents* extent_map, uint64_t* version,
+    Context *completion)
+  : ObjectRequest<I>(ictx, objectno, snap_id, "read",
                      parent_trace, completion),
-    m_op_flags(op_flags), m_read_data(read_data), m_extent_map(extent_map) {
+    m_extents(extents), m_op_flags(op_flags), m_read_data(read_data),
+    m_extent_map(extent_map), m_version(version) {
 }
 
 template <typename I>
@@ -213,11 +214,16 @@ void ObjectReadRequest<I>::read_object() {
   ldout(image_ctx->cct, 20) << dendl;
 
   neorados::ReadOp read_op;
-  if (this->m_object_len >= image_ctx->sparse_read_threshold_bytes) {
-    read_op.sparse_read(this->m_object_off, this->m_object_len, m_read_data,
-                        m_extent_map);
-  } else {
-    read_op.read(this->m_object_off, this->m_object_len, m_read_data);
+  m_extent_results.reserve(this->m_extents.size());
+  for (auto [object_off, object_len]: this->m_extents) {
+    m_extent_results.emplace_back();
+    auto& extent_result = m_extent_results.back();
+    if (object_len >= image_ctx->sparse_read_threshold_bytes) {
+      read_op.sparse_read(object_off, object_len, &(extent_result.first),
+                          &(extent_result.second));
+    } else {
+      read_op.read(object_off, object_len, &(extent_result.first));
+    }
   }
   util::apply_op_flags(m_op_flags, image_ctx->get_read_flags(this->m_snap_id),
                        &read_op);
@@ -226,7 +232,7 @@ void ObjectReadRequest<I>::read_object() {
     {data_object_name(this->m_ictx, this->m_object_no)},
     *image_ctx->get_data_io_context(), std::move(read_op), nullptr,
     librbd::asio::util::get_callback_adapter(
-      [this](int r) { handle_read_object(r); }), nullptr,
+      [this](int r) { handle_read_object(r); }), m_version,
       (this->m_trace.valid() ? this->m_trace.get_info() : nullptr));
 }
 
@@ -245,6 +251,28 @@ void ObjectReadRequest<I>::handle_read_object(int r) {
     return;
   }
 
+  // merge ExtentResults to a single sparse bufferlist
+  int pos = 0;
+  uint64_t object_off = 0;
+  for (auto& [read_data, extent_map] : m_extent_results) {
+    if (extent_map.size() == 0) {
+      extent_map.push_back(std::make_pair(m_extents[pos].first,
+                                          read_data.length()));
+    }
+
+    uint64_t total_extents_len = 0;
+    for (auto& [extent_off, extent_len] : extent_map) {
+      ceph_assert(extent_off >= object_off);
+      object_off = extent_off + extent_len;
+      m_extent_map->push_back(std::make_pair(extent_off, extent_len));
+      total_extents_len += extent_len;
+    }
+    ceph_assert(total_extents_len == read_data.length());
+
+    m_read_data->claim_append(read_data);
+    ++pos;
+  }
+
   this->finish(0);
 }
 
@@ -256,9 +284,8 @@ void ObjectReadRequest<I>::read_parent() {
   auto ctx = create_context_callback<
     ObjectReadRequest<I>, &ObjectReadRequest<I>::handle_read_parent>(this);
 
-  io::util::read_parent<I>(image_ctx, this->m_object_no, this->m_object_off,
-                           this->m_object_len, this->m_snap_id, this->m_trace,
-                           m_read_data, ctx);
+  io::util::read_parent<I>(image_ctx, this->m_object_no, this->m_extents,
+                           this->m_snap_id, this->m_trace, m_read_data, ctx);
 }
 
 template <typename I>
@@ -276,6 +303,11 @@ void ObjectReadRequest<I>::handle_read_parent(int r) {
     return;
   }
 
+  if (this->m_extents.size() > 1) {
+    m_extent_map->insert(m_extent_map->end(),
+                         m_extents.begin(), m_extents.end());
+  }
+
   copyup();
 }
 
@@ -328,9 +360,9 @@ AbstractObjectWriteRequest<I>::AbstractObjectWriteRequest(
     I *ictx, uint64_t object_no, uint64_t object_off, uint64_t len,
     const ::SnapContext &snapc, const char *trace_name,
     const ZTracer::Trace &parent_trace, Context *completion)
-  : ObjectRequest<I>(ictx, object_no, object_off, len, CEPH_NOSNAP, trace_name,
-                     parent_trace, completion),
-    m_snap_seq(snapc.seq.val)
+  : ObjectRequest<I>(ictx, object_no, CEPH_NOSNAP, trace_name, parent_trace,
+                     completion),
+    m_object_off(object_off), m_object_len(len), m_snap_seq(snapc.seq.val)
 {
   m_snaps.insert(m_snaps.end(), snapc.snaps.begin(), snapc.snaps.end());
 
index f696e13f71c47b06de6815bde22fb7e0b1fe3ea1..c34a1f7f3efc2039d262ff61aa71c649681acb76 100644 (file)
@@ -53,9 +53,9 @@ public:
       const ::SnapContext &snapc, uint64_t *mismatch_offset, int op_flags,
       const ZTracer::Trace &parent_trace, Context *completion);
 
-  ObjectRequest(ImageCtxT *ictx, uint64_t objectno, uint64_t off, uint64_t len,
-                librados::snap_t snap_id, const char *trace_name,
-                const ZTracer::Trace &parent_trace, Context *completion);
+  ObjectRequest(ImageCtxT *ictx, uint64_t objectno, librados::snap_t snap_id,
+                const char *trace_name, const ZTracer::Trace &parent_trace,
+                Context *completion);
   virtual ~ObjectRequest() {
     m_trace.event("finish");
   }
@@ -75,7 +75,7 @@ protected:
   bool compute_parent_extents(Extents *parent_extents, bool read_request);
 
   ImageCtxT *m_ictx;
-  uint64_t m_object_no, m_object_off, m_object_len;
+  uint64_t m_object_no;
   librados::snap_t m_snap_id;
   Context *m_completion;
   ZTracer::Trace m_trace;
@@ -91,20 +91,20 @@ template <typename ImageCtxT = ImageCtx>
 class ObjectReadRequest : public ObjectRequest<ImageCtxT> {
 public:
   static ObjectReadRequest* create(
-      ImageCtxT *ictx, uint64_t objectno, uint64_t offset, uint64_t len,
+      ImageCtxT *ictx, uint64_t objectno, const Extents &extents,
       librados::snap_t snap_id, int op_flags,
       const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data,
-      Extents* extent_map, Context *completion) {
-    return new ObjectReadRequest(ictx, objectno, offset, len,
-                                 snap_id, op_flags, parent_trace, read_data,
-                                 extent_map, completion);
+      Extents* extent_map, uint64_t* version, Context *completion) {
+    return new ObjectReadRequest(ictx, objectno, extents, snap_id, op_flags,
+                                 parent_trace, read_data, extent_map, version,
+                                 completion);
   }
 
   ObjectReadRequest(
-      ImageCtxT *ictx, uint64_t objectno, uint64_t offset, uint64_t len,
+      ImageCtxT *ictx, uint64_t objectno, const Extents &extents,
       librados::snap_t snap_id, int op_flags,
       const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data,
-      Extents* extent_map, Context *completion);
+      Extents* extent_map, uint64_t* version, Context *completion);
 
   void send() override;
 
@@ -134,10 +134,16 @@ private:
    * @endverbatim
    */
 
+  const Extents m_extents;
+
+  typedef std::pair<ceph::bufferlist, Extents> ExtentResult;
+  typedef std::vector<ExtentResult> ExtentResults;
+  ExtentResults m_extent_results;
   int m_op_flags;
 
   ceph::bufferlist* m_read_data;
   Extents* m_extent_map;
+  uint64_t* m_version;
 
   void read_object();
   void handle_read_object(int r);
@@ -173,6 +179,8 @@ public:
   void send() override;
 
 protected:
+  uint64_t m_object_off;
+  uint64_t m_object_len;
   bool m_full_object = false;
   bool m_copyup_enabled = true;
 
index 66669ec4d03d373b20b3b73a03230de38df9d5d8..8b83468fe89af5f794fe64db6cb1cbbb5cb01e25 100644 (file)
@@ -215,18 +215,21 @@ void SimpleSchedulerObjectDispatch<I>::shut_down(Context* on_finish) {
 
 template <typename I>
 bool SimpleSchedulerObjectDispatch<I>::read(
-    uint64_t object_no, uint64_t object_off, uint64_t object_len,
-    librados::snap_t snap_id, int op_flags, const ZTracer::Trace &parent_trace,
-    ceph::bufferlist* read_data, Extents* extent_map,
+    uint64_t object_no, const Extents &extents, librados::snap_t snap_id,
+    int op_flags, const ZTracer::Trace &parent_trace,
+    ceph::bufferlist* read_data, Extents* extent_map, uint64_t* version,
     int* object_dispatch_flags, DispatchResult* dispatch_result,
     Context** on_finish, Context* on_dispatched) {
   auto cct = m_image_ctx->cct;
-  ldout(cct, 20) << data_object_name(m_image_ctx, object_no) << " "
-                 << object_off << "~" << object_len << dendl;
+  ldout(cct, 20) << data_object_name(m_image_ctx, object_no) << " " << extents
+                 << dendl;
 
   std::lock_guard locker{m_lock};
-  if (intersects(object_no, object_off, object_len)) {
-    dispatch_delayed_requests(object_no);
+  for (auto [object_off, object_len] : extents) {
+    if (intersects(object_no, object_off, object_len)) {
+      dispatch_delayed_requests(object_no);
+      break;
+    }
   }
 
   return false;
index ca134ef385c574244078a9fcc44be0d386732469..def70af5e906c23e23f24f08f09a63a75322fe7a 100644 (file)
@@ -50,10 +50,10 @@ public:
   void shut_down(Context* on_finish) override;
 
   bool read(
-      uint64_t object_no, uint64_t object_off, uint64_t object_len,
-      librados::snap_t snap_id, int op_flags,
-      const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data,
-      Extents* extent_map, int* object_dispatch_flags,
+      uint64_t object_no, const Extents &extents, librados::snap_t snap_id,
+      int op_flags, const ZTracer::Trace &parent_trace,
+      ceph::bufferlist* read_data, Extents* extent_map,
+      uint64_t* version, int* object_dispatch_flags,
       DispatchResult* dispatch_result, Context** on_finish,
       Context* on_dispatched) override;
 
index 78d8a5a83fa889e9892114b53d2c7146c38a803b..50334c063d52bf663de109a0504e512a8fb86dd7 100644 (file)
@@ -80,10 +80,9 @@ bool assemble_write_same_extent(
 }
 
 template <typename I>
-void read_parent(I *image_ctx, uint64_t object_no, uint64_t off,
-                 uint64_t len, librados::snap_t snap_id,
-                 const ZTracer::Trace &trace, ceph::bufferlist* data,
-                 Context* on_finish) {
+void read_parent(I *image_ctx, uint64_t object_no, const Extents &extents,
+                 librados::snap_t snap_id, const ZTracer::Trace &trace,
+                 ceph::bufferlist* data, Context* on_finish) {
 
   auto cct = image_ctx->cct;
 
@@ -91,8 +90,10 @@ void read_parent(I *image_ctx, uint64_t object_no, uint64_t off,
 
   // calculate reverse mapping onto the image
   Extents parent_extents;
-  Striper::extent_to_file(cct, &image_ctx->layout, object_no, off, len,
-                          parent_extents);
+  for (auto [object_off, object_len]: extents) {
+    Striper::extent_to_file(cct, &image_ctx->layout, object_no, object_off,
+                            object_len, parent_extents);
+  }
 
   uint64_t parent_overlap = 0;
   uint64_t object_overlap = 0;
@@ -125,6 +126,6 @@ void read_parent(I *image_ctx, uint64_t object_no, uint64_t off,
 } // namespace librbd
 
 template void librbd::io::util::read_parent(
-    librbd::ImageCtx *image_ctx, uint64_t object_no, uint64_t off, uint64_t len,
+    librbd::ImageCtx *image_ctx, uint64_t object_no, const Extents &extents,
     librados::snap_t snap_id, const ZTracer::Trace &trace,
     ceph::bufferlist* data, Context* on_finish);
index 295bf0469cac851c456de4dbf9f907cb80f61101..81161adc8313b85f8bb24c04014971fdf35bf499 100644 (file)
@@ -30,10 +30,9 @@ bool assemble_write_same_extent(const LightweightObjectExtent &object_extent,
                                 bool force_write);
 
 template <typename ImageCtxT = librbd::ImageCtx>
-void read_parent(ImageCtxT *image_ctx, uint64_t object_no, uint64_t off,
-                 uint64_t len, librados::snap_t snap_id,
-                 const ZTracer::Trace &trace, ceph::bufferlist* data,
-                 Context* on_finish);
+void read_parent(ImageCtxT *image_ctx, uint64_t object_no, const Extents &extents,
+                 librados::snap_t snap_id, const ZTracer::Trace &trace,
+                 ceph::bufferlist* data, Context* on_finish);
 
 } // namespace util
 } // namespace io
index 48176e6434512ae8c8f6800a25e194d3c5c4b454..cd03389c5c134eae4f0714ed1bd4f306c921aec0 100644 (file)
@@ -38,10 +38,10 @@ public:
   void shut_down(Context* on_finish) override;
 
   bool read(
-      uint64_t object_no, uint64_t object_off, uint64_t object_len,
-      librados::snap_t snap_id, int op_flags,
-      const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data,
-      io::Extents* extent_map, int* object_dispatch_flags,
+      uint64_t object_no, const io::Extents &extents, librados::snap_t snap_id,
+      int op_flags, const ZTracer::Trace &parent_trace,
+      ceph::bufferlist* read_data, io::Extents* extent_map,
+      uint64_t* version, int* object_dispatch_flags,
       io::DispatchResult* dispatch_result, Context** on_finish,
       Context* on_dispatched) {
     return false;
index d649b58d999c03a8e2ff11b60fe6407f6dc4d38d..c65c4974dcd9236a5cfdbd23b097073aed24a5e4 100644 (file)
@@ -50,9 +50,9 @@ struct Mock {
     s_instance = this;
   }
 
-  MOCK_METHOD8(read_parent,
-               void(librbd::MockParentImageCacheImageCtx *, uint64_t, uint64_t,
-                    uint64_t, librados::snap_t, const ZTracer::Trace &,
+  MOCK_METHOD7(read_parent,
+               void(librbd::MockParentImageCacheImageCtx *, uint64_t,
+                    const io::Extents &, librados::snap_t, const ZTracer::Trace &,
                     ceph::bufferlist*, Context*));
 };
 
@@ -62,9 +62,9 @@ Mock *Mock::s_instance = nullptr;
 
 template<> void read_parent(
     librbd::MockParentImageCacheImageCtx *image_ctx, uint64_t object_no,
-    uint64_t off, uint64_t len, librados::snap_t snap_id,
+    const io::Extents &extents, librados::snap_t snap_id,
     const ZTracer::Trace &trace, ceph::bufferlist* data, Context* on_finish) {
-  Mock::s_instance->read_parent(image_ctx, object_no, off, len, snap_id, trace,
+  Mock::s_instance->read_parent(image_ctx, object_no, extents, snap_id, trace,
                                 data, on_finish);
 }
 
@@ -136,11 +136,11 @@ public :
   }
 
   void expect_read_parent(MockUtils &mock_utils, uint64_t object_no,
-                          uint64_t off, uint64_t len, librados::snap_t snap_id,
+                          const io::Extents &extents, librados::snap_t snap_id,
                           int r) {
     EXPECT_CALL(mock_utils,
-                read_parent(_, object_no, off, len, snap_id, _, _, _))
-      .WillOnce(WithArg<7>(CompleteContext(r, static_cast<asio::ContextWQ*>(nullptr))));
+                read_parent(_, object_no, extents, snap_id, _, _, _))
+      .WillOnce(WithArg<6>(CompleteContext(r, static_cast<asio::ContextWQ*>(nullptr))));
   }
 
   void expect_cache_close(MockParentImageCache& mparent_image_cache, int ret_val) {
@@ -370,9 +370,9 @@ TEST_F(TestMockParentCacheObjectDispatch, test_read) {
   C_SaferCond on_dispatched;
   io::DispatchResult dispatch_result;
   ceph::bufferlist read_data;
-  mock_parent_image_cache->read(0, 0, 4096, CEPH_NOSNAP, 0, {}, &read_data,
-                                nullptr, nullptr, &dispatch_result, nullptr,
-                                &on_dispatched);
+  mock_parent_image_cache->read(0, {{0, 4096}}, CEPH_NOSNAP, 0, {}, &read_data,
+                                nullptr, nullptr, nullptr, &dispatch_result,
+                                nullptr, &on_dispatched);
   ASSERT_EQ(0, on_dispatched.wait());
 
   mock_parent_image_cache->get_cache_client()->close();
@@ -418,13 +418,13 @@ TEST_F(TestMockParentCacheObjectDispatch, test_read_dne) {
   expect_cache_lookup_object(*mock_parent_image_cache, "");
 
   MockUtils mock_utils;
-  expect_read_parent(mock_utils, 0, 0, 4096, CEPH_NOSNAP, 0);
+  expect_read_parent(mock_utils, 0, {{0, 4096}}, CEPH_NOSNAP, 0);
 
   C_SaferCond on_dispatched;
   io::DispatchResult dispatch_result;
-  mock_parent_image_cache->read(0, 0, 4096, CEPH_NOSNAP, 0, {}, nullptr,
-                                nullptr, nullptr, &dispatch_result, nullptr,
-                                &on_dispatched);
+  mock_parent_image_cache->read(0, {{0, 4096}}, CEPH_NOSNAP, 0, {}, nullptr,
+                                nullptr, nullptr, nullptr, &dispatch_result,
+                                nullptr, &on_dispatched);
   ASSERT_EQ(0, on_dispatched.wait());
 
   mock_parent_image_cache->get_cache_client()->close();
index a5ab6dba533db2a79648ff5e435194d57cee8480..123bf52c149d966d911ed78ab5a8f2d1ad36fcef 100644 (file)
@@ -66,8 +66,8 @@ struct Mock {
     s_instance = this;
   }
 
-  MOCK_METHOD8(read_parent,
-               void(librbd::MockTestImageCtx *, uint64_t, uint64_t, uint64_t,
+  MOCK_METHOD7(read_parent,
+               void(librbd::MockTestImageCtx *, uint64_t, const Extents &,
                     librados::snap_t, const ZTracer::Trace &, ceph::bufferlist*,
                     Context*));
 };
@@ -77,10 +77,11 @@ Mock *Mock::s_instance = nullptr;
 } // anonymous namespace
 
 template<> void read_parent(
-    librbd::MockTestImageCtx *image_ctx, uint64_t object_no, uint64_t off,
-    uint64_t len, librados::snap_t snap_id, const ZTracer::Trace &trace,
-    ceph::bufferlist* data, Context* on_finish) {
-  Mock::s_instance->read_parent(image_ctx, object_no, off, len, snap_id, trace,
+    librbd::MockTestImageCtx *image_ctx, uint64_t object_no,
+    const Extents &extents, librados::snap_t snap_id,
+    const ZTracer::Trace &trace, ceph::bufferlist* data,
+    Context* on_finish) {
+  Mock::s_instance->read_parent(image_ctx, object_no, extents, snap_id, trace,
                                 data, on_finish);
 }
 
@@ -197,11 +198,11 @@ struct TestMockIoObjectRequest : public TestMockFixture {
   }
 
   void expect_read_parent(MockUtils &mock_utils, uint64_t object_no,
-                          uint64_t off, uint64_t len, librados::snap_t snap_id,
+                          const Extents &extents, librados::snap_t snap_id,
                           int r) {
     EXPECT_CALL(mock_utils,
-                read_parent(_, object_no, off, len, snap_id, _, _, _))
-      .WillOnce(WithArg<7>(CompleteContext(r, static_cast<asio::ContextWQ*>(nullptr))));
+                read_parent(_, object_no, extents, snap_id, _, _, _))
+      .WillOnce(WithArg<6>(CompleteContext(r, static_cast<asio::ContextWQ*>(nullptr))));
   }
 
   void expect_copyup(MockCopyupRequest& mock_copyup_request, int r) {
@@ -352,14 +353,28 @@ TEST_F(TestMockIoObjectRequest, Read) {
   expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
   expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096,
               std::string(4096, '1'), 0);
+  expect_read(mock_image_ctx, ictx->get_object_name(0), 8192, 4096,
+              std::string(4096, '2'), 0);
 
   bufferlist bl;
   Extents extent_map;
   C_SaferCond ctx;
+  uint64_t version;
   auto req = MockObjectReadRequest::create(
-    &mock_image_ctx, 0, 0, 4096, CEPH_NOSNAP, 0, {}, &bl, &extent_map, &ctx);
+          &mock_image_ctx, 0, {{0, 4096}, {8192, 4096}}, CEPH_NOSNAP, 0, {},
+          &bl, &extent_map, &version, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
+
+  bufferlist expected_bl;
+  expected_bl.append(std::string(4096, '1'));
+  expected_bl.append(std::string(4096, '2'));
+  Extents exepected_extent_map = {{0, 4096}, {8192, 4096}};
+
+  ASSERT_TRUE(expected_bl.contents_equal(bl));
+  ASSERT_EQ(exepected_extent_map.size(), extent_map.size());
+  ASSERT_TRUE(std::equal(extent_map.begin(), extent_map.end(),
+                         exepected_extent_map.begin()));
 }
 
 TEST_F(TestMockIoObjectRequest, SparseReadThreshold) {
@@ -385,8 +400,9 @@ TEST_F(TestMockIoObjectRequest, SparseReadThreshold) {
   Extents extent_map;
   C_SaferCond ctx;
   auto req = MockObjectReadRequest::create(
-    &mock_image_ctx, 0, 0, ictx->sparse_read_threshold_bytes, CEPH_NOSNAP, 0,
-    {}, &bl, &extent_map, &ctx);
+    &mock_image_ctx, 0,
+    {{0, ictx->sparse_read_threshold_bytes}}, CEPH_NOSNAP, 0, {}, &bl,
+    &extent_map, nullptr, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
@@ -412,7 +428,8 @@ TEST_F(TestMockIoObjectRequest, ReadError) {
   Extents extent_map;
   C_SaferCond ctx;
   auto req = MockObjectReadRequest::create(
-    &mock_image_ctx, 0, 0, 4096, CEPH_NOSNAP, 0, {}, &bl, &extent_map, &ctx);
+    &mock_image_ctx, 0, {{0, 4096}}, CEPH_NOSNAP, 0, {}, &bl, &extent_map,
+    nullptr, &ctx);
   req->send();
   ASSERT_EQ(-EPERM, ctx.wait());
 }
@@ -451,13 +468,14 @@ TEST_F(TestMockIoObjectRequest, ParentRead) {
   expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096, "", -ENOENT);
 
   MockUtils mock_utils;
-  expect_read_parent(mock_utils, 0, 0, 4096, CEPH_NOSNAP, 0);
+  expect_read_parent(mock_utils, 0, {{0, 4096}}, CEPH_NOSNAP, 0);
 
   bufferlist bl;
   Extents extent_map;
   C_SaferCond ctx;
   auto req = MockObjectReadRequest::create(
-    &mock_image_ctx, 0, 0, 4096, CEPH_NOSNAP, 0, {}, &bl, &extent_map, &ctx);
+    &mock_image_ctx, 0, {{0, 4096}}, CEPH_NOSNAP, 0, {}, &bl, &extent_map,
+    nullptr, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
@@ -496,13 +514,14 @@ TEST_F(TestMockIoObjectRequest, ParentReadError) {
   expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096, "", -ENOENT);
 
   MockUtils mock_utils;
-  expect_read_parent(mock_utils, 0, 0, 4096, CEPH_NOSNAP, -EPERM);
+  expect_read_parent(mock_utils, 0, {{0, 4096}}, CEPH_NOSNAP, -EPERM);
 
   bufferlist bl;
   Extents extent_map;
   C_SaferCond ctx;
   auto req = MockObjectReadRequest::create(
-    &mock_image_ctx, 0, 0, 4096, CEPH_NOSNAP, 0, {}, &bl, &extent_map, &ctx);
+    &mock_image_ctx, 0, {{0, 4096}}, CEPH_NOSNAP, 0, {}, &bl, &extent_map,
+    nullptr, &ctx);
   req->send();
   ASSERT_EQ(-EPERM, ctx.wait());
 }
@@ -541,7 +560,7 @@ TEST_F(TestMockIoObjectRequest, CopyOnRead) {
   expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096, "", -ENOENT);
 
   MockUtils mock_utils;
-  expect_read_parent(mock_utils, 0, 0, 4096, CEPH_NOSNAP, 0);
+  expect_read_parent(mock_utils, 0, {{0, 4096}}, CEPH_NOSNAP, 0);
 
   MockCopyupRequest mock_copyup_request;
   expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
@@ -552,7 +571,8 @@ TEST_F(TestMockIoObjectRequest, CopyOnRead) {
   Extents extent_map;
   C_SaferCond ctx;
   auto req = MockObjectReadRequest::create(
-    &mock_image_ctx, 0, 0, 4096, CEPH_NOSNAP, 0, {}, &bl, &extent_map, &ctx);
+    &mock_image_ctx, 0, {{0, 4096}}, CEPH_NOSNAP, 0, {}, &bl, &extent_map,
+    nullptr, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
index ab2b0ba1d7c02d904731aede47121973eab58b4e..9ce77ad0fb068694b1da2c42975914f35e033ae2 100644 (file)
@@ -132,8 +132,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Read) {
   C_SaferCond cond;
   Context *on_finish = &cond;
   ASSERT_FALSE(mock_simple_scheduler_object_dispatch.read(
-      0, 0, 4096, CEPH_NOSNAP, 0, {}, nullptr, nullptr, nullptr, nullptr,
-      &on_finish, nullptr));
+      0, {{0, 4096}}, CEPH_NOSNAP, 0, {}, nullptr, nullptr, nullptr, nullptr,
+      nullptr, &on_finish, nullptr));
   ASSERT_EQ(on_finish, &cond); // not modified
   on_finish->complete(0);
   ASSERT_EQ(0, cond.wait());
index 5cfe98f48068882b8d1877c1843630081246bab6..943b3a06a1e08365b0343abd4b7055274d3a6e3f 100644 (file)
@@ -25,17 +25,17 @@ public:
   MOCK_METHOD1(shut_down, void(Context*));
 
   MOCK_METHOD8(execute_read,
-               bool(uint64_t, uint64_t, uint64_t, librados::snap_t,
-                    ceph::bufferlist*, Extents*, DispatchResult*, Context*));
+               bool(uint64_t, const Extents&, librados::snap_t,
+                    ceph::bufferlist*, Extents*, uint64_t*,
+                    DispatchResult*, Context*));
   bool read(
-      uint64_t object_no, uint64_t object_off, uint64_t object_len,
-      librados::snap_t snap_id, int op_flags,
-      const ZTracer::Trace& parent_trace, ceph::bufferlist* read_data,
-      Extents* extent_map, int* dispatch_flags,
-      DispatchResult* dispatch_result, Context** on_finish,
-      Context* on_dispatched) {
-    return execute_read(object_no, object_off, object_len, snap_id, read_data,
-                        extent_map, dispatch_result, on_dispatched);
+      uint64_t object_no, const Extents& extents, librados::snap_t snap_id,
+      int op_flags, const ZTracer::Trace& parent_trace,
+      ceph::bufferlist* read_data, Extents* extent_map, uint64_t* version,
+      int* dispatch_flags, DispatchResult* dispatch_result,
+      Context** on_finish, Context* on_dispatched) {
+    return execute_read(object_no, extents, snap_id, read_data, extent_map,
+                        version, dispatch_result, on_dispatched);
   }
 
   MOCK_METHOD9(execute_discard,