]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: trim operation should issue object copyups for overlap extent
authorJason Dillaman <dillaman@redhat.com>
Mon, 11 May 2015 16:42:41 +0000 (12:42 -0400)
committerJason Dillaman <dillaman@redhat.com>
Fri, 5 Jun 2015 15:54:40 +0000 (11:54 -0400)
Now that child images can be disassociated from their parents even
when snapshots exist, trim operations need to copyup parent objects
before deleting the object from the child.

Fixes: #11579
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/librbd/AsyncTrimRequest.cc
src/librbd/AsyncTrimRequest.h

index 31a6d63870df6dd9c77f18ab82c3734f9b1da486..801cf8959820de21beaefb07d7f070f9cd8e640a 100644 (file)
 namespace librbd
 {
 
-class AsyncTrimObjectContext : public C_AsyncObjectThrottle {
+class C_CopyupObject : public C_AsyncObjectThrottle {
 public:
-  AsyncTrimObjectContext(AsyncObjectThrottle &throttle, ImageCtx *image_ctx,
-                        uint64_t object_no)
+  C_CopyupObject(AsyncObjectThrottle &throttle, ImageCtx *image_ctx,
+                 ::SnapContext snapc, uint64_t object_no)
+    : C_AsyncObjectThrottle(throttle, *image_ctx), m_snapc(snapc),
+      m_object_no(object_no)
+  {
+  }
+
+  virtual int send() {
+    assert(m_image_ctx.owner_lock.is_locked());
+    assert(!m_image_ctx.image_watcher->is_lock_supported() ||
+           m_image_ctx.image_watcher->is_lock_owner());
+
+    string oid = m_image_ctx.get_object_name(m_object_no);
+    ldout(m_image_ctx.cct, 10) << "removing (with copyup) " << oid << dendl;
+
+    AbstractWrite *req = new AioTrim(&m_image_ctx, oid, m_object_no, m_snapc,
+                                     this);
+    req->send();
+    return 0;
+  }
+private:
+  ::SnapContext m_snapc;
+  uint64_t m_object_no;
+};
+
+class C_RemoveObject : public C_AsyncObjectThrottle {
+public:
+  C_RemoveObject(AsyncObjectThrottle &throttle, ImageCtx *image_ctx,
+                 uint64_t object_no)
     : C_AsyncObjectThrottle(throttle, *image_ctx), m_object_no(object_no)
   {
   }
 
   virtual int send() {
     assert(m_image_ctx.owner_lock.is_locked());
+    assert(!m_image_ctx.image_watcher->is_lock_supported() ||
+           m_image_ctx.image_watcher->is_lock_owner());
     if (!m_image_ctx.object_map.object_may_exist(m_object_no)) {
       return 1;
     }
 
-    if (m_image_ctx.image_watcher->is_lock_supported() &&
-        !m_image_ctx.image_watcher->is_lock_owner()) {
-      return -ERESTART;
-    }
-
     string oid = m_image_ctx.get_object_name(m_object_no);
     ldout(m_image_ctx.cct, 10) << "removing " << oid << dendl;
 
@@ -61,7 +85,8 @@ private:
 AsyncTrimRequest::AsyncTrimRequest(ImageCtx &image_ctx, Context *on_finish,
                                   uint64_t original_size, uint64_t new_size,
                                   ProgressContext &prog_ctx)
-  : AsyncRequest(image_ctx, on_finish), m_new_size(new_size), m_prog_ctx(prog_ctx)
+  : AsyncRequest(image_ctx, on_finish), m_new_size(new_size),
+    m_prog_ctx(prog_ctx)
 {
   uint64_t period = m_image_ctx.get_stripe_period();
   uint64_t new_num_periods = ((m_new_size + period - 1) / period);
@@ -89,6 +114,11 @@ bool AsyncTrimRequest::should_complete(int r)
   }
 
   switch (m_state) {
+  case STATE_COPYUP_OBJECTS:
+    ldout(cct, 5) << " COPYUP_OBJECTS" << dendl;
+    send_pre_remove();
+    break;
+
   case STATE_PRE_REMOVE:
     ldout(cct, 5) << " PRE_REMOVE" << dendl;
     {
@@ -112,7 +142,7 @@ bool AsyncTrimRequest::should_complete(int r)
 
   case STATE_CLEAN_BOUNDARY:
     ldout(cct, 5) << "CLEAN_BOUNDARY" << dendl;
-    finish();
+    finish(0);
     break;
 
   case STATE_FINISHED:
@@ -128,12 +158,58 @@ bool AsyncTrimRequest::should_complete(int r)
 }
 
 void AsyncTrimRequest::send() {
+  send_copyup_objects();
+}
+
+void AsyncTrimRequest::send_copyup_objects() {
   assert(m_image_ctx.owner_lock.is_locked());
-  if (m_delete_start < m_num_objects) {
-    send_pre_remove();
-  } else {
+  assert(!m_image_ctx.image_watcher->is_lock_supported() ||
+         m_image_ctx.image_watcher->is_lock_owner());
+
+  if (m_delete_start >= m_num_objects) {
     send_clean_boundary();
+    return;
+  }
+
+  ::SnapContext snapc;
+  bool has_snapshots;
+  uint64_t parent_overlap;
+  {
+    RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+    RWLock::RLocker parent_locker(m_image_ctx.parent_lock);
+
+    snapc = m_image_ctx.snapc;
+    has_snapshots = !m_image_ctx.snaps.empty();
+    int r = m_image_ctx.get_parent_overlap(m_image_ctx.get_copyup_snap_id(),
+                                           &parent_overlap);
+    assert(r == 0);
+  }
+
+  // copyup is only required for portion of image that overlaps parent
+  uint64_t copyup_end = Striper::get_num_objects(m_image_ctx.layout,
+                                                 parent_overlap);
+  // TODO: protect against concurrent shrink and snap create?
+  if (copyup_end <= m_delete_start || !has_snapshots) {
+    send_pre_remove();
+    return;
   }
+
+  uint64_t copyup_start = m_delete_start;
+  m_delete_start = copyup_end;
+
+  ldout(m_image_ctx.cct, 5) << this << " send_copyup_objects: "
+                           << " start object=" << copyup_start << ", "
+                           << " end object=" << copyup_end << dendl;
+  m_state = STATE_COPYUP_OBJECTS;
+
+  Context *ctx = create_callback_context();
+  AsyncObjectThrottle::ContextFactory context_factory(
+    boost::lambda::bind(boost::lambda::new_ptr<C_CopyupObject>(),
+      boost::lambda::_1, &m_image_ctx, snapc, boost::lambda::_2));
+  AsyncObjectThrottle *throttle = new AsyncObjectThrottle(
+    this, m_image_ctx, context_factory, ctx, &m_prog_ctx, copyup_start,
+    copyup_end);
+  throttle->start_ops(m_image_ctx.concurrent_management_ops);
 }
 
 void AsyncTrimRequest::send_remove_objects() {
@@ -146,7 +222,7 @@ void AsyncTrimRequest::send_remove_objects() {
 
   Context *ctx = create_callback_context();
   AsyncObjectThrottle::ContextFactory context_factory(
-    boost::lambda::bind(boost::lambda::new_ptr<AsyncTrimObjectContext>(),
+    boost::lambda::bind(boost::lambda::new_ptr<C_RemoveObject>(),
       boost::lambda::_1, &m_image_ctx, boost::lambda::_2));
   AsyncObjectThrottle *throttle = new AsyncObjectThrottle(
     this, m_image_ctx, context_factory, ctx, &m_prog_ctx, m_delete_start,
@@ -156,6 +232,10 @@ void AsyncTrimRequest::send_remove_objects() {
 
 void AsyncTrimRequest::send_pre_remove() {
   assert(m_image_ctx.owner_lock.is_locked());
+  if (m_delete_start >= m_num_objects) {
+    send_clean_boundary();
+    return;
+  }
 
   bool remove_objects = false;
   {
@@ -228,16 +308,17 @@ void AsyncTrimRequest::send_clean_boundary() {
   assert(m_image_ctx.owner_lock.is_locked());
   CephContext *cct = m_image_ctx.cct;
   if (m_delete_off <= m_new_size) {
-    finish();
+    finish(0);
     return;
   }
 
   // should have been canceled prior to releasing lock
   assert(!m_image_ctx.image_watcher->is_lock_supported() ||
          m_image_ctx.image_watcher->is_lock_owner());
+  uint64_t delete_len = m_delete_off - m_new_size;
   ldout(m_image_ctx.cct, 5) << this << " send_clean_boundary: "
-                           << " delete_start=" << m_delete_start
-                           << " num_objects=" << m_num_objects << dendl;
+                           << " delete_off=" << m_delete_off
+                           << " length=" << delete_len << dendl;
   m_state = STATE_CLEAN_BOUNDARY;
 
   ::SnapContext snapc;
@@ -249,8 +330,8 @@ void AsyncTrimRequest::send_clean_boundary() {
   // discard the weird boundary
   std::vector<ObjectExtent> extents;
   Striper::file_to_extents(cct, m_image_ctx.format_string,
-                          &m_image_ctx.layout, m_new_size,
-                          m_delete_off - m_new_size, 0, extents);
+                          &m_image_ctx.layout, m_new_size, delete_len, 0,
+                           extents);
 
   ContextCompletion *completion =
     new ContextCompletion(create_callback_context(), true);
@@ -261,8 +342,8 @@ void AsyncTrimRequest::send_clean_boundary() {
 
     AbstractWrite *req;
     if (p->offset == 0) {
-      req = new AioRemove(&m_image_ctx, p->oid.name, p->objectno, snapc,
-                          req_comp);
+      req = new AioTrim(&m_image_ctx, p->oid.name, p->objectno, snapc,
+                        req_comp);
     } else {
       req = new AioTruncate(&m_image_ctx, p->oid.name, p->objectno,
                             p->offset, snapc, req_comp);
@@ -272,9 +353,9 @@ void AsyncTrimRequest::send_clean_boundary() {
   completion->finish_adding_requests();
 }
 
-void AsyncTrimRequest::finish() {
+void AsyncTrimRequest::finish(int r) {
   m_state = STATE_FINISHED;
-  async_complete(0);
+  async_complete(r);
 }
 
 } // namespace librbd
index 85540f35a4e735dfdfbaeb6a9aee002277fe3ca8..cf69831993f0ecc8317df04d26ab700529f4a178 100644 (file)
@@ -31,7 +31,11 @@ protected:
    *      |   .                                           .
    *      |   . . . . . . . . . . . .                     .
    *      |                         .                     .
-   *      v                         v                     .
+   *      v                         .                     .
+   * STATE_COPYUP_OBJECTS . . .     .                     .
+   *      |                   .     .                     .
+   *      |                   .     .                     .
+   *      v                   v     v                     .
    * STATE_PRE_REMOVE ---> STATE_REMOVE_OBJECTS           .
    *                                |   .   .             .
    *        /-----------------------/   .   . . . . . .   .
@@ -44,6 +48,8 @@ protected:
    *
    * @endverbatim
    *
+   * The _COPYUP_OBJECTS state is skipped if there is no parent overlap
+   * within the new image size and the image does not have any snapshots.
    * The _PRE_REMOVE/_POST_REMOVE states are skipped if the object map
    * isn't enabled. The _REMOVE_OBJECTS state is skipped if no whole objects
    * are removed.  The _CLEAN_BOUNDARY state is skipped if no boundary
@@ -52,6 +58,7 @@ protected:
    */ 
 
   enum State {
+    STATE_COPYUP_OBJECTS,
     STATE_PRE_REMOVE,
     STATE_REMOVE_OBJECTS,
     STATE_POST_REMOVE,
@@ -70,11 +77,12 @@ private:
   uint64_t m_new_size;
   ProgressContext &m_prog_ctx;
 
+  void send_copyup_objects();
   void send_remove_objects();
   void send_pre_remove();
   void send_post_remove();
   void send_clean_boundary();
-  void finish();
+  void finish(int r);
 };
 
 } // namespace librbd