// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "librbd/operation/EnableFeaturesRequest.h" #include "common/dout.h" #include "common/errno.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/CreateRequest.h" #include "librbd/mirror/EnableRequest.h" #include "librbd/object_map/CreateRequest.h" #define dout_subsys ceph_subsys_rbd #undef dout_prefix #define dout_prefix *_dout << "librbd::EnableFeaturesRequest: " namespace librbd { namespace operation { using util::create_async_context_callback; using util::create_context_callback; using util::create_rados_callback; template EnableFeaturesRequest::EnableFeaturesRequest(I &image_ctx, Context *on_finish, uint64_t journal_op_tid, uint64_t features) : Request(image_ctx, on_finish, journal_op_tid), m_features(features) { } template void EnableFeaturesRequest::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 EnableFeaturesRequest::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 EnableFeaturesRequest::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< EnableFeaturesRequest, &EnableFeaturesRequest::handle_prepare_lock>(this))); } template Context *EnableFeaturesRequest::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 EnableFeaturesRequest::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< EnableFeaturesRequest, &EnableFeaturesRequest::handle_block_writes>(this)); } template Context *EnableFeaturesRequest::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; send_get_mirror_mode(); return nullptr; } template void EnableFeaturesRequest::send_get_mirror_mode() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; if ((m_features & RBD_FEATURE_JOURNALING) == 0) { Context *ctx = create_context_callback< EnableFeaturesRequest, &EnableFeaturesRequest::handle_get_mirror_mode>(this); ctx->complete(-ENOENT); return; } ldout(cct, 20) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; cls_client::mirror_mode_get_start(&op); using klass = EnableFeaturesRequest; 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 *EnableFeaturesRequest::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; cls::rbd::MirrorMode mirror_mode = cls::rbd::MIRROR_MODE_DISABLED; if (*result == 0) { bufferlist::iterator it = m_out_bl.begin(); *result = cls_client::mirror_mode_get_finish(&it, &mirror_mode); } else if (*result == -ENOENT) { *result = 0; } if (*result < 0) { lderr(cct) << "failed to retrieve pool mirror mode: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } m_enable_mirroring = (mirror_mode == cls::rbd::MIRROR_MODE_POOL); bool create_journal = false; do { 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; } m_features &= ~image_ctx.features; m_new_features = image_ctx.features | m_features; m_features_mask = m_features; if ((m_features & RBD_FEATURE_OBJECT_MAP) != 0) { if ((m_new_features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) { lderr(cct) << "cannot enable object-map. exclusive-lock must be " "enabled before enabling object-map." << dendl; *result = -EINVAL; break; } m_enable_flags |= RBD_FLAG_OBJECT_MAP_INVALID; m_features_mask |= RBD_FEATURE_EXCLUSIVE_LOCK; } if ((m_features & RBD_FEATURE_FAST_DIFF) != 0) { if ((m_new_features & RBD_FEATURE_OBJECT_MAP) == 0) { lderr(cct) << "cannot enable fast-diff. object-map must be " "enabled before enabling fast-diff." << dendl; *result = -EINVAL; break; } m_enable_flags |= RBD_FLAG_FAST_DIFF_INVALID; m_features_mask |= (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_EXCLUSIVE_LOCK); } if ((m_features & RBD_FEATURE_JOURNALING) != 0) { if ((m_new_features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) { lderr(cct) << "cannot enable journaling. exclusive-lock must be " "enabled before enabling journaling." << dendl; *result = -EINVAL; break; } m_features_mask |= RBD_FEATURE_EXCLUSIVE_LOCK; create_journal = true; } } while (false); if (*result < 0) { return handle_finish(*result); } if (create_journal) { send_create_journal(); return nullptr; } send_append_op_event(); return nullptr; } template void EnableFeaturesRequest::send_create_journal() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; journal::TagData tag_data(librbd::Journal<>::LOCAL_MIRROR_UUID); Context *ctx = create_context_callback< EnableFeaturesRequest, &EnableFeaturesRequest::handle_create_journal>(this); journal::CreateRequest *req = journal::CreateRequest::create( image_ctx.md_ctx, image_ctx.id, image_ctx.journal_order, image_ctx.journal_splay_width, image_ctx.journal_pool, cls::journal::Tag::TAG_CLASS_NEW, tag_data, librbd::Journal<>::IMAGE_CLIENT_ID, image_ctx.op_work_queue, ctx); req->send(); } template Context *EnableFeaturesRequest::handle_create_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 create journal: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } send_append_op_event(); return nullptr; } template void EnableFeaturesRequest::send_append_op_event() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; if (!this->template append_op_event< EnableFeaturesRequest, &EnableFeaturesRequest::handle_append_op_event>(this)) { send_update_flags(); } ldout(cct, 20) << this << " " << __func__ << dendl; } template Context *EnableFeaturesRequest::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_update_flags(); return nullptr; } template void EnableFeaturesRequest::send_update_flags() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; if (m_enable_flags == 0) { send_set_features(); return; } ldout(cct, 20) << this << " " << __func__ << ": enable_flags=" << m_enable_flags << dendl; Context *ctx = create_context_callback< EnableFeaturesRequest, &EnableFeaturesRequest::handle_update_flags>(this); image::SetFlagsRequest *req = image::SetFlagsRequest::create(&image_ctx, m_enable_flags, m_enable_flags, ctx); req->send(); } template Context *EnableFeaturesRequest::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_set_features(); return nullptr; } template void EnableFeaturesRequest::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 = EnableFeaturesRequest; 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 *EnableFeaturesRequest::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 < 0) { lderr(cct) << "failed to update features: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } send_create_object_map(); return nullptr; } template void EnableFeaturesRequest::send_create_object_map() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; if (((image_ctx.features & RBD_FEATURE_OBJECT_MAP) != 0) || ((m_features & RBD_FEATURE_OBJECT_MAP) == 0)) { send_enable_mirror_image(); return; } ldout(cct, 20) << this << " " << __func__ << dendl; Context *ctx = create_context_callback< EnableFeaturesRequest, &EnableFeaturesRequest::handle_create_object_map>(this); object_map::CreateRequest *req = object_map::CreateRequest::create(&image_ctx, ctx); req->send(); } template Context *EnableFeaturesRequest::handle_create_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 create object map: " << cpp_strerror(*result) << dendl; return handle_finish(*result); } send_enable_mirror_image(); return nullptr; } template void EnableFeaturesRequest::send_enable_mirror_image() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << dendl; if (!m_enable_mirroring) { send_notify_update(); return; } Context *ctx = create_context_callback< EnableFeaturesRequest, &EnableFeaturesRequest::handle_enable_mirror_image>(this); mirror::EnableRequest *req = mirror::EnableRequest::create(&image_ctx, ctx); req->send(); } template Context *EnableFeaturesRequest::handle_enable_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 enable mirroring: " << cpp_strerror(*result) << dendl; // not fatal } send_notify_update(); return nullptr; } template void EnableFeaturesRequest::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< EnableFeaturesRequest, &EnableFeaturesRequest::handle_notify_update>(this); image_ctx.notify_update(ctx); } template Context *EnableFeaturesRequest::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; return handle_finish(*result); } template Context *EnableFeaturesRequest::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(); } if (m_writes_blocked) { 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::EnableFeaturesRequest;