// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "librbd/operation/RebuildObjectMapRequest.h" #include "common/dout.h" #include "common/errno.h" #include "librbd/AsyncObjectThrottle.h" #include "librbd/ExclusiveLock.h" #include "librbd/ImageCtx.h" #include "librbd/internal.h" #include "librbd/ObjectMap.h" #include "librbd/operation/ResizeRequest.h" #include "librbd/operation/TrimRequest.h" #include "librbd/operation/ObjectMapIterate.h" #include "librbd/Utils.h" #include #include #define dout_subsys ceph_subsys_rbd #undef dout_prefix #define dout_prefix *_dout << "librbd::RebuildObjectMapRequest: " namespace librbd { namespace operation { template void RebuildObjectMapRequest::send() { send_resize_object_map(); } template bool RebuildObjectMapRequest::should_complete(int r) { CephContext *cct = m_image_ctx.cct; ldout(cct, 5) << this << " should_complete: " << " r=" << r << dendl; RWLock::RLocker owner_lock(m_image_ctx.owner_lock); switch (m_state) { case STATE_RESIZE_OBJECT_MAP: ldout(cct, 5) << "RESIZE_OBJECT_MAP" << dendl; if (r == -ESTALE && !m_attempted_trim) { // objects are still flagged as in-use -- delete them m_attempted_trim = true; send_trim_image(); return false; } else if (r == 0) { send_verify_objects(); } break; case STATE_TRIM_IMAGE: ldout(cct, 5) << "TRIM_IMAGE" << dendl; if (r == 0) { send_resize_object_map(); } break; case STATE_VERIFY_OBJECTS: ldout(cct, 5) << "VERIFY_OBJECTS" << dendl; if (r == 0) { send_save_object_map(); } break; case STATE_SAVE_OBJECT_MAP: ldout(cct, 5) << "SAVE_OBJECT_MAP" << dendl; if (r == 0) { send_update_header(); } break; case STATE_UPDATE_HEADER: ldout(cct, 5) << "UPDATE_HEADER" << dendl; if (r == 0) { return true; } break; default: assert(false); break; } if (r == -ERESTART) { ldout(cct, 5) << "rebuild object map operation interrupted" << dendl; return true; } else if (r < 0) { lderr(cct) << "rebuild object map encountered an error: " << cpp_strerror(r) << dendl; return true; } return false; } template void RebuildObjectMapRequest::send_resize_object_map() { assert(m_image_ctx.owner_lock.is_locked()); CephContext *cct = m_image_ctx.cct; m_image_ctx.snap_lock.get_read(); assert(m_image_ctx.object_map != nullptr); uint64_t size = get_image_size(); uint64_t num_objects = Striper::get_num_objects(m_image_ctx.layout, size); if (m_image_ctx.object_map->size() == num_objects) { m_image_ctx.snap_lock.put_read(); send_verify_objects(); return; } ldout(cct, 5) << this << " send_resize_object_map" << dendl; m_state = STATE_RESIZE_OBJECT_MAP; // should have been canceled prior to releasing lock assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); m_image_ctx.object_map->aio_resize(size, OBJECT_NONEXISTENT, this->create_callback_context()); m_image_ctx.snap_lock.put_read(); } template void RebuildObjectMapRequest::send_trim_image() { CephContext *cct = m_image_ctx.cct; RWLock::RLocker l(m_image_ctx.owner_lock); // should have been canceled prior to releasing lock assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); ldout(cct, 5) << this << " send_trim_image" << dendl; m_state = STATE_TRIM_IMAGE; uint64_t new_size; uint64_t orig_size; { RWLock::RLocker l(m_image_ctx.snap_lock); assert(m_image_ctx.object_map != nullptr); new_size = get_image_size(); orig_size = m_image_ctx.get_object_size() * m_image_ctx.object_map->size(); } TrimRequest *req = TrimRequest::create(m_image_ctx, this->create_callback_context(), orig_size, new_size, m_prog_ctx); req->send(); } template bool update_object_map(I& image_ctx, uint64_t object_no, uint8_t current_state, uint8_t new_state) { CephContext *cct = image_ctx.cct; uint64_t snap_id = image_ctx.snap_id; uint8_t state = (*image_ctx.object_map)[object_no]; if (state == OBJECT_EXISTS && new_state == OBJECT_NONEXISTENT && snap_id == CEPH_NOSNAP) { // might be writing object to OSD concurrently new_state = state; } if (new_state != state) { ldout(cct, 15) << image_ctx.get_object_name(object_no) << " rebuild updating object map " << static_cast(state) << "->" << static_cast(new_state) << dendl; (*image_ctx.object_map)[object_no] = new_state; } return false; } template void RebuildObjectMapRequest::send_verify_objects() { assert(m_image_ctx.owner_lock.is_locked()); CephContext *cct = m_image_ctx.cct; m_state = STATE_VERIFY_OBJECTS; ldout(cct, 5) << this << " send_verify_objects" << dendl; ObjectMapIterateRequest *req = new ObjectMapIterateRequest(m_image_ctx, this->create_callback_context(), m_prog_ctx, update_object_map); req->send(); } template void RebuildObjectMapRequest::send_save_object_map() { assert(m_image_ctx.owner_lock.is_locked()); CephContext *cct = m_image_ctx.cct; ldout(cct, 5) << this << " send_save_object_map" << dendl; m_state = STATE_SAVE_OBJECT_MAP; // should have been canceled prior to releasing lock assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); RWLock::RLocker snap_locker(m_image_ctx.snap_lock); assert(m_image_ctx.object_map != nullptr); m_image_ctx.object_map->aio_save(this->create_callback_context()); } template void RebuildObjectMapRequest::send_update_header() { assert(m_image_ctx.owner_lock.is_locked()); // should have been canceled prior to releasing lock assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); ldout(m_image_ctx.cct, 5) << this << " send_update_header" << dendl; m_state = STATE_UPDATE_HEADER; librados::ObjectWriteOperation op; uint64_t flags = RBD_FLAG_OBJECT_MAP_INVALID | RBD_FLAG_FAST_DIFF_INVALID; cls_client::set_flags(&op, m_image_ctx.snap_id, 0, flags); librados::AioCompletion *comp = this->create_callback_completion(); int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op); assert(r == 0); comp->release(); RWLock::WLocker snap_locker(m_image_ctx.snap_lock); m_image_ctx.update_flags(m_image_ctx.snap_id, flags, false); } template uint64_t RebuildObjectMapRequest::get_image_size() const { assert(m_image_ctx.snap_lock.is_locked()); if (m_image_ctx.snap_id == CEPH_NOSNAP) { if (!m_image_ctx.resize_reqs.empty()) { return m_image_ctx.resize_reqs.front()->get_image_size(); } else { return m_image_ctx.size; } } return m_image_ctx.get_image_size(m_image_ctx.snap_id); } } // namespace operation } // namespace librbd template class librbd::operation::RebuildObjectMapRequest;