]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: align discard requests to default bluestore allocation size
authorJason Dillaman <dillaman@redhat.com>
Thu, 14 Feb 2019 14:53:34 +0000 (09:53 -0500)
committerJason Dillaman <dillaman@redhat.com>
Tue, 26 Feb 2019 12:42:35 +0000 (07:42 -0500)
This will match the new krbd behavior of aligning discards to the
default bluestore minimum allocation size. Requests that are too
small (or the misaligned head / tail of the request) will be
ignored since it will not result in freed space.

Fixes: http://tracker.ceph.com/issues/38146
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
32 files changed:
src/common/options.cc
src/librbd/ImageCtx.cc
src/librbd/ImageCtx.h
src/librbd/cache/ImageCache.h
src/librbd/cache/ImageWriteback.cc
src/librbd/cache/ImageWriteback.h
src/librbd/cache/PassthroughImageCache.cc
src/librbd/cache/PassthroughImageCache.h
src/librbd/io/ImageDispatchSpec.cc
src/librbd/io/ImageDispatchSpec.h
src/librbd/io/ImageRequest.cc
src/librbd/io/ImageRequest.h
src/librbd/io/ImageRequestWQ.cc
src/librbd/io/ImageRequestWQ.h
src/librbd/io/ObjectRequest.cc
src/librbd/io/ObjectRequest.h
src/librbd/io/Types.h
src/librbd/journal/Replay.cc
src/librbd/journal/Types.cc
src/librbd/journal/Types.h
src/librbd/librbd.cc
src/test/librbd/deep_copy/test_mock_ObjectCopyRequest.cc
src/test/librbd/io/test_mock_ImageRequest.cc
src/test/librbd/io/test_mock_ObjectRequest.cc
src/test/librbd/journal/test_Entries.cc
src/test/librbd/journal/test_Replay.cc
src/test/librbd/journal/test_mock_Replay.cc
src/test/librbd/mock/MockImageCtx.h
src/test/librbd/mock/cache/MockImageCache.h
src/test/librbd/test_Migration.cc
src/test/rbd_mirror/test_ImageSync.cc
src/test/rbd_mirror/test_mock_ImageReplayer.cc

