// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "librbd/image/SetSnapRequest.h" #include "common/dout.h" #include "common/errno.h" #include "librbd/ExclusiveLock.h" #include "librbd/ImageCtx.h" #include "librbd/ObjectMap.h" #include "librbd/Utils.h" #include "librbd/image/RefreshParentRequest.h" #include "librbd/io/ImageDispatcherInterface.h" #define dout_subsys ceph_subsys_rbd #undef dout_prefix #define dout_prefix *_dout << "librbd::image::SetSnapRequest: " namespace librbd { namespace image { using util::create_context_callback; template SetSnapRequest::SetSnapRequest(I &image_ctx, uint64_t snap_id, Context *on_finish) : m_image_ctx(image_ctx), m_snap_id(snap_id), m_on_finish(on_finish), m_exclusive_lock(nullptr), m_object_map(nullptr), m_refresh_parent(nullptr), m_writes_blocked(false) { } template SetSnapRequest::~SetSnapRequest() { ceph_assert(!m_writes_blocked); delete m_refresh_parent; if (m_object_map) { m_object_map->put(); } if (m_exclusive_lock) { m_exclusive_lock->put(); } } template void SetSnapRequest::send() { if (m_snap_id == CEPH_NOSNAP) { send_init_exclusive_lock(); } else { send_block_writes(); } } template void SetSnapRequest::send_init_exclusive_lock() { { std::shared_lock image_locker{m_image_ctx.image_lock}; if (m_image_ctx.exclusive_lock != nullptr) { ceph_assert(m_image_ctx.snap_id == CEPH_NOSNAP); send_complete(); return; } } if (m_image_ctx.read_only || !m_image_ctx.test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) { int r = 0; if (send_refresh_parent(&r) != nullptr) { send_complete(); } return; } CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << dendl; m_exclusive_lock = ExclusiveLock::create(m_image_ctx); using klass = SetSnapRequest; Context *ctx = create_context_callback< klass, &klass::handle_init_exclusive_lock>(this); std::shared_lock owner_locker{m_image_ctx.owner_lock}; m_exclusive_lock->init(m_image_ctx.features, ctx); } template Context *SetSnapRequest::handle_init_exclusive_lock(int *result) { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to initialize exclusive lock: " << cpp_strerror(*result) << dendl; finalize(); return m_on_finish; } return send_refresh_parent(result); } template void SetSnapRequest::send_block_writes() { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << dendl; m_writes_blocked = true; using klass = SetSnapRequest; Context *ctx = create_context_callback< klass, &klass::handle_block_writes>(this); std::shared_lock owner_locker{m_image_ctx.owner_lock}; m_image_ctx.io_image_dispatcher->block_writes(ctx); } template Context *SetSnapRequest::handle_block_writes(int *result) { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to block writes: " << cpp_strerror(*result) << dendl; finalize(); return m_on_finish; } { std::shared_lock image_locker{m_image_ctx.image_lock}; auto it = m_image_ctx.snap_info.find(m_snap_id); if (it == m_image_ctx.snap_info.end()) { ldout(cct, 5) << "failed to locate snapshot '" << m_snap_id << "'" << dendl; *result = -ENOENT; finalize(); return m_on_finish; } } return send_shut_down_exclusive_lock(result); } template Context *SetSnapRequest::send_shut_down_exclusive_lock(int *result) { { std::shared_lock image_locker{m_image_ctx.image_lock}; m_exclusive_lock = m_image_ctx.exclusive_lock; } if (m_exclusive_lock == nullptr) { return send_refresh_parent(result); } CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << dendl; using klass = SetSnapRequest; Context *ctx = create_context_callback< klass, &klass::handle_shut_down_exclusive_lock>(this); m_exclusive_lock->shut_down(ctx); return nullptr; } template Context *SetSnapRequest::handle_shut_down_exclusive_lock(int *result) { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to shut down exclusive lock: " << cpp_strerror(*result) << dendl; finalize(); return m_on_finish; } return send_refresh_parent(result); } template Context *SetSnapRequest::send_refresh_parent(int *result) { CephContext *cct = m_image_ctx.cct; ParentImageInfo parent_md; bool refresh_parent; { std::shared_lock image_locker{m_image_ctx.image_lock}; const auto parent_info = m_image_ctx.get_parent_info(m_snap_id); if (parent_info == nullptr) { *result = -ENOENT; lderr(cct) << "failed to retrieve snapshot parent info" << dendl; finalize(); return m_on_finish; } parent_md = *parent_info; refresh_parent = RefreshParentRequest::is_refresh_required( m_image_ctx, parent_md, m_image_ctx.migration_info); } if (!refresh_parent) { if (m_snap_id == CEPH_NOSNAP) { // object map is loaded when exclusive lock is acquired *result = apply(); finalize(); return m_on_finish; } else { // load snapshot object map return send_open_object_map(result); } } ldout(cct, 10) << __func__ << dendl; using klass = SetSnapRequest; Context *ctx = create_context_callback< klass, &klass::handle_refresh_parent>(this); m_refresh_parent = RefreshParentRequest::create(m_image_ctx, parent_md, m_image_ctx.migration_info, ctx); m_refresh_parent->send(); return nullptr; } template Context *SetSnapRequest::handle_refresh_parent(int *result) { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to refresh snapshot parent: " << cpp_strerror(*result) << dendl; finalize(); return m_on_finish; } if (m_snap_id == CEPH_NOSNAP) { // object map is loaded when exclusive lock is acquired *result = apply(); if (*result < 0) { finalize(); return m_on_finish; } return send_finalize_refresh_parent(result); } else { // load snapshot object map return send_open_object_map(result); } } template Context *SetSnapRequest::send_open_object_map(int *result) { if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP)) { *result = apply(); if (*result < 0) { finalize(); return m_on_finish; } return send_finalize_refresh_parent(result); } CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << dendl; using klass = SetSnapRequest; Context *ctx = create_context_callback< klass, &klass::handle_open_object_map>(this); m_object_map = ObjectMap::create(m_image_ctx, m_snap_id); m_object_map->open(ctx); return nullptr; } template Context *SetSnapRequest::handle_open_object_map(int *result) { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to open object map: " << cpp_strerror(*result) << dendl; m_object_map->put(); m_object_map = nullptr; } *result = apply(); if (*result < 0) { finalize(); return m_on_finish; } return send_finalize_refresh_parent(result); } template Context *SetSnapRequest::send_finalize_refresh_parent(int *result) { if (m_refresh_parent == nullptr) { finalize(); return m_on_finish; } CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << this << " " << __func__ << dendl; using klass = SetSnapRequest; Context *ctx = create_context_callback< klass, &klass::handle_finalize_refresh_parent>(this); m_refresh_parent->finalize(ctx); return nullptr; } template Context *SetSnapRequest::handle_finalize_refresh_parent(int *result) { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to close parent image: " << cpp_strerror(*result) << dendl; } finalize(); return m_on_finish; } template int SetSnapRequest::apply() { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << dendl; std::scoped_lock locker{m_image_ctx.owner_lock, m_image_ctx.image_lock}; if (m_snap_id != CEPH_NOSNAP) { ceph_assert(m_image_ctx.exclusive_lock == nullptr); int r = m_image_ctx.snap_set(m_snap_id); if (r < 0) { return r; } } else { std::swap(m_image_ctx.exclusive_lock, m_exclusive_lock); m_image_ctx.snap_unset(); } if (m_refresh_parent != nullptr) { m_refresh_parent->apply(); } std::swap(m_object_map, m_image_ctx.object_map); return 0; } template void SetSnapRequest::finalize() { if (m_writes_blocked) { m_image_ctx.io_image_dispatcher->unblock_writes(); m_writes_blocked = false; } } template void SetSnapRequest::send_complete() { finalize(); m_on_finish->complete(0); delete this; } } // namespace image } // namespace librbd template class librbd::image::SetSnapRequest;