--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/image/RefreshParentRequest.h"
+#include "include/rados/librados.hpp"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "common/WorkQueue.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/Utils.h"
+#include "librbd/image/CloseRequest.h"
+#include "librbd/image/OpenRequest.h"
+#include "librbd/image/SetSnapRequest.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::image::RefreshParentRequest: "
+
+namespace librbd {
+namespace image {
+
+using util::create_async_context_callback;
+using util::create_context_callback;
+
+template <typename I>
+RefreshParentRequest<I>::RefreshParentRequest(I &child_image_ctx,
+ const ParentInfo &parent_md,
+ Context *on_finish)
+ : m_child_image_ctx(child_image_ctx), m_parent_md(parent_md),
+ m_on_finish(on_finish), m_parent_image_ctx(nullptr),
+ m_parent_snap_id(CEPH_NOSNAP), m_error_result(0) {
+}
+
+template <typename I>
+bool RefreshParentRequest<I>::is_refresh_required(I &child_image_ctx,
+ const ParentInfo &parent_md) {
+ assert(child_image_ctx.snap_lock.is_locked());
+ assert(child_image_ctx.parent_lock.is_locked());
+ return (is_open_required(child_image_ctx, parent_md) ||
+ is_close_required(child_image_ctx, parent_md));
+}
+
+template <typename I>
+bool RefreshParentRequest<I>::is_close_required(I &child_image_ctx,
+ const ParentInfo &parent_md) {
+ return (child_image_ctx.parent != nullptr &&
+ (parent_md.spec.pool_id == -1 || parent_md.overlap == 0));
+}
+
+template <typename I>
+bool RefreshParentRequest<I>::is_open_required(I &child_image_ctx,
+ const ParentInfo &parent_md) {
+ return (parent_md.spec.pool_id > -1 && parent_md.overlap > 0 &&
+ (child_image_ctx.parent == nullptr ||
+ child_image_ctx.parent->md_ctx.get_id() != parent_md.spec.pool_id ||
+ child_image_ctx.parent->id != parent_md.spec.image_id ||
+ child_image_ctx.parent->snap_id != parent_md.spec.snap_id));
+}
+
+template <typename I>
+void RefreshParentRequest<I>::send() {
+ if (is_open_required(m_child_image_ctx, m_parent_md)) {
+ send_open_parent();
+ } else {
+ // parent will be closed (if necessary) during finalize
+ send_complete(0);
+ }
+}
+
+template <typename I>
+void RefreshParentRequest<I>::apply() {
+ if (m_child_image_ctx.parent != nullptr) {
+ // closing parent image
+ m_child_image_ctx.clear_nonexistence_cache();
+ }
+ assert(m_child_image_ctx.snap_lock.is_wlocked());
+ assert(m_child_image_ctx.parent_lock.is_wlocked());
+ std::swap(m_child_image_ctx.parent, m_parent_image_ctx);
+}
+
+template <typename I>
+void RefreshParentRequest<I>::finalize(Context *on_finish) {
+ CephContext *cct = m_child_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ m_on_finish = on_finish;
+ if (m_parent_image_ctx != nullptr) {
+ send_close_parent();
+ } else {
+ send_complete(0);
+ }
+}
+
+template <typename I>
+void RefreshParentRequest<I>::send_open_parent() {
+ assert(m_parent_md.spec.pool_id >= 0);
+
+ CephContext *cct = m_child_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ librados::Rados rados(m_child_image_ctx.md_ctx);
+
+ librados::IoCtx parent_io_ctx;
+ int r = rados.ioctx_create2(m_parent_md.spec.pool_id, parent_io_ctx);
+ assert(r == 0);
+
+ // since we don't know the image and snapshot name, set their ids and
+ // reset the snap_name and snap_exists fields after we read the header
+ m_parent_image_ctx = new I("", m_parent_md.spec.image_id, NULL, parent_io_ctx,
+ true);
+
+ // set rados flags for reading the parent image
+ if (m_child_image_ctx.balance_parent_reads) {
+ m_parent_image_ctx->set_read_flag(librados::OPERATION_BALANCE_READS);
+ } else if (m_child_image_ctx.localize_parent_reads) {
+ m_parent_image_ctx->set_read_flag(librados::OPERATION_LOCALIZE_READS);
+ }
+
+ using klass = RefreshParentRequest<I>;
+ Context *ctx = create_async_context_callback(
+ m_child_image_ctx, create_context_callback<
+ klass, &klass::handle_open_parent, false>(this));
+ OpenRequest<I> *req = OpenRequest<I>::create(m_parent_image_ctx, false, ctx);
+ req->send();
+}
+
+template <typename I>
+Context *RefreshParentRequest<I>::handle_open_parent(int *result) {
+ CephContext *cct = m_child_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << " r=" << *result << dendl;
+
+ save_result(result);
+ if (*result < 0) {
+ lderr(cct) << "failed to open parent image: " << cpp_strerror(*result)
+ << dendl;
+
+ // image already closed by open state machine
+ delete m_parent_image_ctx;
+ m_parent_image_ctx = nullptr;
+
+ return m_on_finish;
+ }
+
+ send_set_parent_snap();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshParentRequest<I>::send_set_parent_snap() {
+ assert(m_parent_md.spec.snap_id != CEPH_NOSNAP);
+
+ CephContext *cct = m_child_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ cls::rbd::SnapshotNamespace snap_namespace;
+ std::string snap_name;
+ {
+ RWLock::RLocker snap_locker(m_parent_image_ctx->snap_lock);
+ const SnapInfo *info = m_parent_image_ctx->get_snap_info(m_parent_md.spec.snap_id);
+ if (!info) {
+ lderr(cct) << "failed to locate snapshot: Snapshot with this id not found" << dendl;
+ send_complete(-ENOENT);
+ return;
+ }
+ snap_namespace = info->snap_namespace;
+ snap_name = info->name;
+ }
+
+ using klass = RefreshParentRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_set_parent_snap, false>(this);
+ SetSnapRequest<I> *req = SetSnapRequest<I>::create(
+ *m_parent_image_ctx, snap_namespace, snap_name, ctx);
+ req->send();
+}
+
+template <typename I>
+Context *RefreshParentRequest<I>::handle_set_parent_snap(int *result) {
+ CephContext *cct = m_child_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << " r=" << *result << dendl;
+
+ save_result(result);
+ if (*result < 0) {
+ lderr(cct) << "failed to set parent snapshot: " << cpp_strerror(*result)
+ << dendl;
+ send_close_parent();
+ return nullptr;
+ }
+
+ return m_on_finish;
+}
+
+template <typename I>
+void RefreshParentRequest<I>::send_close_parent() {
+ assert(m_parent_image_ctx != nullptr);
+
+ CephContext *cct = m_child_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ using klass = RefreshParentRequest<I>;
+ Context *ctx = create_async_context_callback(
+ m_child_image_ctx, create_context_callback<
+ klass, &klass::handle_close_parent, false>(this));
+ CloseRequest<I> *req = CloseRequest<I>::create(m_parent_image_ctx, ctx);
+ req->send();
+}
+
+template <typename I>
+Context *RefreshParentRequest<I>::handle_close_parent(int *result) {
+ CephContext *cct = m_child_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << " r=" << *result << dendl;
+
+ delete m_parent_image_ctx;
+ if (*result < 0) {
+ lderr(cct) << "failed to close parent image: " << cpp_strerror(*result)
+ << dendl;
+ }
+
+ if (m_error_result < 0) {
+ // propagate errors from opening the image
+ *result = m_error_result;
+ } else {
+ // ignore errors from closing the image
+ *result = 0;
+ }
+
+ return m_on_finish;
+}
+
+template <typename I>
+void RefreshParentRequest<I>::send_complete(int r) {
+ CephContext *cct = m_child_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ m_on_finish->complete(r);
+}
+
+} // namespace image
+} // namespace librbd
+
+template class librbd::image::RefreshParentRequest<librbd::ImageCtx>;