diff options
Diffstat (limited to 'src/librbd/image/DetachChildRequest.cc')
-rw-r--r-- | src/librbd/image/DetachChildRequest.cc | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/src/librbd/image/DetachChildRequest.cc b/src/librbd/image/DetachChildRequest.cc new file mode 100644 index 000000000..ab39dbcd7 --- /dev/null +++ b/src/librbd/image/DetachChildRequest.cc @@ -0,0 +1,392 @@ +// -*- 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 <string> + +#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 <typename I> +DetachChildRequest<I>::~DetachChildRequest() { + ceph_assert(m_parent_image_ctx == nullptr); +} + +template <typename I> +void DetachChildRequest<I>::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<I>, + &DetachChildRequest<I>::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 <typename I> +void DetachChildRequest<I>::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<I>, + &DetachChildRequest<I>::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 <typename I> +void DetachChildRequest<I>::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 <typename I> +void DetachChildRequest<I>::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<I>, + &DetachChildRequest<I>::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 <typename I> +void DetachChildRequest<I>::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<typename I> +void DetachChildRequest<I>::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<I>, + &DetachChildRequest<I>::handle_clone_v2_open_parent>(this); + m_parent_image_ctx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT, ctx); +} + +template<typename I> +void DetachChildRequest<I>::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<typename I> +void DetachChildRequest<I>::clone_v2_remove_snapshot() { + auto cct = m_image_ctx.cct; + ldout(cct, 5) << dendl; + + auto ctx = create_context_callback< + DetachChildRequest<I>, + &DetachChildRequest<I>::handle_clone_v2_remove_snapshot>(this); + m_parent_image_ctx->operations->snap_remove(m_parent_snap_namespace, + m_parent_snap_name, ctx); +} + +template<typename I> +void DetachChildRequest<I>::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<typename I> +void DetachChildRequest<I>::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<I>, + &DetachChildRequest<I>::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<typename I> +void DetachChildRequest<I>::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<typename I> +void DetachChildRequest<I>::clone_v2_remove_parent_from_trash() { + auto cct = m_image_ctx.cct; + ldout(cct, 5) << dendl; + + auto ctx = create_context_callback< + DetachChildRequest<I>, + &DetachChildRequest<I>::handle_clone_v2_remove_parent_from_trash>(this); + auto req = librbd::trash::RemoveRequest<I>::create( + m_parent_io_ctx, m_parent_image_ctx, m_image_ctx.op_work_queue, false, + m_no_op, ctx); + req->send(); +} + +template<typename I> +void DetachChildRequest<I>::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<typename I> +void DetachChildRequest<I>::clone_v2_close_parent() { + auto cct = m_image_ctx.cct; + ldout(cct, 5) << dendl; + + auto ctx = create_context_callback< + DetachChildRequest<I>, + &DetachChildRequest<I>::handle_clone_v2_close_parent>(this); + m_parent_image_ctx->state->close(ctx); +} + +template<typename I> +void DetachChildRequest<I>::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<typename I> +void DetachChildRequest<I>::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<I>, + &DetachChildRequest<I>::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<typename I> +void DetachChildRequest<I>::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 <typename I> +void DetachChildRequest<I>::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<librbd::ImageCtx>; |