// -*- 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 SetHeadRequest::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 void SetHeadRequest::send() { send_set_size(); } template void SetHeadRequest::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 void SetHeadRequest::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 void SetHeadRequest::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::create(*m_image_ctx, ctx); req->send(); } template void SetHeadRequest::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 void SetHeadRequest::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::create( *m_image_ctx, m_parent_spec, m_parent_overlap, false, ctx); req->send(); } template void SetHeadRequest::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 Context *SetHeadRequest::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 void SetHeadRequest::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;