// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "librbd/image/DetachChildRequest.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/Operations.h" #include "librbd/Utils.h" #include "librbd/asio/ContextWQ.h" #include "librbd/journal/DisabledPolicy.h" #include "librbd/trash/RemoveRequest.h" #include #define dout_subsys ceph_subsys_rbd #undef dout_prefix #define dout_prefix *_dout << "librbd::image::DetachChildRequest: " << this \ << " " << __func__ << ": " namespace librbd { namespace image { using util::create_context_callback; using util::create_rados_callback; template DetachChildRequest::~DetachChildRequest() { ceph_assert(m_parent_image_ctx == nullptr); } template void DetachChildRequest::send() { { std::shared_lock image_locker{m_image_ctx.image_lock}; // use oldest snapshot or HEAD for parent spec if (!m_image_ctx.snap_info.empty()) { m_parent_spec = m_image_ctx.snap_info.begin()->second.parent.spec; } else { m_parent_spec = m_image_ctx.parent_md.spec; } } if (m_parent_spec.pool_id == -1) { // ignore potential race with parent disappearing m_image_ctx.op_work_queue->queue(create_context_callback< DetachChildRequest, &DetachChildRequest::finish>(this), 0); return; } else if (!m_image_ctx.test_op_features(RBD_OPERATION_FEATURE_CLONE_CHILD)) { clone_v1_remove_child(); return; } clone_v2_child_detach(); } template void DetachChildRequest::clone_v2_child_detach() { auto cct = m_image_ctx.cct; ldout(cct, 5) << dendl; librados::ObjectWriteOperation op; cls_client::child_detach(&op, m_parent_spec.snap_id, {m_image_ctx.md_ctx.get_id(), m_image_ctx.md_ctx.get_namespace(), m_image_ctx.id}); int r = util::create_ioctx(m_image_ctx.md_ctx, "parent image", m_parent_spec.pool_id, m_parent_spec.pool_namespace, &m_parent_io_ctx); if (r < 0) { if (r == -ENOENT) { r = 0; } finish(r); return; } m_parent_header_name = util::header_name(m_parent_spec.image_id); auto aio_comp = create_rados_callback< DetachChildRequest, &DetachChildRequest::handle_clone_v2_child_detach>(this); r = m_parent_io_ctx.aio_operate(m_parent_header_name, aio_comp, &op); ceph_assert(r == 0); aio_comp->release(); } template void DetachChildRequest::handle_clone_v2_child_detach(int r) { auto cct = m_image_ctx.cct; ldout(cct, 5) << "r=" << r << dendl; if (r < 0 && r != -ENOENT) { lderr(cct) << "error detaching child from parent: " << cpp_strerror(r) << dendl; finish(r); return; } clone_v2_get_snapshot(); } template void DetachChildRequest::clone_v2_get_snapshot() { auto cct = m_image_ctx.cct; ldout(cct, 5) << dendl; librados::ObjectReadOperation op; cls_client::snapshot_get_start(&op, m_parent_spec.snap_id); m_out_bl.clear(); auto aio_comp = create_rados_callback< DetachChildRequest, &DetachChildRequest::handle_clone_v2_get_snapshot>(this); int r = m_parent_io_ctx.aio_operate(m_parent_header_name, aio_comp, &op, &m_out_bl); ceph_assert(r == 0); aio_comp->release(); } template void DetachChildRequest::handle_clone_v2_get_snapshot(int r) { auto cct = m_image_ctx.cct; ldout(cct, 5) << "r=" << r << dendl; bool remove_snapshot = false; if (r == 0) { cls::rbd::SnapshotInfo snap_info; auto it = m_out_bl.cbegin(); r = cls_client::snapshot_get_finish(&it, &snap_info); if (r == 0) { m_parent_snap_namespace = snap_info.snapshot_namespace; m_parent_snap_name = snap_info.name; if (cls::rbd::get_snap_namespace_type(m_parent_snap_namespace) == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH && snap_info.child_count == 0) { // snapshot is in trash w/ zero children, so remove it remove_snapshot = true; } } } if (r < 0 && r != -ENOENT) { ldout(cct, 5) << "failed to retrieve snapshot: " << cpp_strerror(r) << dendl; } if (!remove_snapshot) { finish(0); return; } clone_v2_open_parent(); } template void DetachChildRequest::clone_v2_open_parent() { auto cct = m_image_ctx.cct; ldout(cct, 5) << dendl; m_parent_image_ctx = I::create("", m_parent_spec.image_id, nullptr, m_parent_io_ctx, false); // ensure non-primary images can be modified m_parent_image_ctx->read_only_mask &= ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY; auto ctx = create_context_callback< DetachChildRequest, &DetachChildRequest::handle_clone_v2_open_parent>(this); m_parent_image_ctx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT, ctx); } template void DetachChildRequest::handle_clone_v2_open_parent(int r) { auto cct = m_image_ctx.cct; ldout(cct, 5) << "r=" << r << dendl; if (r < 0) { ldout(cct, 5) << "failed to open parent for read/write: " << cpp_strerror(r) << dendl; m_parent_image_ctx = nullptr; finish(0); return; } // do not attempt to open the parent journal when removing the trash // snapshot, because the parent may be not promoted if (m_parent_image_ctx->test_features(RBD_FEATURE_JOURNALING)) { std::unique_lock image_locker{m_parent_image_ctx->image_lock}; m_parent_image_ctx->set_journal_policy(new journal::DisabledPolicy()); } // disallow any proxied maintenance operations { std::shared_lock owner_locker{m_parent_image_ctx->owner_lock}; if (m_parent_image_ctx->exclusive_lock != nullptr) { m_parent_image_ctx->exclusive_lock->block_requests(0); } } clone_v2_remove_snapshot(); } template void DetachChildRequest::clone_v2_remove_snapshot() { auto cct = m_image_ctx.cct; ldout(cct, 5) << dendl; auto ctx = create_context_callback< DetachChildRequest, &DetachChildRequest::handle_clone_v2_remove_snapshot>(this); m_parent_image_ctx->operations->snap_remove(m_parent_snap_namespace, m_parent_snap_name, ctx); } template void DetachChildRequest::handle_clone_v2_remove_snapshot(int r) { auto cct = m_image_ctx.cct; ldout(cct, 5) << "r=" << r << dendl; if (r < 0 && r != -ENOENT) { ldout(cct, 5) << "failed to remove trashed clone snapshot: " << cpp_strerror(r) << dendl; clone_v2_close_parent(); return; } if (m_parent_image_ctx->snaps.empty()) { clone_v2_get_parent_trash_entry(); } else { clone_v2_close_parent(); } } template void DetachChildRequest::clone_v2_get_parent_trash_entry() { auto cct = m_image_ctx.cct; ldout(cct, 5) << dendl; librados::ObjectReadOperation op; cls_client::trash_get_start(&op, m_parent_image_ctx->id); m_out_bl.clear(); auto aio_comp = create_rados_callback< DetachChildRequest, &DetachChildRequest::handle_clone_v2_get_parent_trash_entry>(this); int r = m_parent_io_ctx.aio_operate(RBD_TRASH, aio_comp, &op, &m_out_bl); ceph_assert(r == 0); aio_comp->release(); } template void DetachChildRequest::handle_clone_v2_get_parent_trash_entry(int r) { auto cct = m_image_ctx.cct; ldout(cct, 5) << "r=" << r << dendl; if (r < 0 && r != -ENOENT) { ldout(cct, 5) << "failed to get parent trash entry: " << cpp_strerror(r) << dendl; clone_v2_close_parent(); return; } bool in_trash = false; if (r == 0) { cls::rbd::TrashImageSpec trash_spec; auto it = m_out_bl.cbegin(); r = cls_client::trash_get_finish(&it, &trash_spec); if (r == 0 && trash_spec.source == cls::rbd::TRASH_IMAGE_SOURCE_USER_PARENT && trash_spec.state == cls::rbd::TRASH_IMAGE_STATE_NORMAL && trash_spec.deferment_end_time <= ceph_clock_now()) { in_trash = true; } } if (in_trash) { clone_v2_remove_parent_from_trash(); } else { clone_v2_close_parent(); } } template void DetachChildRequest::clone_v2_remove_parent_from_trash() { auto cct = m_image_ctx.cct; ldout(cct, 5) << dendl; auto ctx = create_context_callback< DetachChildRequest, &DetachChildRequest::handle_clone_v2_remove_parent_from_trash>(this); auto req = librbd::trash::RemoveRequest::create( m_parent_io_ctx, m_parent_image_ctx, m_image_ctx.op_work_queue, false, m_no_op, ctx); req->send(); } template void DetachChildRequest::handle_clone_v2_remove_parent_from_trash(int r) { auto cct = m_image_ctx.cct; ldout(cct, 5) << "r=" << r << dendl; if (r < 0) { ldout(cct, 5) << "failed to remove parent image:" << cpp_strerror(r) << dendl; } m_parent_image_ctx = nullptr; finish(0); } template void DetachChildRequest::clone_v2_close_parent() { auto cct = m_image_ctx.cct; ldout(cct, 5) << dendl; auto ctx = create_context_callback< DetachChildRequest, &DetachChildRequest::handle_clone_v2_close_parent>(this); m_parent_image_ctx->state->close(ctx); } template void DetachChildRequest::handle_clone_v2_close_parent(int r) { auto cct = m_image_ctx.cct; ldout(cct, 5) << "r=" << r << dendl; if (r < 0) { ldout(cct, 5) << "failed to close parent image:" << cpp_strerror(r) << dendl; } m_parent_image_ctx = nullptr; finish(0); } template void DetachChildRequest::clone_v1_remove_child() { auto cct = m_image_ctx.cct; ldout(cct, 5) << dendl; m_parent_spec.pool_namespace = ""; librados::ObjectWriteOperation op; librbd::cls_client::remove_child(&op, m_parent_spec, m_image_ctx.id); auto aio_comp = create_rados_callback< DetachChildRequest, &DetachChildRequest::handle_clone_v1_remove_child>(this); int r = m_image_ctx.md_ctx.aio_operate(RBD_CHILDREN, aio_comp, &op); ceph_assert(r == 0); aio_comp->release(); } template void DetachChildRequest::handle_clone_v1_remove_child(int r) { auto cct = m_image_ctx.cct; ldout(cct, 5) << "r=" << r << dendl; if (r == -ENOENT) { r = 0; } else if (r < 0) { lderr(cct) << "failed to remove child from children list: " << cpp_strerror(r) << dendl; finish(r); return; } finish(0); } template void DetachChildRequest::finish(int r) { auto cct = m_image_ctx.cct; ldout(cct, 5) << "r=" << r << dendl; m_on_finish->complete(r); delete this; } } // namespace image } // namespace librbd template class librbd::image::DetachChildRequest;