]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/librbd/image/CloneRequest.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / librbd / image / CloneRequest.cc
index a1c30c187db52fdc43a2293f221269afa76c1c9f..24da545113079f5c9d41a10e4f38616e67c460bb 100644 (file)
@@ -5,18 +5,20 @@
 #include "cls/rbd/cls_rbd_types.h"
 #include "common/dout.h"
 #include "common/errno.h"
-#include "include/assert.h"
+#include "include/ceph_assert.h"
 #include "librbd/ImageState.h"
-#include "librbd/Journal.h"
+#include "librbd/Utils.h"
+#include "librbd/image/AttachChildRequest.h"
+#include "librbd/image/AttachParentRequest.h"
 #include "librbd/image/CloneRequest.h"
 #include "librbd/image/CreateRequest.h"
 #include "librbd/image/RemoveRequest.h"
-#include "librbd/image/RefreshRequest.h"
 #include "librbd/mirror/EnableRequest.h"
 
 #define dout_subsys ceph_subsys_rbd
 #undef dout_prefix
-#define dout_prefix *_dout << "librbd::image::CloneRequest: "
+#define dout_prefix *_dout << "librbd::image::CloneRequest: " << this << " " \
+                           << __func__ << ": "
 
 #define MAX_KEYS 64
 
@@ -28,16 +30,22 @@ using util::create_context_callback;
 using util::create_async_context_callback;
 
 template <typename I>
-CloneRequest<I>::CloneRequest(I *p_imctx, IoCtx &c_ioctx,
+CloneRequest<I>::CloneRequest(ConfigProxy& config,
+                              IoCtx& parent_io_ctx,
+                              const std::string& parent_image_id,
+                              const std::string& parent_snap_name,
+                              uint64_t parent_snap_id,
+                              IoCtx &c_ioctx,
                              const std::string &c_name,
                              const std::string &c_id,
                              ImageOptions c_options,
                              const std::string &non_primary_global_image_id,
                              const std::string &primary_mirror_uuid,
                              ContextWQ *op_work_queue, Context *on_finish)
-  : m_p_imctx(p_imctx), m_ioctx(c_ioctx), m_name(c_name), m_id(c_id),
-    m_opts(c_options),
-    m_pspec(m_p_imctx->md_ctx.get_id(), m_p_imctx->id, m_p_imctx->snap_id),
+  : m_config(config), m_parent_io_ctx(parent_io_ctx),
+    m_parent_image_id(parent_image_id), m_parent_snap_name(parent_snap_name),
+    m_parent_snap_id(parent_snap_id), m_ioctx(c_ioctx), m_name(c_name),
+    m_id(c_id), m_opts(c_options),
     m_non_primary_global_image_id(non_primary_global_image_id),
     m_primary_mirror_uuid(primary_mirror_uuid),
     m_op_work_queue(op_work_queue), m_on_finish(on_finish),
@@ -51,292 +59,337 @@ CloneRequest<I>::CloneRequest(I *p_imctx, IoCtx &c_ioctx,
     m_opts.set(RBD_IMAGE_OPTION_FORMAT, static_cast<uint64_t>(2));
   }
 
-  ldout(m_cct, 20) << "clone " << &m_p_imctx->md_ctx << " name " << m_p_imctx->name
-                << " snap " << m_p_imctx->snap_name << " to child " << &m_ioctx
-                << " name " << m_name << " opts = " << &m_opts << dendl;
-  return;
+  ldout(m_cct, 20) << "parent_pool_id=" << parent_io_ctx.get_id() << ", "
+                   << "parent_image_id=" << parent_image_id << ", "
+                  << "parent_snap=" << parent_snap_name << "/"
+                   << parent_snap_id << " clone to "
+                   << "pool_id=" << m_ioctx.get_id() << ", "
+                   << "name=" << m_name << ", "
+                   << "opts=" << m_opts << dendl;
 }
 
 template <typename I>
 void CloneRequest<I>::send() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+  ldout(m_cct, 20) << dendl;
   validate_options();
 }
 
 template <typename I>
 void CloneRequest<I>::validate_options() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+  ldout(m_cct, 20) << dendl;
 
   uint64_t format = 0;
   m_opts.get(RBD_IMAGE_OPTION_FORMAT, &format);
   if (format < 2) {
     lderr(m_cct) << "format 2 or later required for clone" << dendl;
-    return complete(-EINVAL);
+    complete(-EINVAL);
+    return;
   }
 
   if (m_opts.get(RBD_IMAGE_OPTION_FEATURES, &m_features) == 0) {
     if (m_features & ~RBD_FEATURES_ALL) {
       lderr(m_cct) << "librbd does not support requested features" << dendl;
-      return complete(-ENOSYS);
+      complete(-ENOSYS);
+      return;
     }
     m_use_p_features = false;
   }
 
