From: Jason Dillaman Date: Thu, 30 Jul 2015 20:25:54 +0000 (-0400) Subject: librbd: initial conversion of snapshot ops to async versions X-Git-Tag: v10.0.2~193^2~28 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=4944f20c60a01cef0ce27fb786ccf5281dade8c2;p=ceph.git librbd: initial conversion of snapshot ops to async versions Object map updates are not currently integrated until those methods are converted to async state machines. Signed-off-by: Jason Dillaman --- diff --git a/src/librbd/ImageWatcher.cc b/src/librbd/ImageWatcher.cc index 014ce940721..a21b6c204d5 100644 --- a/src/librbd/ImageWatcher.cc +++ b/src/librbd/ImageWatcher.cc @@ -1036,8 +1036,12 @@ void ImageWatcher::handle_payload(const SnapCreatePayload &payload, if (m_lock_owner_state == LOCK_OWNER_STATE_LOCKED) { ldout(m_image_ctx.cct, 10) << this << " remote snap_create request: " << payload.snap_name << dendl; - int r = librbd::snap_create_helper(&m_image_ctx, NULL, + C_SaferCond cond_ctx; + int r = librbd::snap_create_helper(&m_image_ctx, &cond_ctx, payload.snap_name.c_str()); + if (r == 0) { + r = cond_ctx.wait(); + } ::encode(ResponseMessage(r), *out); } @@ -1050,9 +1054,13 @@ void ImageWatcher::handle_payload(const SnapRenamePayload &payload, ldout(m_image_ctx.cct, 10) << this << " remote snap_rename request: " << payload.src_snap_id << " to " << payload.dst_snap_name << dendl; - int r = librbd::snap_rename_helper(&m_image_ctx, NULL, + C_SaferCond cond_ctx; + int r = librbd::snap_rename_helper(&m_image_ctx, &cond_ctx, payload.src_snap_id, payload.dst_snap_name.c_str()); + if (r == 0) { + r = cond_ctx.wait(); + } ::encode(ResponseMessage(r), *out); } @@ -1063,8 +1071,12 @@ void ImageWatcher::handle_payload(const SnapRemovePayload &payload, if (m_lock_owner_state == LOCK_OWNER_STATE_LOCKED) { ldout(m_image_ctx.cct, 10) << this << " remote snap_remove request: " << payload.snap_name << dendl; - int r = librbd::snap_remove_helper(&m_image_ctx, NULL, + C_SaferCond cond_ctx; + int r = librbd::snap_remove_helper(&m_image_ctx, &cond_ctx, payload.snap_name.c_str()); + if (r == 0) { + r = cond_ctx.wait(); + } ::encode(ResponseMessage(r), *out); } diff --git a/src/librbd/Makefile.am b/src/librbd/Makefile.am index 90776dac70b..246e94c11a4 100644 --- a/src/librbd/Makefile.am +++ b/src/librbd/Makefile.am @@ -29,6 +29,12 @@ librbd_internal_la_SOURCES = \ librbd/operation/RebuildObjectMapRequest.cc \ librbd/operation/Request.cc \ librbd/operation/ResizeRequest.cc \ + librbd/operation/SnapshotCreateRequest.cc \ + librbd/operation/SnapshotProtectRequest.cc \ + librbd/operation/SnapshotRemoveRequest.cc \ + librbd/operation/SnapshotRenameRequest.cc \ + librbd/operation/SnapshotRollbackRequest.cc \ + librbd/operation/SnapshotUnprotectRequest.cc \ librbd/operation/TrimRequest.cc noinst_LTLIBRARIES += librbd_internal.la @@ -81,6 +87,12 @@ noinst_HEADERS += \ librbd/operation/RebuildObjectMapRequest.h \ librbd/operation/Request.h \ librbd/operation/ResizeRequest.h \ + librbd/operation/SnapshotCreateRequest.h \ + librbd/operation/SnapshotProtectRequest.h \ + librbd/operation/SnapshotRemoveRequest.h \ + librbd/operation/SnapshotRenameRequest.h \ + librbd/operation/SnapshotRollbackRequest.h \ + librbd/operation/SnapshotUnprotectRequest.h \ librbd/operation/TrimRequest.h endif # WITH_RBD diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc index 2cd85bdc36f..e296068d001 100644 --- a/src/librbd/internal.cc +++ b/src/librbd/internal.cc @@ -5,6 +5,7 @@ #include #include +#include "include/types.h" #include "common/ceph_context.h" #include "common/dout.h" #include "common/errno.h" @@ -32,6 +33,12 @@ #include "librbd/operation/FlattenRequest.h" #include "librbd/operation/RebuildObjectMapRequest.h" #include "librbd/operation/ResizeRequest.h" +#include "librbd/operation/SnapshotCreateRequest.h" +#include "librbd/operation/SnapshotProtectRequest.h" +#include "librbd/operation/SnapshotRemoveRequest.h" +#include "librbd/operation/SnapshotRenameRequest.h" +#include "librbd/operation/SnapshotRollbackRequest.h" +#include "librbd/operation/SnapshotUnprotectRequest.h" #include "librbd/operation/TrimRequest.h" #include "include/util.h" @@ -562,54 +569,6 @@ int invoke_async_request(ImageCtx *ictx, const std::string& request_type, return (*opts_)->empty(); } - void rollback_object(ImageCtx *ictx, uint64_t snap_id, const string& oid, - SimpleThrottle& throttle) - { - Context *req_comp = new C_SimpleThrottle(&throttle); - librados::AioCompletion *rados_completion = - librados::Rados::aio_create_completion(req_comp, NULL, rados_ctx_cb); - librados::ObjectWriteOperation op; - op.selfmanaged_snap_rollback(snap_id); - ictx->data_ctx.aio_operate(oid, rados_completion, &op); - ldout(ictx->cct, 10) << "scheduling selfmanaged_snap_rollback on " - << oid << " to " << snap_id << dendl; - rados_completion->release(); - } - - int rollback_image(ImageCtx *ictx, uint64_t snap_id, - ProgressContext& prog_ctx) - { - uint64_t bsize = ictx->get_object_size(); - uint64_t numseg; - { - RWLock::RLocker l(ictx->snap_lock); - numseg = Striper::get_num_objects(ictx->layout, ictx->get_current_size()); - } - - int r; - CephContext *cct = ictx->cct; - SimpleThrottle throttle(ictx->concurrent_management_ops, true); - - for (uint64_t i = 0; i < numseg; i++) { - string oid = ictx->get_object_name(i); - rollback_object(ictx, snap_id, ictx->get_object_name(i), throttle); - prog_ctx.update_progress(i * bsize, numseg * bsize); - } - - r = throttle.wait_for_ret(); - if (r < 0) { - ldout(cct, 10) << "failed to rollback at least one object: " - << cpp_strerror(r) << dendl; - return r; - } - - { - RWLock::WLocker l(ictx->snap_lock); - ictx->object_map.rollback(snap_id); - } - return 0; - } - int list(IoCtx& io_ctx, vector& names) { CephContext *cct = (CephContext *)io_ctx.cct(); @@ -760,8 +719,7 @@ int invoke_async_request(ImageCtx *ictx, const std::string& request_type, return 0; } - int snap_create_helper(ImageCtx* ictx, Context* ctx, - const char* snap_name) { + int snap_create_helper(ImageCtx* ictx, Context* ctx, const char* snap_name) { assert(ictx->owner_lock.is_locked()); assert(!ictx->image_watcher->is_lock_supported() || ictx->image_watcher->is_lock_owner()); @@ -774,107 +732,9 @@ int invoke_async_request(ImageCtx *ictx, const std::string& request_type, return r; } - RWLock::WLocker md_locker(ictx->md_lock); - r = ictx->flush(); - if (r < 0) { - return r; - } - - do { - r = add_snap(ictx, snap_name); - } while (r == -ESTALE); - - if (r < 0) { - return r; - } - - if (ctx != NULL) { - ctx->complete(0); - } - return 0; - } - - int snap_rename(ImageCtx *ictx, const char *srcname, const char *dstname) - { - ldout(ictx->cct, 20) << "snap_rename " << ictx << " from " << srcname << " to " << dstname << dendl; - - snapid_t snap_id; - if (ictx->read_only) { - return -EROFS; - } - - int r = ictx_check(ictx); - if (r < 0) - return r; - - { - RWLock::RLocker l(ictx->snap_lock); - snap_id = ictx->get_snap_id(srcname); - if (snap_id == CEPH_NOSNAP) { - return -ENOENT; - } - if (ictx->get_snap_id(dstname) != CEPH_NOSNAP) { - return -EEXIST; - } - } - - r = invoke_async_request(ictx, "snap_rename", true, - boost::bind(&snap_rename_helper, ictx, _1, - snap_id, dstname), - boost::bind(&ImageWatcher::notify_snap_rename, - ictx->image_watcher, snap_id, - dstname)); - if (r < 0 && r != -EEXIST) { - return r; - } - - ictx->perfcounter->inc(l_librbd_snap_rename); - notify_change(ictx->md_ctx, ictx->header_oid, ictx); - return 0; - } - - int snap_rename_helper(ImageCtx* ictx, Context* ctx, - const uint64_t src_snap_id, - const char* dst_name) { - assert(ictx->owner_lock.is_locked()); - assert(!ictx->image_watcher->is_lock_supported() || - ictx->image_watcher->is_lock_owner()); - - ldout(ictx->cct, 20) << __func__ << " " << ictx << " from " - << src_snap_id << " to " << dst_name << dendl; - - int r = ictx_check(ictx, ictx->owner_lock); - if (r < 0) { - return r; - } - r = rename_snap(ictx, src_snap_id, dst_name); - - if (r < 0) { - return r; - } - - if (ctx != NULL) { - ctx->complete(0); - } - return 0; - } - - static int scan_for_parents(ImageCtx *ictx, parent_spec &pspec, - snapid_t oursnap_id) - { - if (pspec.pool_id != -1) { - map::iterator it; - for (it = ictx->snap_info.begin(); - it != ictx->snap_info.end(); ++it) { - // skip our snap id (if checking base image, CEPH_NOSNAP won't match) - if (it->first == oursnap_id) - continue; - if (it->second.parent.spec == pspec) - break; - } - if (it == ictx->snap_info.end()) - return -ENOENT; - } + operation::SnapshotCreateRequest *req = + new operation::SnapshotCreateRequest(*ictx, ctx, snap_name); + req->send(); return 0; } @@ -909,7 +769,13 @@ int invoke_async_request(ImageCtx *ictx, const std::string& request_type, } } else { RWLock::RLocker owner_lock(ictx->owner_lock); - r = snap_remove_helper(ictx, NULL, snap_name); + C_SaferCond cond_ctx; + r = snap_remove_helper(ictx, &cond_ctx, snap_name); + if (r < 0) { + return r; + } + + r = cond_ctx.wait(); if (r < 0) { return r; } @@ -939,59 +805,101 @@ int invoke_async_request(ImageCtx *ictx, const std::string& request_type, return r; } - RWLock::RLocker md_locker(ictx->md_lock); - snap_t snap_id; + uint64_t snap_id; { - RWLock::WLocker snap_locker(ictx->snap_lock); + RWLock::RLocker snap_locker(ictx->snap_lock); snap_id = ictx->get_snap_id(snap_name); if (snap_id == CEPH_NOSNAP) { + lderr(ictx->cct) << "No such snapshot found." << dendl; return -ENOENT; } - r = ictx->object_map.snapshot_remove(snap_id); + bool is_protected; + r = ictx->is_snap_protected(snap_id, &is_protected); if (r < 0) { - lderr(ictx->cct) << "snap_remove: failed to remove snapshot object map" - << dendl; - return r; + ctx->complete(r); + return; + } else if (is_protected) { + lderr(ictx->cct) << "snapshot is protected" << dendl; + ctx->complete(-EBUSY); + return; } + } - { - parent_spec our_pspec; - RWLock::RLocker parent_locker(ictx->parent_lock); - r = ictx->get_parent_spec(snap_id, &our_pspec); - if (r < 0) { - lderr(ictx->cct) << "snap_remove: can't get parent spec" << dendl; - return r; - } + operation::SnapshotRemoveRequest *req = + new operation::SnapshotRemoveRequest(*ictx, ctx, snap_name, snap_id); + req->send(); + return 0; + } - if (ictx->parent_md.spec != our_pspec && - (scan_for_parents(ictx, our_pspec, snap_id) == -ENOENT)) { - r = cls_client::remove_child(&ictx->md_ctx, RBD_CHILDREN, - our_pspec, ictx->id); - if (r < 0 && r != -ENOENT) { - lderr(ictx->cct) << "snap_remove: failed to deregister from parent " - << "image" << dendl; - return r; - } - } + int snap_rename(ImageCtx *ictx, const char *srcname, const char *dstname) + { + ldout(ictx->cct, 20) << "snap_rename " << ictx << " from " << srcname << " to " << dstname << dendl; + + snapid_t snap_id; + if (ictx->read_only) { + return -EROFS; + } + + int r = ictx_check(ictx); + if (r < 0) + return r; + + { + RWLock::RLocker l(ictx->snap_lock); + snap_id = ictx->get_snap_id(srcname); + if (snap_id == CEPH_NOSNAP) { + return -ENOENT; } + if (ictx->get_snap_id(dstname) != CEPH_NOSNAP) { + return -EEXIST; + } + } + + if (ictx->test_features(RBD_FEATURE_JOURNALING)) { + r = invoke_async_request(ictx, "snap_rename", true, + boost::bind(&snap_rename_helper, ictx, _1, + snap_id, dstname), + boost::bind(&ImageWatcher::notify_snap_rename, + ictx->image_watcher, snap_id, + dstname)); + if (r < 0 && r != -EEXIST) { + return r; + } + } else { + RWLock::RLocker owner_lock(ictx->owner_lock); + C_SaferCond cond_ctx; + snap_rename_helper(ictx, &cond_ctx, snap_id, dstname); - r = rm_snap(ictx, snap_name, snap_id); + r = cond_ctx.wait(); if (r < 0) { return r; } } - r = ictx->data_ctx.selfmanaged_snap_remove(snap_id); + ictx->perfcounter->inc(l_librbd_snap_rename); + notify_change(ictx->md_ctx, ictx->header_oid, ictx); + return 0; + } + + int snap_rename_helper(ImageCtx* ictx, Context* ctx, + const uint64_t src_snap_id, const char* dst_name) { + assert(ictx->owner_lock.is_locked()); + if ((ictx->features & RBD_FEATURE_JOURNALING) != 0) { + assert(!ictx->image_watcher->is_lock_supported() || + ictx->image_watcher->is_lock_owner()); + } + ldout(ictx->cct, 20) << __func__ << " " << ictx << " from " + << src_snap_id << " to " << dst_name << dendl; + + int r = ictx_check(ictx, ictx->owner_lock); if (r < 0) { - lderr(ictx->cct) << "snap_remove: failed to remove RADOS snapshot" - << dendl; return r; } - if (ctx != NULL) { - ctx->complete(0); - } + operation::SnapshotRenameRequest *req = + new operation::SnapshotRenameRequest(*ictx, ctx, src_snap_id, dst_name); + req->send(); return 0; } @@ -1007,31 +915,18 @@ int invoke_async_request(ImageCtx *ictx, const std::string& request_type, if (r < 0) return r; - RWLock::RLocker l(ictx->md_lock); - RWLock::RLocker l2(ictx->snap_lock); - if ((ictx->features & RBD_FEATURE_LAYERING) == 0) { - lderr(ictx->cct) << "snap_protect: image must support layering" - << dendl; - return -ENOSYS; - } - snap_t snap_id = ictx->get_snap_id(snap_name); - if (snap_id == CEPH_NOSNAP) - return -ENOENT; + // TODO integrate w/ watch/notify + RWLock::RLocker owner_locker(ictx->owner_lock); - bool is_protected; - r = ictx->is_snap_protected(snap_id, &is_protected); - if (r < 0) + C_SaferCond cond_ctx; + operation::SnapshotProtectRequest *request = + new operation::SnapshotProtectRequest(*ictx, &cond_ctx, snap_name); + request->send(); + r = cond_ctx.wait(); + if (r < 0) { return r; + } - if (is_protected) - return -EBUSY; - - r = cls_client::set_protection_status(&ictx->md_ctx, - ictx->header_oid, - snap_id, - RBD_PROTECTION_STATUS_PROTECTED); - if (r < 0) - return r; notify_change(ictx->md_ctx, ictx->header_oid, ictx); return 0; } @@ -1048,115 +943,20 @@ int invoke_async_request(ImageCtx *ictx, const std::string& request_type, if (r < 0) return r; - RWLock::RLocker l(ictx->md_lock); - RWLock::RLocker l2(ictx->snap_lock); - if ((ictx->features & RBD_FEATURE_LAYERING) == 0) { - lderr(ictx->cct) << "snap_unprotect: image must support layering" - << dendl; - return -ENOSYS; - } - snap_t snap_id = ictx->get_snap_id(snap_name); - if (snap_id == CEPH_NOSNAP) - return -ENOENT; + // TODO integrate w/ watch/notify + RWLock::RLocker owner_locker(ictx->owner_lock); - bool is_unprotected; - r = ictx->is_snap_unprotected(snap_id, &is_unprotected); - if (r < 0) + C_SaferCond cond_ctx; + operation::SnapshotUnprotectRequest *request + = new operation::SnapshotUnprotectRequest(*ictx, &cond_ctx, snap_name); + request->send(); + r = cond_ctx.wait(); + if (r < 0) { return r; - - if (is_unprotected) { - lderr(ictx->cct) << "snap_unprotect: snapshot is already unprotected" - << dendl; - return -EINVAL; } - r = cls_client::set_protection_status(&ictx->md_ctx, - ictx->header_oid, - snap_id, - RBD_PROTECTION_STATUS_UNPROTECTING); - if (r < 0) - return r; - notify_change(ictx->md_ctx, ictx->header_oid, ictx); - - parent_spec pspec(ictx->md_ctx.get_id(), ictx->id, snap_id); - // search all pools for children depending on this snapshot - Rados rados(ictx->md_ctx); - rados.wait_for_latest_osdmap(); - - // protect against pools being renamed/deleted - std::list > pools; - rados.pool_list2(pools); - - for (std::list >::const_iterator it = - pools.begin(); it != pools.end(); ++it) { - int64_t base_tier; - r = rados.pool_get_base_tier(it->first, &base_tier); - if (r == -ENOENT) { - ldout(ictx->cct, 1) << "pool " << it->second << " no longer exists" - << dendl; - continue; - } else if (r < 0) { - lderr(ictx->cct) << "snap_unprotect: error retrieving base tier for " - << "pool " << it->second << dendl; - goto reprotect_and_return_err; - } - if (it->first != base_tier) { - // pool is a cache; skip it - continue; - } - - IoCtx pool_ioctx; - r = rados.ioctx_create2(it->first, pool_ioctx); - if (r == -ENOENT) { - ldout(ictx->cct, 1) << "pool " << it->second << " no longer exists" - << dendl; - continue; - } else if (r < 0) { - lderr(ictx->cct) << "snap_unprotect: can't create ioctx for pool " - << it->second << dendl; - goto reprotect_and_return_err; - } - - std::set children; - r = cls_client::get_children(&pool_ioctx, RBD_CHILDREN, pspec, children); - // key should not exist for this parent if there is no entry - if (((r < 0) && (r != -ENOENT))) { - lderr(ictx->cct) << "can't get children for pool " << it->second - << dendl; - goto reprotect_and_return_err; - } - // if we found a child, can't unprotect - if (r == 0) { - lderr(ictx->cct) << "snap_unprotect: can't unprotect; at least " - << children.size() << " child(ren) in pool " << it->second << dendl; - r = -EBUSY; - goto reprotect_and_return_err; - } - } - - // didn't find any child in any pool, go ahead with unprotect - r = cls_client::set_protection_status(&ictx->md_ctx, - ictx->header_oid, - snap_id, - RBD_PROTECTION_STATUS_UNPROTECTED); - if (r < 0) { - lderr(ictx->cct) << "snap_unprotect: error setting unprotected status" - << dendl; - goto reprotect_and_return_err; - } notify_change(ictx->md_ctx, ictx->header_oid, ictx); return 0; - -reprotect_and_return_err: - int proterr = cls_client::set_protection_status(&ictx->md_ctx, - ictx->header_oid, - snap_id, - RBD_PROTECTION_STATUS_PROTECTED); - if (proterr < 0) { - lderr(ictx->cct) << "snap_unprotect: can't reprotect image" << dendl; - } - notify_change(ictx->md_ctx, ictx->header_oid, ictx); - return r; } int snap_is_protected(ImageCtx *ictx, const char *snap_name, @@ -2373,142 +2173,6 @@ reprotect_and_return_err: return ictx->get_snap_id(snap_name) != CEPH_NOSNAP; } - - int add_snap(ImageCtx *ictx, const char *snap_name) - { - assert(ictx->owner_lock.is_locked()); - assert(ictx->md_lock.is_wlocked()); - - bool lock_owner = ictx->image_watcher->is_lock_owner(); - if (ictx->image_watcher->is_lock_supported()) { - assert(lock_owner); - } - - uint64_t snap_id; - int r = ictx->md_ctx.selfmanaged_snap_create(&snap_id); - if (r < 0) { - lderr(ictx->cct) << "failed to create snap id: " << cpp_strerror(-r) - << dendl; - return r; - } - - if (ictx->old_format) { - r = cls_client::old_snapshot_add(&ictx->md_ctx, ictx->header_oid, - snap_id, snap_name); - } else { - librados::ObjectWriteOperation op; - if (lock_owner) { - ictx->image_watcher->assert_header_locked(&op); - } - cls_client::snapshot_add(&op, snap_id, snap_name); - r = ictx->md_ctx.operate(ictx->header_oid, &op); - } - - if (r < 0) { - lderr(ictx->cct) << "adding snapshot to header failed: " - << cpp_strerror(r) << dendl; - ictx->data_ctx.selfmanaged_snap_remove(snap_id); - return r; - } - - RWLock::WLocker l(ictx->snap_lock); - if (!ictx->old_format) { - ictx->object_map.snapshot_add(snap_id); - if (lock_owner) { - // immediately start using the new snap context if we - // own the exclusive lock - std::vector snaps; - snaps.push_back(snap_id); - snaps.insert(snaps.end(), ictx->snapc.snaps.begin(), - ictx->snapc.snaps.end()); - - ictx->snapc.seq = snap_id; - ictx->snapc.snaps.swap(snaps); - ictx->data_ctx.selfmanaged_snap_set_write_ctx(ictx->snapc.seq, - ictx->snaps); - } - } - return 0; - } - - int rm_snap(ImageCtx *ictx, const char *snap_name, uint64_t snap_id) - { - assert(ictx->snap_lock.is_wlocked()); - - int r; - if (ictx->old_format) { - r = cls_client::old_snapshot_remove(&ictx->md_ctx, - ictx->header_oid, snap_name); - } else { - r = cls_client::snapshot_remove(&ictx->md_ctx, ictx->header_oid, snap_id); - if (r == 0) { - ictx->rm_snap(snap_name, snap_id); - } - } - - if (r < 0) { - lderr(ictx->cct) << "removing snapshot from header failed: " - << cpp_strerror(r) << dendl; - return r; - } - - return 0; - } - int rename_snap(ImageCtx *ictx, uint64_t src_snap_id, const char *dst_name) - { - assert(ictx->owner_lock.is_locked()); - - int r; - map::iterator it; - { - RWLock::RLocker(ictx->snap_lock); - it = ictx->snap_info.find(src_snap_id); - if (it == ictx->snap_info.end()) { - ldout(ictx->cct, 20) << __func__ << " can not find snap with snap id " - << src_snap_id << dendl; - return -ENOENT; - } - } - bool lock_owner = ictx->image_watcher->is_lock_owner(); - if (ictx->image_watcher->is_lock_supported()) { - assert(lock_owner); - } - - - if (ictx->old_format) { - r = cls_client::old_snapshot_rename(&ictx->md_ctx, ictx->header_oid, - src_snap_id, dst_name); - } else { - librados::ObjectWriteOperation op; - if (lock_owner) { - ictx->image_watcher->assert_header_locked(&op); - } - cls_client::snapshot_rename(&op, src_snap_id, dst_name); - r = ictx->md_ctx.operate(ictx->header_oid, &op); - } - - if (r < 0) { - lderr(ictx->cct) << "rename snapshot name failed: " - << cpp_strerror(r) << dendl; - return r; - } - - RWLock::WLocker snap_locker(ictx->snap_lock); - if (!ictx->old_format) { - if (lock_owner) { - it = ictx->snap_info.find(src_snap_id); - if (it == ictx->snap_info.end()) - return -ENOENT; - ictx->snap_ids.erase(it->second.name); - it->second.name = dst_name; - ictx->snap_ids.insert(make_pair(dst_name,it->first)); - if (ictx->snap_id == src_snap_id) - ictx->snap_name = it->second.name; - } - } - return 0; - } - int ictx_check(ImageCtx *ictx) { RWLock::RLocker owner_locker(ictx->owner_lock); return ictx_check(ictx, ictx->owner_lock); @@ -2829,32 +2493,16 @@ reprotect_and_return_err: ictx->snap_lock.get_read(); new_size = ictx->get_image_size(snap_id); ictx->snap_lock.put_read(); - - // need to flush any pending writes before resizing and rolling back - - // writes might create new snapshots. Rolling back will replace - // the current version, so we have to invalidate that too. - RWLock::WLocker md_locker(ictx->md_lock); - r = ictx->invalidate_cache(); - if (r < 0) { - return r; - } - } - - ldout(cct, 2) << "resizing to snapshot size..." << dendl; - NoOpProgressContext no_op; - C_SaferCond ctx; - async_resize_helper(ictx, &ctx, new_size, no_op); - - r = ctx.wait(); - if (r < 0) { - lderr(cct) << "Error resizing to snapshot size: " - << cpp_strerror(r) << dendl; - return r; } - r = rollback_image(ictx, snap_id, prog_ctx); + // TODO need to wait for journal replay to complete (if enabled) + C_SaferCond cond_ctx; + operation::SnapshotRollbackRequest *request = + new operation::SnapshotRollbackRequest(*ictx, &cond_ctx, snap_name, + snap_id, new_size, prog_ctx); + request->send(); + r = cond_ctx.wait(); if (r < 0) { - lderr(cct) << "Error rolling back image: " << cpp_strerror(-r) << dendl; return r; } diff --git a/src/librbd/internal.h b/src/librbd/internal.h index 3de90a66fd0..0bbb908fe47 100644 --- a/src/librbd/internal.h +++ b/src/librbd/internal.h @@ -48,6 +48,7 @@ enum { }; class Context; +class RWLock; class SimpleThrottle; namespace librbd { @@ -132,16 +133,13 @@ namespace librbd { ProgressContext& prog_ctx); int snap_remove(ImageCtx *ictx, const char *snap_name); int snap_remove_helper(ImageCtx *ictx, Context* ctx, const char *snap_name); - int snap_rename_helper(ImageCtx *ictx, Context* ctx, const uint64_t src_snap_id, - const char *dst_name); int snap_rename(ImageCtx *ictx, const char *srcname, const char *dstname); + int snap_rename_helper(ImageCtx *ictx, Context* ctx, + const uint64_t src_snap_id, const char *dst_name); int snap_protect(ImageCtx *ictx, const char *snap_name); int snap_unprotect(ImageCtx *ictx, const char *snap_name); int snap_is_protected(ImageCtx *ictx, const char *snap_name, bool *is_protected); - int add_snap(ImageCtx *ictx, const char *snap_name); - int rm_snap(ImageCtx *ictx, const char *snap_name, uint64_t snap_id); - int rename_snap(ImageCtx *ictx, uint64_t src_snap_id, const char *dst_name); int refresh_parent(ImageCtx *ictx); int ictx_check(ImageCtx *ictx); int ictx_check(ImageCtx *ictx, const RWLock &owner_lock); @@ -182,10 +180,6 @@ namespace librbd { struct rbd_obj_header_ondisk *header, uint64_t *ver); int tmap_set(librados::IoCtx& io_ctx, const std::string& imgname); int tmap_rm(librados::IoCtx& io_ctx, const std::string& imgname); - void rollback_object(ImageCtx *ictx, uint64_t snap_id, const string& oid, - SimpleThrottle& throttle); - int rollback_image(ImageCtx *ictx, uint64_t snap_id, - ProgressContext& prog_ctx); void image_info(const ImageCtx *ictx, image_info_t& info, size_t info_size); uint64_t oid_to_object_no(const std::string& oid, const std::string& object_prefix); diff --git a/src/librbd/operation/SnapshotCreateRequest.cc b/src/librbd/operation/SnapshotCreateRequest.cc new file mode 100644 index 00000000000..2ee41c37f1e --- /dev/null +++ b/src/librbd/operation/SnapshotCreateRequest.cc @@ -0,0 +1,282 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/operation/SnapshotCreateRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/AioImageRequestWQ.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageWatcher.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::SnapshotCreateRequest: " + +namespace librbd { +namespace operation { + +namespace { + +std::ostream& operator<<(std::ostream& os, + const SnapshotCreateRequest::State& state) { + switch(state) { + case SnapshotCreateRequest::STATE_SUSPEND_REQUESTS: + os << "SUSPEND_REQUESTS"; + break; + case SnapshotCreateRequest::STATE_SUSPEND_AIO: + os << "SUSPEND_AIO"; + break; + case SnapshotCreateRequest::STATE_FLUSH_AIO: + os << "FLUSH_AIO"; + break; + case SnapshotCreateRequest::STATE_ALLOCATE_SNAP_ID: + os << "ALLOCATE_SNAP_ID"; + break; + case SnapshotCreateRequest::STATE_CREATE_SNAP: + os << "CREATE_SNAP"; + break; + case SnapshotCreateRequest::STATE_CREATE_OBJECT_MAP: + os << "CREATE_OBJECT_MAP"; + break; + case SnapshotCreateRequest::STATE_RELEASE_SNAP_ID: + os << "RELEASE_SNAP_ID"; + break; + default: + os << "UNKNOWN (" << static_cast(state) << ")"; + break; + } + return os; +} + +} // anonymous namespace + +SnapshotCreateRequest::SnapshotCreateRequest(ImageCtx &image_ctx, + Context *on_finish, + const std::string &snap_name) + : Request(image_ctx, on_finish), m_snap_name(snap_name), m_ret_val(0), + m_aio_suspended(false), m_requests_suspended(false), + m_snap_id(CEPH_NOSNAP), m_snap_created(false) { +} + +void SnapshotCreateRequest::send_op() { + send_suspend_requests(); +} + +bool SnapshotCreateRequest::should_complete(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": state=" << m_state << ", " + << "r=" << r << dendl; + r = filter_state_return_code(r); + if (r < 0) { + lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl; + if (m_ret_val == 0) { + m_ret_val = r; + } + } + + if (m_ret_val < 0) { + return should_complete_error(); + } + + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + bool finished = false; + switch (m_state) { + case STATE_SUSPEND_REQUESTS: + send_suspend_aio(); + break; + case STATE_SUSPEND_AIO: + send_flush_aio(); + break; + case STATE_FLUSH_AIO: + send_allocate_snap_id(); + break; + case STATE_ALLOCATE_SNAP_ID: + send_create_snap(); + break; + case STATE_CREATE_SNAP: + if (r == 0) { + m_snap_created = true; + finished = send_create_object_map(); + } else { + assert(r == -ESTALE); + send_allocate_snap_id(); + } + break; + case STATE_CREATE_OBJECT_MAP: + finished = true; + break; + default: + assert(false); + break; + } + + if (finished) { + update_snap_context(); + resume_aio(); + resume_requests(); + } + return finished; +} + +bool SnapshotCreateRequest::should_complete_error() { + CephContext *cct = m_image_ctx.cct; + lderr(cct) << this << " " << __func__ << ": " + << "ret_val=" << m_ret_val << dendl; + + // only valid exit points during error recovery + bool finished = true; + if (m_state != STATE_RELEASE_SNAP_ID) { + finished = send_release_snap_id(); + } + + if (finished) { + resume_aio(); + resume_requests(); + } + return finished; +} + +void SnapshotCreateRequest::send_suspend_requests() { + assert(m_image_ctx.owner_lock.is_locked()); + + // TODO suspend (shrink) resize to ensure consistent RBD mirror + send_suspend_aio(); +} + +void SnapshotCreateRequest::send_suspend_aio() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + m_state = STATE_SUSPEND_AIO; + m_aio_suspended = true; + + // can issue a re-entrant callback if no IO in-progress + m_image_ctx.aio_work_queue->block_writes(create_async_callback_context()); +} + +void SnapshotCreateRequest::send_flush_aio() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_FLUSH_AIO; + + // can issue a re-entrant callback if no IO to flush + m_image_ctx.flush(create_async_callback_context()); +} + +void SnapshotCreateRequest::send_allocate_snap_id() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_ALLOCATE_SNAP_ID; + + // TODO create an async version of selfmanaged_snap_create + int r = m_image_ctx.md_ctx.selfmanaged_snap_create(&m_snap_id); + async_complete(r); +} + +void SnapshotCreateRequest::send_create_snap() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_CREATE_SNAP; + + // should have been canceled prior to releasing lock + assert(!m_image_ctx.image_watcher->is_lock_supported() || + m_image_ctx.image_watcher->is_lock_owner()); + + librados::ObjectWriteOperation op; + if (m_image_ctx.old_format) { + cls_client::old_snapshot_add(&op, m_snap_id, m_snap_name); + } else { + if (m_image_ctx.image_watcher->is_lock_owner()) { + m_image_ctx.image_watcher->assert_header_locked(&op); + } + cls_client::snapshot_add(&op, m_snap_id, m_snap_name); + } + + librados::AioCompletion *rados_completion = create_callback_completion(); + int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, + rados_completion, &op); + assert(r == 0); + rados_completion->release(); +} + +bool SnapshotCreateRequest::send_create_object_map() { + // TODO add object map support + return true; +} + +bool SnapshotCreateRequest::send_release_snap_id() { + assert(m_image_ctx.owner_lock.is_locked()); + if (m_snap_id != CEPH_NOSNAP && !m_snap_created) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_id=" << m_snap_id + << dendl; + m_state = STATE_RELEASE_SNAP_ID; + + // TODO add async version of selfmanaged_snap_remove + int r = m_image_ctx.data_ctx.selfmanaged_snap_remove(m_snap_id); + m_snap_id = CEPH_NOSNAP; + + async_complete(r); + return false; + } + return true; +} + +void SnapshotCreateRequest::resume_aio() { + if (m_aio_suspended) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + m_image_ctx.aio_work_queue->unblock_writes(); + m_aio_suspended = false; + } +} + +void SnapshotCreateRequest::resume_requests() { + if (m_requests_suspended) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + // TODO + m_requests_suspended = false; + } +} + +void SnapshotCreateRequest::update_snap_context() { + assert(m_image_ctx.owner_lock.is_locked()); + + RWLock::WLocker snap_locker(m_image_ctx.snap_lock); + if (m_image_ctx.old_format || !m_snap_created) { + return; + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + // should have been canceled prior to releasing lock + assert(!m_image_ctx.image_watcher->is_lock_supported(m_image_ctx.snap_lock) || + m_image_ctx.image_watcher->is_lock_owner()); + + // immediately start using the new snap context if we + // own the exclusive lock + std::vector snaps; + snaps.push_back(m_snap_id); + snaps.insert(snaps.end(), m_image_ctx.snapc.snaps.begin(), + m_image_ctx.snapc.snaps.end()); + + m_image_ctx.snapc.seq = m_snap_id; + m_image_ctx.snapc.snaps.swap(snaps); + m_image_ctx.data_ctx.selfmanaged_snap_set_write_ctx( + m_image_ctx.snapc.seq, m_image_ctx.snaps); +} + +} // namespace operation +} // namespace librbd diff --git a/src/librbd/operation/SnapshotCreateRequest.h b/src/librbd/operation/SnapshotCreateRequest.h new file mode 100644 index 00000000000..1e9d526311f --- /dev/null +++ b/src/librbd/operation/SnapshotCreateRequest.h @@ -0,0 +1,121 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_OPERATION_SNAPSHOT_CREATE_REQUEST_H +#define CEPH_LIBRBD_OPERATION_SNAPSHOT_CREATE_REQUEST_H + +#include "librbd/operation/Request.h" +#include +#include + +class Context; + +namespace librbd { + +class ImageCtx; + +namespace operation { + +class SnapshotCreateRequest : public Request { +public: + /** + * Snap Create goes through the following state machine: + * + * @verbatim + * + * + * | + * v + * STATE_SUSPEND_REQUESTS + * | + * v + * STATE_SUSPEND_AIO + * | + * v + * STATE_FLUSH_AIO * * * * * * * * * * * * * * + * | * + * (retry) v * + * . . . > STATE_ALLOCATE_SNAP_ID * * * + * . | * * + * . v * * + * . . . . STATE_CREATE_SNAP * * * * * * + * | * * + * v * * + * STATE_CREATE_OBJECT_MAP * * + * | * * + * | * * + * | v * + * | STATE_RELEASE_SNAP_ID * + * | | * + * | v * + * \----------------> < * * * * * + * + * @endverbatim + * + * The _CREATE_STATE state may repeat back to the _ALLOCATE_SNAP_ID state + * if a stale snapshot context is allocated. If the create operation needs + * to abort, the error path is followed to record the result in the journal + * (if enabled) and bubble the originating error code back to the client. + */ + enum State { + STATE_SUSPEND_REQUESTS, + STATE_SUSPEND_AIO, + STATE_FLUSH_AIO, + STATE_ALLOCATE_SNAP_ID, + STATE_CREATE_SNAP, + STATE_CREATE_OBJECT_MAP, + STATE_RELEASE_SNAP_ID + }; + + SnapshotCreateRequest(ImageCtx &image_ctx, Context *on_finish, + const std::string &snap_name); + +protected: + virtual void send_op(); + virtual bool should_complete(int r); + + virtual int filter_return_code(int r) const { + if (m_ret_val < 0) { + return m_ret_val; + } + return r; + } + +private: + std::string m_snap_name; + State m_state; + + int m_ret_val; + + bool m_aio_suspended; + bool m_requests_suspended; + + uint64_t m_snap_id; + bool m_snap_created; + + int filter_state_return_code(int r) const { + if (m_state == STATE_CREATE_SNAP && r == -ESTALE) { + return 0; + } + return r; + } + + bool should_complete_error(); + + void send_suspend_requests(); + void send_suspend_aio(); + void send_flush_aio(); + void send_allocate_snap_id(); + void send_create_snap(); + bool send_create_object_map(); + bool send_release_snap_id(); + + void resume_aio(); + void resume_requests(); + void update_snap_context(); +}; + +} // namespace operation +} // namespace librbd + +#endif // CEPH_LIBRBD_OPERATION_SNAPSHOT_CREATE_REQUEST_H diff --git a/src/librbd/operation/SnapshotProtectRequest.cc b/src/librbd/operation/SnapshotProtectRequest.cc new file mode 100644 index 00000000000..3e8a396981f --- /dev/null +++ b/src/librbd/operation/SnapshotProtectRequest.cc @@ -0,0 +1,104 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/operation/SnapshotProtectRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/ImageCtx.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::SnapshotProtectRequest: " + +namespace librbd { +namespace operation { + +namespace { + +std::ostream& operator<<(std::ostream& os, + const SnapshotProtectRequest::State& state) { + switch(state) { + case SnapshotProtectRequest::STATE_PROTECT_SNAP: + os << "PROTECT_SNAP"; + break; + } + return os; +} + +} // anonymous namespace + +SnapshotProtectRequest::SnapshotProtectRequest(ImageCtx &image_ctx, + Context *on_finish, + const std::string &snap_name) + : Request(image_ctx, on_finish), m_snap_name(snap_name) { +} + +void SnapshotProtectRequest::send_op() { + send_protect_snap(); +} + +bool SnapshotProtectRequest::should_complete(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": state=" << m_state << ", " + << "r=" << r << dendl; + if (r < 0) { + lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl; + } + return true; +} + +void SnapshotProtectRequest::send_protect_snap() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + m_state = STATE_PROTECT_SNAP; + + int r = verify_and_send_protect_snap(); + if (r < 0) { + async_complete(r); + return; + } +} + +int SnapshotProtectRequest::verify_and_send_protect_snap() { + RWLock::RLocker md_locker(m_image_ctx.md_lock); + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + + CephContext *cct = m_image_ctx.cct; + if ((m_image_ctx.features & RBD_FEATURE_LAYERING) == 0) { + lderr(cct) << "image must support layering" << dendl; + return -ENOSYS; + } + + uint64_t snap_id = m_image_ctx.get_snap_id(m_snap_name); + if (snap_id == CEPH_NOSNAP) { + return -ENOENT; + } + + bool is_protected; + int r = m_image_ctx.is_snap_protected(snap_id, &is_protected); + if (r < 0) { + return r; + } + + if (is_protected) { + return -EBUSY; + } + + librados::ObjectWriteOperation op; + cls_client::set_protection_status(&op, snap_id, + RBD_PROTECTION_STATUS_PROTECTED); + + librados::AioCompletion *rados_completion = create_callback_completion(); + r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, rados_completion, + &op); + assert(r == 0); + rados_completion->release(); + return 0; +} + +} // namespace operation +} // namespace librbd + diff --git a/src/librbd/operation/SnapshotProtectRequest.h b/src/librbd/operation/SnapshotProtectRequest.h new file mode 100644 index 00000000000..dd3496b96e4 --- /dev/null +++ b/src/librbd/operation/SnapshotProtectRequest.h @@ -0,0 +1,60 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_OPERATION_SNAPSHOT_PROTECT_REQUEST_H +#define CEPH_LIBRBD_OPERATION_SNAPSHOT_PROTECT_REQUEST_H + +#include "librbd/operation/Request.h" +#include +#include + +class Context; + +namespace librbd { + +class ImageCtx; + +namespace operation { + +class SnapshotProtectRequest : public Request { +public: + /** + * Snap Protect goes through the following state machine: + * + * @verbatim + * + * + * | + * v + * STATE_PROTECT_SNAP + * | + * v + * + * + * @endverbatim + * + */ + enum State { + STATE_PROTECT_SNAP + }; + + SnapshotProtectRequest(ImageCtx &image_ctx, Context *on_finish, + const std::string &snap_name); + +protected: + virtual void send_op(); + virtual bool should_complete(int r); + +private: + std::string m_snap_name; + State m_state; + + void send_protect_snap(); + + int verify_and_send_protect_snap(); +}; + +} // namespace operation +} // namespace librbd + +#endif // CEPH_LIBRBD_OPERATION_SNAPSHOT_PROTECT_REQUEST_H diff --git a/src/librbd/operation/SnapshotRemoveRequest.cc b/src/librbd/operation/SnapshotRemoveRequest.cc new file mode 100644 index 00000000000..794a9770b60 --- /dev/null +++ b/src/librbd/operation/SnapshotRemoveRequest.cc @@ -0,0 +1,202 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/operation/SnapshotRemoveRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageWatcher.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::SnapshotRemoveRequest: " + +namespace librbd { +namespace operation { + +namespace { + +std::ostream& operator<<(std::ostream& os, + const SnapshotRemoveRequest::State& state) { + switch(state) { + case SnapshotRemoveRequest::STATE_REMOVE_OBJECT_MAP: + os << "REMOVE_OBJECT_MAP"; + break; + case SnapshotRemoveRequest::STATE_REMOVE_CHILD: + os << "REMOVE_CHILD"; + break; + case SnapshotRemoveRequest::STATE_REMOVE_SNAP: + os << "REMOVE_SNAP"; + break; + case SnapshotRemoveRequest::STATE_RELEASE_SNAP_ID: + os << "RELEASE_SNAP_ID"; + break; + default: + os << "UNKNOWN (" << static_cast(state) << ")"; + break; + } + return os; +} + +} // anonymous namespace + +SnapshotRemoveRequest::SnapshotRemoveRequest(ImageCtx &image_ctx, + Context *on_finish, + const std::string &snap_name, + uint64_t snap_id) + : Request(image_ctx, on_finish), m_snap_name(snap_name), + m_snap_id(snap_id) { +} + +void SnapshotRemoveRequest::send_op() { + send_remove_object_map(); +} + +bool SnapshotRemoveRequest::should_complete(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": state=" << m_state << ", " + << "r=" << r << dendl; + r = filter_state_return_code(r); + if (r < 0) { + return true; + } + + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + bool finished = false; + switch (m_state) { + case STATE_REMOVE_OBJECT_MAP: + send_remove_child(); + break; + case STATE_REMOVE_CHILD: + send_remove_snap(); + break; + case STATE_REMOVE_SNAP: + remove_snap_context(); + send_release_snap_id(); + break; + case STATE_RELEASE_SNAP_ID: + finished = true; + break; + default: + assert(false); + break; + } + + return finished; +} + +void SnapshotRemoveRequest::send_remove_object_map() { + assert(m_image_ctx.owner_lock.is_locked()); + + // TODO add object map support + send_remove_child(); +} + +void SnapshotRemoveRequest::send_remove_child() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + { + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + RWLock::RLocker parent_locker(m_image_ctx.parent_lock); + + parent_spec our_pspec; + int r = m_image_ctx.get_parent_spec(m_snap_id, &our_pspec); + if (r < 0) { + lderr(cct) << "failed to retrieve parent spec" << dendl; + async_complete(r); + return; + } + + if (m_image_ctx.parent_md.spec != our_pspec && + (scan_for_parents(our_pspec) == -ENOENT)) { + // no other references to the parent image + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_REMOVE_CHILD; + + librados::ObjectWriteOperation op; + cls_client::remove_child(&op, our_pspec, m_image_ctx.id); + + librados::AioCompletion *rados_completion = create_callback_completion(); + r = m_image_ctx.md_ctx.aio_operate(RBD_CHILDREN, rados_completion, &op); + assert(r == 0); + rados_completion->release(); + return; + } + } + + // HEAD image or other snapshots still associated with parent + send_remove_snap(); +} + +void SnapshotRemoveRequest::send_remove_snap() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_REMOVE_SNAP; + + librados::ObjectWriteOperation op; + if (m_image_ctx.old_format) { + cls_client::old_snapshot_remove(&op, m_snap_name); + } else { + if (m_image_ctx.image_watcher->is_lock_owner()) { + m_image_ctx.image_watcher->assert_header_locked(&op); + } + cls_client::snapshot_remove(&op, m_snap_id); + } + + librados::AioCompletion *rados_completion = create_callback_completion(); + int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, + rados_completion, &op); + assert(r == 0); + rados_completion->release(); +} + +void SnapshotRemoveRequest::send_release_snap_id() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": " + << "snap_name=" << m_snap_name << ", " + << "snap_id=" << m_snap_id << dendl; + m_state = STATE_RELEASE_SNAP_ID; + + // TODO add async version of selfmanaged_snap_remove + m_image_ctx.data_ctx.selfmanaged_snap_remove(m_snap_id); + async_complete(0); +} + +void SnapshotRemoveRequest::remove_snap_context() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + RWLock::WLocker snap_locker(m_image_ctx.snap_lock); + m_image_ctx.rm_snap(m_snap_name, m_snap_id); +} + +int SnapshotRemoveRequest::scan_for_parents(parent_spec &pspec) { + assert(m_image_ctx.snap_lock.is_locked()); + assert(m_image_ctx.parent_lock.is_locked()); + + if (pspec.pool_id != -1) { + map::iterator it; + for (it = m_image_ctx.snap_info.begin(); + it != m_image_ctx.snap_info.end(); ++it) { + // skip our snap id (if checking base image, CEPH_NOSNAP won't match) + if (it->first == m_snap_id) { + continue; + } + if (it->second.parent.spec == pspec) { + break; + } + } + if (it == m_image_ctx.snap_info.end()) { + return -ENOENT; + } + } + return 0; +} + +} // namespace operation +} // namespace librbd diff --git a/src/librbd/operation/SnapshotRemoveRequest.h b/src/librbd/operation/SnapshotRemoveRequest.h new file mode 100644 index 00000000000..85a3561c769 --- /dev/null +++ b/src/librbd/operation/SnapshotRemoveRequest.h @@ -0,0 +1,89 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_OPERATION_SNAPSHOT_REMOVE_REQUEST_H +#define CEPH_LIBRBD_OPERATION_SNAPSHOT_REMOVE_REQUEST_H + +#include "librbd/operation/Request.h" +#include "librbd/parent_types.h" +#include + +class Context; + +namespace librbd { + +class ImageCtx; + +namespace operation { + +class SnapshotRemoveRequest : public Request { +public: + /** + * Snap Remove goes through the following state machine: + * + * @verbatim + * + * ------\ + * . | + * . v + * . STATE_REMOVE_OBJECT_MAP + * . | . + * . v . + * . . > STATE_REMOVE_CHILD . + * . | . + * . | . . . . + * . | . + * . v v + * . . > STATE_REMOVE_SNAP + * | + * v + * STATE_RELEASE_SNAP_ID + * | + * v + * + * + * @endverbatim + * + * The _REMOVE_OBJECT_MAP state is skipped if the object map is not enabled. + * The _REMOVE_CHILD state is skipped if the parent is still in-use. + */ + enum State { + STATE_REMOVE_OBJECT_MAP, + STATE_REMOVE_CHILD, + STATE_REMOVE_SNAP, + STATE_RELEASE_SNAP_ID + }; + + SnapshotRemoveRequest(ImageCtx &image_ctx, Context *on_finish, + const std::string &snap_name, uint64_t snap_id); + +protected: + virtual void send_op(); + virtual bool should_complete(int r); + +private: + std::string m_snap_name; + uint64_t m_snap_id; + State m_state; + + int filter_state_return_code(int r) const { + if (m_state == STATE_REMOVE_CHILD && r == -ENOENT) { + return 0; + } + return r; + } + + void send_remove_object_map(); + void send_remove_child(); + void send_remove_snap(); + void send_release_snap_id(); + + void remove_snap_context(); + int scan_for_parents(parent_spec &pspec); + +}; + +} // namespace operation +} // namespace librbd + +#endif // CEPH_LIBRBD_OPERATION_SNAPSHOT_REMOVE_REQUEST_H diff --git a/src/librbd/operation/SnapshotRenameRequest.cc b/src/librbd/operation/SnapshotRenameRequest.cc new file mode 100644 index 00000000000..c5804afc771 --- /dev/null +++ b/src/librbd/operation/SnapshotRenameRequest.cc @@ -0,0 +1,81 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/operation/SnapshotRenameRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageWatcher.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::SnapshotRenameRequest: " + +namespace librbd { +namespace operation { + +namespace { + +std::ostream& operator<<(std::ostream& os, + const SnapshotRenameRequest::State& state) { + switch(state) { + case SnapshotRenameRequest::STATE_RENAME_SNAP: + os << "RENAME_SNAP"; + break; + } + return os; +} + +} // anonymous namespace + +SnapshotRenameRequest::SnapshotRenameRequest(ImageCtx &image_ctx, + Context *on_finish, + uint64_t snap_id, + const std::string &snap_name) + : Request(image_ctx, on_finish), m_snap_id(snap_id), m_snap_name(snap_name) { +} + +void SnapshotRenameRequest::send_op() { + send_rename_snap(); +} + +bool SnapshotRenameRequest::should_complete(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": state=" << m_state << ", " + << "r=" << r << dendl; + if (r < 0) { + lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl; + } + return true; +} + +void SnapshotRenameRequest::send_rename_snap() { + assert(m_image_ctx.owner_lock.is_locked()); + RWLock::RLocker md_locker(m_image_ctx.md_lock); + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + m_state = STATE_RENAME_SNAP; + + librados::ObjectWriteOperation op; + if (m_image_ctx.old_format) { + cls_client::old_snapshot_rename(&op, m_snap_id, m_snap_name); + } else { + if (m_image_ctx.image_watcher->is_lock_owner()) { + m_image_ctx.image_watcher->assert_header_locked(&op); + } + cls_client::snapshot_rename(&op, m_snap_id, m_snap_name); + } + + librados::AioCompletion *rados_completion = create_callback_completion(); + int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, + rados_completion, &op); + assert(r == 0); + rados_completion->release(); +} + +} // namespace operation +} // namespace librbd + diff --git a/src/librbd/operation/SnapshotRenameRequest.h b/src/librbd/operation/SnapshotRenameRequest.h new file mode 100644 index 00000000000..5857816f3a1 --- /dev/null +++ b/src/librbd/operation/SnapshotRenameRequest.h @@ -0,0 +1,59 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_OPERATION_SNAPSHOT_RENAME_REQUEST_H +#define CEPH_LIBRBD_OPERATION_SNAPSHOT_RENAME_REQUEST_H + +#include "librbd/operation/Request.h" +#include +#include + +class Context; + +namespace librbd { + +class ImageCtx; + +namespace operation { + +class SnapshotRenameRequest : public Request { +public: + /** + * Snap Rename goes through the following state machine: + * + * @verbatim + * + * + * | + * v + * STATE_RENAME_SNAP + * | + * v + * + * + * @endverbatim + * + */ + enum State { + STATE_RENAME_SNAP + }; + + SnapshotRenameRequest(ImageCtx &image_ctx, Context *on_finish, + uint64_t snap_id, const std::string &snap_name); + +protected: + virtual void send_op(); + virtual bool should_complete(int r); + +private: + uint64_t m_snap_id; + std::string m_snap_name; + State m_state; + + void send_rename_snap(); +}; + +} // namespace operation +} // namespace librbd + +#endif // CEPH_LIBRBD_OPERATION_SNAPSHOT_RENAME_REQUEST_H diff --git a/src/librbd/operation/SnapshotRollbackRequest.cc b/src/librbd/operation/SnapshotRollbackRequest.cc new file mode 100644 index 00000000000..33972eff4e9 --- /dev/null +++ b/src/librbd/operation/SnapshotRollbackRequest.cc @@ -0,0 +1,191 @@ +// -*- 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/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 { + +namespace { + +std::ostream& operator<<(std::ostream& os, + const SnapshotRollbackRequest::State& state) { + switch(state) { + case SnapshotRollbackRequest::STATE_RESIZE_IMAGE: + os << "RESIZE_IMAGE"; + break; + case SnapshotRollbackRequest::STATE_ROLLBACK_OBJECT_MAP: + os << "ROLLBACK_OBJECT_MAP"; + break; + case SnapshotRollbackRequest::STATE_ROLLBACK_OBJECTS: + os << "ROLLBACK_OBJECTS"; + break; + case SnapshotRollbackRequest::STATE_INVALIDATE_CACHE: + os << "INVALIDATE_CACHE"; + break; + default: + os << "UNKNOWN (" << static_cast(state) << ")"; + break; + } + return os; +} + +class C_RollbackObject : public C_AsyncObjectThrottle<> { +public: + C_RollbackObject(AsyncObjectThrottle<> &throttle, ImageCtx *image_ctx, + uint64_t snap_id, uint64_t object_num) + : C_AsyncObjectThrottle(throttle, *image_ctx), m_snap_id(snap_id), + m_object_num(object_num) { + } + + virtual int send() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "C_RollbackObject: " << __func__ << ": object_num=" + << m_object_num << dendl; + + std::string oid = m_image_ctx.get_object_name(m_object_num); + + librados::AioCompletion *rados_completion = + librados::Rados::aio_create_completion(this, NULL, rados_ctx_cb); + librados::ObjectWriteOperation op; + op.selfmanaged_snap_rollback(m_snap_id); + m_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; +}; + +} // anonymous namespace + +SnapshotRollbackRequest::SnapshotRollbackRequest(ImageCtx &image_ctx, + Context *on_finish, + const std::string &snap_name, + uint64_t snap_id, + uint64_t snap_size, + ProgressContext &prog_ctx) + : Request(image_ctx, on_finish), m_snap_name(snap_name), m_snap_id(snap_id), + m_snap_size(snap_size), m_prog_ctx(prog_ctx) { +} + +void SnapshotRollbackRequest::send_op() { + send_resize_image(); +} + +bool SnapshotRollbackRequest::should_complete(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": state=" << m_state << ", " + << "r=" << r << dendl; + if (r < 0) { + lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl; + return true; + } + + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + bool finished = false; + switch (m_state) { + case STATE_RESIZE_IMAGE: + send_rollback_object_map(); + break; + case STATE_ROLLBACK_OBJECT_MAP: + send_rollback_objects(); + break; + case STATE_ROLLBACK_OBJECTS: + finished = send_invalidate_cache(); + break; + case STATE_INVALIDATE_CACHE: + finished = true; + break; + default: + assert(false); + break; + } + return finished; +} + +void SnapshotRollbackRequest::send_resize_image() { + assert(m_image_ctx.owner_lock.is_locked()); + + uint64_t current_size; + { + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + current_size = m_image_ctx.get_image_size(CEPH_NOSNAP); + } + + if (current_size == m_snap_size) { + send_rollback_object_map(); + return; + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_RESIZE_IMAGE; + + ResizeRequest *req = new ResizeRequest(m_image_ctx, create_callback_context(), + m_snap_size, m_no_op_prog_ctx); + req->send(); +} + +void SnapshotRollbackRequest::send_rollback_object_map() { + assert(m_image_ctx.owner_lock.is_locked()); + + // TODO add object map support + send_rollback_objects(); +} + +void SnapshotRollbackRequest::send_rollback_objects() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_ROLLBACK_OBJECTS; + + uint64_t num_objects; + { + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + num_objects = Striper::get_num_objects(m_image_ctx.layout, + m_image_ctx.get_current_size()); + } + + Context *ctx = create_callback_context(); + AsyncObjectThrottle<>::ContextFactory context_factory( + boost::lambda::bind(boost::lambda::new_ptr(), + boost::lambda::_1, &m_image_ctx, m_snap_id, boost::lambda::_2)); + AsyncObjectThrottle<> *throttle = new AsyncObjectThrottle<>( + this, m_image_ctx, context_factory, ctx, &m_prog_ctx, 0, num_objects); + throttle->start_ops(m_image_ctx.concurrent_management_ops); +} + +bool SnapshotRollbackRequest::send_invalidate_cache() { + assert(m_image_ctx.owner_lock.is_locked()); + + if (m_image_ctx.object_cacher == NULL) { + return true; + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_INVALIDATE_CACHE; + + m_image_ctx.invalidate_cache(create_callback_context()); + return false; +} + +} // namespace operation +} // namespace librbd diff --git a/src/librbd/operation/SnapshotRollbackRequest.h b/src/librbd/operation/SnapshotRollbackRequest.h new file mode 100644 index 00000000000..972547e8c2a --- /dev/null +++ b/src/librbd/operation/SnapshotRollbackRequest.h @@ -0,0 +1,84 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_OPERATION_SNAPSHOT_ROLLBACK_REQUEST_H +#define CEPH_LIBRBD_OPERATION_SNAPSHOT_ROLLBACK_REQUEST_H + +#include "librbd/operation/Request.h" +#include "librbd/internal.h" +#include + +class Context; + +namespace librbd { + +class ImageCtx; +class ProgressContext; + +namespace operation { + +class SnapshotRollbackRequest : public Request { +public: + /** + * Snap Rollback goes through the following state machine: + * + * @verbatim + * + * ---------\ + * . | + * . v + * . STATE_RESIZE_IMAGE + * . | + * . (skip path) v + * . . . . > STATE_ROLLBACK_OBJECT_MAP + * . | + * . v + * . . . . > STATE_ROLLBACK_OBJECTS . . . + * | . + * v . + * STATE_INVALIDATE_CACHE . + * | . + * v . + * < . . . . . . . . + * + * @endverbatim + * + * The _RESIZE_IMAGE state is skipped if the image doesn't need to be resized. + * The _ROLLBACK_OBJECT_MAP state is skipped if the object map isn't enabled. + * The _INVALIDATE_CACHE state is skipped if the cache isn't enabled. + */ + enum State { + STATE_RESIZE_IMAGE, + STATE_ROLLBACK_OBJECT_MAP, + STATE_ROLLBACK_OBJECTS, + STATE_INVALIDATE_CACHE + }; + + SnapshotRollbackRequest(ImageCtx &image_ctx, Context *on_finish, + const std::string &snap_name, uint64_t snap_id, + uint64_t snap_size, ProgressContext &prog_ctx); + +protected: + virtual void send_op(); + virtual bool should_complete(int r); + +private: + std::string m_snap_name; + uint64_t m_snap_id; + uint64_t m_snap_size; + ProgressContext &m_prog_ctx; + + NoOpProgressContext m_no_op_prog_ctx; + State m_state; + + void send_resize_image(); + void send_rollback_object_map(); + void send_rollback_objects(); + bool send_invalidate_cache(); + +}; + +} // namespace operation +} // namespace librbd + +#endif // CEPH_LIBRBD_OPERATION_SNAPSHOT_ROLLBACK_REQUEST_H diff --git a/src/librbd/operation/SnapshotUnprotectRequest.cc b/src/librbd/operation/SnapshotUnprotectRequest.cc new file mode 100644 index 00000000000..6d6d334f40c --- /dev/null +++ b/src/librbd/operation/SnapshotUnprotectRequest.cc @@ -0,0 +1,317 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/operation/SnapshotUnprotectRequest.h" +#include "include/rados/librados.hpp" +#include "include/stringify.h" +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/AsyncObjectThrottle.h" +#include "librbd/ImageCtx.h" +#include "librbd/internal.h" +#include "librbd/parent_types.h" +#include +#include +#include +#include +#include + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::SnapshotUnprotectRequest: " + +namespace librbd { +namespace operation { + +namespace { + +typedef std::pair Pool; +typedef std::vector Pools; + +std::ostream& operator<<(std::ostream& os, + const SnapshotUnprotectRequest::State& state) { + switch(state) { + case SnapshotUnprotectRequest::STATE_UNPROTECT_SNAP_START: + os << "UNPROTECT_SNAP_START"; + break; + case SnapshotUnprotectRequest::STATE_SCAN_POOL_CHILDREN: + os << "SCAN_POOL_CHILDREN"; + break; + case SnapshotUnprotectRequest::STATE_UNPROTECT_SNAP_FINISH: + os << "UNPROTECT_SNAP_FINISH"; + break; + case SnapshotUnprotectRequest::STATE_UNPROTECT_SNAP_ROLLBACK: + os << "UNPROTECT_SNAP_ROLLBACK"; + break; + default: + os << "UNKNOWN (" << static_cast(state) << ")"; + break; + } + return os; +} + +class C_ScanPoolChildren : public C_AsyncObjectThrottle<> { +public: + C_ScanPoolChildren(AsyncObjectThrottle<> &throttle, ImageCtx *image_ctx, + const parent_spec &pspec, const Pools &pools, + size_t pool_idx) + : C_AsyncObjectThrottle(throttle, *image_ctx), m_pspec(pspec), + m_pool(pools[pool_idx]) { + } + + virtual int send() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << this << " scanning pool '" << m_pool.second << "'" + << dendl; + + librados::Rados rados(m_image_ctx.md_ctx); + int64_t base_tier; + int r = rados.pool_get_base_tier(m_pool.first, &base_tier); + if (r == -ENOENT) { + ldout(cct, 1) << "pool '" << m_pool.second << "' no longer exists" + << dendl; + return 1; + } else if (r < 0) { + lderr(cct) << "error retrieving base tier for pool '" + << m_pool.second << "'" << dendl; + return r; + } + if (m_pool.first != base_tier) { + // pool is a cache; skip it + return 1; + } + + r = rados.ioctx_create2(m_pool.first, m_pool_ioctx); + if (r == -ENOENT) { + ldout(cct, 1) << "pool '" << m_pool.second << "' no longer exists" + << dendl; + return 1; + } else if (r < 0) { + lderr(cct) << "can't create ioctx for pool '" << m_pool.second + << "'" << dendl; + return r; + } + + cls_client::get_children(&m_pool_ioctx, RBD_CHILDREN, m_pspec, &m_children, + this); + return 0; + } + +protected: + virtual void finish(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << this << " retrieved children: r=" << r << dendl; + + if (r == -ENOENT) { + // no children -- proceed with unprotect + r = 0; + } else if (r < 0) { + lderr(cct) << "cannot get children for pool '" << m_pool.second << "'" + << dendl; + } else { + lderr(cct) << "cannot unprotect: at least " << m_children.size() << " " + << "child(ren) [" << joinify(m_children.begin(), + m_children.end(), + std::string(",")) << "] " + << "in pool '" << m_pool.second << "'" << dendl; + r = -EBUSY; + } + C_AsyncObjectThrottle::finish(r); + } + +private: + parent_spec m_pspec; + Pool m_pool; + + IoCtx m_pool_ioctx; + std::set m_children; + bufferlist m_children_bl; +}; + +} // anonymous namespace + +SnapshotUnprotectRequest::SnapshotUnprotectRequest(ImageCtx &image_ctx, + Context *on_finish, + const std::string &snap_name) + : Request(image_ctx, on_finish), m_snap_name(snap_name), m_ret_val(0), + m_snap_id(CEPH_NOSNAP) { +} + +void SnapshotUnprotectRequest::send_op() { + send_unprotect_snap_start(); +} + +bool SnapshotUnprotectRequest::should_complete(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": state=" << m_state << ", " + << "r=" << r << dendl; + if (r < 0) { + lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl; + if (m_ret_val == 0) { + m_ret_val = r; + } + } + + // use a different state machine once an error is encountered + if (m_ret_val < 0) { + return should_complete_error(); + } + + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + bool finished = false; + switch (m_state) { + case STATE_UNPROTECT_SNAP_START: + send_scan_pool_children(); + break; + case STATE_SCAN_POOL_CHILDREN: + send_unprotect_snap_finish(); + break; + case STATE_UNPROTECT_SNAP_FINISH: + finished = true; + break; + default: + assert(false); + break; + } + return finished; +} + +bool SnapshotUnprotectRequest::should_complete_error() { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + CephContext *cct = m_image_ctx.cct; + lderr(cct) << this << " " << __func__ << ": " + << "ret_val=" << m_ret_val << dendl; + + bool finished = true; + if (m_state == STATE_SCAN_POOL_CHILDREN || + m_state == STATE_UNPROTECT_SNAP_FINISH) { + send_unprotect_snap_rollback(); + finished = false; + } + return finished; +} + +void SnapshotUnprotectRequest::send_unprotect_snap_start() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + m_state = STATE_UNPROTECT_SNAP_START; + + int r = verify_and_send_unprotect_snap_start(); + if (r < 0) { + async_complete(r); + return; + } +} + +void SnapshotUnprotectRequest::send_scan_pool_children() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_SCAN_POOL_CHILDREN; + + // search all pools for children depending on this snapshot + // TODO add async version of wait_for_latest_osdmap + librados::Rados rados(m_image_ctx.md_ctx); + rados.wait_for_latest_osdmap(); + + // protect against pools being renamed/deleted + std::list pool_list; + rados.pool_list2(pool_list); + + parent_spec pspec(m_image_ctx.md_ctx.get_id(), m_image_ctx.id, m_snap_id); + Pools pools(pool_list.begin(), pool_list.end()); + + Context *ctx = create_callback_context(); + AsyncObjectThrottle<>::ContextFactory context_factory( + boost::lambda::bind(boost::lambda::new_ptr(), + boost::lambda::_1, &m_image_ctx, pspec, pools, boost::lambda::_2)); + AsyncObjectThrottle<> *throttle = new AsyncObjectThrottle<>( + this, m_image_ctx, context_factory, ctx, NULL, 0, + pools.size()); + throttle->start_ops(m_image_ctx.concurrent_management_ops); +} + +void SnapshotUnprotectRequest::send_unprotect_snap_finish() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + m_state = STATE_UNPROTECT_SNAP_FINISH; + + librados::ObjectWriteOperation op; + cls_client::set_protection_status(&op, m_snap_id, + RBD_PROTECTION_STATUS_UNPROTECTED); + + librados::AioCompletion *comp = create_callback_completion(); + int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op); + assert(r == 0); + comp->release(); +} + +void SnapshotUnprotectRequest::send_unprotect_snap_rollback() { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + m_state = STATE_UNPROTECT_SNAP_ROLLBACK; + + librados::ObjectWriteOperation op; + cls_client::set_protection_status(&op, m_snap_id, + RBD_PROTECTION_STATUS_PROTECTED); + + librados::AioCompletion *comp = create_callback_completion(); + int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op); + assert(r == 0); + comp->release(); +} + +int SnapshotUnprotectRequest::verify_and_send_unprotect_snap_start() { + RWLock::RLocker md_locker(m_image_ctx.md_lock); + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + + CephContext *cct = m_image_ctx.cct; + if ((m_image_ctx.features & RBD_FEATURE_LAYERING) == 0) { + lderr(cct) << "image must support layering" << dendl; + return -ENOSYS; + } + + m_snap_id = m_image_ctx.get_snap_id(m_snap_name); + if (m_snap_id == CEPH_NOSNAP) { + return -ENOENT; + } + + bool is_unprotected; + int r = m_image_ctx.is_snap_unprotected(m_snap_id, &is_unprotected); + if (r < 0) { + return r; + } + + if (is_unprotected) { + lderr(cct) << "snapshot is already unprotected" << dendl; + return -EINVAL; + } + + librados::ObjectWriteOperation op; + cls_client::set_protection_status(&op, m_snap_id, + RBD_PROTECTION_STATUS_UNPROTECTING); + + librados::AioCompletion *comp = create_callback_completion(); + r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op); + assert(r == 0); + comp->release(); + + // TODO legacy code threw a notification post UNPROTECTING update -- required? + return 0; +} + +} // namespace operation +} // namespace librbd + diff --git a/src/librbd/operation/SnapshotUnprotectRequest.h b/src/librbd/operation/SnapshotUnprotectRequest.h new file mode 100644 index 00000000000..faf1e138ef4 --- /dev/null +++ b/src/librbd/operation/SnapshotUnprotectRequest.h @@ -0,0 +1,86 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_OPERATION_SNAPSHOT_UNPROTECT_REQUEST_H +#define CEPH_LIBRBD_OPERATION_SNAPSHOT_UNPROTECT_REQUEST_H + +#include "librbd/operation/Request.h" +#include +#include + +class Context; + +namespace librbd { + +class ImageCtx; + +namespace operation { + +class SnapshotUnprotectRequest : public Request { +public: + /** + * Snap Unprotect goes through the following state machine: + * + * @verbatim + * + * + * | + * v + * STATE_UNPROTECT_SNAP_START + * | + * v + * STATE_SCAN_POOL_CHILDREN * * * * > STATE_UNPROTECT_SNAP_ROLLBACK + * | | + * v | + * STATE_UNPROTECT_SNAP_FINISH | + * | | + * v | + * <----------------------------/ + * + * @endverbatim + * + * If the unprotect operation needs to abort, the error path is followed + * to rollback the unprotect in-progress status on the image. + */ + enum State { + STATE_UNPROTECT_SNAP_START, + STATE_SCAN_POOL_CHILDREN, + STATE_UNPROTECT_SNAP_FINISH, + STATE_UNPROTECT_SNAP_ROLLBACK + }; + + SnapshotUnprotectRequest(ImageCtx &image_ctx, Context *on_finish, + const std::string &snap_name); + +protected: + virtual void send_op(); + virtual bool should_complete(int r); + + virtual int filter_return_code(int r) const { + if (m_ret_val < 0) { + return m_ret_val; + } + return 0; + } + +private: + std::string m_snap_name; + State m_state; + + int m_ret_val; + uint64_t m_snap_id; + + bool should_complete_error(); + + void send_unprotect_snap_start(); + void send_scan_pool_children(); + void send_unprotect_snap_finish(); + void send_unprotect_snap_rollback(); + + int verify_and_send_unprotect_snap_start(); +}; + +} // namespace operation +} // namespace librbd + +#endif // CEPH_LIBRBD_OPERATION_SNAPSHOT_UNPROTECT_REQUEST_H