// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "librbd/operation/DisableFeaturesRequest.h" #include "common/dout.h" #include "common/errno.h" #include "cls/rbd/cls_rbd_client.h" #include "librbd/ExclusiveLock.h" #include "librbd/ImageCtx.h" #include "librbd/ImageState.h" #include "librbd/Journal.h" #include "librbd/Utils.h" #include "librbd/image/SetFlagsRequest.h" #include "librbd/io/ImageRequestWQ.h" #include "librbd/journal/RemoveRequest.h" #include "librbd/mirror/DisableRequest.h" #include "librbd/object_map/RemoveRequest.h" #define dout_subsys ceph_subsys_rbd #undef dout_prefix #define dout_prefix *_dout << "librbd::DisableFeaturesRequest: " namespace librbd { namespace operation { using util::create_async_context_callback; using util::create_context_callback; using util::create_rados_callback; template DisableFeaturesRequest::DisableFeaturesRequest(I &image_ctx, Context *on_finish, uint64_t journal_op_tid, uint64_t features, bool force) : Request(image_ctx, on_finish, journal_op_tid), m_features(features), m_force(force) { } template void DisableFeaturesRequest::send_op() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; assert(image_ctx.owner_lock.is_locked()); ldout(cct, 20) << this << " " << __func__ << ": features=" << m_features << dendl; send_prepare_lock(); } template bool DisableFeaturesRequest::should_complete(int r) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << "r=" << r << dendl; if (r < 0) { lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl; } return true; } template void DisableFeaturesRequest::send_prepare_lock() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; image_ctx.state->prepare_lock(create_async_context_callback( image_ctx, create_context_callback< DisableFeaturesRequest, &DisableFeaturesRequest::handle_prepare_lock>(this))); } template Context *DisableFeaturesRequest::handle_prepare_lock(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to lock image: " << cpp_strerror(*result) << dendl; return this->create_context_finisher(*result); } send_block_writes(); return nullptr; } template void DisableFeaturesRequest::send_block_writes() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; RWLock::WLocker locker(image_ctx.owner_lock); image_ctx.io_work_queue->block_writes(create_context_callback< DisableFeaturesRequest, &DisableFeaturesRequest::handle_block_writes>(this)); } template Context *DisableFeaturesRequest::handle_block_writes(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to block writes: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } m_writes_blocked = true; { RWLock::WLocker locker(image_ctx.owner_lock); // avoid accepting new requests from peers while we manipulate // the image features if (image_ctx.exclusive_lock != nullptr && (image_ctx.journal == nullptr || !image_ctx.journal->is_journal_replaying())) { image_ctx.exclusive_lock->block_requests(0); m_requests_blocked = true; } } send_acquire_exclusive_lock(); return nullptr; } template void DisableFeaturesRequest::send_acquire_exclusive_lock() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; Context *ctx = create_context_callback< DisableFeaturesRequest, &DisableFeaturesRequest::handle_acquire_exclusive_lock>(this); { RWLock::WLocker locker(image_ctx.owner_lock); // if disabling features w/ exclusive lock supported, we need to // acquire the lock to temporarily block IO against the image if (image_ctx.exclusive_lock != nullptr && !image_ctx.exclusive_lock->is_lock_owner()) { m_acquired_lock = true; image_ctx.exclusive_lock->acquire_lock(ctx); return; } } ctx->complete(0); } template Context *DisableFeaturesRequest::handle_acquire_exclusive_lock(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to lock image: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } else if (m_acquired_lock && (image_ctx.exclusive_lock == nullptr || !image_ctx.exclusive_lock->is_lock_owner())) { lderr(cct) << "failed to acquire exclusive lock" << dendl; *result = -EROFS; return handle_finish(*result); } do { RWLock::WLocker locker(image_ctx.owner_lock); m_features &= image_ctx.features; m_new_features = image_ctx.features & ~m_features; m_features_mask = m_features; if ((m_features & RBD_FEATURE_EXCLUSIVE_LOCK) != 0) { if ((m_new_features & RBD_FEATURE_OBJECT_MAP) != 0 || (m_new_features & RBD_FEATURE_JOURNALING) != 0) { lderr(cct) << "cannot disable exclusive-lock. object-map " "or journaling must be disabled before " "disabling exclusive-lock." << dendl; *result = -EINVAL; break; } m_features_mask |= (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_JOURNALING); } if ((m_features & RBD_FEATURE_FAST_DIFF) != 0) { m_disable_flags |= RBD_FLAG_FAST_DIFF_INVALID; } if ((m_features & RBD_FEATURE_OBJECT_MAP) != 0) { if ((m_new_features & RBD_FEATURE_FAST_DIFF) != 0) { lderr(cct) << "cannot disable object-map. fast-diff must be " "disabled before disabling object-map." << dendl; *result = -EINVAL; break; } m_disable_flags |= RBD_FLAG_OBJECT_MAP_INVALID; } } while (false); if (*result < 0) { return handle_finish(*result); } send_get_mirror_mode(); return nullptr; } template void DisableFeaturesRequest::send_get_mirror_mode() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; if ((m_features & RBD_FEATURE_JOURNALING) == 0) { send_append_op_event(); return; } ldout(cct, 20) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; cls_client::mirror_mode_get_start(&op); using klass = DisableFeaturesRequest; librados::AioCompletion *comp = create_rados_callback(this); m_out_bl.clear(); int r = image_ctx.md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl); assert(r == 0); comp->release(); } template Context *DisableFeaturesRequest::handle_get_mirror_mode(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result == 0) { bufferlist::iterator it = m_out_bl.begin(); *result = cls_client::mirror_mode_get_finish(&it, &m_mirror_mode); } if (*result < 0 && *result != -ENOENT) { lderr(cct) << "failed to retrieve pool mirror mode: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } ldout(cct, 20) << this << " " << __func__ << ": m_mirror_mode=" << m_mirror_mode << dendl; send_get_mirror_image(); return nullptr; } template void DisableFeaturesRequest::send_get_mirror_image() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; if (m_mirror_mode != cls::rbd::MIRROR_MODE_IMAGE) { send_disable_mirror_image(); return; } ldout(cct, 20) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; cls_client::mirror_image_get_start(&op, image_ctx.id); using klass = DisableFeaturesRequest; librados::AioCompletion *comp = create_rados_callback(this); m_out_bl.clear(); int r = image_ctx.md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl); assert(r == 0); comp->release(); } template Context *DisableFeaturesRequest::handle_get_mirror_image(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; cls::rbd::MirrorImage mirror_image; if (*result == 0) { bufferlist::iterator it = m_out_bl.begin(); *result = cls_client::mirror_image_get_finish(&it, &mirror_image); } if (*result < 0 && *result != -ENOENT) { lderr(cct) << "failed to retrieve pool mirror image: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } if ((mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_ENABLED) && !m_force) { lderr(cct) << "cannot disable journaling: image mirroring " << "enabled and mirror pool mode set to image" << dendl; *result = -EINVAL; return handle_finish(*result); } send_disable_mirror_image(); return nullptr; } template void DisableFeaturesRequest::send_disable_mirror_image() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; Context *ctx = create_context_callback< DisableFeaturesRequest, &DisableFeaturesRequest::handle_disable_mirror_image>(this); mirror::DisableRequest *req = mirror::DisableRequest::create(&image_ctx, m_force, true, ctx); req->send(); } template Context *DisableFeaturesRequest::handle_disable_mirror_image(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to disable image mirroring: " << cpp_strerror(*result) << dendl; // not fatal } send_close_journal(); return nullptr; } template void DisableFeaturesRequest::send_close_journal() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; { RWLock::WLocker locker(image_ctx.owner_lock); if (image_ctx.journal != nullptr) { ldout(cct, 20) << this << " " << __func__ << dendl; std::swap(m_journal, image_ctx.journal); Context *ctx = create_context_callback< DisableFeaturesRequest, &DisableFeaturesRequest::handle_close_journal>(this); m_journal->close(ctx); return; } } send_remove_journal(); } template Context *DisableFeaturesRequest::handle_close_journal(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to close image journal: " << cpp_strerror(*result) << dendl; } assert(m_journal != nullptr); delete m_journal; m_journal = nullptr; send_remove_journal(); return nullptr; } template void DisableFeaturesRequest::send_remove_journal() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; Context *ctx = create_context_callback< DisableFeaturesRequest, &DisableFeaturesRequest::handle_remove_journal>(this); journal::RemoveRequest *req = journal::RemoveRequest::create( image_ctx.md_ctx, image_ctx.id, librbd::Journal<>::IMAGE_CLIENT_ID, image_ctx.op_work_queue, ctx); req->send(); } template Context *DisableFeaturesRequest::handle_remove_journal(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to remove image journal: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } send_append_op_event(); return nullptr; } template void DisableFeaturesRequest::send_append_op_event() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; if (!this->template append_op_event< DisableFeaturesRequest, &DisableFeaturesRequest::handle_append_op_event>(this)) { send_remove_object_map(); } ldout(cct, 20) << this << " " << __func__ << dendl; } template Context *DisableFeaturesRequest::handle_append_op_event(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to commit journal entry: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } send_remove_object_map(); return nullptr; } template void DisableFeaturesRequest::send_remove_object_map() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; if ((m_features & RBD_FEATURE_OBJECT_MAP) == 0) { send_set_features(); return; } Context *ctx = create_context_callback< DisableFeaturesRequest, &DisableFeaturesRequest::handle_remove_object_map>(this); object_map::RemoveRequest *req = object_map::RemoveRequest::create(&image_ctx, ctx); req->send(); } template Context *DisableFeaturesRequest::handle_remove_object_map(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to remove object map: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } send_set_features(); return nullptr; } template void DisableFeaturesRequest::send_set_features() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": new_features=" << m_new_features << ", features_mask=" << m_features_mask << dendl; librados::ObjectWriteOperation op; librbd::cls_client::set_features(&op, m_new_features, m_features_mask); using klass = DisableFeaturesRequest; librados::AioCompletion *comp = create_rados_callback(this); int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, comp, &op); assert(r == 0); comp->release(); } template Context *DisableFeaturesRequest::handle_set_features(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result == -EINVAL && (m_features_mask & RBD_FEATURE_JOURNALING) != 0) { // NOTE: infernalis OSDs will not accept a mask with new features, so // re-attempt with a reduced mask. ldout(cct, 5) << this << " " << __func__ << ": re-attempt with a reduced mask" << dendl; m_features_mask &= ~RBD_FEATURE_JOURNALING; send_set_features(); } if (*result < 0) { lderr(cct) << "failed to update features: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } send_update_flags(); return nullptr; } template void DisableFeaturesRequest::send_update_flags() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; if (m_disable_flags == 0) { send_notify_update(); return; } ldout(cct, 20) << this << " " << __func__ << ": disable_flags=" << m_disable_flags << dendl; Context *ctx = create_context_callback< DisableFeaturesRequest, &DisableFeaturesRequest::handle_update_flags>(this); image::SetFlagsRequest *req = image::SetFlagsRequest::create(&image_ctx, 0, m_disable_flags, ctx); req->send(); } template Context *DisableFeaturesRequest::handle_update_flags(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to update image flags: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } send_notify_update(); return nullptr; } template void DisableFeaturesRequest::send_notify_update() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; Context *ctx = create_context_callback< DisableFeaturesRequest, &DisableFeaturesRequest::handle_notify_update>(this); image_ctx.notify_update(ctx); } template Context *DisableFeaturesRequest::handle_notify_update(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; if (image_ctx.exclusive_lock == nullptr || !m_acquired_lock) { return handle_finish(*result); } send_release_exclusive_lock(); return nullptr; } template void DisableFeaturesRequest::send_release_exclusive_lock() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; Context *ctx = create_context_callback< DisableFeaturesRequest, &DisableFeaturesRequest::handle_release_exclusive_lock>(this); image_ctx.exclusive_lock->release_lock(ctx); } template Context *DisableFeaturesRequest::handle_release_exclusive_lock(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl; return handle_finish(*result); } template Context *DisableFeaturesRequest::handle_finish(int r) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << r << dendl; { RWLock::WLocker locker(image_ctx.owner_lock); if (image_ctx.exclusive_lock != nullptr && m_requests_blocked) { image_ctx.exclusive_lock->unblock_requests(); } image_ctx.io_work_queue->unblock_writes(); } image_ctx.state->handle_prepare_lock_complete(); return this->create_context_finisher(r); } } // namespace operation } // namespace librbd template class librbd::operation::DisableFeaturesRequest;