// -*- 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/perf_counters.h" #include "osdc/Striper.h" #include "librbd/ExclusiveLock.h" #include "librbd/ImageCtx.h" #include "librbd/ImageState.h" #include "librbd/ImageWatcher.h" #include "librbd/ObjectMap.h" #include "librbd/Types.h" #include "librbd/Utils.h" #include "librbd/api/Config.h" #include "librbd/asio/ContextWQ.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/MigrateRequest.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 "librbd/operation/SparsifyRequest.h" #include #include #include #define dout_subsys ceph_subsys_rbd #undef dout_prefix #define dout_prefix *_dout << "librbd::Operations: " namespace librbd { using namespace boost::placeholders; namespace { std::ostream &operator<<(std::ostream &out, const Operation &op) { switch (op) { case OPERATION_CHECK_OBJECT_MAP: out << "check object map"; break; case OPERATION_FLATTEN: out << "flatten"; break; case OPERATION_METADATA_UPDATE: out << "metadata update"; break; case OPERATION_MIGRATE: out << "migrate"; break; case OPERATION_REBUILD_OBJECT_MAP: out << "rebuild object map"; break; case OPERATION_RENAME: out << "rename"; break; case OPERATION_RESIZE: out << "resize"; break; case OPERATION_SNAP_CREATE: out << "snap create"; break; case OPERATION_SNAP_PROTECT: out << "snap protect"; break; case OPERATION_SNAP_REMOVE: out << "snap remove"; break; case OPERATION_SNAP_RENAME: out << "snap rename"; break; case OPERATION_SNAP_ROLLBACK: out << "snap rollback"; break; case OPERATION_SNAP_UNPROTECT: out << "snap unprotect"; break; case OPERATION_SPARSIFY: out << "sparsify"; break; case OPERATION_UPDATE_FEATURES: out << "update features"; break; default: ceph_abort(); break; } return out; } 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; Operation operation; exclusive_lock::OperationRequestType 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, Operation operation, exclusive_lock::OperationRequestType 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), operation(operation), 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 ceph::shared_mutex &owner_lock(image_ctx.owner_lock); owner_lock.lock_shared(); image_ctx.image_lock.lock_shared(); if (image_ctx.read_only || (!permit_snapshot && image_ctx.snap_id != CEPH_NOSNAP)) { image_ctx.image_lock.unlock_shared(); owner_lock.unlock_shared(); complete(-EROFS); return; } image_ctx.image_lock.unlock_shared(); if (image_ctx.exclusive_lock == nullptr) { send_local_request(); owner_lock.unlock_shared(); return; } else if (image_ctx.image_watcher == nullptr) { owner_lock.unlock_shared(); complete(-EROFS); return; } if (image_ctx.exclusive_lock->is_lock_owner() && image_ctx.exclusive_lock->accept_request(request_type, nullptr)) { send_local_request(); owner_lock.unlock_shared(); 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, image_ctx.exclusive_lock)); 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.unlock_shared(); } void handle_acquire_exclusive_lock(int r) { CephContext *cct = image_ctx.cct; ldout(cct, 20) << __func__ << ": r=" << r << dendl; if (r < 0) { complete(r == -EBLOCKLISTED ? -EBLOCKLISTED : -EROFS); return; } // context can complete before owner_lock is unlocked ceph::shared_mutex &owner_lock(image_ctx.owner_lock); owner_lock.lock_shared(); if (image_ctx.exclusive_lock == nullptr || image_ctx.exclusive_lock->is_lock_owner()) { send_local_request(); owner_lock.unlock_shared(); return; } send_remote_request(); owner_lock.unlock_shared(); } void send_remote_request() { ceph_assert(ceph_mutex_is_locked(image_ctx.owner_lock)); 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_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) << operation << " 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) << operation << " timed out notifying lock owner" << dendl; send_refresh_image(); } void send_local_request() { auto ctx = new LambdaContext( [this](int r) { if (r == -ERESTART) { image_ctx.operations->finish_op(operation, r); send_refresh_image(); return; } execute_local_request(); }); image_ctx.operations->start_op(operation, ctx); } void execute_local_request() { std::shared_lock owner_locker{image_ctx.owner_lock}; 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; image_ctx.operations->finish_op(operation, r); 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_queue_lock(ceph::make_mutex( util::unique_lock_name("librbd::Operations::m_queue_lock", this))) { } template void Operations::start_op(Operation op, Context *ctx) { CephContext *cct = m_image_ctx.cct; ldout(cct, 20) << __func__ << ": " << op << " " << ctx << dendl; ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); bool requires_lock = m_image_ctx.exclusive_lock != nullptr; ctx = util::create_async_context_callback( m_image_ctx, new LambdaContext( [this, op, requires_lock, ctx](int r) { Context *finish_op_ctx = nullptr; if (requires_lock && r == 0) { std::shared_lock owner_locker{m_image_ctx.owner_lock}; std::shared_lock image_locker{m_image_ctx.image_lock}; auto exclusive_lock = m_image_ctx.exclusive_lock; if (exclusive_lock == nullptr || (finish_op_ctx = exclusive_lock->start_op(&r)) == nullptr) { ldout(m_image_ctx.cct, 20) << "lock owner lost, restarting" << dendl; r = -ERESTART; } } ldout(m_image_ctx.cct, 20) << "start " << op << " " << ctx << dendl; ctx->complete(r); if (finish_op_ctx != nullptr) { finish_op_ctx->complete(0); } })); std::unique_lock locker{m_queue_lock}; if (!m_in_flight_ops.insert(op).second) { ldout(cct, 20) << __func__ << ": " << op << " in flight" << dendl; m_queued_ops[op].push_back(ctx); return; } ctx->complete(0); } template void Operations::finish_op(Operation op, int r) { CephContext *cct = m_image_ctx.cct; ldout(cct, 20) << __func__ << ": " << op << " r=" << r << dendl; std::unique_lock locker{m_queue_lock}; auto &queue = m_queued_ops[op]; if (queue.empty()) { m_in_flight_ops.erase(op); return; } auto ctx = queue.front(); queue.pop_front(); // propagate -ERESTART through all the queue ctx->complete(r == -ERESTART ? r : 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; } { std::shared_lock image_locker{m_image_ctx.image_lock}; if (m_image_ctx.parent_md.spec.pool_id == -1) { lderr(cct) << "image has no parent" << dendl; return -EINVAL; } } uint64_t request_id = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_FLATTEN, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, 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) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); ceph_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 || m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } m_image_ctx.image_lock.lock_shared(); // 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.image_lock.unlock_shared(); on_finish->complete(-EINVAL); return; } if (m_image_ctx.snap_id != CEPH_NOSNAP) { lderr(cct) << "snapshots cannot be flattened" << dendl; m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EROFS); return; } uint64_t overlap; int r = m_image_ctx.get_parent_overlap(CEPH_NOSNAP, &overlap); ceph_assert(r == 0); ceph_assert(overlap <= m_image_ctx.size); uint64_t overlap_objects = Striper::get_num_objects(m_image_ctx.layout, overlap); m_image_ctx.image_lock.unlock_shared(); operation::FlattenRequest *req = new operation::FlattenRequest( m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), overlap_objects, 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 = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_REBUILD_OBJECT_MAP, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, 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) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); ceph_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 || m_image_ctx.operations_disabled) { 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(OPERATION_CHECK_OBJECT_MAP, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, true, boost::bind(&Operations::check_object_map, this, boost::ref(prog_ctx), _1), [this](Context *c) { m_image_ctx.op_work_queue->queue(c, -EOPNOTSUPP); }); return r; } template void Operations::object_map_iterate(ProgressContext &prog_ctx, operation::ObjectIterateWork handle_mismatch, Context *on_finish) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); ceph_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; } uint64_t request_id = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_RENAME, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, true, boost::bind(&Operations::execute_rename, this, dstname, _1), boost::bind(&ImageWatcher::notify_rename, m_image_ctx.image_watcher, request_id, dstname, _1)); if (r < 0 && r != -EEXIST) { return r; } m_image_ctx.set_image_name(dstname); return 0; } template void Operations::execute_rename(const std::string &dest_name, Context *on_finish) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { ceph_assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); } if (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } CephContext *cct = m_image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": dest_name=" << dest_name << dendl; if (m_image_ctx.old_format) { m_image_ctx.image_lock.lock_shared(); if (m_image_ctx.name == dest_name) { m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EEXIST); return; } m_image_ctx.image_lock.unlock_shared(); // unregister watch before and register back after rename on_finish = new C_NotifyUpdate(m_image_ctx, on_finish); on_finish = new LambdaContext([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 LambdaContext([this, dest_name, on_finish](int r) { std::shared_lock owner_locker{m_image_ctx.owner_lock}; 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.image_lock.lock_shared(); ldout(cct, 5) << this << " " << __func__ << ": " << "size=" << m_image_ctx.size << ", " << "new_size=" << size << dendl; m_image_ctx.image_lock.unlock_shared(); 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 = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_RESIZE, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, 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) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); ceph_assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); CephContext *cct = m_image_ctx.cct; m_image_ctx.image_lock.lock_shared(); 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.operations_disabled) { m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EROFS); return; } else if (m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP, m_image_ctx.image_lock) && !ObjectMap<>::is_compatible(m_image_ctx.layout, size)) { m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EINVAL); return; } m_image_ctx.image_lock.unlock_shared(); 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 std::string& snap_name, uint64_t flags, ProgressContext &prog_ctx) { 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, flags, prog_ctx, &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 std::string& snap_name, uint64_t flags, ProgressContext &prog_ctx, 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.image_lock.lock_shared(); if (m_image_ctx.get_snap_id(snap_namespace, snap_name) != CEPH_NOSNAP) { m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EEXIST); return; } m_image_ctx.image_lock.unlock_shared(); uint64_t request_id = util::reserve_async_request_id(); C_InvokeAsyncRequest *req = new C_InvokeAsyncRequest( m_image_ctx, OPERATION_SNAP_CREATE, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, true, boost::bind(&Operations::execute_snap_create, this, snap_namespace, snap_name, _1, 0, flags, boost::ref(prog_ctx)), boost::bind(&ImageWatcher::notify_snap_create, m_image_ctx.image_watcher, request_id, snap_namespace, snap_name, flags, boost::ref(prog_ctx), _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, uint64_t flags, ProgressContext &prog_ctx) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); ceph_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; if (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } m_image_ctx.image_lock.lock_shared(); if (m_image_ctx.get_snap_id(snap_namespace, snap_name) != CEPH_NOSNAP) { m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EEXIST); return; } m_image_ctx.image_lock.unlock_shared(); operation::SnapshotCreateRequest *req = new operation::SnapshotCreateRequest( m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), snap_namespace, snap_name, journal_op_tid, flags, prog_ctx); req->send(); } template int Operations::snap_rollback(const cls::rbd::SnapshotNamespace& snap_namespace, const std::string& 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; C_SaferCond cond_ctx; { std::shared_lock owner_locker{m_image_ctx.owner_lock}; { // need to drop image_lock before invalidating cache std::shared_lock image_locker{m_image_ctx.image_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(exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, false); if (r < 0) { return r; } Context *ctx = new LambdaContext( [this, ctx=&cond_ctx](int r) { m_image_ctx.operations->finish_op(OPERATION_SNAP_ROLLBACK, r); ctx->complete(r); }); ctx = new LambdaContext( [this, snap_namespace, snap_name, &prog_ctx, ctx](int r) { if (r < 0) { ctx->complete(r); return; } std::shared_lock l{m_image_ctx.owner_lock}; execute_snap_rollback(snap_namespace, snap_name, prog_ctx, ctx); }); m_image_ctx.operations->start_op(OPERATION_SNAP_ROLLBACK, 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) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); CephContext *cct = m_image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name << dendl; if (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } m_image_ctx.image_lock.lock_shared(); 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.image_lock.unlock_shared(); on_finish->complete(-ENOENT); return; } uint64_t new_size = m_image_ctx.get_image_size(snap_id); m_image_ctx.image_lock.unlock_shared(); // 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 std::string& 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 std::string& 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.image_lock.lock_shared(); if (m_image_ctx.get_snap_id(snap_namespace, snap_name) == CEPH_NOSNAP) { m_image_ctx.image_lock.unlock_shared(); 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.image_lock.unlock_shared(); if (proxy_op) { uint64_t request_id = util::reserve_async_request_id(); auto request_type = exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL; if (cls::rbd::get_snap_namespace_type(snap_namespace) == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) { request_type = exclusive_lock::OPERATION_REQUEST_TYPE_TRASH_SNAP_REMOVE; } C_InvokeAsyncRequest *req = new C_InvokeAsyncRequest( m_image_ctx, OPERATION_SNAP_REMOVE, request_type, true, boost::bind(&Operations::execute_snap_remove, this, snap_namespace, snap_name, _1), boost::bind(&ImageWatcher::notify_snap_remove, m_image_ctx.image_watcher, request_id, snap_namespace, snap_name, _1), {-ENOENT}, on_finish); req->send(); } else { std::shared_lock 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) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); { if ((m_image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0) { ceph_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; if (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } m_image_ctx.image_lock.lock_shared(); 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.image_lock.unlock_shared(); 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.image_lock.unlock_shared(); on_finish->complete(r); return; } else if (is_protected) { lderr(m_image_ctx.cct) << "snapshot is protected" << dendl; m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EBUSY); return; } m_image_ctx.image_lock.unlock_shared(); 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; { std::shared_lock l{m_image_ctx.image_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)) { uint64_t request_id = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_SNAP_RENAME, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, true, boost::bind(&Operations::execute_snap_rename, this, snap_id, dstname, _1), boost::bind(&ImageWatcher::notify_snap_rename, m_image_ctx.image_watcher, request_id, snap_id, dstname, _1)); if (r < 0 && r != -EEXIST) { return r; } } else { C_SaferCond cond_ctx; { std::shared_lock owner_lock{m_image_ctx.owner_lock}; 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) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); if ((m_image_ctx.features & RBD_FEATURE_JOURNALING) != 0) { ceph_assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); } if (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } m_image_ctx.image_lock.lock_shared(); 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.image_lock.unlock_shared(); on_finish->complete(-EEXIST); return; } m_image_ctx.image_lock.unlock_shared(); 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 std::string& 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; } { std::shared_lock image_locker{m_image_ctx.image_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)) { uint64_t request_id = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_SNAP_PROTECT, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, true, boost::bind(&Operations::execute_snap_protect, this, snap_namespace, snap_name, _1), boost::bind(&ImageWatcher::notify_snap_protect, m_image_ctx.image_watcher, request_id, snap_namespace, snap_name, _1)); if (r < 0 && r != -EBUSY) { return r; } } else { C_SaferCond cond_ctx; { std::shared_lock owner_lock{m_image_ctx.owner_lock}; 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) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { ceph_assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); } if (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } m_image_ctx.image_lock.lock_shared(); 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.image_lock.unlock_shared(); on_finish->complete(r); return; } else if (is_protected) { m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EBUSY); return; } m_image_ctx.image_lock.unlock_shared(); 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 std::string& 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; } { std::shared_lock image_locker{m_image_ctx.image_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)) { uint64_t request_id = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_SNAP_UNPROTECT, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, true, boost::bind(&Operations::execute_snap_unprotect, this, snap_namespace, snap_name, _1), boost::bind(&ImageWatcher::notify_snap_unprotect, m_image_ctx.image_watcher, request_id, snap_namespace, snap_name, _1)); if (r < 0 && r != -EINVAL) { return r; } } else { C_SaferCond cond_ctx; { std::shared_lock owner_lock{m_image_ctx.owner_lock}; 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) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { ceph_assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); } if (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } m_image_ctx.image_lock.lock_shared(); 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.image_lock.unlock_shared(); on_finish->complete(r); return; } else if (is_unprotected) { m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EINVAL); return; } m_image_ctx.image_lock.unlock_shared(); 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; } C_SaferCond limit_ctx; { std::shared_lock owner_lock{m_image_ctx.owner_lock}; r = prepare_image_update(exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, true); 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) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); 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) || ((features & ~RBD_FEATURES_MUTABLE_INTERNAL) != features)) { lderr(cct) << "cannot update immutable features" << dendl; return -EINVAL; } bool set_object_map = (features & RBD_FEATURE_OBJECT_MAP) == RBD_FEATURE_OBJECT_MAP; bool set_fast_diff = (features & RBD_FEATURE_FAST_DIFF) == RBD_FEATURE_FAST_DIFF; bool exist_fast_diff = (m_image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0; bool exist_object_map = (m_image_ctx.features & RBD_FEATURE_OBJECT_MAP) != 0; if ((enabled && ((set_object_map && !exist_fast_diff) || (set_fast_diff && !exist_object_map))) || (!enabled && (set_object_map && exist_fast_diff))) { features |= (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF); } if (features == 0) { lderr(cct) << "update requires at least one feature" << dendl; return -EINVAL; } { std::shared_lock image_locker{m_image_ctx.image_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)) { std::unique_lock image_locker{m_image_ctx.image_lock}; m_image_ctx.set_journal_policy(new journal::DisabledPolicy()); disabling_journal = true; } BOOST_SCOPE_EXIT_ALL( (this)(disabling_journal) ) { if (disabling_journal) { std::unique_lock image_locker{m_image_ctx.image_lock}; m_image_ctx.set_journal_policy( new journal::StandardPolicy(&m_image_ctx)); } }; // The journal options are not passed to the lock owner in the // update features request. Therefore, if journaling is being // enabled, the lock should be locally acquired instead of // attempting to send the request to the peer. if (enabled && (features & RBD_FEATURE_JOURNALING) != 0) { C_SaferCond cond_ctx; { std::shared_lock owner_lock{m_image_ctx.owner_lock}; r = prepare_image_update(exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, true); if (r < 0) { return r; } execute_update_features(features, enabled, &cond_ctx, 0); } r = cond_ctx.wait(); } else { uint64_t request_id = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_UPDATE_FEATURES, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, false, boost::bind(&Operations::execute_update_features, this, features, enabled, _1, 0), boost::bind(&ImageWatcher::notify_update_features, m_image_ctx.image_watcher, request_id, 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) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); ceph_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 (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } 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; std::string config_key; bool config_override = util::is_metadata_config_override(key, &config_key); if (config_override) { // validate config setting if (!librbd::api::Config::is_option_name(&m_image_ctx, config_key)) { lderr(cct) << "validation for " << key << " failed: not allowed image level override" << dendl; return -EINVAL; } int r = ConfigProxy{false}.set_val(config_key.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; } uint64_t request_id = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_METADATA_UPDATE, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, false, boost::bind(&Operations::execute_metadata_set, this, key, value, _1), boost::bind(&ImageWatcher::notify_metadata_set, m_image_ctx.image_watcher, request_id, key, value, _1)); if (config_override && r >= 0) { // apply new config key immediately r = m_image_ctx.state->refresh_if_required(); } ldout(cct, 20) << "metadata_set finished" << dendl; return r; } template void Operations::execute_metadata_set(const std::string &key, const std::string &value, Context *on_finish) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); CephContext *cct = m_image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": key=" << key << ", value=" << value << dendl; if (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } operation::MetadataSetRequest *request = new operation::MetadataSetRequest(m_image_ctx, new C_NotifyUpdate(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; 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; uint64_t request_id = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_METADATA_UPDATE, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, false, boost::bind(&Operations::execute_metadata_remove, this, key, _1), boost::bind(&ImageWatcher::notify_metadata_remove, m_image_ctx.image_watcher, request_id, key, _1)); if (r == -ENOENT) { r = 0; } std::string config_key; if (util::is_metadata_config_override(key, &config_key) && r >= 0) { // apply new config key immediately r = m_image_ctx.state->refresh_if_required(); } ldout(cct, 20) << "metadata_remove finished" << dendl; return r; } template void Operations::execute_metadata_remove(const std::string &key, Context *on_finish) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); CephContext *cct = m_image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": key=" << key << dendl; if (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } operation::MetadataRemoveRequest *request = new operation::MetadataRemoveRequest( m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), key); request->send(); } template int Operations::migrate(ProgressContext &prog_ctx) { CephContext *cct = m_image_ctx.cct; ldout(cct, 20) << "migrate" << dendl; int r = m_image_ctx.state->refresh_if_required(); if (r < 0) { return r; } if (m_image_ctx.read_only) { return -EROFS; } { std::shared_lock image_locker{m_image_ctx.image_lock}; if (m_image_ctx.migration_info.empty()) { lderr(cct) << "image has no migrating parent" << dendl; return -EINVAL; } } uint64_t request_id = util::reserve_async_request_id(); r = invoke_async_request(OPERATION_MIGRATE, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, false, boost::bind(&Operations::execute_migrate, this, boost::ref(prog_ctx), _1), boost::bind(&ImageWatcher::notify_migrate, m_image_ctx.image_watcher, request_id, boost::ref(prog_ctx), _1)); if (r < 0 && r != -EINVAL) { return r; } ldout(cct, 20) << "migrate finished" << dendl; return 0; } template void Operations::execute_migrate(ProgressContext &prog_ctx, Context *on_finish) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); ceph_assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); CephContext *cct = m_image_ctx.cct; ldout(cct, 20) << "migrate" << dendl; if (m_image_ctx.read_only || m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } m_image_ctx.image_lock.lock_shared(); if (m_image_ctx.migration_info.empty()) { lderr(cct) << "image has no migrating parent" << dendl; m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EINVAL); return; } if (m_image_ctx.snap_id != CEPH_NOSNAP) { lderr(cct) << "snapshots cannot be migrated" << dendl; m_image_ctx.image_lock.unlock_shared(); on_finish->complete(-EROFS); return; } m_image_ctx.image_lock.unlock_shared(); operation::MigrateRequest *req = new operation::MigrateRequest( m_image_ctx, new C_NotifyUpdate(m_image_ctx, on_finish), prog_ctx); req->send(); } template int Operations::sparsify(size_t sparse_size, ProgressContext &prog_ctx) { CephContext *cct = m_image_ctx.cct; ldout(cct, 20) << "sparsify" << dendl; if (sparse_size < 4096 || sparse_size > m_image_ctx.get_object_size() || (sparse_size & (sparse_size - 1)) != 0) { lderr(cct) << "sparse size should be power of two not less than 4096" << " and not larger image object size" << dendl; return -EINVAL; } uint64_t request_id = util::reserve_async_request_id(); int r = invoke_async_request(OPERATION_SPARSIFY, exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, false, boost::bind(&Operations::execute_sparsify, this, sparse_size, boost::ref(prog_ctx), _1), boost::bind(&ImageWatcher::notify_sparsify, m_image_ctx.image_watcher, request_id, sparse_size, boost::ref(prog_ctx), _1)); if (r < 0 && r != -EINVAL) { return r; } ldout(cct, 20) << "resparsify finished" << dendl; return 0; } template void Operations::execute_sparsify(size_t sparse_size, ProgressContext &prog_ctx, Context *on_finish) { ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock)); ceph_assert(m_image_ctx.exclusive_lock == nullptr || m_image_ctx.exclusive_lock->is_lock_owner()); CephContext *cct = m_image_ctx.cct; ldout(cct, 20) << "sparsify" << dendl; if (m_image_ctx.operations_disabled) { on_finish->complete(-EROFS); return; } auto req = new operation::SparsifyRequest( m_image_ctx, sparse_size, new C_NotifyUpdate(m_image_ctx, on_finish), prog_ctx); req->send(); } template int Operations::prepare_image_update( exclusive_lock::OperationRequestType request_type, bool request_lock) { ceph_assert(ceph_mutex_is_rlocked(m_image_ctx.owner_lock)); if (m_image_ctx.image_watcher == nullptr) { return -EROFS; } // need to upgrade to a write lock C_SaferCond ctx; m_image_ctx.owner_lock.unlock_shared(); bool attempting_lock = false; { std::unique_lock 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_request(request_type, nullptr))) { attempting_lock = true; m_image_ctx.exclusive_lock->block_requests(0); if (request_lock) { m_image_ctx.exclusive_lock->acquire_lock(&ctx); } else { m_image_ctx.exclusive_lock->try_acquire_lock(&ctx); } } } int r = 0; if (attempting_lock) { r = ctx.wait(); } m_image_ctx.owner_lock.lock_shared(); if (attempting_lock && m_image_ctx.exclusive_lock != nullptr) { m_image_ctx.exclusive_lock->unblock_requests(); } if (r == -EAGAIN || r == -EBUSY) { r = 0; } if (r < 0) { return r; } else if (m_image_ctx.exclusive_lock != nullptr && !m_image_ctx.exclusive_lock->is_lock_owner()) { return m_image_ctx.exclusive_lock->get_unlocked_op_error(); } return 0; } template int Operations::invoke_async_request( Operation op, exclusive_lock::OperationRequestType 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, op, request_type, permit_snapshot, local_request, remote_request, {}, &ctx); req->send(); return ctx.wait(); } } // namespace librbd template class librbd::Operations;