// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "librbd/operation/SnapshotRollbackRequest.h" #include "include/rados/librados.hpp" #include "common/dout.h" #include "common/errno.h" #include "librbd/AsyncObjectThrottle.h" #include "librbd/ImageCtx.h" #include "librbd/ObjectMap.h" #include "librbd/Utils.h" #include "librbd/io/ImageDispatcherInterface.h" #include "librbd/io/ObjectDispatcherInterface.h" #include "librbd/operation/ResizeRequest.h" #include "osdc/Striper.h" #include #include #define dout_subsys ceph_subsys_rbd #undef dout_prefix #define dout_prefix *_dout << "librbd::SnapshotRollbackRequest: " namespace librbd { namespace operation { using util::create_context_callback; using util::create_rados_callback; namespace { template class C_RollbackObject : public C_AsyncObjectThrottle { public: C_RollbackObject(AsyncObjectThrottle &throttle, I *image_ctx, uint64_t snap_id, uint64_t object_num, uint64_t head_num_objects, decltype(I::object_map) snap_object_map) : C_AsyncObjectThrottle(throttle, *image_ctx), m_snap_id(snap_id), m_object_num(object_num), m_head_num_objects(head_num_objects), m_snap_object_map(snap_object_map) { } int send() override { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 20) << "C_RollbackObject: " << __func__ << ": object_num=" << m_object_num << dendl; { std::shared_lock image_locker{image_ctx.image_lock}; if (m_object_num < m_head_num_objects && m_snap_object_map != nullptr && !image_ctx.object_map->object_may_exist(m_object_num) && !m_snap_object_map->object_may_exist(m_object_num)) { return 1; } } std::string oid = image_ctx.get_object_name(m_object_num); librados::ObjectWriteOperation op; op.selfmanaged_snap_rollback(m_snap_id); librados::AioCompletion *rados_completion = util::create_rados_callback(this); image_ctx.data_ctx.aio_operate(oid, rados_completion, &op); rados_completion->release(); return 0; } private: uint64_t m_snap_id; uint64_t m_object_num; uint64_t m_head_num_objects; decltype(I::object_map) m_snap_object_map; }; } // anonymous namespace template SnapshotRollbackRequest::SnapshotRollbackRequest(I &image_ctx, Context *on_finish, const cls::rbd::SnapshotNamespace &snap_namespace, const std::string &snap_name, uint64_t snap_id, uint64_t snap_size, ProgressContext &prog_ctx) : Request(image_ctx, on_finish), m_snap_namespace(snap_namespace), m_snap_name(snap_name), m_snap_id(snap_id), m_snap_size(snap_size), m_prog_ctx(prog_ctx), m_object_map(nullptr), m_snap_object_map(nullptr) { } template SnapshotRollbackRequest::~SnapshotRollbackRequest() { I &image_ctx = this->m_image_ctx; if (m_blocking_writes) { image_ctx.io_image_dispatcher->unblock_writes(); } if (m_object_map) { m_object_map->put(); m_object_map = nullptr; } if (m_snap_object_map) { m_snap_object_map->put(); m_snap_object_map = nullptr; } } template void SnapshotRollbackRequest::send_op() { send_block_writes(); } template void SnapshotRollbackRequest::send_block_writes() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << dendl; m_blocking_writes = true; image_ctx.io_image_dispatcher->block_writes(create_context_callback< SnapshotRollbackRequest, &SnapshotRollbackRequest::handle_block_writes>(this)); } template Context *SnapshotRollbackRequest::handle_block_writes(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to block writes: " << cpp_strerror(*result) << dendl; return this->create_context_finisher(*result); } send_resize_image(); return nullptr; } template void SnapshotRollbackRequest::send_resize_image() { I &image_ctx = this->m_image_ctx; uint64_t current_size; { std::shared_lock owner_locker{image_ctx.owner_lock}; std::shared_lock image_locker{image_ctx.image_lock}; current_size = image_ctx.get_image_size(CEPH_NOSNAP); } m_head_num_objects = Striper::get_num_objects(image_ctx.layout, current_size); if (current_size == m_snap_size) { send_get_snap_object_map(); return; } CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << dendl; std::shared_lock owner_locker{image_ctx.owner_lock}; Context *ctx = create_context_callback< SnapshotRollbackRequest, &SnapshotRollbackRequest::handle_resize_image>(this); ResizeRequest *req = ResizeRequest::create(image_ctx, ctx, m_snap_size, true, m_no_op_prog_ctx, 0, true); req->send(); } template Context *SnapshotRollbackRequest::handle_resize_image(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to resize image for rollback: " << cpp_strerror(*result) << dendl; return this->create_context_finisher(*result); } send_get_snap_object_map(); return nullptr; } template void SnapshotRollbackRequest::send_get_snap_object_map() { I &image_ctx = this->m_image_ctx; uint64_t flags = 0; bool object_map_enabled; CephContext *cct = image_ctx.cct; { std::shared_lock owner_locker{image_ctx.owner_lock}; std::shared_lock image_locker{image_ctx.image_lock}; object_map_enabled = (image_ctx.object_map != nullptr); int r = image_ctx.get_flags(m_snap_id, &flags); if (r < 0) { object_map_enabled = false; } } if (object_map_enabled && (flags & RBD_FLAG_OBJECT_MAP_INVALID) != 0) { lderr(cct) << "warning: object-map is invalid for snapshot" << dendl; object_map_enabled = false; } if (!object_map_enabled) { send_rollback_object_map(); return; } ldout(cct, 5) << this << " " << __func__ << dendl; m_snap_object_map = image_ctx.create_object_map(m_snap_id); Context *ctx = create_context_callback< SnapshotRollbackRequest, &SnapshotRollbackRequest::handle_get_snap_object_map>(this); m_snap_object_map->open(ctx); return; } template Context *SnapshotRollbackRequest::handle_get_snap_object_map(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << this << " " << __func__ << ": failed to open object map: " << cpp_strerror(*result) << dendl; m_snap_object_map->put(); m_snap_object_map = nullptr; } send_rollback_object_map(); return nullptr; } template void SnapshotRollbackRequest::send_rollback_object_map() { I &image_ctx = this->m_image_ctx; { std::shared_lock owner_locker{image_ctx.owner_lock}; std::shared_lock image_locker{image_ctx.image_lock}; if (image_ctx.object_map != nullptr) { CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << dendl; Context *ctx = create_context_callback< SnapshotRollbackRequest, &SnapshotRollbackRequest::handle_rollback_object_map>(this); image_ctx.object_map->rollback(m_snap_id, ctx); return; } } send_rollback_objects(); } template Context *SnapshotRollbackRequest::handle_rollback_object_map(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << this << " " << __func__ << ": failed to roll back object " << "map: " << cpp_strerror(*result) << dendl; ceph_assert(m_object_map == nullptr); apply(); return this->create_context_finisher(*result); } send_rollback_objects(); return nullptr; } template void SnapshotRollbackRequest::send_rollback_objects() { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << dendl; std::shared_lock owner_locker{image_ctx.owner_lock}; uint64_t num_objects; { std::shared_lock image_locker{image_ctx.image_lock}; num_objects = Striper::get_num_objects(image_ctx.layout, image_ctx.get_current_size()); } Context *ctx = create_context_callback< SnapshotRollbackRequest, &SnapshotRollbackRequest::handle_rollback_objects>(this); typename AsyncObjectThrottle::ContextFactory context_factory( boost::lambda::bind(boost::lambda::new_ptr >(), boost::lambda::_1, &image_ctx, m_snap_id, boost::lambda::_2, m_head_num_objects, m_snap_object_map)); AsyncObjectThrottle *throttle = new AsyncObjectThrottle( this, image_ctx, context_factory, ctx, &m_prog_ctx, 0, num_objects); throttle->start_ops( image_ctx.config.template get_val("rbd_concurrent_management_ops")); } template Context *SnapshotRollbackRequest::handle_rollback_objects(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl; if (*result == -ERESTART) { ldout(cct, 5) << "snapshot rollback operation interrupted" << dendl; return this->create_context_finisher(*result); } else if (*result < 0) { lderr(cct) << "failed to rollback objects: " << cpp_strerror(*result) << dendl; return this->create_context_finisher(*result); } return send_refresh_object_map(); } template Context *SnapshotRollbackRequest::send_refresh_object_map() { I &image_ctx = this->m_image_ctx; bool object_map_enabled; { std::shared_lock owner_locker{image_ctx.owner_lock}; std::shared_lock image_locker{image_ctx.image_lock}; object_map_enabled = (image_ctx.object_map != nullptr); } if (!object_map_enabled) { return send_invalidate_cache(); } CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << dendl; m_object_map = image_ctx.create_object_map(CEPH_NOSNAP); Context *ctx = create_context_callback< SnapshotRollbackRequest, &SnapshotRollbackRequest::handle_refresh_object_map>(this); m_object_map->open(ctx); return nullptr; } template Context *SnapshotRollbackRequest::handle_refresh_object_map(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << this << " " << __func__ << ": failed to open object map: " << cpp_strerror(*result) << dendl; m_object_map->put(); m_object_map = nullptr; apply(); return this->create_context_finisher(*result); } return send_invalidate_cache(); } template Context *SnapshotRollbackRequest::send_invalidate_cache() { I &image_ctx = this->m_image_ctx; apply(); CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << dendl; if(m_object_map != nullptr) { Context *ctx = create_context_callback< SnapshotRollbackRequest, &SnapshotRollbackRequest::handle_invalidate_cache>(this, m_object_map); image_ctx.io_image_dispatcher->invalidate_cache(ctx); } else { Context *ctx = create_context_callback< SnapshotRollbackRequest, &SnapshotRollbackRequest::handle_invalidate_cache>(this); image_ctx.io_image_dispatcher->invalidate_cache(ctx); } return nullptr; } template Context *SnapshotRollbackRequest::handle_invalidate_cache(int *result) { I &image_ctx = this->m_image_ctx; CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to invalidate cache: " << cpp_strerror(*result) << dendl; } return this->create_context_finisher(*result); } template void SnapshotRollbackRequest::apply() { I &image_ctx = this->m_image_ctx; std::shared_lock owner_locker{image_ctx.owner_lock}; std::unique_lock image_locker{image_ctx.image_lock}; if (image_ctx.object_map != nullptr) { std::swap(m_object_map, image_ctx.object_map); } } } // namespace operation } // namespace librbd template class librbd::operation::SnapshotRollbackRequest;