diff options
Diffstat (limited to 'src/librbd/deep_copy/SetHeadRequest.cc')
-rw-r--r-- | src/librbd/deep_copy/SetHeadRequest.cc | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/src/librbd/deep_copy/SetHeadRequest.cc b/src/librbd/deep_copy/SetHeadRequest.cc new file mode 100644 index 000000000..1e056b958 --- /dev/null +++ b/src/librbd/deep_copy/SetHeadRequest.cc @@ -0,0 +1,223 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "SetHeadRequest.h" +#include "common/errno.h" +#include "cls/rbd/cls_rbd_client.h" +#include "cls/rbd/cls_rbd_types.h" +#include "librbd/ExclusiveLock.h" +#include "librbd/Utils.h" +#include "librbd/image/AttachParentRequest.h" +#include "librbd/image/DetachParentRequest.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::deep_copy::SetHeadRequest: " \ + << this << " " << __func__ << ": " + +namespace librbd { +namespace deep_copy { + +using librbd::util::create_context_callback; +using librbd::util::create_rados_callback; + +template <typename I> +SetHeadRequest<I>::SetHeadRequest(I *image_ctx, uint64_t size, + const cls::rbd::ParentImageSpec &spec, + uint64_t parent_overlap, + Context *on_finish) + : m_image_ctx(image_ctx), m_size(size), m_parent_spec(spec), + m_parent_overlap(parent_overlap), m_on_finish(on_finish), + m_cct(image_ctx->cct) { + ceph_assert(m_parent_overlap <= m_size); +} + +template <typename I> +void SetHeadRequest<I>::send() { + send_set_size(); +} + +template <typename I> +void SetHeadRequest<I>::send_set_size() { + m_image_ctx->image_lock.lock_shared(); + if (m_image_ctx->size == m_size) { + m_image_ctx->image_lock.unlock_shared(); + send_detach_parent(); + return; + } + m_image_ctx->image_lock.unlock_shared(); + + ldout(m_cct, 20) << dendl; + + // Change the image size on disk so that the snapshot picks up + // the expected size. We can do this because the last snapshot + // we process is the sync snapshot which was created to match the + // image size. We also don't need to worry about trimming because + // we track the highest possible object number within the sync record + librados::ObjectWriteOperation op; + librbd::cls_client::set_size(&op, m_size); + + int r; + auto finish_op_ctx = start_lock_op(&r); + if (finish_op_ctx == nullptr) { + lderr(m_cct) << "lost exclusive lock" << dendl; + finish(r); + return; + } + + auto ctx = new LambdaContext([this, finish_op_ctx](int r) { + handle_set_size(r); + finish_op_ctx->complete(0); + }); + librados::AioCompletion *comp = create_rados_callback(ctx); + r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op); + ceph_assert(r == 0); + comp->release(); +} + +template <typename I> +void SetHeadRequest<I>::handle_set_size(int r) { + ldout(m_cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(m_cct) << "failed to update image size: " << cpp_strerror(r) << dendl; + finish(r); + return; + } + + { + // adjust in-memory image size now that it's updated on disk + std::unique_lock image_locker{m_image_ctx->image_lock}; + if (m_image_ctx->size > m_size) { + if (m_image_ctx->parent_md.spec.pool_id != -1 && + m_image_ctx->parent_md.overlap > m_size) { + m_image_ctx->parent_md.overlap = m_size; + } + } + m_image_ctx->size = m_size; + } + + send_detach_parent(); +} + +template <typename I> +void SetHeadRequest<I>::send_detach_parent() { + m_image_ctx->image_lock.lock_shared(); + if (m_image_ctx->parent_md.spec.pool_id == -1 || + (m_image_ctx->parent_md.spec == m_parent_spec && + m_image_ctx->parent_md.overlap == m_parent_overlap)) { + m_image_ctx->image_lock.unlock_shared(); + send_attach_parent(); + return; + } + m_image_ctx->image_lock.unlock_shared(); + + ldout(m_cct, 20) << dendl; + + int r; + auto finish_op_ctx = start_lock_op(&r); + if (finish_op_ctx == nullptr) { + lderr(m_cct) << "lost exclusive lock" << dendl; + finish(r); + return; + } + + auto ctx = new LambdaContext([this, finish_op_ctx](int r) { + handle_detach_parent(r); + finish_op_ctx->complete(0); + }); + auto req = image::DetachParentRequest<I>::create(*m_image_ctx, ctx); + req->send(); +} + +template <typename I> +void SetHeadRequest<I>::handle_detach_parent(int r) { + ldout(m_cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(m_cct) << "failed to remove parent: " << cpp_strerror(r) << dendl; + finish(r); + return; + } + + { + // adjust in-memory parent now that it's updated on disk + std::unique_lock image_locker{m_image_ctx->image_lock}; + m_image_ctx->parent_md.spec = {}; + m_image_ctx->parent_md.overlap = 0; + } + + send_attach_parent(); +} + +template <typename I> +void SetHeadRequest<I>::send_attach_parent() { + m_image_ctx->image_lock.lock_shared(); + if (m_image_ctx->parent_md.spec == m_parent_spec && + m_image_ctx->parent_md.overlap == m_parent_overlap) { + m_image_ctx->image_lock.unlock_shared(); + finish(0); + return; + } + m_image_ctx->image_lock.unlock_shared(); + + ldout(m_cct, 20) << dendl; + + int r; + auto finish_op_ctx = start_lock_op(&r); + if (finish_op_ctx == nullptr) { + lderr(m_cct) << "lost exclusive lock" << dendl; + finish(r); + return; + } + + auto ctx = new LambdaContext([this, finish_op_ctx](int r) { + handle_attach_parent(r); + finish_op_ctx->complete(0); + }); + auto req = image::AttachParentRequest<I>::create( + *m_image_ctx, m_parent_spec, m_parent_overlap, false, ctx); + req->send(); +} + +template <typename I> +void SetHeadRequest<I>::handle_attach_parent(int r) { + ldout(m_cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(m_cct) << "failed to attach parent: " << cpp_strerror(r) << dendl; + finish(r); + return; + } + + { + // adjust in-memory parent now that it's updated on disk + std::unique_lock image_locker{m_image_ctx->image_lock}; + m_image_ctx->parent_md.spec = m_parent_spec; + m_image_ctx->parent_md.overlap = m_parent_overlap; + } + + finish(0); +} + +template <typename I> +Context *SetHeadRequest<I>::start_lock_op(int* r) { + std::shared_lock owner_locker{m_image_ctx->owner_lock}; + if (m_image_ctx->exclusive_lock == nullptr) { + return new LambdaContext([](int r) {}); + } + return m_image_ctx->exclusive_lock->start_op(r); +} + +template <typename I> +void SetHeadRequest<I>::finish(int r) { + ldout(m_cct, 20) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace deep_copy +} // namespace librbd + +template class librbd::deep_copy::SetHeadRequest<librbd::ImageCtx>; |