From: Jason Dillaman Date: Mon, 11 May 2015 16:42:41 +0000 (-0400) Subject: librbd: trim operation should issue object copyups for overlap extent X-Git-Tag: v9.0.2~13^2~6 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=f8c831e6dd776ad601e86440d2a982b4c7aee8ee;p=ceph.git librbd: trim operation should issue object copyups for overlap extent 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 --- diff --git a/src/librbd/AsyncTrimRequest.cc b/src/librbd/AsyncTrimRequest.cc index 31a6d63870df..801cf8959820 100644 --- a/src/librbd/AsyncTrimRequest.cc +++ b/src/librbd/AsyncTrimRequest.cc @@ -24,25 +24,49 @@ 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(), + 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(), + boost::lambda::bind(boost::lambda::new_ptr(), 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 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 diff --git a/src/librbd/AsyncTrimRequest.h b/src/librbd/AsyncTrimRequest.h index 85540f35a4e7..cf69831993f0 100644 --- a/src/librbd/AsyncTrimRequest.h +++ b/src/librbd/AsyncTrimRequest.h @@ -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