index e5752c2a2f0e94b20dfb0e61c89061111b9c0a95..d7140d76b11214b6da39c4bac9a954fe8c2a0749 100644 (file)
@@ -7074,6 +7074,22 @@ static std::vector<Option> get_rbd_options() {
     .set_default(true)
     .set_description("skip discard (zero) of unaligned extents within an object"),
 
+    Option("rbd_discard_granularity_bytes", Option::TYPE_UINT,
+           Option::LEVEL_ADVANCED)
+    .set_default(64_K)
+    .set_min_max(4_K, 32_M)
+    .set_validator([](std::string *value, std::string *error_message){
+        uint64_t f = strict_si_cast<uint64_t>(value->c_str(), error_message);
+        if (!error_message->empty()) {
+          return -EINVAL;
+        } else if (!isp2(f)) {
+          *error_message = "value must be a power of two";
+          return -EINVAL;
+        }
+        return 0;
+      })
+    .set_description("minimum aligned size of discard operations"),
+
     Option("rbd_enable_alloc_hint", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
     .set_default(true)
     .set_description("when writing a object, it will issue a hint to osd backend to indicate the expected size object need"),
index c4c17a974ba8649ce7550500d1a6efd062c398e2..f6daba7d3d4f219fcc2d95bf3bbd39874d3d4473 100644 (file)
@@ -788,6 +788,7 @@ public:
 #define ASSIGN_OPTION(param, type)              \
     param = config.get_val<type>("rbd_"#param)
 
+    bool skip_partial_discard = true;
     ASSIGN_OPTION(non_blocking_aio, bool);
     ASSIGN_OPTION(cache, bool);
     ASSIGN_OPTION(cache_writethrough_until_flush, bool);
@@ -801,6 +802,7 @@ public:
     ASSIGN_OPTION(mtime_update_interval, uint64_t);
     ASSIGN_OPTION(atime_update_interval, uint64_t);
     ASSIGN_OPTION(skip_partial_discard, bool);
+    ASSIGN_OPTION(discard_granularity_bytes, uint64_t);
     ASSIGN_OPTION(blkin_trace_all, bool);
 
 #undef ASSIGN_OPTION
@@ -808,6 +810,9 @@ public:
     if (sparse_read_threshold_bytes == 0) {
       sparse_read_threshold_bytes = get_object_size();
     }
+    if (!skip_partial_discard) {
+      discard_granularity_bytes = 0;
+    }
 
     io_work_queue->apply_qos_schedule_tick_min(
       config.get_val<uint64_t>("rbd_qos_schedule_tick_min"));
index 3e97c712951cabed30e10f3c9fb1ae48d163e099..6e6eb3403b0e3ceb0a2e1853f81af32910ffd041 100644 (file)
@@ -182,7 +182,7 @@ namespace librbd {
     uint64_t readahead_disable_after_bytes;
     bool clone_copy_on_read;
     bool enable_alloc_hint;
-    bool skip_partial_discard;
+    uint32_t discard_granularity_bytes = 0;
     bool blkin_trace_all;
     uint64_t mirroring_replay_delay;
     uint64_t mtime_update_interval;
index f394a1a80a1e8a91a3b3c612508da2556062af3f..71af11f2111a7a50b60b6916ee6f63ddfb5eefcf 100644 (file)
@@ -28,7 +28,8 @@ struct ImageCache {
   virtual void aio_write(Extents&& image_extents, ceph::bufferlist&& bl,
                          int fadvise_flags, Context *on_finish) = 0;
   virtual void aio_discard(uint64_t offset, uint64_t length,
-                           bool skip_partial_discard, Context *on_finish) = 0;
+                           uint32_t discard_granularity_bytes,
+                           Context *on_finish) = 0;
   virtual void aio_flush(Context *on_finish) = 0;
   virtual void aio_writesame(uint64_t offset, uint64_t length,
                              ceph::bufferlist&& bl,
index 54b964d9091582d27d4eea27fb6dbf82043a2aea..ad479fbd39912bcb2f6166670bd06b05a8fc2b07 100644 (file)
@@ -53,8 +53,8 @@ void ImageWriteback<I>::aio_write(Extents &&image_extents,
 
 template <typename I>
 void ImageWriteback<I>::aio_discard(uint64_t offset, uint64_t length,
-                                    bool skip_partial_discard,
-                                   Context *on_finish) {
+                                   uint32_t discard_granularity_bytes,
+                                    Context *on_finish) {
   CephContext *cct = m_image_ctx.cct;
   ldout(cct, 20) << "offset=" << offset << ", "
                  << "length=" << length << ", "
@@ -63,7 +63,7 @@ void ImageWriteback<I>::aio_discard(uint64_t offset, uint64_t length,
   auto aio_comp = io::AioCompletion::create_and_start(on_finish, &m_image_ctx,
                                                       io::AIO_TYPE_DISCARD);
   io::ImageDiscardRequest<I> req(m_image_ctx, aio_comp, {{offset, length}},
-                                 skip_partial_discard, {});
+                                 discard_granularity_bytes, {});
   req.set_bypass_image_cache();
   req.send();
 }
index 6a790768713583fd825e90c385f04a95ab2ca445..382c57c1d390d39c3259a51594978ab6bdbd6768 100644 (file)
@@ -31,7 +31,7 @@ public:
   void aio_write(Extents &&image_extents, ceph::bufferlist&& bl,
                  int fadvise_flags, Context *on_finish);
   void aio_discard(uint64_t offset, uint64_t length,
-                   bool skip_partial_discard, Context *on_finish);
+                   uint32_t discard_granularity_bytes, Context *on_finish);
   void aio_flush(Context *on_finish);
   void aio_writesame(uint64_t offset, uint64_t length,
                      ceph::bufferlist&& bl,
index 5b22fc7a6ced33b3075e4669ff2d1bc6ab0db4a1..c3672f531a2ad75ecb00d24e3b40744387ffc21a 100644 (file)
@@ -45,13 +45,15 @@ void PassthroughImageCache<I>::aio_write(Extents &&image_extents,
 
 template <typename I>
 void PassthroughImageCache<I>::aio_discard(uint64_t offset, uint64_t length,
-                                           bool skip_partial_discard, Context *on_finish) {
+                                           uint32_t discard_granularity_bytes,
+                                           Context *on_finish) {
   CephContext *cct = m_image_ctx.cct;
   ldout(cct, 20) << "offset=" << offset << ", "
                  << "length=" << length << ", "
                  << "on_finish=" << on_finish << dendl;
 
-  m_image_writeback.aio_discard(offset, length, skip_partial_discard, on_finish);
+  m_image_writeback.aio_discard(offset, length, discard_granularity_bytes,
+                                on_finish);
 }
 
 template <typename I>
index 5e676cefeb2d639e6496eec754f32b950234d686..4be69a50aed5db11886ff8f8bb87c7e8483cf4fe 100644 (file)
@@ -27,7 +27,8 @@ public:
   void aio_write(Extents&& image_extents, ceph::bufferlist&& bl,
                  int fadvise_flags, Context *on_finish) override;
   void aio_discard(uint64_t offset, uint64_t length,
-                   bool skip_partial_discard, Context *on_finish) override;
+                   uint32_t discard_granularity_bytes,
+                   Context *on_finish) override;
   void aio_flush(Context *on_finish) override;
   void aio_writesame(uint64_t offset, uint64_t length,
                      ceph::bufferlist&& bl,
index dd28d16edd4b61bcc49d5ba2cd23b4864216283b..dd39e1d5b013b1ee06f388f5cf57624eabfccf0a 100644 (file)
@@ -28,7 +28,7 @@ struct ImageDispatchSpec<I>::SendVisitor
   void operator()(Discard& discard) const {
     ImageRequest<I>::aio_discard(
       &spec->m_image_ctx, spec->m_aio_comp, std::move(spec->m_image_extents),
-      discard.skip_partial_discard, spec->m_parent_trace);
+      discard.discard_granularity_bytes, spec->m_parent_trace);
   }
 
   void operator()(Write& write) const {
index 82b9fc223d6b2cb3f1b3360dedae1373a7fe7978..7cdc078a0886c74bd372f5dbb4f591df01a87a45 100644 (file)
@@ -30,10 +30,10 @@ public:
   };
 
   struct Discard {
-    bool skip_partial_discard;
+    uint32_t discard_granularity_bytes;
 
-    Discard(bool skip_partial_discard)
-      : skip_partial_discard(skip_partial_discard) {
+    Discard(uint32_t discard_granularity_bytes)
+      : discard_granularity_bytes(discard_granularity_bytes) {
     }
   };
 
@@ -82,10 +82,10 @@ public:
 
   static ImageDispatchSpec* create_discard_request(
       ImageCtxT &image_ctx, AioCompletion *aio_comp, uint64_t off, uint64_t len,
-      bool skip_partial_discard, const ZTracer::Trace &parent_trace) {
+      uint32_t discard_granularity_bytes, const ZTracer::Trace &parent_trace) {
     return new ImageDispatchSpec(image_ctx, aio_comp, {{off, len}},
-                                 Discard{skip_partial_discard}, 0,
-                                 parent_trace);
+                                 Discard{discard_granularity_bytes},
+                                 0, parent_trace);
   }
 
   static ImageDispatchSpec* create_write_request(
index e0c242cf046b0f0f53f7c392ef8d5d11862d2720..1b6d9756d4af04a5404f8b3c775f3ac9e39679ac 100644 (file)
@@ -19,6 +19,7 @@
 #include "common/perf_counters.h"
 #include "common/WorkQueue.h"
 #include "osdc/Striper.h"
+#include <algorithm>
 #include <functional>
 
 #define dout_subsys ceph_subsys_rbd
@@ -98,10 +99,10 @@ void ImageRequest<I>::aio_write(I *ictx, AioCompletion *c,
 template <typename I>
 void ImageRequest<I>::aio_discard(I *ictx, AioCompletion *c,
                                   Extents &&image_extents,
-                                  bool skip_partial_discard,
+                                  uint32_t discard_granularity_bytes,
                                  const ZTracer::Trace &parent_trace) {
   ImageDiscardRequest<I> req(*ictx, c, std::move(image_extents),
-                             skip_partial_discard, parent_trace);
+                             discard_granularity_bytes, parent_trace);
   req.send();
 }
 
@@ -376,7 +377,7 @@ void AbstractImageWriteRequest<I>::send_request() {
                   image_ctx.journal->is_journal_appending());
   }
 
-  int ret = validate_object_extents(object_extents);
+  int ret = prune_object_extents(&object_extents);
   if (ret < 0) {
     aio_comp->fail(ret);
     return;
@@ -497,7 +498,7 @@ uint64_t ImageDiscardRequest<I>::append_journal_event(bool synchronous) {
     journal::EventEntry event_entry(
       journal::AioDiscardEvent(extent.first,
                                extent.second,
-                               this->m_skip_partial_discard));
+                               this->m_discard_granularity_bytes));
     tid = image_ctx.journal->append_io_event(std::move(event_entry),
                                              extent.first, extent.second,
                                              synchronous, 0);
@@ -516,7 +517,8 @@ void ImageDiscardRequest<I>::send_image_cache_request() {
   for (auto &extent : this->m_image_extents) {
     C_AioRequest *req_comp = new C_AioRequest(aio_comp);
     image_ctx.image_cache->aio_discard(extent.first, extent.second,
-                                       this->m_skip_partial_discard, req_comp);
+                                       this->m_discard_granularity_bytes,
+                                       req_comp);
   }
 }
 
@@ -525,14 +527,11 @@ ObjectDispatchSpec *ImageDiscardRequest<I>::create_object_request(
     const ObjectExtent &object_extent, const ::SnapContext &snapc,
     uint64_t journal_tid, Context *on_finish) {
   I &image_ctx = this->m_image_ctx;
-  int discard_flags = OBJECT_DISCARD_FLAG_DISABLE_CLONE_REMOVE;
-  if (m_skip_partial_discard) {
-    discard_flags |=  OBJECT_DISCARD_FLAG_SKIP_PARTIAL;
-  }
   auto req = ObjectDispatchSpec::create_discard(
     &image_ctx, OBJECT_DISPATCH_LAYER_NONE, object_extent.oid.name,
     object_extent.objectno, object_extent.offset, object_extent.length, snapc,
-    discard_flags, journal_tid, this->m_trace, on_finish);
+    OBJECT_DISCARD_FLAG_DISABLE_CLONE_REMOVE, journal_tid, this->m_trace,
+    on_finish);
   return req;
 }
 
@@ -543,6 +542,59 @@ void ImageDiscardRequest<I>::update_stats(size_t length) {
   image_ctx.perfcounter->inc(l_librbd_discard_bytes, length);
 }
 
+template <typename I>
+int ImageDiscardRequest<I>::prune_object_extents(
+    ObjectExtents* object_extents) const {
+  if (m_discard_granularity_bytes == 0) {
+    return 0;
+  }
+
+  // Align the range to discard_granularity_bytes boundary and skip
+  // and discards that are too small to free up any space.
+  //
+  // discard_granularity_bytes >= object_size && tail truncation
+  // is a special case for filestore
+  bool prune_required = false;
+  auto object_size = this->m_image_ctx.layout.object_size;
+  auto discard_granularity_bytes = std::min<uint64_t>(
+    m_discard_granularity_bytes, object_size);
+  auto xform_lambda =
+    [discard_granularity_bytes, object_size, &prune_required]
+    (ObjectExtent& object_extent) {
+      auto& offset = object_extent.offset;
+      auto& length = object_extent.length;
+      auto next_offset = offset + length;
+
+      if ((discard_granularity_bytes < object_size) ||
+          (next_offset < object_size)) {
+        static_assert(sizeof(offset) == sizeof(discard_granularity_bytes));
+        offset = p2roundup(offset, discard_granularity_bytes);
+        next_offset = p2align(next_offset, discard_granularity_bytes);
+        if (offset >= next_offset) {
+          prune_required = true;
+          length = 0;
+        } else {
+          length = next_offset - offset;
+        }
+      }
+    };
+  std::for_each(object_extents->begin(), object_extents->end(),
+                xform_lambda);
+
+  if (prune_required) {
+    // one or more object extents were skipped
+    auto remove_lambda =
+      [](const ObjectExtent& object_extent) {
+        return (object_extent.length == 0);
+      };
+    object_extents->erase(
+      std::remove_if(object_extents->begin(), object_extents->end(),
+                     remove_lambda),
+      object_extents->end());
+  }
+  return 0;
+}
+
 template <typename I>
 void ImageFlushRequest<I>::send_request() {
   I &image_ctx = this->m_image_ctx;
@@ -744,15 +796,15 @@ void ImageCompareAndWriteRequest<I>::update_stats(size_t length) {
 }
 
 template <typename I>
-int ImageCompareAndWriteRequest<I>::validate_object_extents(
-    const ObjectExtents &object_extents) const {
-  if (object_extents.size() > 1)
+int ImageCompareAndWriteRequest<I>::prune_object_extents(
+    ObjectExtents* object_extents) const {
+  if (object_extents->size() > 1)
     return -EINVAL;
 
   I &image_ctx = this->m_image_ctx;
   uint64_t sector_size = 512ULL;
   uint64_t su = image_ctx.layout.stripe_unit;
-  ObjectExtent object_extent = object_extents.front();
+  ObjectExtent object_extent = object_extents->front();
   if (object_extent.offset % sector_size + object_extent.length > sector_size ||
       (su != 0 && (object_extent.offset % su + object_extent.length > su)))
     return -EINVAL;
index 0b8d4179c0262fb8c22a51b64a72056e22e72c0d..d7d10019d3ed0618c390fe6eef764f59c496dd07 100644 (file)
@@ -40,8 +40,9 @@ public:
                         Extents &&image_extents, bufferlist &&bl, int op_flags,
                        const ZTracer::Trace &parent_trace);
   static void aio_discard(ImageCtxT *ictx, AioCompletion *c,
-                          Extents &&image_extents, bool skip_partial_discard,
-                         const ZTracer::Trace &parent_trace);
+                          Extents &&image_extents,
+                          uint32_t discard_granularity_bytes,
+                          const ZTracer::Trace &parent_trace);
   static void aio_flush(ImageCtxT *ictx, AioCompletion *c,
                         FlushSource flush_source,
                         const ZTracer::Trace &parent_trace);
@@ -140,8 +141,7 @@ protected:
 
   void send_request() override;
 
-  virtual int validate_object_extents(
-      const ObjectExtents &object_extents) const {
+  virtual int prune_object_extents(ObjectExtents* object_extents) const {
     return 0;
   }
 
@@ -203,11 +203,12 @@ template <typename ImageCtxT = ImageCtx>
 class ImageDiscardRequest : public AbstractImageWriteRequest<ImageCtxT> {
 public:
   ImageDiscardRequest(ImageCtxT &image_ctx, AioCompletion *aio_comp,
-                      Extents&& image_extents, bool skip_partial_discard,
-                     const ZTracer::Trace &parent_trace)
+                      Extents&& image_extents,
+                     uint32_t discard_granularity_bytes,
+                      const ZTracer::Trace &parent_trace)
     : AbstractImageWriteRequest<ImageCtxT>(
        image_ctx, aio_comp, std::move(image_extents), "discard", parent_trace),
-      m_skip_partial_discard(skip_partial_discard) {
+      m_discard_granularity_bytes(discard_granularity_bytes) {
   }
 
 protected:
@@ -229,8 +230,11 @@ protected:
 
   uint64_t append_journal_event(bool synchronous) override;
   void update_stats(size_t length) override;
+
+  int prune_object_extents(ObjectExtents* object_extents) const override;
+
 private:
-  bool m_skip_partial_discard;
+  uint32_t m_discard_granularity_bytes;
 };
 
 template <typename ImageCtxT = ImageCtx>
@@ -337,8 +341,7 @@ protected:
     return "aio_compare_and_write";
   }
 
-  int validate_object_extents(
-      const ObjectExtents &object_extents) const override;
+  int prune_object_extents(ObjectExtents* object_extents) const override;
 
 private:
   bufferlist m_cmp_bl;
index 81c39d9b1420a0c8b7508705750acda90db8b424..2b38bf3b8d40ce6f87f669e251a5fce5e1fb830a 100644 (file)
@@ -158,7 +158,7 @@ ssize_t ImageRequestWQ<I>::write(uint64_t off, uint64_t len,
 
 template <typename I>
 ssize_t ImageRequestWQ<I>::discard(uint64_t off, uint64_t len,
-                                  bool skip_partial_discard) {
+                                  uint32_t discard_granularity_bytes) {
   CephContext *cct = m_image_ctx.cct;
   ldout(cct, 20) << "ictx=" << &m_image_ctx << ", off=" << off << ", "
                  << "len = " << len << dendl;
@@ -173,7 +173,7 @@ ssize_t ImageRequestWQ<I>::discard(uint64_t off, uint64_t len,
 
   C_SaferCond cond;
   AioCompletion *c = AioCompletion::create(&cond);
-  aio_discard(c, off, len, skip_partial_discard, false);
+  aio_discard(c, off, len, discard_granularity_bytes, false);
 
   r = cond.wait();
   if (r < 0) {
@@ -338,7 +338,8 @@ void ImageRequestWQ<I>::aio_write(AioCompletion *c, uint64_t off, uint64_t len,
 
 template <typename I>
 void ImageRequestWQ<I>::aio_discard(AioCompletion *c, uint64_t off,
-                                   uint64_t len, bool skip_partial_discard,
+                                   uint64_t len,
+                                    uint32_t discard_granularity_bytes,
                                    bool native_async) {
   CephContext *cct = m_image_ctx.cct;
   FUNCTRACE(cct);
@@ -364,11 +365,11 @@ void ImageRequestWQ<I>::aio_discard(AioCompletion *c, uint64_t off,
   RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
   if (m_image_ctx.non_blocking_aio || writes_blocked()) {
     queue(ImageDispatchSpec<I>::create_discard_request(
-            m_image_ctx, c, off, len, skip_partial_discard, trace));
+            m_image_ctx, c, off, len, discard_granularity_bytes, trace));
   } else {
     c->start_op();
     ImageRequest<I>::aio_discard(&m_image_ctx, c, {{off, len}},
-                                skip_partial_discard, trace);
+                                 discard_granularity_bytes, trace);
     finish_in_flight_io();
   }
   trace.event("finish");
index 5c726985964c416baa9c4f8b50c1330beaade9d6..a9134fc4a1bf58659d4fd3ae524e37a539b4a7c2 100644 (file)
@@ -34,7 +34,8 @@ public:
   ssize_t read(uint64_t off, uint64_t len, ReadResult &&read_result,
                int op_flags);
   ssize_t write(uint64_t off, uint64_t len, bufferlist &&bl, int op_flags);
-  ssize_t discard(uint64_t off, uint64_t len, bool skip_partial_discard);
+  ssize_t discard(uint64_t off, uint64_t len,
+                  uint32_t discard_granularity_bytes);
   ssize_t writesame(uint64_t off, uint64_t len, bufferlist &&bl, int op_flags);
   ssize_t compare_and_write(uint64_t off, uint64_t len,
                             bufferlist &&cmp_bl, bufferlist &&bl,
@@ -46,7 +47,7 @@ public:
   void aio_write(AioCompletion *c, uint64_t off, uint64_t len,
                  bufferlist &&bl, int op_flags, bool native_async=true);
   void aio_discard(AioCompletion *c, uint64_t off, uint64_t len,
-                   bool skip_partial_discard, bool native_async=true);
+                   uint32_t discard_granularity_bytes, bool native_async=true);
   void aio_flush(AioCompletion *c, bool native_async=true);
   void aio_writesame(AioCompletion *c, uint64_t off, uint64_t len,
                      bufferlist &&bl, int op_flags, bool native_async=true);
index fd1c509f153f60371b218abf2e4fb961c00813b9..611e83677a6f5ab63955af2234e76418adfcd02e 100644 (file)
@@ -663,21 +663,6 @@ void ObjectWriteRequest<I>::add_write_ops(librados::ObjectWriteOperation *wr) {
   wr->set_op_flags2(m_op_flags);
 }
 
-template <typename I>
-void ObjectDiscardRequest<I>::send() {
-  I *image_ctx = this->m_ictx;
-  auto cct = image_ctx->cct;
-  if ((m_discard_flags & OBJECT_DISCARD_FLAG_SKIP_PARTIAL) != 0 &&
-      this->m_object_off + this->m_object_len < image_ctx->layout.object_size) {
-    ldout(cct, 20) << "oid " << this->m_oid << " " << this->m_object_off << "~"
-                  << this->m_object_len << ": skip partial discard" << dendl;
-    this->async_finish(0);
-    return;
-  }
-
-  AbstractObjectWriteRequest<I>::send();
-}
-
 template <typename I>
 void ObjectWriteSameRequest<I>::add_write_ops(
     librados::ObjectWriteOperation *wr) {
index 7c111c968d46af954e5cd2108331a3b59d4ba9b2..63fd1da0df9cb05ada4e0567d1b46ed0ac30f4f5 100644 (file)
@@ -341,8 +341,6 @@ public:
     return OBJECT_EXISTS;
   }
 
-  void send() override;
-
 protected:
   bool is_no_op_for_nonexistent_object() const override {
     return (!this->has_parent());
index 7e09c9018c503c8aa32388aba007db501e952404..6bd42eac3186889719b721ff98bdb7f323cddf09 100644 (file)
@@ -66,8 +66,7 @@ enum ObjectDispatchLayer {
 
 enum {
   OBJECT_DISCARD_FLAG_DISABLE_CLONE_REMOVE      = 1UL << 0,
-  OBJECT_DISCARD_FLAG_DISABLE_OBJECT_MAP_UPDATE = 1UL << 1,
-  OBJECT_DISCARD_FLAG_SKIP_PARTIAL              = 1UL << 2
+  OBJECT_DISCARD_FLAG_DISABLE_OBJECT_MAP_UPDATE = 1UL << 1
 };
 
 enum {
index 739034439f1991fe9a166c1c0fb0d30e2b53122a..25dd61203f7bc26d81aaaa8e16c31ddababd4034 100644 (file)
@@ -353,7 +353,7 @@ void Replay<I>::handle_event(const journal::AioDiscardEvent &event,
   if (!clipped_io(event.offset, aio_comp)) {
     io::ImageRequest<I>::aio_discard(&m_image_ctx, aio_comp,
                                      {{event.offset, event.length}},
-                                     event.skip_partial_discard, {});
+                                     event.discard_granularity_bytes, {});
   }
 
   if (flush_required) {
index 75e71a21e0349e596c8c8d34f90d0c4f5c1af082..ee919d35d7b54ee874a1521279b6585577406057 100644 (file)
@@ -75,22 +75,38 @@ void AioDiscardEvent::encode(bufferlist& bl) const {
   using ceph::encode;
   encode(offset, bl);
   encode(length, bl);
+  bool skip_partial_discard = (discard_granularity_bytes > 0);
   encode(skip_partial_discard, bl);
+  encode(discard_granularity_bytes, bl);
 }
 
 void AioDiscardEvent::decode(__u8 version, bufferlist::const_iterator& it) {
   using ceph::decode;
   decode(offset, it);
   decode(length, it);
+
+  bool skip_partial_discard = false;
   if (version >= 4) {
     decode(skip_partial_discard, it);
   }
+
+  if (version >= 5) {
+    decode(discard_granularity_bytes, it);
+  } else {
+    if (skip_partial_discard) {
+      // use a size larger than the maximum object size which will
+      // truncated down to object size during IO processing
+      discard_granularity_bytes = std::numeric_limits<uint32_t>::max();
+    } else {
+      discard_granularity_bytes = 0;
+    }
+  }
 }
 
 void AioDiscardEvent::dump(Formatter *f) const {
   f->dump_unsigned("offset", offset);
   f->dump_unsigned("length", length);
-  f->dump_bool("skip_partial_discard", skip_partial_discard);
+  f->dump_unsigned("discard_granularity_bytes", discard_granularity_bytes);
 }
 
 uint32_t AioWriteEvent::get_fixed_size() {
@@ -399,7 +415,7 @@ EventType EventEntry::get_event_type() const {
 }
 
 void EventEntry::encode(bufferlist& bl) const {
-  ENCODE_START(4, 1, bl);
+  ENCODE_START(5, 1, bl);
   boost::apply_visitor(EncodeVisitor(bl), event);
   ENCODE_FINISH(bl);
   encode_metadata(bl);
@@ -501,7 +517,7 @@ void EventEntry::decode_metadata(bufferlist::const_iterator& it) {
 
 void EventEntry::generate_test_instances(std::list<EventEntry *> &o) {
   o.push_back(new EventEntry(AioDiscardEvent()));
-  o.push_back(new EventEntry(AioDiscardEvent(123, 345, false), utime_t(1, 1)));
+  o.push_back(new EventEntry(AioDiscardEvent(123, 345, 4096), utime_t(1, 1)));
 
   bufferlist bl;
   bl.append(std::string(32, '1'));
index 2486d416e292cd8008cbe713fc808855d4c2ccb5..ae5681ade0677b31f5c19bef9616ab9b63f5ca58 100644 (file)
@@ -51,14 +51,16 @@ enum EventType {
 struct AioDiscardEvent {
   static const EventType TYPE = EVENT_TYPE_AIO_DISCARD;
 
-  uint64_t offset;
-  uint64_t length;
-  bool skip_partial_discard;
+  uint64_t offset = 0;
+  uint64_t length = 0;
+  uint32_t discard_granularity_bytes = 0;
 
-  AioDiscardEvent() : offset(0), length(0), skip_partial_discard(false) {
+  AioDiscardEvent() {
   }
-  AioDiscardEvent(uint64_t _offset, uint64_t _length, bool _skip_partial_discard)
-    : offset(_offset), length(_length), skip_partial_discard(_skip_partial_discard) {
+  AioDiscardEvent(uint64_t _offset, uint64_t _length,
+                  uint32_t discard_granularity_bytes)
+    : offset(_offset), length(_length),
+      discard_granularity_bytes(discard_granularity_bytes) {
   }
 
   void encode(bufferlist& bl) const;
index 3fa1bea1eef39ae3bdbc3868f4092abf66437ea3..3969751e5b098ebf772ec2ccaba8e5248206857e 100644 (file)
@@ -2205,7 +2205,8 @@ namespace librbd {
         tracepoint(librbd, discard_exit, -EINVAL);
         return -EINVAL;
     }
-    int r = ictx->io_work_queue->discard(ofs, len, ictx->skip_partial_discard);
+    int r = ictx->io_work_queue->discard(
+      ofs, len, ictx->discard_granularity_bytes);
     tracepoint(librbd, discard_exit, r);
     return r;
   }
@@ -2224,7 +2225,7 @@ namespace librbd {
 
     bool discard_zero = ictx->config.get_val<bool>("rbd_discard_on_zeroed_write_same");
     if (discard_zero && mem_is_zero(bl.c_str(), bl.length())) {
-      int r = ictx->io_work_queue->discard(ofs, len, false);
+      int r = ictx->io_work_queue->discard(ofs, len, 0);
       tracepoint(librbd, writesame_exit, r);
       return r;
     }
@@ -2295,7 +2296,8 @@ namespace librbd {
   {
     ImageCtx *ictx = (ImageCtx *)ctx;
     tracepoint(librbd, aio_discard_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, off, len, c->pc);
-    ictx->io_work_queue->aio_discard(get_aio_completion(c), off, len, ictx->skip_partial_discard);
+    ictx->io_work_queue->aio_discard(
+      get_aio_completion(c), off, len, ictx->discard_granularity_bytes);
     tracepoint(librbd, aio_discard_exit, 0);
     return 0;
   }
@@ -2361,7 +2363,7 @@ namespace librbd {
 
     bool discard_zero = ictx->config.get_val<bool>("rbd_discard_on_zeroed_write_same");
     if (discard_zero && mem_is_zero(bl.c_str(), bl.length())) {
-      ictx->io_work_queue->aio_discard(get_aio_completion(c), off, len, false);
+      ictx->io_work_queue->aio_discard(get_aio_completion(c), off, len, 0);
       tracepoint(librbd, aio_writesame_exit, 0);
       return 0;
     }
@@ -5119,7 +5121,8 @@ extern "C" int rbd_discard(rbd_image_t image, uint64_t ofs, uint64_t len)
     return -EINVAL;
   }
 
-  int r = ictx->io_work_queue->discard(ofs, len, ictx->skip_partial_discard);
+  int r = ictx->io_work_queue->discard(
+    ofs, len, ictx->discard_granularity_bytes);
   tracepoint(librbd, discard_exit, r);
   return r;
 }
@@ -5139,7 +5142,7 @@ extern "C" ssize_t rbd_writesame(rbd_image_t image, uint64_t ofs, size_t len,
 
   bool discard_zero = ictx->config.get_val<bool>("rbd_discard_on_zeroed_write_same");
   if (discard_zero && mem_is_zero(buf, data_len)) {
-    int r = ictx->io_work_queue->discard(ofs, len, false);
+    int r = ictx->io_work_queue->discard(ofs, len, 0);
     tracepoint(librbd, writesame_exit, r);
     return r;
   }
@@ -5258,7 +5261,8 @@ extern "C" int rbd_aio_discard(rbd_image_t image, uint64_t off, uint64_t len,
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   librbd::RBD::AioCompletion *comp = (librbd::RBD::AioCompletion *)c;
   tracepoint(librbd, aio_discard_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, off, len, comp->pc);
-  ictx->io_work_queue->aio_discard(get_aio_completion(comp), off, len, ictx->skip_partial_discard);
+  ictx->io_work_queue->aio_discard(
+    get_aio_completion(comp), off, len, ictx->discard_granularity_bytes);
   tracepoint(librbd, aio_discard_exit, 0);
   return 0;
 }
@@ -5361,7 +5365,7 @@ extern "C" int rbd_aio_writesame(rbd_image_t image, uint64_t off, size_t len,
 
   bool discard_zero = ictx->config.get_val<bool>("rbd_discard_on_zeroed_write_same");
   if (discard_zero && mem_is_zero(buf, data_len)) {
-    ictx->io_work_queue->aio_discard(get_aio_completion(comp), off, len, false);
+    ictx->io_work_queue->aio_discard(get_aio_completion(comp), off, len, 0);
     tracepoint(librbd, aio_writesame_exit, 0);
     return 0;
   }
index 8e70f153794a44ccd98f5aa03dcf2b92e7c059d1..73be5c2fe8e8e34fd86071112237041cbac468c8 100644 (file)
@@ -749,7 +749,7 @@ TEST_F(TestMockDeepCopyObjectCopyRequest, WriteSnaps) {
 TEST_F(TestMockDeepCopyObjectCopyRequest, Trim) {
   ASSERT_EQ(0, m_src_image_ctx->operations->metadata_set(
               "conf_rbd_skip_partial_discard", "false"));
-  m_src_image_ctx->skip_partial_discard = false;
+  m_src_image_ctx->discard_granularity_bytes = 0;
 
   // scribble some data
   interval_set<uint64_t> one;
@@ -760,7 +760,7 @@ TEST_F(TestMockDeepCopyObjectCopyRequest, Trim) {
   uint64_t trim_offset = rand() % one.range_end();
   ASSERT_LE(0, m_src_image_ctx->io_work_queue->discard(
     trim_offset, one.range_end() - trim_offset,
-    m_src_image_ctx->skip_partial_discard));
+    m_src_image_ctx->discard_granularity_bytes));
   ASSERT_EQ(0, create_snap("copy"));
 
   librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
@@ -813,7 +813,7 @@ TEST_F(TestMockDeepCopyObjectCopyRequest, Remove) {
   // remove the object
   uint64_t object_size = 1 << m_src_image_ctx->order;
   ASSERT_LE(0, m_src_image_ctx->io_work_queue->discard(
-    0, object_size, m_src_image_ctx->skip_partial_discard));
+    0, object_size, m_src_image_ctx->discard_granularity_bytes));
   ASSERT_EQ(0, create_snap("copy"));
   librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
   librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
index 9fb071ba08d60a52e64acf93f8bff6c90a61a617..5f5851c3751f689bb3509d39b9cc140b1ad667f3 100644 (file)
@@ -88,6 +88,22 @@ struct TestMockIoImageRequest : public TestMockFixture {
     }
   }
 
+  void expect_object_discard_request(MockTestImageCtx &mock_image_ctx,
+                                     uint64_t object_no, uint64_t offset,
+                                     uint32_t length, int r) {
+    EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, send(_))
+      .WillOnce(Invoke([&mock_image_ctx, object_no, offset, length, r]
+                (ObjectDispatchSpec* spec) {
+                  auto* discard_spec = boost::get<ObjectDispatchSpec::DiscardRequest>(&spec->request);
+                  ASSERT_TRUE(discard_spec != nullptr);
+                  ASSERT_EQ(object_no, discard_spec->object_no);
+                  ASSERT_EQ(offset, discard_spec->object_off);
+                  ASSERT_EQ(length, discard_spec->object_len);
+
+                  spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+                  mock_image_ctx.image_ctx->op_work_queue->queue(&spec->dispatcher_ctx, r);
+                }));
+  }
 
   void expect_object_request_send(MockTestImageCtx &mock_image_ctx,
                                   int r) {
@@ -216,6 +232,90 @@ TEST_F(TestMockIoImageRequest, AioReadAccessTimestamp) {
   ASSERT_EQ(1, aio_comp_ctx_2.wait());
 }
 
+TEST_F(TestMockIoImageRequest, PartialDiscard) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ictx->discard_granularity_bytes = 0;
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  mock_image_ctx.journal = nullptr;
+
+  InSequence seq;
+  expect_get_modify_timestamp(mock_image_ctx, false);
+  expect_object_discard_request(mock_image_ctx, 0, 16, 63, 0);
+  expect_object_discard_request(mock_image_ctx, 0, 84, 100, 0);
+
+  C_SaferCond aio_comp_ctx;
+  AioCompletion *aio_comp = AioCompletion::create_and_start(
+    &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+  MockImageDiscardRequest mock_aio_image_discard(
+    mock_image_ctx, aio_comp, {{16, 63}, {84, 100}},
+    ictx->discard_granularity_bytes, {});
+  {
+    RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
+    mock_aio_image_discard.send();
+  }
+  ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, TailDiscard) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, resize(ictx, ictx->layout.object_size));
+  ictx->discard_granularity_bytes = 2 * ictx->layout.object_size;
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  mock_image_ctx.journal = nullptr;
+
+  InSequence seq;
+  expect_get_modify_timestamp(mock_image_ctx, false);
+  expect_object_discard_request(
+    mock_image_ctx, 0, ictx->layout.object_size - 1024, 1024, 0);
+
+  C_SaferCond aio_comp_ctx;
+  AioCompletion *aio_comp = AioCompletion::create_and_start(
+    &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+  MockImageDiscardRequest mock_aio_image_discard(
+    mock_image_ctx, aio_comp,
+    {{ictx->layout.object_size - 1024, 1024}},
+    ictx->discard_granularity_bytes, {});
+  {
+    RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
+    mock_aio_image_discard.send();
+  }
+  ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
+TEST_F(TestMockIoImageRequest, DiscardGranularity) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, resize(ictx, ictx->layout.object_size));
+  ictx->discard_granularity_bytes = 32;
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  mock_image_ctx.journal = nullptr;
+
+  InSequence seq;
+  expect_get_modify_timestamp(mock_image_ctx, false);
+  expect_object_discard_request(mock_image_ctx, 0, 32, 32, 0);
+  expect_object_discard_request(mock_image_ctx, 0, 96, 64, 0);
+  expect_object_discard_request(
+    mock_image_ctx, 0, ictx->layout.object_size - 32, 32, 0);
+
+  C_SaferCond aio_comp_ctx;
+  AioCompletion *aio_comp = AioCompletion::create_and_start(
+    &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
+  MockImageDiscardRequest mock_aio_image_discard(
+    mock_image_ctx, aio_comp,
+    {{16, 63}, {96, 31}, {84, 100}, {ictx->layout.object_size - 33, 33}},
+    ictx->discard_granularity_bytes, {});
+  {
+    RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
+    mock_aio_image_discard.send();
+  }
+  ASSERT_EQ(0, aio_comp_ctx.wait());
+}
+
 TEST_F(TestMockIoImageRequest, AioWriteJournalAppendDisabled) {
   REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
 
@@ -251,6 +351,7 @@ TEST_F(TestMockIoImageRequest, AioDiscardJournalAppendDisabled) {
 
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ictx->discard_granularity_bytes = 0;
 
   MockTestImageCtx mock_image_ctx(*ictx);
   MockTestJournal mock_journal;
@@ -264,10 +365,8 @@ TEST_F(TestMockIoImageRequest, AioDiscardJournalAppendDisabled) {
   C_SaferCond aio_comp_ctx;
   AioCompletion *aio_comp = AioCompletion::create_and_start(
     &aio_comp_ctx, ictx, AIO_TYPE_DISCARD);
-  MockImageDiscardRequest mock_aio_image_discard(mock_image_ctx, aio_comp,
-                                                 {{0, 1}},
-                                                 ictx->skip_partial_discard,
-                                                 {});
+  MockImageDiscardRequest mock_aio_image_discard(
+    mock_image_ctx, aio_comp, {{0, 1}}, ictx->discard_granularity_bytes, {});
   {
     RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
     mock_aio_image_discard.send();
index 72ae152517264ca3bfb96d32c74fc7be573babab..07f1888c8a24e63f97d9ff1fdd53e4b67f27bfac 100644 (file)
@@ -1024,37 +1024,6 @@ TEST_F(TestMockIoObjectRequest, DiscardNoOp) {
   ASSERT_EQ(0, ctx.wait());
 }
 
-TEST_F(TestMockIoObjectRequest, SkipPartialDiscard) {
-  librbd::ImageCtx *ictx;
-  ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
-  MockTestImageCtx mock_image_ctx(*ictx);
-  expect_get_object_size(mock_image_ctx);
-
-  MockExclusiveLock mock_exclusive_lock;
-  if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
-    mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
-    expect_is_lock_owner(mock_exclusive_lock);
-  }
-
-  MockObjectMap mock_object_map;
-  if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
-    mock_image_ctx.object_map = &mock_object_map;
-  }
-
-  expect_op_work_queue(mock_image_ctx);
-
-  InSequence seq;
-  expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
-
-  C_SaferCond ctx;
-  auto req = MockObjectDiscardRequest::create_discard(
-    &mock_image_ctx, ictx->get_object_name(0), 0, 0, 1, mock_image_ctx.snapc,
-    OBJECT_DISCARD_FLAG_SKIP_PARTIAL, {}, &ctx);
-  req->send();
-  ASSERT_EQ(0, ctx.wait());
-}
-
 TEST_F(TestMockIoObjectRequest, WriteSame) {
   librbd::ImageCtx *ictx;
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
index 242a352f874a53decaff1d1af4a937c46a936f91..7b013e76ad0f05d2313d83348cab319d65792275 100644 (file)
@@ -175,7 +175,8 @@ TEST_F(TestJournalEntries, AioDiscard) {
   C_SaferCond cond_ctx;
   auto c = librbd::io::AioCompletion::create(&cond_ctx);
   c->get();
-  ictx->io_work_queue->aio_discard(c, 123, 234, ictx->skip_partial_discard);
+  ictx->io_work_queue->aio_discard(c, 123, 234,
+                                   ictx->discard_granularity_bytes);
   ASSERT_EQ(0, c->wait_for_complete());
   c->put();
 
index 82e862381f8a3dda32d0d05b069bd5ee10c2e088..b7137b5c612b20537bfab02c2731fe486836b14c 100644 (file)
@@ -142,8 +142,8 @@ TEST_F(TestJournalReplay, AioDiscardEvent) {
 
   // inject a discard operation into the journal
   inject_into_journal(ictx,
-                      librbd::journal::AioDiscardEvent(0, payload.size(),
-                                                       ictx->skip_partial_discard));
+                      librbd::journal::AioDiscardEvent(
+                        0, payload.size(), ictx->discard_granularity_bytes));
   close_image(ictx);
 
   // re-open the journal so that it replays the new entry
@@ -155,7 +155,7 @@ TEST_F(TestJournalReplay, AioDiscardEvent) {
                                 librbd::io::ReadResult{read_result}, 0);
   ASSERT_EQ(0, aio_comp->wait_for_complete());
   aio_comp->release();
-  if (ictx->skip_partial_discard) {
+  if (ictx->discard_granularity_bytes > 0) {
     ASSERT_EQ(payload, read_payload);
   } else {
     ASSERT_EQ(std::string(read_payload.size(), '\0'), read_payload);
@@ -170,11 +170,11 @@ TEST_F(TestJournalReplay, AioDiscardEvent) {
 
   // replay several envents and check the commit position
   inject_into_journal(ictx,
-                      librbd::journal::AioDiscardEvent(0, payload.size(),
-                                                       ictx->skip_partial_discard));
+                      librbd::journal::AioDiscardEvent(
+                        0, payload.size(), ictx->discard_granularity_bytes));
   inject_into_journal(ictx,
-                      librbd::journal::AioDiscardEvent(0, payload.size(),
-                                                       ictx->skip_partial_discard));
+                      librbd::journal::AioDiscardEvent(
+                        0, payload.size(), ictx->discard_granularity_bytes));
   close_image(ictx);
 
   ASSERT_EQ(0, open_image(m_image_name, &ictx));
@@ -186,7 +186,7 @@ TEST_F(TestJournalReplay, AioDiscardEvent) {
   // verify lock ordering constraints
   aio_comp = new librbd::io::AioCompletion();
   ictx->io_work_queue->aio_discard(aio_comp, 0, read_payload.size(),
-                                   ictx->skip_partial_discard);
+                                   ictx->discard_granularity_bytes);
   ASSERT_EQ(0, aio_comp->wait_for_complete());
   aio_comp->release();
 }
index 19b40ddc4d9985c9073018364a7e0c536781cc4c..62014bab867dfce76bdcf1a3516ee5adb6b5fc84 100644 (file)
@@ -38,12 +38,13 @@ struct ImageRequest<MockReplayImageCtx> {
   }
 
   MOCK_METHOD3(aio_discard, void(AioCompletion *c, const Extents& image_extents,
-                                 bool skip_partial_discard));
+                                 uint32_t discard_granularity_bytes));
   static void aio_discard(MockReplayImageCtx *ictx, AioCompletion *c,
-                          Extents&& image_extents, bool skip_partial_discard,
+                          Extents&& image_extents,
+                          uint32_t discard_granularity_bytes,
                           const ZTracer::Trace &parent_trace) {
     ceph_assert(s_instance != nullptr);
-    s_instance->aio_discard(c, image_extents, skip_partial_discard);
+    s_instance->aio_discard(c, image_extents, discard_granularity_bytes);
   }
 
   MOCK_METHOD1(aio_flush, void(AioCompletion *c));
@@ -147,9 +148,9 @@ public:
 
   void expect_aio_discard(MockIoImageRequest &mock_io_image_request,
                           io::AioCompletion **aio_comp, uint64_t off,
-                          uint64_t len, bool skip_partial_discard) {
+                          uint64_t len, uint32_t discard_granularity_bytes) {
     EXPECT_CALL(mock_io_image_request, aio_discard(_, io::Extents{{off, len}},
-                                                   skip_partial_discard))
+                                                   discard_granularity_bytes))
                   .WillOnce(SaveArg<0>(aio_comp));
   }
 
@@ -385,9 +386,11 @@ TEST_F(TestMockJournalReplay, AioDiscard) {
   io::AioCompletion *aio_comp;
   C_SaferCond on_ready;
   C_SaferCond on_safe;
-  expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456, ictx->skip_partial_discard);
+  expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456,
+                     ictx->discard_granularity_bytes);
   when_process(mock_journal_replay,
-               EventEntry{AioDiscardEvent(123, 456, ictx->skip_partial_discard)},
+               EventEntry{AioDiscardEvent(123, 456,
+                                          ictx->discard_granularity_bytes)},
                &on_ready, &on_safe);
 
   when_complete(mock_image_ctx, aio_comp, 0);
@@ -582,9 +585,11 @@ TEST_F(TestMockJournalReplay, IOError) {
   io::AioCompletion *aio_comp;
   C_SaferCond on_ready;
   C_SaferCond on_safe;
-  expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456, ictx->skip_partial_discard);
+  expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456,
+                     ictx->discard_granularity_bytes);
   when_process(mock_journal_replay,
-               EventEntry{AioDiscardEvent(123, 456, ictx->skip_partial_discard)},
+               EventEntry{AioDiscardEvent(123, 456,
+                                          ictx->discard_granularity_bytes)},
                &on_ready, &on_safe);
 
   when_complete(mock_image_ctx, aio_comp, -EINVAL);
@@ -619,12 +624,14 @@ TEST_F(TestMockJournalReplay, SoftFlushIO) {
     io::AioCompletion *aio_comp;
     io::AioCompletion *flush_comp = nullptr;
     C_SaferCond on_ready;
-    expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456, ictx->skip_partial_discard);
+    expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456,
+                       ictx->discard_granularity_bytes);
     if (i == io_count - 1) {
       expect_aio_flush(mock_io_image_request, &flush_comp);
     }
     when_process(mock_journal_replay,
-                 EventEntry{AioDiscardEvent(123, 456, ictx->skip_partial_discard)},
+                 EventEntry{AioDiscardEvent(123, 456,
+                                            ictx->discard_granularity_bytes)},
                  &on_ready, &on_safes[i]);
     when_complete(mock_image_ctx, aio_comp, 0);
     ASSERT_EQ(0, on_ready.wait());
@@ -710,9 +717,11 @@ TEST_F(TestMockJournalReplay, Flush) {
   io::AioCompletion *aio_comp = nullptr;
   C_SaferCond on_ready;
   C_SaferCond on_safe;
-  expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456, ictx->skip_partial_discard);
+  expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456,
+                     ictx->discard_granularity_bytes);
   when_process(mock_journal_replay,
-               EventEntry{AioDiscardEvent(123, 456, ictx->skip_partial_discard)},
+               EventEntry{AioDiscardEvent(123, 456,
+                                          ictx->discard_granularity_bytes)},
                &on_ready, &on_safe);
 
   when_complete(mock_image_ctx, aio_comp, 0);
@@ -2056,9 +2065,9 @@ TEST_F(TestMockJournalReplay, WritebackCacheDisabled) {
   io::AioCompletion *aio_comp;
   C_SaferCond on_ready;
   C_SaferCond on_safe;
-  expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456, false);
+  expect_aio_discard(mock_io_image_request, &aio_comp, 123, 456, 0);
   when_process(mock_journal_replay,
-               EventEntry{AioDiscardEvent(123, 456, false)},
+               EventEntry{AioDiscardEvent(123, 456, 0)},
                &on_ready, &on_safe);
 
   when_complete(mock_image_ctx, aio_comp, 0);
index 88e06b7e3b1b14abf4aee686ac7f7cc1a47000f7..5d2c3d28a5d781793b71c5ad3e6d9c25614c2773 100644 (file)
@@ -93,6 +93,7 @@ struct MockImageCtx {
       exclusive_lock(NULL), journal(NULL),
       trace_endpoint(image_ctx.trace_endpoint),
       sparse_read_threshold_bytes(image_ctx.sparse_read_threshold_bytes),
+      discard_granularity_bytes(image_ctx.discard_granularity_bytes),
       mirroring_replay_delay(image_ctx.mirroring_replay_delay),
       non_blocking_aio(image_ctx.non_blocking_aio),
       blkin_trace_all(image_ctx.blkin_trace_all),
@@ -299,6 +300,7 @@ struct MockImageCtx {
   ZTracer::Endpoint trace_endpoint;
 
   uint64_t sparse_read_threshold_bytes;
+  uint32_t discard_granularity_bytes;
   int mirroring_replay_delay;
   bool non_blocking_aio;
   bool blkin_trace_all;
index 473f7c8a8b8495830a48aa897c82fd5875f9e8c4..dd16a90f62e5530858c74f215b55018f5e430fa3 100644 (file)
@@ -28,7 +28,7 @@ struct MockImageCache {
     aio_write_mock(image_extents, bl, fadvise_flags, on_finish);
   }
 
-  MOCK_METHOD4(aio_discard, void(uint64_t, uint64_t, bool, Context *));
+  MOCK_METHOD4(aio_discard, void(uint64_t, uint64_t, uint32_t, Context *));
   MOCK_METHOD1(aio_flush, void(Context *));
   MOCK_METHOD5(aio_writesame_mock, void(uint64_t, uint64_t, ceph::bufferlist& bl,
                                         int, Context *));
index ace266948fec3e8b1276fd0c4c5e0fbba292bd4d..d6c8fac62c0c798f1a2ec6be36e6ede07b4376e6 100644 (file)
@@ -157,14 +157,12 @@ struct TestMigration : public TestFixture {
     m_ictxs.insert(*ictx);
 
     ASSERT_EQ(0, (*ictx)->state->open(flags));
-    (*ictx)->skip_partial_discard = false;
+    (*ictx)->discard_granularity_bytes = 0;
   }
 
   void open_image(librados::IoCtx& io_ctx, const std::string &name,
                   librbd::ImageCtx **ictx) {
     open_image(io_ctx, name, "", false, 0, ictx);
-    ASSERT_EQ(0, (*ictx)->state->open(0));
-    (*ictx)->skip_partial_discard = false;
   }
 
   void migration_prepare(librados::IoCtx& dst_io_ctx,
index f59a8f452bbc474282a68199d86c4fabfc4cf35a..fbd3381116d280531640c2bed0e513e2cc3273c6 100644 (file)
@@ -47,7 +47,9 @@ void scribble(librbd::ImageCtx *image_ctx, int num_ops, uint64_t max_size)
     uint64_t len = 1 + rand() % max_size;
 
     if (rand() % 4 == 0) {
-      ASSERT_EQ((int)len, image_ctx->io_work_queue->discard(off, len, image_ctx->skip_partial_discard));
+      ASSERT_EQ((int)len,
+                image_ctx->io_work_queue->discard(
+                  off, len, image_ctx->discard_granularity_bytes));
     } else {
       bufferlist bl;
       bl.append(std::string(len, '1'));
@@ -224,8 +226,9 @@ TEST_F(TestImageSync, Discard) {
 
   ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap", nullptr));
 
-  ASSERT_EQ((int)len - 2, m_remote_image_ctx->io_work_queue->discard(off + 1,
-                                                                     len - 2, m_remote_image_ctx->skip_partial_discard));
+  ASSERT_EQ((int)len - 2,
+            m_remote_image_ctx->io_work_queue->discard(
+              off + 1, len - 2, m_remote_image_ctx->discard_granularity_bytes));
   {
     RWLock::RLocker owner_locker(m_remote_image_ctx->owner_lock);
     ASSERT_EQ(0, flush(m_remote_image_ctx));
index e68455360e737b10a5724a42abd61e51b457bf85..96aee2f9bf7a45fa1d338c5ecf50769ad49bccc4 100644 (file)
@@ -1291,7 +1291,7 @@ TEST_F(TestMockImageReplayer, DelayedReplay) {
   // process with delay
   EXPECT_CALL(mock_replay_entry, get_data());
   librbd::journal::EventEntry event_entry(
-    librbd::journal::AioDiscardEvent(123, 345, false), ceph_clock_now());
+    librbd::journal::AioDiscardEvent(123, 345, 0), ceph_clock_now());
   EXPECT_CALL(mock_local_replay, decode(_, _))
     .WillOnce(DoAll(SetArgPointee<1>(event_entry),
                     Return(0)));