// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "SnapshotCopyRequest.h" #include "SnapshotCreateRequest.h" #include "common/errno.h" #include "common/WorkQueue.h" #include "journal/Journaler.h" #include "librbd/ExclusiveLock.h" #include "librbd/Operations.h" #include "librbd/Utils.h" #include "librbd/journal/Types.h" #define dout_context g_ceph_context #define dout_subsys ceph_subsys_rbd_mirror #undef dout_prefix #define dout_prefix *_dout << "rbd::mirror::image_sync::SnapshotCopyRequest: " \ << this << " " << __func__ namespace rbd { namespace mirror { namespace image_sync { namespace { template const std::string &get_snapshot_name(I *image_ctx, librados::snap_t snap_id) { auto snap_it = std::find_if(image_ctx->snap_ids.begin(), image_ctx->snap_ids.end(), [snap_id]( const std::pair< std::pair, librados::snap_t> &pair) { return pair.second == snap_id; }); assert(snap_it != image_ctx->snap_ids.end()); return snap_it->first.second; } } // anonymous namespace using librbd::util::create_context_callback; using librbd::util::unique_lock_name; template SnapshotCopyRequest::SnapshotCopyRequest(I *local_image_ctx, I *remote_image_ctx, SnapMap *snap_map, Journaler *journaler, librbd::journal::MirrorPeerClientMeta *meta, ContextWQ *work_queue, Context *on_finish) : BaseRequest("rbd::mirror::image_sync::SnapshotCopyRequest", local_image_ctx->cct, on_finish), m_local_image_ctx(local_image_ctx), m_remote_image_ctx(remote_image_ctx), m_snap_map(snap_map), m_journaler(journaler), m_client_meta(meta), m_work_queue(work_queue), m_snap_seqs(meta->snap_seqs), m_lock(unique_lock_name("SnapshotCopyRequest::m_lock", this)) { m_snap_map->clear(); // snap ids ordered from oldest to newest m_remote_snap_ids.insert(remote_image_ctx->snaps.begin(), remote_image_ctx->snaps.end()); m_local_snap_ids.insert(local_image_ctx->snaps.begin(), local_image_ctx->snaps.end()); } template void SnapshotCopyRequest::send() { librbd::ParentSpec remote_parent_spec; int r = validate_parent(m_remote_image_ctx, &remote_parent_spec); if (r < 0) { derr << ": remote image parent spec mismatch" << dendl; error(r); return; } r = validate_parent(m_local_image_ctx, &m_local_parent_spec); if (r < 0) { derr << ": local image parent spec mismatch" << dendl; error(r); return; } send_snap_unprotect(); } template void SnapshotCopyRequest::cancel() { Mutex::Locker locker(m_lock); dout(20) << dendl; m_canceled = true; } template void SnapshotCopyRequest::send_snap_unprotect() { SnapIdSet::iterator snap_id_it = m_local_snap_ids.begin(); if (m_prev_snap_id != CEPH_NOSNAP) { snap_id_it = m_local_snap_ids.upper_bound(m_prev_snap_id); } for (; snap_id_it != m_local_snap_ids.end(); ++snap_id_it) { librados::snap_t local_snap_id = *snap_id_it; m_local_image_ctx->snap_lock.get_read(); bool local_unprotected; int r = m_local_image_ctx->is_snap_unprotected(local_snap_id, &local_unprotected); if (r < 0) { derr << ": failed to retrieve local snap unprotect status: " << cpp_strerror(r) << dendl; m_local_image_ctx->snap_lock.put_read(); finish(r); return; } m_local_image_ctx->snap_lock.put_read(); if (local_unprotected) { // snap is already unprotected -- check next snap continue; } // if local snapshot is protected and (1) it isn't in our mapping // table, or (2) the remote snapshot isn't protected, unprotect it auto snap_seq_it = std::find_if( m_snap_seqs.begin(), m_snap_seqs.end(), [local_snap_id](const SnapSeqs::value_type& pair) { return pair.second == local_snap_id; }); if (snap_seq_it != m_snap_seqs.end()) { m_remote_image_ctx->snap_lock.get_read(); bool remote_unprotected; r = m_remote_image_ctx->is_snap_unprotected(snap_seq_it->first, &remote_unprotected); if (r < 0) { derr << ": failed to retrieve remote snap unprotect status: " << cpp_strerror(r) << dendl; m_remote_image_ctx->snap_lock.put_read(); finish(r); return; } m_remote_image_ctx->snap_lock.put_read(); if (remote_unprotected) { // remote is unprotected -- unprotect local snap break; } } else { // remote snapshot doesn't exist -- unprotect local snap break; } } if (snap_id_it == m_local_snap_ids.end()) { // no local snapshots to unprotect m_prev_snap_id = CEPH_NOSNAP; send_snap_remove(); return; } m_prev_snap_id = *snap_id_it; m_snap_name = get_snapshot_name(m_local_image_ctx, m_prev_snap_id); dout(20) << ": " << "snap_name=" << m_snap_name << ", " << "snap_id=" << m_prev_snap_id << dendl; auto finish_op_ctx = start_local_op(); if (finish_op_ctx == nullptr) { derr << ": lost exclusive lock" << dendl; finish(-EROFS); return; } auto ctx = new FunctionContext([this, finish_op_ctx](int r) { handle_snap_unprotect(r); finish_op_ctx->complete(0); }); RWLock::RLocker owner_locker(m_local_image_ctx->owner_lock); m_local_image_ctx->operations->execute_snap_unprotect( cls::rbd::UserSnapshotNamespace(), m_snap_name.c_str(), ctx); } template void SnapshotCopyRequest::handle_snap_unprotect(int r) { dout(20) << ": r=" << r << dendl; if (r < 0) { derr << ": failed to unprotect snapshot '" << m_snap_name << "': " << cpp_strerror(r) << dendl; finish(r); return; } if (handle_cancellation()) { return; } send_snap_unprotect(); } template void SnapshotCopyRequest::send_snap_remove() { SnapIdSet::iterator snap_id_it = m_local_snap_ids.begin(); if (m_prev_snap_id != CEPH_NOSNAP) { snap_id_it = m_local_snap_ids.upper_bound(m_prev_snap_id); } for (; snap_id_it != m_local_snap_ids.end(); ++snap_id_it) { librados::snap_t local_snap_id = *snap_id_it; cls::rbd::SnapshotNamespace snap_namespace; m_local_image_ctx->snap_lock.get_read(); int r = m_local_image_ctx->get_snap_namespace(local_snap_id, &snap_namespace); m_local_image_ctx->snap_lock.put_read(); if (r < 0) { derr << ": failed to retrieve local snap namespace: " << m_snap_name << dendl; finish(r); return; } if (boost::get(&snap_namespace) == nullptr) { continue; } // if the local snapshot isn't in our mapping table, remove it auto snap_seq_it = std::find_if( m_snap_seqs.begin(), m_snap_seqs.end(), [local_snap_id](const SnapSeqs::value_type& pair) { return pair.second == local_snap_id; }); if (snap_seq_it == m_snap_seqs.end()) { break; } } if (snap_id_it == m_local_snap_ids.end()) { // no local snapshots to delete m_prev_snap_id = CEPH_NOSNAP; send_snap_create(); return; } m_prev_snap_id = *snap_id_it; m_snap_name = get_snapshot_name(m_local_image_ctx, m_prev_snap_id); dout(20) << ": " << "snap_name=" << m_snap_name << ", " << "snap_id=" << m_prev_snap_id << dendl; auto finish_op_ctx = start_local_op(); if (finish_op_ctx == nullptr) { derr << ": lost exclusive lock" << dendl; finish(-EROFS); return; } auto ctx = new FunctionContext([this, finish_op_ctx](int r) { handle_snap_remove(r); finish_op_ctx->complete(0); }); RWLock::RLocker owner_locker(m_local_image_ctx->owner_lock); m_local_image_ctx->operations->execute_snap_remove( cls::rbd::UserSnapshotNamespace(), m_snap_name.c_str(), ctx); } template void SnapshotCopyRequest::handle_snap_remove(int r) { dout(20) << ": r=" << r << dendl; if (r < 0) { derr << ": failed to remove snapshot '" << m_snap_name << "': " << cpp_strerror(r) << dendl; finish(r); return; } if (handle_cancellation()) { return; } send_snap_remove(); } template void SnapshotCopyRequest::send_snap_create() { SnapIdSet::iterator snap_id_it = m_remote_snap_ids.begin(); if (m_prev_snap_id != CEPH_NOSNAP) { snap_id_it = m_remote_snap_ids.upper_bound(m_prev_snap_id); } for (; snap_id_it != m_remote_snap_ids.end(); ++snap_id_it) { librados::snap_t remote_snap_id = *snap_id_it; cls::rbd::SnapshotNamespace snap_namespace; m_remote_image_ctx->snap_lock.get_read(); int r = m_remote_image_ctx->get_snap_namespace(remote_snap_id, &snap_namespace); m_remote_image_ctx->snap_lock.put_read(); if (r < 0) { derr << ": failed to retrieve remote snap namespace: " << m_snap_name << dendl; finish(r); return; } // if the remote snapshot isn't in our mapping table, create it if (m_snap_seqs.find(remote_snap_id) == m_snap_seqs.end() && boost::get(&snap_namespace) != nullptr) { break; } } if (snap_id_it == m_remote_snap_ids.end()) { // no remote snapshots to create m_prev_snap_id = CEPH_NOSNAP; send_snap_protect(); return; } m_prev_snap_id = *snap_id_it; m_snap_name = get_snapshot_name(m_remote_image_ctx, m_prev_snap_id); m_remote_image_ctx->snap_lock.get_read(); auto snap_info_it = m_remote_image_ctx->snap_info.find(m_prev_snap_id); if (snap_info_it == m_remote_image_ctx->snap_info.end()) { m_remote_image_ctx->snap_lock.put_read(); derr << ": failed to retrieve remote snap info: " << m_snap_name << dendl; finish(-ENOENT); return; } uint64_t size = snap_info_it->second.size; m_snap_namespace = snap_info_it->second.snap_namespace; librbd::ParentSpec parent_spec; uint64_t parent_overlap = 0; if (snap_info_it->second.parent.spec.pool_id != -1) { parent_spec = m_local_parent_spec; parent_overlap = snap_info_it->second.parent.overlap; } m_remote_image_ctx->snap_lock.put_read(); dout(20) << ": " << "snap_name=" << m_snap_name << ", " << "snap_id=" << m_prev_snap_id << ", " << "size=" << size << ", " << "parent_info=[" << "pool_id=" << parent_spec.pool_id << ", " << "image_id=" << parent_spec.image_id << ", " << "snap_id=" << parent_spec.snap_id << ", " << "overlap=" << parent_overlap << "]" << dendl; Context *finish_op_ctx = start_local_op(); if (finish_op_ctx == nullptr) { derr << ": lost exclusive lock" << dendl; finish(-EROFS); return; } auto ctx = new FunctionContext([this, finish_op_ctx](int r) { handle_snap_create(r); finish_op_ctx->complete(0); }); SnapshotCreateRequest *req = SnapshotCreateRequest::create( m_local_image_ctx, m_snap_name, m_snap_namespace, size, parent_spec, parent_overlap, ctx); req->send(); } template void SnapshotCopyRequest::handle_snap_create(int r) { dout(20) << ": r=" << r << dendl; if (r < 0) { derr << ": failed to create snapshot '" << m_snap_name << "': " << cpp_strerror(r) << dendl; finish(r); return; } if (handle_cancellation()) { return; } assert(m_prev_snap_id != CEPH_NOSNAP); auto snap_it = m_local_image_ctx->snap_ids.find({cls::rbd::UserSnapshotNamespace(), m_snap_name}); assert(snap_it != m_local_image_ctx->snap_ids.end()); librados::snap_t local_snap_id = snap_it->second; dout(20) << ": mapping remote snap id " << m_prev_snap_id << " to " << local_snap_id << dendl; m_snap_seqs[m_prev_snap_id] = local_snap_id; send_snap_create(); } template void SnapshotCopyRequest::send_snap_protect() { SnapIdSet::iterator snap_id_it = m_remote_snap_ids.begin(); if (m_prev_snap_id != CEPH_NOSNAP) { snap_id_it = m_remote_snap_ids.upper_bound(m_prev_snap_id); } for (; snap_id_it != m_remote_snap_ids.end(); ++snap_id_it) { librados::snap_t remote_snap_id = *snap_id_it; m_remote_image_ctx->snap_lock.get_read(); bool remote_protected; int r = m_remote_image_ctx->is_snap_protected(remote_snap_id, &remote_protected); if (r < 0) { derr << ": failed to retrieve remote snap protect status: " << cpp_strerror(r) << dendl; m_remote_image_ctx->snap_lock.put_read(); finish(r); return; } m_remote_image_ctx->snap_lock.put_read(); if (!remote_protected) { // snap is not protected -- check next snap continue; } // if local snapshot is not protected, protect it auto snap_seq_it = m_snap_seqs.find(remote_snap_id); assert(snap_seq_it != m_snap_seqs.end()); m_local_image_ctx->snap_lock.get_read(); bool local_protected; r = m_local_image_ctx->is_snap_protected(snap_seq_it->second, &local_protected); if (r < 0) { derr << ": failed to retrieve local snap protect status: " << cpp_strerror(r) << dendl; m_local_image_ctx->snap_lock.put_read(); finish(r); return; } m_local_image_ctx->snap_lock.put_read(); if (!local_protected) { break; } } if (snap_id_it == m_remote_snap_ids.end()) { // no local snapshots to protect m_prev_snap_id = CEPH_NOSNAP; send_update_client(); return; } m_prev_snap_id = *snap_id_it; m_snap_name = get_snapshot_name(m_remote_image_ctx, m_prev_snap_id); dout(20) << ": " << "snap_name=" << m_snap_name << ", " << "snap_id=" << m_prev_snap_id << dendl; auto finish_op_ctx = start_local_op(); if (finish_op_ctx == nullptr) { derr << ": lost exclusive lock" << dendl; finish(-EROFS); return; } auto ctx = new FunctionContext([this, finish_op_ctx](int r) { handle_snap_protect(r); finish_op_ctx->complete(0); }); RWLock::RLocker owner_locker(m_local_image_ctx->owner_lock); m_local_image_ctx->operations->execute_snap_protect( cls::rbd::UserSnapshotNamespace(), m_snap_name.c_str(), ctx); } template void SnapshotCopyRequest::handle_snap_protect(int r) { dout(20) << ": r=" << r << dendl; if (r < 0) { derr << ": failed to protect snapshot '" << m_snap_name << "': " << cpp_strerror(r) << dendl; finish(r); return; } if (handle_cancellation()) { return; } send_snap_protect(); } template void SnapshotCopyRequest::send_update_client() { dout(20) << dendl; compute_snap_map(); librbd::journal::MirrorPeerClientMeta client_meta(*m_client_meta); client_meta.snap_seqs = m_snap_seqs; librbd::journal::ClientData client_data(client_meta); bufferlist data_bl; ::encode(client_data, data_bl); Context *ctx = create_context_callback< SnapshotCopyRequest, &SnapshotCopyRequest::handle_update_client>( this); m_journaler->update_client(data_bl, ctx); } template void SnapshotCopyRequest::handle_update_client(int r) { dout(20) << ": r=" << r << dendl; if (r < 0) { derr << ": failed to update client data: " << cpp_strerror(r) << dendl; finish(r); return; } if (handle_cancellation()) { return; } m_client_meta->snap_seqs = m_snap_seqs; finish(0); } template bool SnapshotCopyRequest::handle_cancellation() { { Mutex::Locker locker(m_lock); if (!m_canceled) { return false; } } dout(10) << ": snapshot copy canceled" << dendl; finish(-ECANCELED); return true; } template void SnapshotCopyRequest::error(int r) { dout(20) << ": r=" << r << dendl; m_work_queue->queue(new FunctionContext([this, r](int r1) { finish(r); })); } template void SnapshotCopyRequest::compute_snap_map() { SnapIds local_snap_ids; for (auto &pair : m_snap_seqs) { local_snap_ids.reserve(1 + local_snap_ids.size()); local_snap_ids.insert(local_snap_ids.begin(), pair.second); m_snap_map->insert(std::make_pair(pair.first, local_snap_ids)); } } template int SnapshotCopyRequest::validate_parent(I *image_ctx, librbd::ParentSpec *spec) { RWLock::RLocker owner_locker(image_ctx->owner_lock); RWLock::RLocker snap_locker(image_ctx->snap_lock); // ensure remote image's parent specs are still consistent *spec = image_ctx->parent_md.spec; for (auto &snap_info_pair : image_ctx->snap_info) { auto &parent_spec = snap_info_pair.second.parent.spec; if (parent_spec.pool_id == -1) { continue; } else if (spec->pool_id == -1) { *spec = parent_spec; continue; } if (*spec != parent_spec) { return -EINVAL; } } return 0; } template Context *SnapshotCopyRequest::start_local_op() { RWLock::RLocker owner_locker(m_local_image_ctx->owner_lock); if (m_local_image_ctx->exclusive_lock == nullptr) { return nullptr; } return m_local_image_ctx->exclusive_lock->start_op(); } } // namespace image_sync } // namespace mirror } // namespace rbd template class rbd::mirror::image_sync::SnapshotCopyRequest;