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);
}
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);
}
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);
}
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
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
#include <errno.h>
#include <limits.h>
+#include "include/types.h"
#include "common/ceph_context.h"
#include "common/dout.h"
#include "common/errno.h"
#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"
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<string>& names)
{
CephContext *cct = (CephContext *)io_ctx.cct();
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());
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<snap_t, SnapInfo>::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;
}
}
} 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;
}
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;
}
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;
}
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<std::pair<int64_t, std::string> > pools;
- rados.pool_list2(pools);
-
- for (std::list<std::pair<int64_t, std::string> >::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<std::string> 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,
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<snapid_t> 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<snap_t, SnapInfo>::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);
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;
}
};
class Context;
+class RWLock;
class SimpleThrottle;
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);
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);
--- /dev/null
+// -*- 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<uint32_t>(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<snapid_t> 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
--- /dev/null
+// -*- 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 <iosfwd>
+#include <string>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace operation {
+
+class SnapshotCreateRequest : public Request {
+public:
+ /**
+ * Snap Create goes through the following state machine:
+ *
+ * @verbatim
+ *
+ * <start>
+ * |
+ * 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 *
+ * \----------------> <finish> < * * * * *
+ *
+ * @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
--- /dev/null
+// -*- 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
+
--- /dev/null
+// -*- 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 <iosfwd>
+#include <string>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace operation {
+
+class SnapshotProtectRequest : public Request {
+public:
+ /**
+ * Snap Protect goes through the following state machine:
+ *
+ * @verbatim
+ *
+ * <start>
+ * |
+ * v
+ * STATE_PROTECT_SNAP
+ * |
+ * v
+ * <finish>
+ *
+ * @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
--- /dev/null
+// -*- 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<uint32_t>(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<uint64_t, SnapInfo>::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
--- /dev/null
+// -*- 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 <string>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace operation {
+
+class SnapshotRemoveRequest : public Request {
+public:
+ /**
+ * Snap Remove goes through the following state machine:
+ *
+ * @verbatim
+ *
+ * <start> ------\
+ * . |
+ * . v
+ * . STATE_REMOVE_OBJECT_MAP
+ * . | .
+ * . v .
+ * . . > STATE_REMOVE_CHILD .
+ * . | .
+ * . | . . . .
+ * . | .
+ * . v v
+ * . . > STATE_REMOVE_SNAP
+ * |
+ * v
+ * STATE_RELEASE_SNAP_ID
+ * |
+ * v
+ * <finish>
+ *
+ * @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
--- /dev/null
+// -*- 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
+
--- /dev/null
+// -*- 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 <iosfwd>
+#include <string>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace operation {
+
+class SnapshotRenameRequest : public Request {
+public:
+ /**
+ * Snap Rename goes through the following state machine:
+ *
+ * @verbatim
+ *
+ * <start>
+ * |
+ * v
+ * STATE_RENAME_SNAP
+ * |
+ * v
+ * <finish>
+ *
+ * @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
--- /dev/null
+// -*- 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 <boost/lambda/bind.hpp>
+#include <boost/lambda/construct.hpp>
+
+#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<uint32_t>(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<C_RollbackObject>(),
+ 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
--- /dev/null
+// -*- 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 <string>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+class ProgressContext;
+
+namespace operation {
+
+class SnapshotRollbackRequest : public Request {
+public:
+ /**
+ * Snap Rollback goes through the following state machine:
+ *
+ * @verbatim
+ *
+ * <start> ---------\
+ * . |
+ * . v
+ * . STATE_RESIZE_IMAGE
+ * . |
+ * . (skip path) v
+ * . . . . > STATE_ROLLBACK_OBJECT_MAP
+ * . |
+ * . v
+ * . . . . > STATE_ROLLBACK_OBJECTS . . .
+ * | .
+ * v .
+ * STATE_INVALIDATE_CACHE .
+ * | .
+ * v .
+ * <finish> < . . . . . . . .
+ *
+ * @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
--- /dev/null
+// -*- 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 <list>
+#include <set>
+#include <vector>
+#include <boost/lambda/bind.hpp>
+#include <boost/lambda/construct.hpp>
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::SnapshotUnprotectRequest: "
+
+namespace librbd {
+namespace operation {
+
+namespace {
+
+typedef std::pair<int64_t, std::string> Pool;
+typedef std::vector<Pool> 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<uint32_t>(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<std::string> 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> 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<C_ScanPoolChildren>(),
+ 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
+
--- /dev/null
+// -*- 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 <iosfwd>
+#include <string>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace operation {
+
+class SnapshotUnprotectRequest : public Request {
+public:
+ /**
+ * Snap Unprotect goes through the following state machine:
+ *
+ * @verbatim
+ *
+ * <start>
+ * |
+ * v
+ * STATE_UNPROTECT_SNAP_START
+ * |
+ * v
+ * STATE_SCAN_POOL_CHILDREN * * * * > STATE_UNPROTECT_SNAP_ROLLBACK
+ * | |
+ * v |
+ * STATE_UNPROTECT_SNAP_FINISH |
+ * | |
+ * v |
+ * <finish> <----------------------------/
+ *
+ * @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