summaryrefslogtreecommitdiffstats
path: root/src/librbd/operation/SnapshotRemoveRequest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/librbd/operation/SnapshotRemoveRequest.cc')
-rw-r--r--src/librbd/operation/SnapshotRemoveRequest.cc505
1 files changed, 505 insertions, 0 deletions
diff --git a/src/librbd/operation/SnapshotRemoveRequest.cc b/src/librbd/operation/SnapshotRemoveRequest.cc
new file mode 100644
index 000000000..b78be8a0a
--- /dev/null
+++ b/src/librbd/operation/SnapshotRemoveRequest.cc
@@ -0,0 +1,505 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/operation/SnapshotRemoveRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "include/ceph_assert.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/Utils.h"
+#include "librbd/image/DetachChildRequest.h"
+#include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::SnapshotRemoveRequest: " << this << " " \
+ << __func__ << ": "
+
+namespace librbd {
+namespace operation {
+
+using util::create_context_callback;
+using util::create_rados_callback;
+
+template <typename I>
+SnapshotRemoveRequest<I>::SnapshotRemoveRequest(
+ I &image_ctx, Context *on_finish,
+ const cls::rbd::SnapshotNamespace &snap_namespace,
+ const std::string &snap_name, uint64_t snap_id)
+ : Request<I>(image_ctx, on_finish), m_snap_namespace(snap_namespace),
+ m_snap_name(snap_name), m_snap_id(snap_id) {
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::send_op() {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+
+ ceph_assert(ceph_mutex_is_locked(image_ctx.owner_lock));
+ {
+ std::shared_lock image_locker{image_ctx.image_lock};
+ if (image_ctx.snap_info.find(m_snap_id) == image_ctx.snap_info.end()) {
+ lderr(cct) << "snapshot doesn't exist" << dendl;
+ this->async_complete(-ENOENT);
+ return;
+ }
+ }
+
+ trash_snap();
+}
+
+template <typename I>
+bool SnapshotRemoveRequest<I>::should_complete(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+ if (r < 0 && r != -EBUSY) {
+ lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl;
+ }
+ return true;
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::trash_snap() {
+ I &image_ctx = this->m_image_ctx;
+ if (image_ctx.old_format) {
+ release_snap_id();
+ return;
+ } else if (cls::rbd::get_snap_namespace_type(m_snap_namespace) ==
+ cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) {
+ get_snap();
+ return;
+ }
+
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << dendl;
+
+ librados::ObjectWriteOperation op;
+ cls_client::snapshot_trash_add(&op, m_snap_id);
+
+ auto aio_comp = create_rados_callback<
+ SnapshotRemoveRequest<I>,
+ &SnapshotRemoveRequest<I>::handle_trash_snap>(this);
+ int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, aio_comp, &op);
+ ceph_assert(r == 0);
+ aio_comp->release();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_trash_snap(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r == -EOPNOTSUPP) {
+ // trash / clone v2 not supported
+ detach_child();
+ return;
+ } else if (r < 0 && r != -EEXIST) {
+ lderr(cct) << "failed to move snapshot to trash: " << cpp_strerror(r)
+ << dendl;
+ this->complete(r);
+ return;
+ }
+
+ m_trashed_snapshot = true;
+ get_snap();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::get_snap() {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::snapshot_get_start(&op, m_snap_id);
+
+ auto aio_comp = create_rados_callback<
+ SnapshotRemoveRequest<I>,
+ &SnapshotRemoveRequest<I>::handle_get_snap>(this);
+ m_out_bl.clear();
+ int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, aio_comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ aio_comp->release();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_get_snap(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r == 0) {
+ cls::rbd::SnapshotInfo snap_info;
+
+ auto it = m_out_bl.cbegin();
+ r = cls_client::snapshot_get_finish(&it, &snap_info);
+ m_child_attached = (snap_info.child_count > 0);
+ if (r == 0 && m_child_attached) {
+ list_children();
+ return;
+ }
+ }
+
+ if (r < 0) {
+ lderr(cct) << "failed to retrieve snapshot: " << cpp_strerror(r)
+ << dendl;
+ this->complete(r);
+ return;
+ }
+
+ detach_child();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::list_children() {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::children_list_start(&op, m_snap_id);
+
+ m_out_bl.clear();
+ m_child_images.clear();
+ auto aio_comp = create_rados_callback<
+ SnapshotRemoveRequest<I>,
+ &SnapshotRemoveRequest<I>::handle_list_children>(this);
+ int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, aio_comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ aio_comp->release();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_list_children(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r == 0) {
+ auto it = m_out_bl.cbegin();
+ r = cls_client::children_list_finish(&it, &m_child_images);
+ }
+
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to retrieve child: " << cpp_strerror(r)
+ << dendl;
+ this->complete(r);
+ return;
+ }
+
+ detach_stale_child();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::detach_stale_child() {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << dendl;
+
+ for (auto& child_image : m_child_images) {
+ m_child_attached = true;
+ IoCtx ioctx;
+ int r = util::create_ioctx(image_ctx.md_ctx, "child image",
+ child_image.pool_id,
+ child_image.pool_namespace, &ioctx);
+ if (r == -ENOENT) {
+ librados::ObjectWriteOperation op;
+ cls_client::child_detach(&op, m_snap_id,
+ {child_image.pool_id,
+ child_image.pool_namespace,
+ child_image.image_id});
+ auto aio_comp = create_rados_callback<
+ SnapshotRemoveRequest<I>,
+ &SnapshotRemoveRequest<I>::handle_detach_stale_child>(this);
+ r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, aio_comp, &op);
+ ceph_assert(r == 0);
+ aio_comp->release();
+ return;
+ } else if (r < 0) {
+ this->async_complete(r);
+ return;
+ }
+ }
+
+ detach_child();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_detach_stale_child(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to detach stale child: " << cpp_strerror(r)
+ << dendl;
+ this->complete(r);
+ return;
+ }
+
+ m_child_attached = false;
+ list_children();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::detach_child() {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+
+ bool detach_child = false;
+ {
+ std::shared_lock image_locker{image_ctx.image_lock};
+
+ cls::rbd::ParentImageSpec our_pspec;
+ int r = image_ctx.get_parent_spec(m_snap_id, &our_pspec);
+ if (r < 0) {
+ if (r == -ENOENT) {
+ ldout(cct, 1) << "No such snapshot" << dendl;
+ } else {
+ lderr(cct) << "failed to retrieve parent spec" << dendl;
+ }
+
+ this->async_complete(r);
+ return;
+ }
+
+ if (image_ctx.parent_md.spec != our_pspec &&
+ (scan_for_parents(our_pspec) == -ENOENT)) {
+ // no other references to the parent image
+ detach_child = true;
+ }
+ }
+
+ if (!detach_child) {
+ // HEAD image or other snapshots still associated with parent
+ remove_object_map();
+ return;
+ }
+
+ ldout(cct, 5) << dendl;
+ auto ctx = create_context_callback<
+ SnapshotRemoveRequest<I>,
+ &SnapshotRemoveRequest<I>::handle_detach_child>(this);
+ auto req = image::DetachChildRequest<I>::create(image_ctx, ctx);
+ req->send();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_detach_child(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to detach child from parent: " << cpp_strerror(r)
+ << dendl;
+ this->complete(r);
+ return;
+ }
+
+ remove_object_map();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::remove_object_map() {
+ I &image_ctx = this->m_image_ctx;
+ if (m_child_attached) {
+ // if a clone v2 child is attached to this snapshot, we cannot
+ // proceed. It's only an error if the snap was already in the trash
+ this->complete(m_trashed_snapshot ? 0 : -EBUSY);
+ return;
+ }
+
+ CephContext *cct = image_ctx.cct;
+
+ {
+ std::shared_lock owner_lock{image_ctx.owner_lock};
+ std::unique_lock image_locker{image_ctx.image_lock};
+ if (image_ctx.object_map != nullptr) {
+ ldout(cct, 5) << dendl;
+
+ auto ctx = create_context_callback<
+ SnapshotRemoveRequest<I>,
+ &SnapshotRemoveRequest<I>::handle_remove_object_map>(this);
+ image_ctx.object_map->snapshot_remove(m_snap_id, ctx);
+ return;
+ }
+ }
+
+ // object map disabled
+ remove_image_state();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_remove_object_map(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to remove snapshot object map: " << cpp_strerror(r)
+ << dendl;
+ this->complete(r);
+ return;
+ }
+
+ remove_image_state();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::remove_image_state() {
+ I &image_ctx = this->m_image_ctx;
+ auto type = cls::rbd::get_snap_namespace_type(m_snap_namespace);
+
+ if (type != cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR) {
+ release_snap_id();
+ return;
+ }
+
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << dendl;
+
+ auto ctx = create_context_callback<
+ SnapshotRemoveRequest<I>,
+ &SnapshotRemoveRequest<I>::handle_remove_image_state>(this);
+ auto req = mirror::snapshot::RemoveImageStateRequest<I>::create(
+ &image_ctx, m_snap_id, ctx);
+ req->send();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_remove_image_state(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to remove image state: " << cpp_strerror(r)
+ << dendl;
+ if (r != -ENOENT) {
+ this->complete(r);
+ return;
+ }
+ }
+
+ release_snap_id();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::release_snap_id() {
+ I &image_ctx = this->m_image_ctx;
+
+ if (!image_ctx.data_ctx.is_valid()) {
+ remove_snap();
+ return;
+ }
+
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "snap_name=" << m_snap_name << ", "
+ << "snap_id=" << m_snap_id << dendl;
+
+ auto aio_comp = create_rados_callback<
+ SnapshotRemoveRequest<I>,
+ &SnapshotRemoveRequest<I>::handle_release_snap_id>(this);
+ image_ctx.data_ctx.aio_selfmanaged_snap_remove(m_snap_id, aio_comp);
+ aio_comp->release();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_release_snap_id(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to release snap id: " << cpp_strerror(r) << dendl;
+ this->complete(r);
+ return;
+ }
+
+ remove_snap();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::remove_snap() {
+ I &image_ctx = this->m_image_ctx;
+
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << dendl;
+
+ librados::ObjectWriteOperation op;
+ if (image_ctx.old_format) {
+ cls_client::old_snapshot_remove(&op, m_snap_name);
+ } else {
+ cls_client::snapshot_remove(&op, m_snap_id);
+ }
+
+ auto aio_comp = create_rados_callback<
+ SnapshotRemoveRequest<I>,
+ &SnapshotRemoveRequest<I>::handle_remove_snap>(this);
+ int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, aio_comp, &op);
+ ceph_assert(r == 0);
+ aio_comp->release();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_remove_snap(int r) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to remove snapshot: " << cpp_strerror(r) << dendl;
+ this->complete(r);
+ return;
+ }
+
+ remove_snap_context();
+ this->complete(0);
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::remove_snap_context() {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 5) << dendl;
+
+ std::unique_lock image_locker{image_ctx.image_lock};
+ image_ctx.rm_snap(m_snap_namespace, m_snap_name, m_snap_id);
+}
+
+template <typename I>
+int SnapshotRemoveRequest<I>::scan_for_parents(
+ cls::rbd::ParentImageSpec &pspec) {
+ I &image_ctx = this->m_image_ctx;
+ ceph_assert(ceph_mutex_is_locked(image_ctx.image_lock));
+
+ if (pspec.pool_id != -1) {
+ map<uint64_t, SnapInfo>::iterator it;
+ for (it = image_ctx.snap_info.begin();
+ it != image_ctx.snap_info.end(); ++it) {
+ // skip our snap id (if checking base image, CEPH_NOSNAP won't match)
+ if (it->first == m_snap_id) {
+ continue;
+ }
+ if (it->second.parent.spec == pspec) {
+ break;
+ }
+ }
+ if (it == image_ctx.snap_info.end()) {
+ return -ENOENT;
+ }
+ }
+ return 0;
+}
+
+} // namespace operation
+} // namespace librbd
+
+template class librbd::operation::SnapshotRemoveRequest<librbd::ImageCtx>;