X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Ftools%2Frbd_mirror%2Fimage_sync%2FSnapshotCopyRequest.cc;fp=src%2Fceph%2Fsrc%2Ftools%2Frbd_mirror%2Fimage_sync%2FSnapshotCopyRequest.cc;h=f5f0701d1134c1cb0ed346f93eacabaade27e006;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc b/src/ceph/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc new file mode 100644 index 0000000..f5f0701 --- /dev/null +++ b/src/ceph/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc @@ -0,0 +1,614 @@ +// -*- 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;