X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Flibrbd%2FOperations.cc;fp=src%2Fceph%2Fsrc%2Flibrbd%2FOperations.cc;h=3fc256d72ceebf9f685604750b59d85d706e3091;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/librbd/Operations.cc b/src/ceph/src/librbd/Operations.cc new file mode 100644 index 0000000..3fc256d --- /dev/null +++ b/src/ceph/src/librbd/Operations.cc @@ -0,0 +1,1520 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "cls/rbd/cls_rbd_types.h" +#include "librbd/Operations.h" +#include "common/dout.h" +#include "common/errno.h" +#include "common/WorkQueue.h" + +#include "librbd/ExclusiveLock.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageState.h" +#include "librbd/ImageWatcher.h" +#include "librbd/ObjectMap.h" +#include "librbd/Utils.h" +#include "librbd/journal/DisabledPolicy.h" +#include "librbd/journal/StandardPolicy.h" +#include "librbd/operation/DisableFeaturesRequest.h" +#include "librbd/operation/EnableFeaturesRequest.h" +#include "librbd/operation/FlattenRequest.h" +#include "librbd/operation/MetadataRemoveRequest.h" +#include "librbd/operation/MetadataSetRequest.h" +#include "librbd/operation/ObjectMapIterate.h" +#include "librbd/operation/RebuildObjectMapRequest.h" +#include "librbd/operation/RenameRequest.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/SnapshotLimitRequest.h" +#include +#include +#include + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::Operations: " + +namespace librbd { + +namespace { + +template +struct C_NotifyUpdate : public Context { + I &image_ctx; + Context *on_finish; + bool notified = false; + + C_NotifyUpdate(I &image_ctx, Context *on_finish) + : image_ctx(image_ctx), on_finish(on_finish) { + } + + void complete(int r) override { + CephContext *cct = image_ctx.cct; + if (notified) { + if (r == -ETIMEDOUT) { + // don't fail the op if a peer fails to get the update notification + lderr(cct) << "update notification timed-out" << dendl; + r = 0; + } else if (r == -ENOENT) { + // don't fail if header is missing (e.g. v1 image rename) + ldout(cct, 5) << "update notification on missing header" << dendl; + r = 0; + } else if (r < 0) { + lderr(cct) << "update notification failed: " << cpp_strerror(r) + << dendl; + } + Context::complete(r); + return; + } + + if (r < 0) { + // op failed -- no need to send update notification + Context::complete(r); + return; + } + + notified = true; + image_ctx.notify_update(this); + } + void finish(int r) override { + on_finish->complete(r); + } +}; + +template +struct C_InvokeAsyncRequest : public Context { + /** + * @verbatim + * + * + * | + * . . . . . . | . . . . . . . . . . . . . . . . . . + * . . | . . + * . v v v . + * . REFRESH_IMAGE (skip if not needed) . + * . | . + * . v . + * . ACQUIRE_LOCK (skip if exclusive lock . + * . | disabled or has lock) . + * . | . + * . /--------/ \--------\ . . . . . . . . . . . . . + * . | | . + * . v v . + * LOCAL_REQUEST REMOTE_REQUEST + * | | + * | | + * \--------\ /--------/ + * | + * v + * + * + * @endverbatim + */ + + I &image_ctx; + std::string request_type; + bool permit_snapshot; + boost::function local; + boost::function remote; + std::set filter_error_codes; + Context *on_finish; + bool request_lock = false; + + C_InvokeAsyncRequest(I &image_ctx, const std::string& request_type, + bool permit_snapshot, + const boost::function& local, + const boost::function& remote, + const std::set &filter_error_codes, + Context *on_finish) + : image_ctx(image_ctx), request_type(request_type), + permit_snapshot(permit_snapshot), local(local), remote(remote), + filter_error_codes(filter_error_codes), on_finish(on_finish) { + } + + void send() { + send_refresh_image(); + } + + void send_refresh_image() { + if (!image_ctx.state->is_refresh_required()) { + send_acquire_exclusive_lock(); + return; + } + + CephContext *cct = image_ctx.cct; + ldout(cct, 20) << __func__ << dendl; + + Context *ctx = util::create_context_callback< + C_InvokeAsyncRequest, + &C_InvokeAsyncRequest::handle_refresh_image>(this); + image_ctx.state->refresh(ctx); + } + + void handle_refresh_image(int r) { + CephContext *cct = image_ctx.cct; + ldout(cct, 20) << __func__ << ": r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to refresh image: " << cpp_strerror(r) << dendl; + complete(r); + return; + } + + send_acquire_exclusive_lock(); + } + + void send_acquire_exclusive_lock() { + // context can complete before owner_lock is unlocked + RWLock &owner_lock(image_ctx.owner_lock); + owner_lock.get_read(); + image_ctx.snap_lock.get_read(); + if (image_ctx.read_only || + (!permit_snapshot && image_ctx.snap_id != CEPH_NOSNAP)) { + image_ctx.snap_lock.put_read(); + owner_lock.put_read(); + complete(-EROFS); + return; + } + image_ctx.snap_lock.put_read(); + + if (image_ctx.exclusive_lock == nullptr) { + send_local_request(); + owner_lock.put_read(); + return; + } else if (image_ctx.image_watcher == nullptr) { + owner_lock.put_read(); + complete(-EROFS); + return; + } + + if (image_ctx.exclusive_lock->is_lock_owner() && + image_ctx.exclusive_lock->accept_requests()) { + send_local_request(); + owner_lock.put_read(); + return; + } + + CephContext *cct = image_ctx.cct; + ldout(cct, 20) << __func__ << dendl; + + Context *ctx = util::create_async_context_callback( + image_ctx, util::create_context_callback< + C_InvokeAsyncRequest, + &C_InvokeAsyncRequest::handle_acquire_exclusive_lock>(this)); + + if (request_lock) { + // current lock owner doesn't support op -- try to perform + // the action locally + request_lock = false; + image_ctx.exclusive_lock->acquire_lock(ctx); + } else { + image_ctx.exclusive_lock->try_acquire_lock(ctx); + } + owner_lock.put_read(); + } + + void handle_acquire_exclusive_lock(int r) { + CephContext *cct = image_ctx.cct; + ldout(cct, 20) << __func__ << ": r=" << r << dendl; + + if (r < 0) { + complete(-EROFS); + return; + } + + // context can complete before owner_lock is unlocked + RWLock &owner_lock(image_ctx.owner_lock); + owner_lock.get_read(); + if (image_ctx.exclusive_lock->is_lock_owner()) { + send_local_request(); + owner_lock.put_read(); + return; + } + + send_remote_request(); + owner_lock.put_read(); + } + + void send_remote_request() { + assert(image_ctx.owner_lock.is_locked()); + + CephContext *cct = image_ctx.cct; + ldout(cct, 20) << __func__ << dendl; + + Context *ctx = util::create_context_callback< + C_InvokeAsyncRequest, &C_InvokeAsyncRequest::handle_remote_request>( + this); + remote(ctx); + } + + void handle_remote_request(int r) { + CephContext *cct = image_ctx.cct; + ldout(cct, 20) << __func__ << ": r=" << r << dendl; + + if (r == -EOPNOTSUPP) { + ldout(cct, 5) << request_type << " not supported by current lock owner" + << dendl; + request_lock = true; + send_refresh_image(); + return; + } else if (r != -ETIMEDOUT && r != -ERESTART) { + image_ctx.state->handle_update_notification(); + + complete(r); + return; + } + + ldout(cct, 5) << request_type << " timed out notifying lock owner" + << dendl; + send_refresh_image(); + } + + void send_local_request() { + assert(image_ctx.owner_lock.is_locked()); + + CephContext *cct = image_ctx.cct; + ldout(cct, 20) << __func__ << dendl; + + Context *ctx = util::create_async_context_callback( + image_ctx, util::create_context_callback< + C_InvokeAsyncRequest, + &C_InvokeAsyncRequest::handle_local_request>(this)); + local(ctx); + } + + void handle_local_request(int r) { + CephContext *cct = image_ctx.cct; + ldout(cct, 20) << __func__ << ": r=" << r << dendl; + + if (r == -ERESTART) { + send_refresh_image(); + return; + } + complete(r); + } + + void finish(int r) override { + if (filter_error_codes.count(r) != 0) { + r = 0; + } + on_finish->complete(r); + } +}; + +template +bool needs_invalidate(I& image_ctx, uint64_t object_no, + uint8_t current_state, uint8_t new_state) { + if ( (current_state == OBJECT_EXISTS || + current_state == OBJECT_EXISTS_CLEAN) && + (new_state == OBJECT_NONEXISTENT || + new_state == OBJECT_PENDING)) { + return false; + } + return true; +} + +} // anonymous namespace + +template +Operations::Operations(I &image_ctx) + : m_image_ctx(image_ctx), m_async_request_seq(0) { +} + +template +int Operations::flatten(ProgressContext &prog_ctx) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "flatten" << dendl; + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + if (m_image_ctx.read_only) { + return -EROFS; + } + + { + RWLock::RLocker parent_locker(m_image_ctx.parent_lock); + if (m_image_ctx.parent_md.spec.pool_id == -1) { + lderr(cct) << "image has no parent" << dendl; + return -EINVAL; + } + } + + uint64_t request_id = ++m_async_request_seq; + r = invoke_async_request("flatten", false, + boost::bind(&Operations::execute_flatten, this, + boost::ref(prog_ctx), _1), + boost::bind(&ImageWatcher::notify_flatten, + m_image_ctx.image_watcher, request_id, + boost::ref(prog_ctx), _1)); + + if (r < 0 && r != -EINVAL) { + return r; + } + ldout(cct, 20) << "flatten finished" << dendl; + return 0; +} + +template +void Operations::execute_flatten(ProgressContext &prog_ctx, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 20) << "flatten" << dendl; + + if (m_image_ctx.read_only) { + on_finish->complete(-EROFS); + return; + } + + m_image_ctx.snap_lock.get_read(); + m_image_ctx.parent_lock.get_read(); + + // can't flatten a non-clone + if (m_image_ctx.parent_md.spec.pool_id == -1) { + lderr(cct) << "image has no parent" << dendl; + m_image_ctx.parent_lock.put_read(); + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EINVAL); + return; + } + if (m_image_ctx.snap_id != CEPH_NOSNAP) { + lderr(cct) << "snapshots cannot be flattened" << dendl; + m_image_ctx.parent_lock.put_read(); + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EROFS); + return; + } + + ::SnapContext snapc = m_image_ctx.snapc; + assert(m_image_ctx.parent != NULL); + + uint64_t overlap; + int r = m_image_ctx.get_parent_overlap(CEPH_NOSNAP, &overlap); + assert(r == 0); + assert(overlap <= m_image_ctx.size); + + uint64_t object_size = m_image_ctx.get_object_size(); + uint64_t overlap_objects = Striper::get_num_objects(m_image_ctx.layout, + overlap); + + m_image_ctx.parent_lock.put_read(); + m_image_ctx.snap_lock.put_read(); + + operation::FlattenRequest *req = new operation::FlattenRequest( + m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), object_size, + overlap_objects, snapc, prog_ctx); + req->send(); +} + +template +int Operations::rebuild_object_map(ProgressContext &prog_ctx) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << "rebuild_object_map" << dendl; + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + uint64_t request_id = ++m_async_request_seq; + r = invoke_async_request("rebuild object map", true, + boost::bind(&Operations::execute_rebuild_object_map, + this, boost::ref(prog_ctx), _1), + boost::bind(&ImageWatcher::notify_rebuild_object_map, + m_image_ctx.image_watcher, request_id, + boost::ref(prog_ctx), _1)); + + ldout(cct, 10) << "rebuild object map finished" << dendl; + if (r < 0) { + return r; + } + return 0; +} + +template +void Operations::execute_rebuild_object_map(ProgressContext &prog_ctx, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + if (m_image_ctx.read_only) { + on_finish->complete(-EROFS); + return; + } + if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP)) { + lderr(cct) << "image must support object-map feature" << dendl; + on_finish->complete(-EINVAL); + return; + } + + operation::RebuildObjectMapRequest *req = + new operation::RebuildObjectMapRequest( + m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), prog_ctx); + req->send(); +} + +template +int Operations::check_object_map(ProgressContext &prog_ctx) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + r = invoke_async_request("check object map", true, + boost::bind(&Operations::check_object_map, this, + boost::ref(prog_ctx), _1), + [] (Context *c) { c->complete(-EOPNOTSUPP); }); + + return r; +} + +template +void Operations::object_map_iterate(ProgressContext &prog_ctx, + operation::ObjectIterateWork handle_mismatch, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + + if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP)) { + on_finish->complete(-EINVAL); + return; + } + + operation::ObjectMapIterateRequest *req = + new operation::ObjectMapIterateRequest(m_image_ctx, on_finish, + prog_ctx, handle_mismatch); + req->send(); +} + +template +void Operations::check_object_map(ProgressContext &prog_ctx, + Context *on_finish) { + object_map_iterate(prog_ctx, needs_invalidate, on_finish); +} + +template +int Operations::rename(const char *dstname) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": dest_name=" << dstname + << dendl; + + int r = librbd::detect_format(m_image_ctx.md_ctx, dstname, NULL, NULL); + if (r < 0 && r != -ENOENT) { + lderr(cct) << "error checking for existing image called " + << dstname << ":" << cpp_strerror(r) << dendl; + return r; + } + if (r == 0) { + lderr(cct) << "rbd image " << dstname << " already exists" << dendl; + return -EEXIST; + } + + if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { + r = invoke_async_request("rename", true, + boost::bind(&Operations::execute_rename, this, + dstname, _1), + boost::bind(&ImageWatcher::notify_rename, + m_image_ctx.image_watcher, dstname, + _1)); + if (r < 0 && r != -EEXIST) { + return r; + } + } else { + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + C_SaferCond cond_ctx; + execute_rename(dstname, &cond_ctx); + + r = cond_ctx.wait(); + if (r < 0) { + return r; + } + } + + m_image_ctx.set_image_name(dstname); + return 0; +} + +template +void Operations::execute_rename(const std::string &dest_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + } + + m_image_ctx.snap_lock.get_read(); + if (m_image_ctx.name == dest_name) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EEXIST); + return; + } + m_image_ctx.snap_lock.put_read(); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": dest_name=" << dest_name + << dendl; + + if (m_image_ctx.old_format) { + // unregister watch before and register back after rename + on_finish = new C_NotifyUpdate(m_image_ctx, on_finish); + on_finish = new FunctionContext([this, on_finish](int r) { + if (m_image_ctx.old_format) { + m_image_ctx.image_watcher->set_oid(m_image_ctx.header_oid); + } + m_image_ctx.image_watcher->register_watch(on_finish); + }); + on_finish = new FunctionContext([this, dest_name, on_finish](int r) { + operation::RenameRequest *req = new operation::RenameRequest( + m_image_ctx, on_finish, dest_name); + req->send(); + }); + m_image_ctx.image_watcher->unregister_watch(on_finish); + return; + } + operation::RenameRequest *req = new operation::RenameRequest( + m_image_ctx, on_finish, dest_name); + req->send(); +} + +template +int Operations::resize(uint64_t size, bool allow_shrink, ProgressContext& prog_ctx) { + CephContext *cct = m_image_ctx.cct; + + m_image_ctx.snap_lock.get_read(); + ldout(cct, 5) << this << " " << __func__ << ": " + << "size=" << m_image_ctx.size << ", " + << "new_size=" << size << dendl; + m_image_ctx.snap_lock.put_read(); + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + if (m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP) && + !ObjectMap<>::is_compatible(m_image_ctx.layout, size)) { + lderr(cct) << "New size not compatible with object map" << dendl; + return -EINVAL; + } + + uint64_t request_id = ++m_async_request_seq; + r = invoke_async_request("resize", false, + boost::bind(&Operations::execute_resize, this, + size, allow_shrink, boost::ref(prog_ctx), _1, 0), + boost::bind(&ImageWatcher::notify_resize, + m_image_ctx.image_watcher, request_id, + size, allow_shrink, boost::ref(prog_ctx), _1)); + + m_image_ctx.perfcounter->inc(l_librbd_resize); + ldout(cct, 2) << "resize finished" << dendl; + return r; +} + +template +void Operations::execute_resize(uint64_t size, bool allow_shrink, ProgressContext &prog_ctx, + Context *on_finish, + uint64_t journal_op_tid) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + + CephContext *cct = m_image_ctx.cct; + m_image_ctx.snap_lock.get_read(); + ldout(cct, 5) << this << " " << __func__ << ": " + << "size=" << m_image_ctx.size << ", " + << "new_size=" << size << dendl; + + if (m_image_ctx.snap_id != CEPH_NOSNAP || m_image_ctx.read_only) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EROFS); + return; + } else if (m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP, + m_image_ctx.snap_lock) && + !ObjectMap<>::is_compatible(m_image_ctx.layout, size)) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EINVAL); + return; + } + m_image_ctx.snap_lock.put_read(); + + operation::ResizeRequest *req = new operation::ResizeRequest( + m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), size, allow_shrink, + prog_ctx, journal_op_tid, false); + req->send(); +} + +template +int Operations::snap_create(const cls::rbd::SnapshotNamespace &snap_namespace, + const char *snap_name) { + if (m_image_ctx.read_only) { + return -EROFS; + } + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + C_SaferCond ctx; + snap_create(snap_namespace, snap_name, &ctx); + r = ctx.wait(); + + if (r < 0) { + return r; + } + + m_image_ctx.perfcounter->inc(l_librbd_snap_create); + return r; +} + +template +void Operations::snap_create(const cls::rbd::SnapshotNamespace &snap_namespace, + const char *snap_name, + Context *on_finish) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name + << dendl; + + if (m_image_ctx.read_only) { + on_finish->complete(-EROFS); + return; + } + + m_image_ctx.snap_lock.get_read(); + if (m_image_ctx.get_snap_id(snap_namespace, snap_name) != CEPH_NOSNAP) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EEXIST); + return; + } + m_image_ctx.snap_lock.put_read(); + + C_InvokeAsyncRequest *req = new C_InvokeAsyncRequest( + m_image_ctx, "snap_create", true, + boost::bind(&Operations::execute_snap_create, this, snap_namespace, snap_name, + _1, 0, false), + boost::bind(&ImageWatcher::notify_snap_create, m_image_ctx.image_watcher, + snap_namespace, snap_name, _1), + {-EEXIST}, on_finish); + req->send(); +} + +template +void Operations::execute_snap_create(const cls::rbd::SnapshotNamespace &snap_namespace, + const std::string &snap_name, + Context *on_finish, + uint64_t journal_op_tid, + bool skip_object_map) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name + << dendl; + + m_image_ctx.snap_lock.get_read(); + if (m_image_ctx.get_snap_id(snap_namespace, snap_name) != CEPH_NOSNAP) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EEXIST); + return; + } + m_image_ctx.snap_lock.put_read(); + + operation::SnapshotCreateRequest *req = + new operation::SnapshotCreateRequest( + m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), + snap_namespace, snap_name, journal_op_tid, skip_object_map); + req->send(); +} + +template +int Operations::snap_rollback(const cls::rbd::SnapshotNamespace& snap_namespace, + const char *snap_name, + ProgressContext& prog_ctx) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name + << dendl; + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) + return r; + + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + { + // need to drop snap_lock before invalidating cache + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + if (!m_image_ctx.snap_exists) { + return -ENOENT; + } + + if (m_image_ctx.snap_id != CEPH_NOSNAP || m_image_ctx.read_only) { + return -EROFS; + } + + uint64_t snap_id = m_image_ctx.get_snap_id(snap_namespace, snap_name); + if (snap_id == CEPH_NOSNAP) { + lderr(cct) << "No such snapshot found." << dendl; + return -ENOENT; + } + } + + r = prepare_image_update(); + if (r < 0) { + return -EROFS; + } + if (m_image_ctx.exclusive_lock != nullptr && + !m_image_ctx.exclusive_lock->is_lock_owner()) { + return -EROFS; + } + + C_SaferCond cond_ctx; + execute_snap_rollback(snap_namespace, snap_name, prog_ctx, &cond_ctx); + r = cond_ctx.wait(); + if (r < 0) { + return r; + } + + m_image_ctx.perfcounter->inc(l_librbd_snap_rollback); + return r; +} + +template +void Operations::execute_snap_rollback(const cls::rbd::SnapshotNamespace& snap_namespace, + const std::string &snap_name, + ProgressContext& prog_ctx, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name + << dendl; + + m_image_ctx.snap_lock.get_read(); + uint64_t snap_id = m_image_ctx.get_snap_id(snap_namespace, snap_name); + if (snap_id == CEPH_NOSNAP) { + lderr(cct) << "No such snapshot found." << dendl; + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-ENOENT); + return; + } + + uint64_t new_size = m_image_ctx.get_image_size(snap_id); + m_image_ctx.snap_lock.put_read(); + + // async mode used for journal replay + operation::SnapshotRollbackRequest *request = + new operation::SnapshotRollbackRequest( + m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), snap_namespace, snap_name, + snap_id, new_size, prog_ctx); + request->send(); +} + +template +int Operations::snap_remove(const cls::rbd::SnapshotNamespace& snap_namespace, + const char *snap_name) { + if (m_image_ctx.read_only) { + return -EROFS; + } + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + C_SaferCond ctx; + snap_remove(snap_namespace, snap_name, &ctx); + r = ctx.wait(); + + if (r < 0) { + return r; + } + + m_image_ctx.perfcounter->inc(l_librbd_snap_remove); + return 0; +} + +template +void Operations::snap_remove(const cls::rbd::SnapshotNamespace& snap_namespace, + const char *snap_name, + Context *on_finish) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name + << dendl; + + if (m_image_ctx.read_only) { + on_finish->complete(-EROFS); + return; + } + + // quickly filter out duplicate ops + m_image_ctx.snap_lock.get_read(); + if (m_image_ctx.get_snap_id(snap_namespace, snap_name) == CEPH_NOSNAP) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-ENOENT); + return; + } + + bool proxy_op = ((m_image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0 || + (m_image_ctx.features & RBD_FEATURE_JOURNALING) != 0); + m_image_ctx.snap_lock.put_read(); + + if (proxy_op) { + C_InvokeAsyncRequest *req = new C_InvokeAsyncRequest( + m_image_ctx, "snap_remove", true, + boost::bind(&Operations::execute_snap_remove, this, snap_namespace, snap_name, _1), + boost::bind(&ImageWatcher::notify_snap_remove, m_image_ctx.image_watcher, + snap_namespace, snap_name, _1), + {-ENOENT}, on_finish); + req->send(); + } else { + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + execute_snap_remove(snap_namespace, snap_name, on_finish); + } +} + +template +void Operations::execute_snap_remove(const cls::rbd::SnapshotNamespace& snap_namespace, + const std::string &snap_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + { + if ((m_image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0) { + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + } + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name + << dendl; + + m_image_ctx.snap_lock.get_read(); + uint64_t snap_id = m_image_ctx.get_snap_id(snap_namespace, snap_name); + if (snap_id == CEPH_NOSNAP) { + lderr(m_image_ctx.cct) << "No such snapshot found." << dendl; + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-ENOENT); + return; + } + + bool is_protected; + int r = m_image_ctx.is_snap_protected(snap_id, &is_protected); + if (r < 0) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(r); + return; + } else if (is_protected) { + lderr(m_image_ctx.cct) << "snapshot is protected" << dendl; + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EBUSY); + return; + } + m_image_ctx.snap_lock.put_read(); + + operation::SnapshotRemoveRequest *req = + new operation::SnapshotRemoveRequest( + m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), + snap_namespace, snap_name, snap_id); + req->send(); +} + +template +int Operations::snap_rename(const char *srcname, const char *dstname) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": " + << "snap_name=" << srcname << ", " + << "new_snap_name=" << dstname << dendl; + + snapid_t snap_id; + if (m_image_ctx.read_only) { + return -EROFS; + } + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) + return r; + + { + RWLock::RLocker l(m_image_ctx.snap_lock); + snap_id = m_image_ctx.get_snap_id(cls::rbd::UserSnapshotNamespace(), srcname); + if (snap_id == CEPH_NOSNAP) { + return -ENOENT; + } + if (m_image_ctx.get_snap_id(cls::rbd::UserSnapshotNamespace(), dstname) != CEPH_NOSNAP) { + return -EEXIST; + } + } + + if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { + r = invoke_async_request("snap_rename", true, + boost::bind(&Operations::execute_snap_rename, + this, snap_id, dstname, _1), + boost::bind(&ImageWatcher::notify_snap_rename, + m_image_ctx.image_watcher, snap_id, + dstname, _1)); + if (r < 0 && r != -EEXIST) { + return r; + } + } else { + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + C_SaferCond cond_ctx; + execute_snap_rename(snap_id, dstname, &cond_ctx); + + r = cond_ctx.wait(); + if (r < 0) { + return r; + } + } + + m_image_ctx.perfcounter->inc(l_librbd_snap_rename); + return 0; +} + +template +void Operations::execute_snap_rename(const uint64_t src_snap_id, + const std::string &dest_snap_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + if ((m_image_ctx.features & RBD_FEATURE_JOURNALING) != 0) { + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + } + + m_image_ctx.snap_lock.get_read(); + if (m_image_ctx.get_snap_id(cls::rbd::UserSnapshotNamespace(), + dest_snap_name) != CEPH_NOSNAP) { + // Renaming is supported for snapshots from user namespace only. + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EEXIST); + return; + } + m_image_ctx.snap_lock.put_read(); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": " + << "snap_id=" << src_snap_id << ", " + << "new_snap_name=" << dest_snap_name << dendl; + + operation::SnapshotRenameRequest *req = + new operation::SnapshotRenameRequest( + m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), src_snap_id, + dest_snap_name); + req->send(); +} + +template +int Operations::snap_protect(const cls::rbd::SnapshotNamespace& snap_namespace, + const char *snap_name) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name + << dendl; + + if (m_image_ctx.read_only) { + return -EROFS; + } + + if (!m_image_ctx.test_features(RBD_FEATURE_LAYERING)) { + lderr(cct) << "image must support layering" << dendl; + return -ENOSYS; + } + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + { + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + bool is_protected; + r = m_image_ctx.is_snap_protected(m_image_ctx.get_snap_id(snap_namespace, snap_name), + &is_protected); + if (r < 0) { + return r; + } + + if (is_protected) { + return -EBUSY; + } + } + + if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { + r = invoke_async_request("snap_protect", true, + boost::bind(&Operations::execute_snap_protect, + this, snap_namespace, snap_name, _1), + boost::bind(&ImageWatcher::notify_snap_protect, + m_image_ctx.image_watcher, + snap_namespace, snap_name, _1)); + if (r < 0 && r != -EBUSY) { + return r; + } + } else { + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + C_SaferCond cond_ctx; + execute_snap_protect(snap_namespace, snap_name, &cond_ctx); + + r = cond_ctx.wait(); + if (r < 0) { + return r; + } + } + return 0; +} + +template +void Operations::execute_snap_protect(const cls::rbd::SnapshotNamespace& snap_namespace, + const std::string &snap_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + } + + m_image_ctx.snap_lock.get_read(); + bool is_protected; + int r = m_image_ctx.is_snap_protected(m_image_ctx.get_snap_id(snap_namespace, snap_name), + &is_protected); + if (r < 0) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(r); + return; + } else if (is_protected) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EBUSY); + return; + } + m_image_ctx.snap_lock.put_read(); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name + << dendl; + + operation::SnapshotProtectRequest *request = + new operation::SnapshotProtectRequest( + m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), snap_namespace, snap_name); + request->send(); +} + +template +int Operations::snap_unprotect(const cls::rbd::SnapshotNamespace& snap_namespace, + const char *snap_name) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name + << dendl; + + if (m_image_ctx.read_only) { + return -EROFS; + } + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + { + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + bool is_unprotected; + r = m_image_ctx.is_snap_unprotected(m_image_ctx.get_snap_id(snap_namespace, snap_name), + &is_unprotected); + if (r < 0) { + return r; + } + + if (is_unprotected) { + return -EINVAL; + } + } + + if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { + r = invoke_async_request("snap_unprotect", true, + boost::bind(&Operations::execute_snap_unprotect, + this, snap_namespace, snap_name, _1), + boost::bind(&ImageWatcher::notify_snap_unprotect, + m_image_ctx.image_watcher, + snap_namespace, snap_name, _1)); + if (r < 0 && r != -EINVAL) { + return r; + } + } else { + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + C_SaferCond cond_ctx; + execute_snap_unprotect(snap_namespace, snap_name, &cond_ctx); + + r = cond_ctx.wait(); + if (r < 0) { + return r; + } + } + return 0; +} + +template +void Operations::execute_snap_unprotect(const cls::rbd::SnapshotNamespace& snap_namespace, + const std::string &snap_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + } + + m_image_ctx.snap_lock.get_read(); + bool is_unprotected; + int r = m_image_ctx.is_snap_unprotected(m_image_ctx.get_snap_id(snap_namespace, snap_name), + &is_unprotected); + if (r < 0) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(r); + return; + } else if (is_unprotected) { + m_image_ctx.snap_lock.put_read(); + on_finish->complete(-EINVAL); + return; + } + m_image_ctx.snap_lock.put_read(); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name + << dendl; + + operation::SnapshotUnprotectRequest *request = + new operation::SnapshotUnprotectRequest( + m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), snap_namespace, snap_name); + request->send(); +} + +template +int Operations::snap_set_limit(uint64_t limit) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": limit=" << limit << dendl; + + if (m_image_ctx.read_only) { + return -EROFS; + } + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + { + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + C_SaferCond limit_ctx; + + if (m_image_ctx.exclusive_lock != nullptr && + !m_image_ctx.exclusive_lock->is_lock_owner()) { + C_SaferCond lock_ctx; + + m_image_ctx.exclusive_lock->acquire_lock(&lock_ctx); + r = lock_ctx.wait(); + if (r < 0) { + return r; + } + } + + execute_snap_set_limit(limit, &limit_ctx); + r = limit_ctx.wait(); + } + + return r; +} + +template +void Operations::execute_snap_set_limit(const uint64_t limit, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": limit=" << limit + << dendl; + + operation::SnapshotLimitRequest *request = + new operation::SnapshotLimitRequest(m_image_ctx, on_finish, limit); + request->send(); +} + +template +int Operations::update_features(uint64_t features, bool enabled) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": features=" << features + << ", enabled=" << enabled << dendl; + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + if (m_image_ctx.read_only) { + return -EROFS; + } else if (m_image_ctx.old_format) { + lderr(cct) << "old-format images do not support features" << dendl; + return -EINVAL; + } + + uint64_t disable_mask = (RBD_FEATURES_MUTABLE | + RBD_FEATURES_DISABLE_ONLY); + if ((enabled && (features & RBD_FEATURES_MUTABLE) != features) || + (!enabled && (features & disable_mask) != features)) { + lderr(cct) << "cannot update immutable features" << dendl; + return -EINVAL; + } + if (features == 0) { + lderr(cct) << "update requires at least one feature" << dendl; + return -EINVAL; + } + { + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + if (enabled && (features & m_image_ctx.features) != 0) { + lderr(cct) << "one or more requested features are already enabled" + << dendl; + return -EINVAL; + } + if (!enabled && (features & ~m_image_ctx.features) != 0) { + lderr(cct) << "one or more requested features are already disabled" + << dendl; + return -EINVAL; + } + } + + // if disabling journaling, avoid attempting to open the journal + // when acquiring the exclusive lock in case the journal is corrupt + bool disabling_journal = false; + if (!enabled && ((features & RBD_FEATURE_JOURNALING) != 0)) { + RWLock::WLocker snap_locker(m_image_ctx.snap_lock); + m_image_ctx.set_journal_policy(new journal::DisabledPolicy()); + disabling_journal = true; + } + BOOST_SCOPE_EXIT_ALL( (this)(disabling_journal) ) { + if (disabling_journal) { + RWLock::WLocker snap_locker(m_image_ctx.snap_lock); + m_image_ctx.set_journal_policy( + new journal::StandardPolicy(&m_image_ctx)); + } + }; + + r = invoke_async_request("update_features", false, + boost::bind(&Operations::execute_update_features, + this, features, enabled, _1, 0), + boost::bind(&ImageWatcher::notify_update_features, + m_image_ctx.image_watcher, features, + enabled, _1)); + ldout(cct, 2) << "update_features finished" << dendl; + return r; +} + +template +void Operations::execute_update_features(uint64_t features, bool enabled, + Context *on_finish, + uint64_t journal_op_tid) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": features=" << features + << ", enabled=" << enabled << dendl; + + if (enabled) { + operation::EnableFeaturesRequest *req = + new operation::EnableFeaturesRequest( + m_image_ctx, on_finish, journal_op_tid, features); + req->send(); + } else { + operation::DisableFeaturesRequest *req = + new operation::DisableFeaturesRequest( + m_image_ctx, on_finish, journal_op_tid, features, false); + req->send(); + } +} + +template +int Operations::metadata_set(const std::string &key, + const std::string &value) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": key=" << key << ", value=" + << value << dendl; + + string start = m_image_ctx.METADATA_CONF_PREFIX; + size_t conf_prefix_len = start.size(); + + if (key.size() > conf_prefix_len && !key.compare(0, conf_prefix_len, start)) { + // validate config setting + string subkey = key.substr(conf_prefix_len, key.size() - conf_prefix_len); + int r = md_config_t().set_val(subkey.c_str(), value); + if (r < 0) { + return r; + } + } + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + if (m_image_ctx.read_only) { + return -EROFS; + } + + { + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + C_SaferCond metadata_ctx; + + if (m_image_ctx.exclusive_lock != nullptr && + !m_image_ctx.exclusive_lock->is_lock_owner()) { + C_SaferCond lock_ctx; + + m_image_ctx.exclusive_lock->acquire_lock(&lock_ctx); + r = lock_ctx.wait(); + if (r < 0) { + return r; + } + } + + execute_metadata_set(key, value, &metadata_ctx); + r = metadata_ctx.wait(); + } + + return r; +} + +template +void Operations::execute_metadata_set(const std::string &key, + const std::string &value, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": key=" << key << ", value=" + << value << dendl; + + operation::MetadataSetRequest *request = + new operation::MetadataSetRequest(m_image_ctx, on_finish, key, value); + request->send(); +} + +template +int Operations::metadata_remove(const std::string &key) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": key=" << key << dendl; + + if (m_image_ctx.read_only) { + return -EROFS; + } + + int r = m_image_ctx.state->refresh_if_required(); + if (r < 0) { + return r; + } + + if (m_image_ctx.read_only) { + return -EROFS; + } + + std::string value; + r = cls_client::metadata_get(&m_image_ctx.md_ctx, m_image_ctx.header_oid, key, &value); + if(r < 0) + return r; + + { + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + C_SaferCond metadata_ctx; + + if (m_image_ctx.exclusive_lock != nullptr && + !m_image_ctx.exclusive_lock->is_lock_owner()) { + C_SaferCond lock_ctx; + + m_image_ctx.exclusive_lock->acquire_lock(&lock_ctx); + r = lock_ctx.wait(); + if (r < 0) { + return r; + } + } + + execute_metadata_remove(key, &metadata_ctx); + r = metadata_ctx.wait(); + } + + return r; +} + +template +void Operations::execute_metadata_remove(const std::string &key, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": key=" << key << dendl; + + operation::MetadataRemoveRequest *request = + new operation::MetadataRemoveRequest(m_image_ctx, on_finish, key); + request->send(); +} + +template +int Operations::prepare_image_update() { + assert(m_image_ctx.owner_lock.is_locked() && + !m_image_ctx.owner_lock.is_wlocked()); + if (m_image_ctx.image_watcher == NULL) { + return -EROFS; + } + + // need to upgrade to a write lock + bool trying_lock = false; + C_SaferCond ctx; + m_image_ctx.owner_lock.put_read(); + { + RWLock::WLocker owner_locker(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr && + (!m_image_ctx.exclusive_lock->is_lock_owner() || + !m_image_ctx.exclusive_lock->accept_requests())) { + m_image_ctx.exclusive_lock->try_acquire_lock(&ctx); + trying_lock = true; + } + } + + int r = 0; + if (trying_lock) { + r = ctx.wait(); + } + m_image_ctx.owner_lock.get_read(); + + return r; +} + +template +int Operations::invoke_async_request(const std::string& request_type, + bool permit_snapshot, + const boost::function& local_request, + const boost::function& remote_request) { + C_SaferCond ctx; + C_InvokeAsyncRequest *req = new C_InvokeAsyncRequest(m_image_ctx, + request_type, + permit_snapshot, + local_request, + remote_request, + {}, &ctx); + req->send(); + return ctx.wait(); +} + +} // namespace librbd + +template class librbd::Operations;