-  send_validate_parent();
+  std::string default_clone_format = m_config.get_val<std::string>(
+    "rbd_default_clone_format");
+  if (default_clone_format == "1") {
+    m_clone_format = 1;
+  } else if (default_clone_format == "auto") {
+    librados::Rados rados(m_ioctx);
+    int8_t min_compat_client;
+    int8_t require_min_compat_client;
+    int r = rados.get_min_compatible_client(&min_compat_client,
+                                            &require_min_compat_client);
+    if (r < 0) {
+      complete(r);
+      return;
+    }
+    if (std::max(min_compat_client, require_min_compat_client) <
+          CEPH_RELEASE_MIMIC) {
+      m_clone_format = 1;
+    }
+  }
+
+  if (m_clone_format == 1 &&
+      m_parent_io_ctx.get_namespace() != m_ioctx.get_namespace()) {
+    ldout(m_cct, 1) << "clone v2 required for cross-namespace clones" << dendl;
+    complete(-EXDEV);
+    return;
+  }
+
+  open_parent();
 }
 
 template <typename I>
-void CloneRequest<I>::send_validate_parent() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+void CloneRequest<I>::open_parent() {
+  ldout(m_cct, 20) << dendl;
+  ceph_assert(m_parent_snap_name.empty() ^ (m_parent_snap_id == CEPH_NOSNAP));
+
+  if (m_parent_snap_id != CEPH_NOSNAP) {
+    m_parent_image_ctx = I::create("", m_parent_image_id, m_parent_snap_id,
+                                   m_parent_io_ctx, true);
+  } else {
+    m_parent_image_ctx = I::create("", m_parent_image_id,
+                                   m_parent_snap_name.c_str(), m_parent_io_ctx,
+                                   true);
+  }
+
+  Context *ctx = create_context_callback<
+    CloneRequest<I>, &CloneRequest<I>::handle_open_parent>(this);
+  m_parent_image_ctx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT, ctx);
+}
+
+template <typename I>
+void CloneRequest<I>::handle_open_parent(int r) {
+  ldout(m_cct, 20) << "r=" << r << dendl;
+
+  if (r < 0) {
+    m_parent_image_ctx->destroy();
+    m_parent_image_ctx = nullptr;
 
-  if (m_p_imctx->snap_id == CEPH_NOSNAP) {
+    lderr(m_cct) << "failed to open parent image: " << cpp_strerror(r) << dendl;
+    complete(r);
+    return;
+  }
+
+  m_parent_snap_id = m_parent_image_ctx->snap_id;
+  m_pspec = {m_parent_io_ctx.get_id(), m_parent_io_ctx.get_namespace(),
+             m_parent_image_id, m_parent_snap_id};
+  validate_parent();
+}
+
+template <typename I>
+void CloneRequest<I>::validate_parent() {
+  ldout(m_cct, 20) << dendl;
+
+  if (m_parent_image_ctx->operations_disabled) {
+    lderr(m_cct) << "image operations disabled due to unsupported op features"
+                 << dendl;
+    m_r_saved = -EROFS;
+    close_parent();
+    return;
+  }
+
+  if (m_parent_image_ctx->snap_id == CEPH_NOSNAP) {
     lderr(m_cct) << "image to be cloned must be a snapshot" << dendl;
-    return complete(-EINVAL);
+    m_r_saved = -EINVAL;
+    close_parent();
+    return;
   }
 
-  if (m_p_imctx->old_format) {
+  if (m_parent_image_ctx->old_format) {
     lderr(m_cct) << "parent image must be in new format" << dendl;
-    return complete(-EINVAL);
+    m_r_saved = -EINVAL;
+    close_parent();
+    return;
   }
 
-  int r = 0;
+  m_parent_image_ctx->snap_lock.get_read();
+  uint64_t p_features = m_parent_image_ctx->features;
+  m_size = m_parent_image_ctx->get_image_size(m_parent_image_ctx->snap_id);
+
   bool snap_protected;
-  m_p_imctx->snap_lock.get_read();
-  m_p_features = m_p_imctx->features;
-  m_size = m_p_imctx->get_image_size(m_p_imctx->snap_id);
-  r = m_p_imctx->is_snap_protected(m_p_imctx->snap_id, &snap_protected);
-  m_p_imctx->snap_lock.put_read();
+  int r = m_parent_image_ctx->is_snap_protected(m_parent_image_ctx->snap_id, &snap_protected);
+  m_parent_image_ctx->snap_lock.put_read();
 
-  if ((m_p_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
+  if ((p_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
     lderr(m_cct) << "parent image must support layering" << dendl;
-    return complete(-ENOSYS);
+    m_r_saved = -ENOSYS;
+    close_parent();
+    return;
+  }
+  if (m_use_p_features) {
+    m_features = (p_features & ~RBD_FEATURES_IMPLICIT_ENABLE);
   }
 
   if (r < 0) {
     lderr(m_cct) << "unable to locate parent's snapshot" << dendl;
-    return complete(r);
+    m_r_saved = r;
+    close_parent();
+    return;
   }
 
-  if (!snap_protected) {
+  if (m_clone_format == 1 && !snap_protected) {
     lderr(m_cct) << "parent snapshot must be protected" << dendl;
-    return complete(-EINVAL);
-  }
-
-  if ((m_p_features & RBD_FEATURE_JOURNALING) != 0) {
-    m_force_non_primary = !m_non_primary_global_image_id.empty();
-    using klass = CloneRequest<I>;
-    Context *ctx = create_context_callback<
-       klass, &klass::handle_validate_parent>(this);
-
-    Journal<I>::is_tag_owner(m_p_imctx, &m_is_primary, ctx);
+    m_r_saved = -EINVAL;
+    close_parent();
     return;
   }
 
-  send_validate_child();
+  validate_child();
 }
 
 template <typename I>
-void CloneRequest<I>::handle_validate_parent(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+void CloneRequest<I>::validate_child() {
+  ldout(m_cct, 15) << dendl;
 
-  if (r < 0) {
-    lderr(m_cct) << "failed to determine tag ownership: " << cpp_strerror(r)
-              << dendl;
-    return complete(r);
-  }
-
-  if ((m_p_features & RBD_FEATURE_JOURNALING) != 0) {
-    if (!m_is_primary && !m_force_non_primary) {
-      lderr(m_cct) << "parent is non-primary mirrored image" << dendl;
-      return complete(-EINVAL);
-    }
+  if ((m_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
+    lderr(m_cct) << "cloning image must support layering" << dendl;
+    m_r_saved = -ENOSYS;
+    close_parent();
+    return;
   }
 
-  send_validate_child();
-}
-
-template <typename I>
-void CloneRequest<I>::send_validate_child() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
-
   using klass = CloneRequest<I>;
-  librados::AioCompletion *comp = create_rados_callback<klass, &klass::handle_validate_child>(this);
+  librados::AioCompletion *comp = create_rados_callback<
+    klass, &klass::handle_validate_child>(this);
 
   librados::ObjectReadOperation op;
   op.stat(NULL, NULL, NULL);
 
-  int r = m_ioctx.aio_operate(util::old_header_name(m_name), comp, &op, &m_out_bl);
-  assert(r == 0);
+  int r = m_ioctx.aio_operate(util::old_header_name(m_name), comp, &op,
+                              &m_out_bl);
+  ceph_assert(r == 0);
   comp->release();
 }
 
 template <typename I>
 void CloneRequest<I>::handle_validate_child(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
   if (r != -ENOENT) {
     lderr(m_cct) << "rbd image " << m_name << " already exists" << dendl;
-    return complete(r);
+    m_r_saved = r;
+    close_parent();
+    return;
   }
 
-  send_create();
+  create_child();
 }
 
 template <typename I>
-void CloneRequest<I>::send_create() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
-
-  if (m_use_p_features) {
-    m_features = m_p_features;
-  }
+void CloneRequest<I>::create_child() {
+  ldout(m_cct, 15) << dendl;
 
-  uint64_t order = m_p_imctx->order;
+  uint64_t order = m_parent_image_ctx->order;
   if (m_opts.get(RBD_IMAGE_OPTION_ORDER, &order) != 0) {
     m_opts.set(RBD_IMAGE_OPTION_ORDER, order);
   }
-  if ((m_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
-    lderr(m_cct) << "cloning image must support layering" << dendl;
-    return complete(-ENOSYS);
-  }
   m_opts.set(RBD_IMAGE_OPTION_FEATURES, m_features);
 
   using klass = CloneRequest<I>;
-  Context *ctx = create_context_callback<klass, &klass::handle_create>(this);
+  Context *ctx = create_context_callback<
+    klass, &klass::handle_create_child>(this);
 
-  RWLock::RLocker snap_locker(m_p_imctx->snap_lock);
+  RWLock::RLocker snap_locker(m_parent_image_ctx->snap_lock);
   CreateRequest<I> *req = CreateRequest<I>::create(
-    m_ioctx, m_name, m_id, m_size, m_opts, m_non_primary_global_image_id,
-    m_primary_mirror_uuid, true, m_op_work_queue, ctx);
+    m_config, m_ioctx, m_name, m_id, m_size, m_opts,
+    m_non_primary_global_image_id, m_primary_mirror_uuid, true,
+    m_op_work_queue, ctx);
   req->send();
 }
 
 template <typename I>
-void CloneRequest<I>::handle_create(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+void CloneRequest<I>::handle_create_child(int r) {
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
-  if (r < 0) {
+  if (r == -EBADF) {
+    ldout(m_cct, 5) << "image id already in-use" << dendl;
+    complete(r);
+    return;
+  } else if (r < 0) {
     lderr(m_cct) << "error creating child: " << cpp_strerror(r) << dendl;
-    return complete(r);
+    m_r_saved = r;
+    close_parent();
+    return;
   }
-  send_open();
+  open_child();
 }
 
 template <typename I>
-void CloneRequest<I>::send_open() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+void CloneRequest<I>::open_child() {
+  ldout(m_cct, 15) << dendl;
 
-  m_imctx = I::create(m_name, "", NULL, m_ioctx, false);
+  m_imctx = I::create(m_name, "", nullptr, m_ioctx, false);
 
   using klass = CloneRequest<I>;
-  Context *ctx = create_context_callback<klass, &klass::handle_open>(this);
-
-  m_imctx->state->open(true, ctx);
-}
-
-template <typename I>
-void CloneRequest<I>::handle_open(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+  Context *ctx = create_context_callback<
+    klass, &klass::handle_open_child>(this);
 
-  if (r < 0) {
-    lderr(m_cct) << "Error opening new image: " << cpp_strerror(r) << dendl;
-    m_r_saved = r;
-    return send_remove();
+  uint64_t flags = OPEN_FLAG_SKIP_OPEN_PARENT;
+  if ((m_features & RBD_FEATURE_MIGRATING) != 0) {
+    flags |= OPEN_FLAG_IGNORE_MIGRATING;
   }
 
-  send_set_parent();
+  m_imctx->state->open(flags, ctx);
 }
 
 template <typename I>
-void CloneRequest<I>::send_set_parent() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
-
-  librados::ObjectWriteOperation op;
-  librbd::cls_client::set_parent(&op, m_pspec, m_size);
-
-  using klass = CloneRequest<I>;
-  librados::AioCompletion *comp =
-    create_rados_callback<klass, &klass::handle_set_parent>(this);
-  int r = m_imctx->md_ctx.aio_operate(m_imctx->header_oid,
-                                                comp, &op);
-  assert(r == 0);
-  comp->release();
-}
-
-template <typename I>
-void CloneRequest<I>::handle_set_parent(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+void CloneRequest<I>::handle_open_child(int r) {
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
   if (r < 0) {
-    lderr(m_cct) << "couldn't set parent: " << cpp_strerror(r) << dendl;
+    m_imctx->destroy();
+    m_imctx = nullptr;
+
+    lderr(m_cct) << "Error opening new image: " << cpp_strerror(r) << dendl;
     m_r_saved = r;
-    return send_close();
+    remove_child();
+    return;
   }
 
-  send_add_child();
+  attach_parent();
 }
 
 template <typename I>
-void CloneRequest<I>::send_add_child() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
-
-  librados::ObjectWriteOperation op;
-  cls_client::add_child(&op, m_pspec, m_id);
+void CloneRequest<I>::attach_parent() {
+  ldout(m_cct, 15) << dendl;
 
-  using klass = CloneRequest<I>;
-  librados::AioCompletion *comp =
-    create_rados_callback<klass, &klass::handle_add_child>(this);
-  int r = m_ioctx.aio_operate(RBD_CHILDREN, comp, &op);
-  assert(r == 0);
-  comp->release();
+  auto ctx = create_context_callback<
+    CloneRequest<I>, &CloneRequest<I>::handle_attach_parent>(this);
+  auto req = AttachParentRequest<I>::create(
+    *m_imctx, m_pspec, m_size, false, ctx);
+  req->send();
 }
 
 template <typename I>
-void CloneRequest<I>::handle_add_child(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+void CloneRequest<I>::handle_attach_parent(int r) {
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
   if (r < 0) {
-    lderr(m_cct) << "couldn't add child: " << cpp_strerror(r) << dendl;
+    lderr(m_cct) << "failed to attach parent: " << cpp_strerror(r) << dendl;
     m_r_saved = r;
-    return send_close();
+    close_child();
+    return;
   }
 
-  send_refresh();
+  attach_child();
 }
 
 template <typename I>
-void CloneRequest<I>::send_refresh() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+void CloneRequest<I>::attach_child() {
+  ldout(m_cct, 15) << dendl;
 
-  using klass = CloneRequest<I>;
-  RefreshRequest<I> *req = RefreshRequest<I>::create(
-    *m_imctx, false, false,
-    create_context_callback<klass, &klass::handle_refresh>(this));
+  auto ctx = create_context_callback<
+    CloneRequest<I>, &CloneRequest<I>::handle_attach_child>(this);
+  auto req = AttachChildRequest<I>::create(
+    m_imctx, m_parent_image_ctx, m_parent_image_ctx->snap_id, nullptr, 0,
+    m_clone_format, ctx);
   req->send();
 }
 
 template <typename I>
-void CloneRequest<I>::handle_refresh(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
-
-  bool snap_protected = false;
-  if (r == 0) {
-    m_p_imctx->snap_lock.get_read();
-    r = m_p_imctx->is_snap_protected(m_p_imctx->snap_id, &snap_protected);
-    m_p_imctx->snap_lock.put_read();
-  }
+void CloneRequest<I>::handle_attach_child(int r) {
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
-  if (r < 0 || !snap_protected) {
-    m_r_saved = -EINVAL;
-    return send_close();
+  if (r < 0) {
+    lderr(m_cct) << "failed to attach parent: " << cpp_strerror(r) << dendl;
+    m_r_saved = r;
+    close_child();
+    return;
   }
 
-  send_metadata_list();
+  metadata_list();
 }
 
 template <typename I>
-void CloneRequest<I>::send_metadata_list() {
-  ldout(m_cct, 20) << this << " " << __func__ << ": "
-                   << "start_key=" << m_last_metadata_key << dendl;
+void CloneRequest<I>::metadata_list() {
+  ldout(m_cct, 15) << "start_key=" << m_last_metadata_key << dendl;
 
   librados::ObjectReadOperation op;
   cls_client::metadata_list_start(&op, m_last_metadata_key, 0);
@@ -345,18 +398,18 @@ void CloneRequest<I>::send_metadata_list() {
   librados::AioCompletion *comp =
     create_rados_callback<klass, &klass::handle_metadata_list>(this);
   m_out_bl.clear();
-  m_p_imctx->md_ctx.aio_operate(m_p_imctx->header_oid,
+  m_parent_image_ctx->md_ctx.aio_operate(m_parent_image_ctx->header_oid,
                                comp, &op, &m_out_bl);
   comp->release();
 }
 
 template <typename I>
 void CloneRequest<I>::handle_metadata_list(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
   map<string, bufferlist> metadata;
   if (r == 0) {
-    bufferlist::iterator it = m_out_bl.begin();
+    auto it = m_out_bl.cbegin();
     r = cls_client::metadata_list_finish(&it, &metadata);
   }
 
@@ -367,9 +420,8 @@ void CloneRequest<I>::handle_metadata_list(int r) {
     } else {
       lderr(m_cct) << "couldn't list metadata: " << cpp_strerror(r) << dendl;
       m_r_saved = r;
-      send_remove_child();
+      close_child();
     }
-
     return;
   }
 
@@ -379,15 +431,20 @@ void CloneRequest<I>::handle_metadata_list(int r) {
   }
 
   if (metadata.size() == MAX_KEYS) {
-    send_metadata_list();
+    metadata_list();
   } else {
-    send_metadata_set();
+    metadata_set();
   }
 }
 
 template <typename I>
-void CloneRequest<I>::send_metadata_set() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+void CloneRequest<I>::metadata_set() {
+  if (m_pairs.empty()) {
+    get_mirror_mode();
+    return;
+  }
+
+  ldout(m_cct, 15) << dendl;
 
   librados::ObjectWriteOperation op;
   cls_client::metadata_set(&op, m_pairs);
@@ -396,18 +453,18 @@ void CloneRequest<I>::send_metadata_set() {
   librados::AioCompletion *comp =
     create_rados_callback<klass, &klass::handle_metadata_set>(this);
   int r = m_ioctx.aio_operate(m_imctx->header_oid, comp, &op);
-  assert(r == 0);
+  ceph_assert(r == 0);
   comp->release();
 }
 
 template <typename I>
 void CloneRequest<I>::handle_metadata_set(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
   if (r < 0) {
     lderr(m_cct) << "couldn't set metadata: " << cpp_strerror(r) << dendl;
     m_r_saved = r;
-    send_remove_child();
+    close_child();
   } else {
     get_mirror_mode();
   }
@@ -415,10 +472,10 @@ void CloneRequest<I>::handle_metadata_set(int r) {
 
 template <typename I>
 void CloneRequest<I>::get_mirror_mode() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+  ldout(m_cct, 15) << dendl;
 
   if (!m_imctx->test_features(RBD_FEATURE_JOURNALING)) {
-    send_close();
+    close_child();
     return;
   }
 
@@ -436,10 +493,10 @@ void CloneRequest<I>::get_mirror_mode() {
 
 template <typename I>
 void CloneRequest<I>::handle_get_mirror_mode(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
   if (r == 0) {
-    bufferlist::iterator it = m_out_bl.begin();
+    auto it = m_out_bl.cbegin();
     r = cls_client::mirror_mode_get_finish(&it, &m_mirror_mode);
   }
 
@@ -448,23 +505,24 @@ void CloneRequest<I>::handle_get_mirror_mode(int r) {
                  << dendl;
 
     m_r_saved = r;
-    send_remove_child();
+    close_child();
   } else {
     if (m_mirror_mode == cls::rbd::MIRROR_MODE_POOL ||
        !m_non_primary_global_image_id.empty()) {
-      send_enable_mirror();
+      enable_mirror();
     } else {
-      send_close();
+      close_child();
     }
   }
 }
 
 template <typename I>
-void CloneRequest<I>::send_enable_mirror() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+void CloneRequest<I>::enable_mirror() {
+  ldout(m_cct, 15) << dendl;
 
   using klass = CloneRequest<I>;
-  Context *ctx = create_context_callback<klass, &klass::handle_enable_mirror>(this);
+  Context *ctx = create_context_callback<
+    klass, &klass::handle_enable_mirror>(this);
 
   mirror::EnableRequest<I> *req = mirror::EnableRequest<I>::create(
     m_imctx->md_ctx, m_id, m_non_primary_global_image_id,
@@ -474,107 +532,108 @@ void CloneRequest<I>::send_enable_mirror() {
 
 template <typename I>
 void CloneRequest<I>::handle_enable_mirror(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
   if (r < 0) {
     lderr(m_cct) << "failed to enable mirroring: " << cpp_strerror(r)
                << dendl;
     m_r_saved = r;
-    send_remove_child();
-  } else {
-    send_close();
   }
+  close_child();
 }
 
 template <typename I>
-void CloneRequest<I>::send_close() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+void CloneRequest<I>::close_child() {
+  ldout(m_cct, 15) << dendl;
 
-  assert(m_imctx != nullptr);
+  ceph_assert(m_imctx != nullptr);
 
   using klass = CloneRequest<I>;
   Context *ctx = create_async_context_callback(
     *m_imctx, create_context_callback<
-      klass, &klass::handle_close>(this));
+      klass, &klass::handle_close_child>(this));
   m_imctx->state->close(ctx);
 }
 
 template <typename I>
-void CloneRequest<I>::handle_close(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+void CloneRequest<I>::handle_close_child(int r) {
+  ldout(m_cct, 15) << dendl;
 
   m_imctx->destroy();
   m_imctx = nullptr;
 
   if (r < 0) {
     lderr(m_cct) << "couldn't close image: " << cpp_strerror(r) << dendl;
-    return complete(r);
+    if (m_r_saved == 0) {
+      m_r_saved = r;
+    }
   }
 
-  if (m_r_saved == 0) {
-    complete(0);
-  } else {
-    send_remove();
+  if (m_r_saved < 0) {
+    remove_child();
+    return;
   }
+
+  close_parent();
 }
 
 template <typename I>
-void CloneRequest<I>::send_remove_child() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
-
-  librados::ObjectWriteOperation op;
-  cls_client::remove_child(&op, m_pspec, m_id);
+void CloneRequest<I>::remove_child() {
+  ldout(m_cct, 15) << dendl;
 
   using klass = CloneRequest<I>;
-  librados::AioCompletion *comp =
-    create_rados_callback<klass, &klass::handle_remove_child>(this);
-  int r = m_p_imctx->md_ctx.aio_operate(RBD_CHILDREN, comp, &op);
-  assert(r == 0);
-  comp->release();
+  Context *ctx = create_context_callback<
+    klass, &klass::handle_remove_child>(this);
+
+  auto req = librbd::image::RemoveRequest<I>::create(
+   m_ioctx, m_name, m_id, false, false, m_no_op, m_op_work_queue, ctx);
+  req->send();
 }
 
 template <typename I>
 void CloneRequest<I>::handle_remove_child(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
   if (r < 0) {
-     lderr(m_cct) << "Error removing failed clone from list of children: "
-                 << cpp_strerror(r) << dendl;
+    lderr(m_cct) << "Error removing failed clone: "
+                << cpp_strerror(r) << dendl;
   }
 
-  send_close();
+  close_parent();
 }
 
 template <typename I>
-void CloneRequest<I>::send_remove() {
-  ldout(m_cct, 20) << this << " " << __func__ << dendl;
-
-  using klass = CloneRequest<I>;
-  Context *ctx = create_context_callback<klass, &klass::handle_remove>(this);
+void CloneRequest<I>::close_parent() {
+  ldout(m_cct, 20) << dendl;
+  ceph_assert(m_parent_image_ctx != nullptr);
 
-  librbd::image::RemoveRequest<> *req = librbd::image::RemoveRequest<>::create(
-   m_ioctx, m_name, m_id, false, false, m_no_op, m_op_work_queue, ctx);
-  req->send();
+  Context *ctx = create_async_context_callback(
+    *m_parent_image_ctx, create_context_callback<
+      CloneRequest<I>, &CloneRequest<I>::handle_close_parent>(this));
+  m_parent_image_ctx->state->close(ctx);
 }
 
 template <typename I>
-void CloneRequest<I>::handle_remove(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+void CloneRequest<I>::handle_close_parent(int r) {
+  ldout(m_cct, 20) << "r=" << r << dendl;
+
+  m_parent_image_ctx->destroy();
+  m_parent_image_ctx = nullptr;
 
   if (r < 0) {
-    lderr(m_cct) << "Error removing failed clone: "
+    lderr(m_cct) << "failed to close parent image: "
                 << cpp_strerror(r) << dendl;
+    if (m_r_saved == 0) {
+      m_r_saved = r;
+    }
   }
-  complete(r);
+
+  complete(m_r_saved);
 }
 
 template <typename I>
 void CloneRequest<I>::complete(int r) {
-  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
-
-  if (r == 0) {
-    ldout(m_cct, 20) << "done." << dendl;
-  }
+  ldout(m_cct, 15) << "r=" << r << dendl;
 
   m_on_finish->complete(r);
   delete this;