X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Flibrbd%2FImageWatcher.cc;fp=src%2Fceph%2Fsrc%2Flibrbd%2FImageWatcher.cc;h=487eabaa69c18f5c7b374362982ce071b40de1b2;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/librbd/ImageWatcher.cc b/src/ceph/src/librbd/ImageWatcher.cc new file mode 100644 index 0000000..487eaba --- /dev/null +++ b/src/ceph/src/librbd/ImageWatcher.cc @@ -0,0 +1,1013 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/ImageWatcher.h" +#include "librbd/ExclusiveLock.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageState.h" +#include "librbd/internal.h" +#include "librbd/Operations.h" +#include "librbd/TaskFinisher.h" +#include "librbd/Utils.h" +#include "librbd/exclusive_lock/Policy.h" +#include "librbd/image_watcher/NotifyLockOwner.h" +#include "librbd/io/AioCompletion.h" +#include "librbd/watcher/Utils.h" +#include "include/encoding.h" +#include "common/errno.h" +#include "common/WorkQueue.h" +#include + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::ImageWatcher: " + +namespace librbd { + +using namespace image_watcher; +using namespace watch_notify; +using util::create_async_context_callback; +using util::create_context_callback; +using util::create_rados_callback; +using librbd::watcher::util::HandlePayloadVisitor; + +static const double RETRY_DELAY_SECONDS = 1.0; + +template +struct ImageWatcher::C_ProcessPayload : public Context { + ImageWatcher *image_watcher; + uint64_t notify_id; + uint64_t handle; + watch_notify::Payload payload; + + C_ProcessPayload(ImageWatcher *image_watcher_, uint64_t notify_id_, + uint64_t handle_, const watch_notify::Payload &payload) + : image_watcher(image_watcher_), notify_id(notify_id_), handle(handle_), + payload(payload) { + } + + void finish(int r) override { + image_watcher->m_async_op_tracker.start_op(); + if (image_watcher->notifications_blocked()) { + // requests are blocked -- just ack the notification + bufferlist bl; + image_watcher->acknowledge_notify(notify_id, handle, bl); + } else { + image_watcher->process_payload(notify_id, handle, payload); + } + image_watcher->m_async_op_tracker.finish_op(); + } +}; + +template +ImageWatcher::ImageWatcher(I &image_ctx) + : Watcher(image_ctx.md_ctx, image_ctx.op_work_queue, image_ctx.header_oid), + m_image_ctx(image_ctx), + m_task_finisher(new TaskFinisher(*m_image_ctx.cct)), + m_async_request_lock(util::unique_lock_name("librbd::ImageWatcher::m_async_request_lock", this)), + m_owner_client_id_lock(util::unique_lock_name("librbd::ImageWatcher::m_owner_client_id_lock", this)) +{ +} + +template +ImageWatcher::~ImageWatcher() +{ + delete m_task_finisher; +} + +template +void ImageWatcher::unregister_watch(Context *on_finish) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << this << " unregistering image watcher" << dendl; + + cancel_async_requests(); + + FunctionContext *ctx = new FunctionContext([this, on_finish](int r) { + m_task_finisher->cancel_all(on_finish); + }); + Watcher::unregister_watch(ctx); +} + +template +void ImageWatcher::block_notifies(Context *on_finish) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + on_finish = new FunctionContext([this, on_finish](int r) { + cancel_async_requests(); + on_finish->complete(r); + }); + Watcher::block_notifies(on_finish); +} + +template +void ImageWatcher::schedule_async_progress(const AsyncRequestId &request, + uint64_t offset, uint64_t total) { + FunctionContext *ctx = new FunctionContext( + boost::bind(&ImageWatcher::notify_async_progress, this, request, offset, + total)); + m_task_finisher->queue(Task(TASK_CODE_ASYNC_PROGRESS, request), ctx); +} + +template +int ImageWatcher::notify_async_progress(const AsyncRequestId &request, + uint64_t offset, uint64_t total) { + ldout(m_image_ctx.cct, 20) << this << " remote async request progress: " + << request << " @ " << offset + << "/" << total << dendl; + + send_notify(AsyncProgressPayload(request, offset, total)); + return 0; +} + +template +void ImageWatcher::schedule_async_complete(const AsyncRequestId &request, + int r) { + FunctionContext *ctx = new FunctionContext( + boost::bind(&ImageWatcher::notify_async_complete, this, request, r)); + m_task_finisher->queue(ctx); +} + +template +void ImageWatcher::notify_async_complete(const AsyncRequestId &request, + int r) { + ldout(m_image_ctx.cct, 20) << this << " remote async request finished: " + << request << " = " << r << dendl; + + send_notify(AsyncCompletePayload(request, r), + new FunctionContext(boost::bind(&ImageWatcher::handle_async_complete, + this, request, r, _1))); +} + +template +void ImageWatcher::handle_async_complete(const AsyncRequestId &request, + int r, int ret_val) { + ldout(m_image_ctx.cct, 20) << this << " " << __func__ << ": " + << "request=" << request << ", r=" << ret_val + << dendl; + if (ret_val < 0) { + lderr(m_image_ctx.cct) << this << " failed to notify async complete: " + << cpp_strerror(ret_val) << dendl; + if (ret_val == -ETIMEDOUT) { + schedule_async_complete(request, r); + } + } else { + RWLock::WLocker async_request_locker(m_async_request_lock); + m_async_pending.erase(request); + } +} + +template +void ImageWatcher::notify_flatten(uint64_t request_id, + ProgressContext &prog_ctx, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + AsyncRequestId async_request_id(get_client_id(), request_id); + + notify_async_request(async_request_id, FlattenPayload(async_request_id), + prog_ctx, on_finish); +} + +template +void ImageWatcher::notify_resize(uint64_t request_id, uint64_t size, + bool allow_shrink, + ProgressContext &prog_ctx, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + AsyncRequestId async_request_id(get_client_id(), request_id); + + notify_async_request(async_request_id, + ResizePayload(size, allow_shrink, async_request_id), + prog_ctx, on_finish); +} + +template +void ImageWatcher::notify_snap_create(const cls::rbd::SnapshotNamespace &snap_namespace, + const std::string &snap_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + notify_lock_owner(SnapCreatePayload(snap_namespace, snap_name), on_finish); +} + +template +void ImageWatcher::notify_snap_rename(const snapid_t &src_snap_id, + const std::string &dst_snap_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + notify_lock_owner(SnapRenamePayload(src_snap_id, dst_snap_name), on_finish); +} + +template +void ImageWatcher::notify_snap_remove(const cls::rbd::SnapshotNamespace &snap_namespace, + const std::string &snap_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + notify_lock_owner(SnapRemovePayload(snap_namespace, snap_name), on_finish); +} + +template +void ImageWatcher::notify_snap_protect(const cls::rbd::SnapshotNamespace &snap_namespace, + const std::string &snap_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + notify_lock_owner(SnapProtectPayload(snap_namespace, snap_name), on_finish); +} + +template +void ImageWatcher::notify_snap_unprotect(const cls::rbd::SnapshotNamespace &snap_namespace, + const std::string &snap_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + notify_lock_owner(SnapUnprotectPayload(snap_namespace, snap_name), on_finish); +} + +template +void ImageWatcher::notify_rebuild_object_map(uint64_t request_id, + ProgressContext &prog_ctx, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + AsyncRequestId async_request_id(get_client_id(), request_id); + + notify_async_request(async_request_id, + RebuildObjectMapPayload(async_request_id), + prog_ctx, on_finish); +} + +template +void ImageWatcher::notify_rename(const std::string &image_name, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + notify_lock_owner(RenamePayload(image_name), on_finish); +} + +template +void ImageWatcher::notify_update_features(uint64_t features, bool enabled, + Context *on_finish) { + assert(m_image_ctx.owner_lock.is_locked()); + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + notify_lock_owner(UpdateFeaturesPayload(features, enabled), on_finish); +} + +template +void ImageWatcher::notify_header_update(Context *on_finish) { + ldout(m_image_ctx.cct, 10) << this << ": " << __func__ << dendl; + + // supports legacy (empty buffer) clients + send_notify(HeaderUpdatePayload(), on_finish); +} + +template +void ImageWatcher::notify_header_update(librados::IoCtx &io_ctx, + const std::string &oid) { + // supports legacy (empty buffer) clients + bufferlist bl; + ::encode(NotifyMessage(HeaderUpdatePayload()), bl); + io_ctx.notify2(oid, bl, watcher::Notifier::NOTIFY_TIMEOUT, nullptr); +} + +template +void ImageWatcher::schedule_cancel_async_requests() { + FunctionContext *ctx = new FunctionContext( + boost::bind(&ImageWatcher::cancel_async_requests, this)); + m_task_finisher->queue(TASK_CODE_CANCEL_ASYNC_REQUESTS, ctx); +} + +template +void ImageWatcher::cancel_async_requests() { + RWLock::WLocker l(m_async_request_lock); + for (std::map::iterator iter = + m_async_requests.begin(); + iter != m_async_requests.end(); ++iter) { + iter->second.first->complete(-ERESTART); + } + m_async_requests.clear(); +} + +template +void ImageWatcher::set_owner_client_id(const ClientId& client_id) { + assert(m_owner_client_id_lock.is_locked()); + m_owner_client_id = client_id; + ldout(m_image_ctx.cct, 10) << this << " current lock owner: " + << m_owner_client_id << dendl; +} + +template +ClientId ImageWatcher::get_client_id() { + RWLock::RLocker l(this->m_watch_lock); + return ClientId(m_image_ctx.md_ctx.get_instance_id(), this->m_watch_handle); +} + +template +void ImageWatcher::notify_acquired_lock() { + ldout(m_image_ctx.cct, 10) << this << " notify acquired lock" << dendl; + + ClientId client_id = get_client_id(); + { + Mutex::Locker owner_client_id_locker(m_owner_client_id_lock); + set_owner_client_id(client_id); + } + + send_notify(AcquiredLockPayload(client_id)); +} + +template +void ImageWatcher::notify_released_lock() { + ldout(m_image_ctx.cct, 10) << this << " notify released lock" << dendl; + + { + Mutex::Locker owner_client_id_locker(m_owner_client_id_lock); + set_owner_client_id(ClientId()); + } + + send_notify(ReleasedLockPayload(get_client_id())); +} + +template +void ImageWatcher::schedule_request_lock(bool use_timer, int timer_delay) { + assert(m_image_ctx.owner_lock.is_locked()); + + if (m_image_ctx.exclusive_lock == nullptr) { + // exclusive lock dynamically disabled via image refresh + return; + } + assert(m_image_ctx.exclusive_lock && + !m_image_ctx.exclusive_lock->is_lock_owner()); + + RWLock::RLocker watch_locker(this->m_watch_lock); + if (this->m_watch_state == Watcher::WATCH_STATE_REGISTERED) { + ldout(m_image_ctx.cct, 15) << this << " requesting exclusive lock" << dendl; + + FunctionContext *ctx = new FunctionContext( + boost::bind(&ImageWatcher::notify_request_lock, this)); + if (use_timer) { + if (timer_delay < 0) { + timer_delay = RETRY_DELAY_SECONDS; + } + m_task_finisher->add_event_after(TASK_CODE_REQUEST_LOCK, + timer_delay, ctx); + } else { + m_task_finisher->queue(TASK_CODE_REQUEST_LOCK, ctx); + } + } +} + +template +void ImageWatcher::notify_request_lock() { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + + // ExclusiveLock state machine can be dynamically disabled or + // race with task cancel + if (m_image_ctx.exclusive_lock == nullptr || + m_image_ctx.exclusive_lock->is_lock_owner()) { + return; + } + + ldout(m_image_ctx.cct, 10) << this << " notify request lock" << dendl; + + notify_lock_owner(RequestLockPayload(get_client_id(), false), + create_context_callback< + ImageWatcher, &ImageWatcher::handle_request_lock>(this)); +} + +template +void ImageWatcher::handle_request_lock(int r) { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + + // ExclusiveLock state machine cannot transition -- but can be + // dynamically disabled + if (m_image_ctx.exclusive_lock == nullptr) { + return; + } + + if (r == -ETIMEDOUT) { + ldout(m_image_ctx.cct, 5) << this << " timed out requesting lock: retrying" + << dendl; + + // treat this is a dead client -- so retest acquiring the lock + m_image_ctx.exclusive_lock->handle_peer_notification(0); + } else if (r == -EROFS) { + ldout(m_image_ctx.cct, 5) << this << " peer will not release lock" << dendl; + m_image_ctx.exclusive_lock->handle_peer_notification(r); + } else if (r < 0) { + lderr(m_image_ctx.cct) << this << " error requesting lock: " + << cpp_strerror(r) << dendl; + schedule_request_lock(true); + } else { + // lock owner acked -- but resend if we don't see them release the lock + int retry_timeout = m_image_ctx.cct->_conf->template get_val( + "client_notify_timeout"); + ldout(m_image_ctx.cct, 15) << this << " will retry in " << retry_timeout + << " seconds" << dendl; + schedule_request_lock(true, retry_timeout); + } +} + +template +void ImageWatcher::notify_lock_owner(const Payload& payload, + Context *on_finish) { + assert(on_finish != nullptr); + assert(m_image_ctx.owner_lock.is_locked()); + + bufferlist bl; + ::encode(NotifyMessage(payload), bl); + + NotifyLockOwner *notify_lock_owner = NotifyLockOwner::create( + m_image_ctx, this->m_notifier, std::move(bl), on_finish); + notify_lock_owner->send(); +} + +template +Context *ImageWatcher::remove_async_request(const AsyncRequestId &id) { + RWLock::WLocker async_request_locker(m_async_request_lock); + auto it = m_async_requests.find(id); + if (it != m_async_requests.end()) { + Context *on_complete = it->second.first; + m_async_requests.erase(it); + return on_complete; + } + return nullptr; +} + +template +void ImageWatcher::schedule_async_request_timed_out(const AsyncRequestId &id) { + ldout(m_image_ctx.cct, 20) << "scheduling async request time out: " << id + << dendl; + + Context *ctx = new FunctionContext(boost::bind( + &ImageWatcher::async_request_timed_out, this, id)); + + Task task(TASK_CODE_ASYNC_REQUEST, id); + m_task_finisher->cancel(task); + + m_task_finisher->add_event_after(task, m_image_ctx.request_timed_out_seconds, + ctx); +} + +template +void ImageWatcher::async_request_timed_out(const AsyncRequestId &id) { + Context *on_complete = remove_async_request(id); + if (on_complete != nullptr) { + ldout(m_image_ctx.cct, 5) << "async request timed out: " << id << dendl; + m_image_ctx.op_work_queue->queue(on_complete, -ETIMEDOUT); + } +} + +template +void ImageWatcher::notify_async_request(const AsyncRequestId &async_request_id, + const Payload& payload, + ProgressContext& prog_ctx, + Context *on_finish) { + assert(on_finish != nullptr); + assert(m_image_ctx.owner_lock.is_locked()); + + ldout(m_image_ctx.cct, 10) << this << " async request: " << async_request_id + << dendl; + + Context *on_notify = new FunctionContext([this, async_request_id](int r) { + if (r < 0) { + // notification failed -- don't expect updates + Context *on_complete = remove_async_request(async_request_id); + if (on_complete != nullptr) { + on_complete->complete(r); + } + } + }); + + Context *on_complete = new FunctionContext( + [this, async_request_id, on_finish](int r) { + m_task_finisher->cancel(Task(TASK_CODE_ASYNC_REQUEST, async_request_id)); + on_finish->complete(r); + }); + + { + RWLock::WLocker async_request_locker(m_async_request_lock); + m_async_requests[async_request_id] = AsyncRequest(on_complete, &prog_ctx); + } + + schedule_async_request_timed_out(async_request_id); + notify_lock_owner(payload, on_notify); +} + +template +int ImageWatcher::prepare_async_request(const AsyncRequestId& async_request_id, + bool* new_request, Context** ctx, + ProgressContext** prog_ctx) { + if (async_request_id.client_id == get_client_id()) { + return -ERESTART; + } else { + RWLock::WLocker l(m_async_request_lock); + if (m_async_pending.count(async_request_id) == 0) { + m_async_pending.insert(async_request_id); + *new_request = true; + *prog_ctx = new RemoteProgressContext(*this, async_request_id); + *ctx = new RemoteContext(*this, async_request_id, *prog_ctx); + } else { + *new_request = false; + } + } + return 0; +} + +template +bool ImageWatcher::handle_payload(const HeaderUpdatePayload &payload, + C_NotifyAck *ack_ctx) { + ldout(m_image_ctx.cct, 10) << this << " image header updated" << dendl; + + m_image_ctx.state->handle_update_notification(); + m_image_ctx.perfcounter->inc(l_librbd_notify); + if (ack_ctx != nullptr) { + m_image_ctx.state->flush_update_watchers(new C_ResponseMessage(ack_ctx)); + return false; + } + return true; +} + +template +bool ImageWatcher::handle_payload(const AcquiredLockPayload &payload, + C_NotifyAck *ack_ctx) { + ldout(m_image_ctx.cct, 10) << this << " image exclusively locked announcement" + << dendl; + + bool cancel_async_requests = true; + if (payload.client_id.is_valid()) { + Mutex::Locker owner_client_id_locker(m_owner_client_id_lock); + if (payload.client_id == m_owner_client_id) { + cancel_async_requests = false; + } + set_owner_client_id(payload.client_id); + } + + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + // potentially wake up the exclusive lock state machine now that + // a lock owner has advertised itself + m_image_ctx.exclusive_lock->handle_peer_notification(0); + } + if (cancel_async_requests && + (m_image_ctx.exclusive_lock == nullptr || + !m_image_ctx.exclusive_lock->is_lock_owner())) { + schedule_cancel_async_requests(); + } + return true; +} + +template +bool ImageWatcher::handle_payload(const ReleasedLockPayload &payload, + C_NotifyAck *ack_ctx) { + ldout(m_image_ctx.cct, 10) << this << " exclusive lock released" << dendl; + + bool cancel_async_requests = true; + if (payload.client_id.is_valid()) { + Mutex::Locker l(m_owner_client_id_lock); + if (payload.client_id != m_owner_client_id) { + ldout(m_image_ctx.cct, 10) << this << " unexpected owner: " + << payload.client_id << " != " + << m_owner_client_id << dendl; + cancel_async_requests = false; + } else { + set_owner_client_id(ClientId()); + } + } + + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + if (cancel_async_requests && + (m_image_ctx.exclusive_lock == nullptr || + !m_image_ctx.exclusive_lock->is_lock_owner())) { + schedule_cancel_async_requests(); + } + + // alert the exclusive lock state machine that the lock is available + if (m_image_ctx.exclusive_lock != nullptr && + !m_image_ctx.exclusive_lock->is_lock_owner()) { + m_task_finisher->cancel(TASK_CODE_REQUEST_LOCK); + m_image_ctx.exclusive_lock->handle_peer_notification(0); + } + return true; +} + +template +bool ImageWatcher::handle_payload(const RequestLockPayload &payload, + C_NotifyAck *ack_ctx) { + ldout(m_image_ctx.cct, 10) << this << " exclusive lock requested" << dendl; + if (payload.client_id == get_client_id()) { + return true; + } + + RWLock::RLocker l(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr && + m_image_ctx.exclusive_lock->is_lock_owner()) { + int r = 0; + bool accept_request = m_image_ctx.exclusive_lock->accept_requests(&r); + + if (accept_request) { + assert(r == 0); + Mutex::Locker owner_client_id_locker(m_owner_client_id_lock); + if (!m_owner_client_id.is_valid()) { + return true; + } + + ldout(m_image_ctx.cct, 10) << this << " queuing release of exclusive lock" + << dendl; + r = m_image_ctx.get_exclusive_lock_policy()->lock_requested( + payload.force); + } + ::encode(ResponseMessage(r), ack_ctx->out); + } + return true; +} + +template +bool ImageWatcher::handle_payload(const AsyncProgressPayload &payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker l(m_async_request_lock); + std::map::iterator req_it = + m_async_requests.find(payload.async_request_id); + if (req_it != m_async_requests.end()) { + ldout(m_image_ctx.cct, 20) << this << " request progress: " + << payload.async_request_id << " @ " + << payload.offset << "/" << payload.total + << dendl; + schedule_async_request_timed_out(payload.async_request_id); + req_it->second.second->update_progress(payload.offset, payload.total); + } + return true; +} + +template +bool ImageWatcher::handle_payload(const AsyncCompletePayload &payload, + C_NotifyAck *ack_ctx) { + Context *on_complete = remove_async_request(payload.async_request_id); + if (on_complete != nullptr) { + ldout(m_image_ctx.cct, 10) << this << " request finished: " + << payload.async_request_id << "=" + << payload.result << dendl; + on_complete->complete(payload.result); + } + return true; +} + +template +bool ImageWatcher::handle_payload(const FlattenPayload &payload, + C_NotifyAck *ack_ctx) { + + RWLock::RLocker l(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r)) { + bool new_request; + Context *ctx; + ProgressContext *prog_ctx; + r = prepare_async_request(payload.async_request_id, &new_request, + &ctx, &prog_ctx); + if (r == 0 && new_request) { + ldout(m_image_ctx.cct, 10) << this << " remote flatten request: " + << payload.async_request_id << dendl; + m_image_ctx.operations->execute_flatten(*prog_ctx, ctx); + } + + ::encode(ResponseMessage(r), ack_ctx->out); + } else if (r < 0) { + ::encode(ResponseMessage(r), ack_ctx->out); + } + } + return true; +} + +template +bool ImageWatcher::handle_payload(const ResizePayload &payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker l(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r)) { + bool new_request; + Context *ctx; + ProgressContext *prog_ctx; + r = prepare_async_request(payload.async_request_id, &new_request, + &ctx, &prog_ctx); + if (r == 0 && new_request) { + ldout(m_image_ctx.cct, 10) << this << " remote resize request: " + << payload.async_request_id << " " + << payload.size << " " + << payload.allow_shrink << dendl; + m_image_ctx.operations->execute_resize(payload.size, payload.allow_shrink, *prog_ctx, ctx, 0); + } + + ::encode(ResponseMessage(r), ack_ctx->out); + } else if (r < 0) { + ::encode(ResponseMessage(r), ack_ctx->out); + } + } + return true; +} + +template +bool ImageWatcher::handle_payload(const SnapCreatePayload &payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker l(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r)) { + ldout(m_image_ctx.cct, 10) << this << " remote snap_create request: " + << payload.snap_name << dendl; + + m_image_ctx.operations->execute_snap_create(payload.snap_namespace, + payload.snap_name, + new C_ResponseMessage(ack_ctx), + 0, false); + return false; + } else if (r < 0) { + ::encode(ResponseMessage(r), ack_ctx->out); + } + } + return true; +} + +template +bool ImageWatcher::handle_payload(const SnapRenamePayload &payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker l(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r)) { + ldout(m_image_ctx.cct, 10) << this << " remote snap_rename request: " + << payload.snap_id << " to " + << payload.snap_name << dendl; + + m_image_ctx.operations->execute_snap_rename(payload.snap_id, + payload.snap_name, + new C_ResponseMessage(ack_ctx)); + return false; + } else if (r < 0) { + ::encode(ResponseMessage(r), ack_ctx->out); + } + } + return true; +} + +template +bool ImageWatcher::handle_payload(const SnapRemovePayload &payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker l(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r)) { + ldout(m_image_ctx.cct, 10) << this << " remote snap_remove request: " + << payload.snap_name << dendl; + + m_image_ctx.operations->execute_snap_remove(payload.snap_namespace, + payload.snap_name, + new C_ResponseMessage(ack_ctx)); + return false; + } else if (r < 0) { + ::encode(ResponseMessage(r), ack_ctx->out); + } + } + return true; +} + +template +bool ImageWatcher::handle_payload(const SnapProtectPayload& payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r)) { + ldout(m_image_ctx.cct, 10) << this << " remote snap_protect request: " + << payload.snap_name << dendl; + + m_image_ctx.operations->execute_snap_protect(payload.snap_namespace, + payload.snap_name, + new C_ResponseMessage(ack_ctx)); + return false; + } else if (r < 0) { + ::encode(ResponseMessage(r), ack_ctx->out); + } + } + return true; +} + +template +bool ImageWatcher::handle_payload(const SnapUnprotectPayload& payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r)) { + ldout(m_image_ctx.cct, 10) << this << " remote snap_unprotect request: " + << payload.snap_name << dendl; + + m_image_ctx.operations->execute_snap_unprotect(payload.snap_namespace, + payload.snap_name, + new C_ResponseMessage(ack_ctx)); + return false; + } else if (r < 0) { + ::encode(ResponseMessage(r), ack_ctx->out); + } + } + return true; +} + +template +bool ImageWatcher::handle_payload(const RebuildObjectMapPayload& payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker l(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r)) { + bool new_request; + Context *ctx; + ProgressContext *prog_ctx; + r = prepare_async_request(payload.async_request_id, &new_request, + &ctx, &prog_ctx); + if (r == 0 && new_request) { + ldout(m_image_ctx.cct, 10) << this + << " remote rebuild object map request: " + << payload.async_request_id << dendl; + m_image_ctx.operations->execute_rebuild_object_map(*prog_ctx, ctx); + } + + ::encode(ResponseMessage(r), ack_ctx->out); + } else if (r < 0) { + ::encode(ResponseMessage(r), ack_ctx->out); + } + } + return true; +} + +template +bool ImageWatcher::handle_payload(const RenamePayload& payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r)) { + ldout(m_image_ctx.cct, 10) << this << " remote rename request: " + << payload.image_name << dendl; + + m_image_ctx.operations->execute_rename(payload.image_name, + new C_ResponseMessage(ack_ctx)); + return false; + } else if (r < 0) { + ::encode(ResponseMessage(r), ack_ctx->out); + } + } + return true; +} + +template +bool ImageWatcher::handle_payload(const UpdateFeaturesPayload& payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r)) { + ldout(m_image_ctx.cct, 10) << this << " remote update_features request: " + << payload.features << " " + << (payload.enabled ? "enabled" : "disabled") + << dendl; + + m_image_ctx.operations->execute_update_features( + payload.features, payload.enabled, new C_ResponseMessage(ack_ctx), 0); + return false; + } else if (r < 0) { + ::encode(ResponseMessage(r), ack_ctx->out); + } + } + return true; +} + +template +bool ImageWatcher::handle_payload(const UnknownPayload &payload, + C_NotifyAck *ack_ctx) { + RWLock::RLocker l(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + int r; + if (m_image_ctx.exclusive_lock->accept_requests(&r) || r < 0) { + ::encode(ResponseMessage(-EOPNOTSUPP), ack_ctx->out); + } + } + return true; +} + +template +void ImageWatcher::process_payload(uint64_t notify_id, uint64_t handle, + const Payload &payload) { + apply_visitor(HandlePayloadVisitor>(this, notify_id, handle), + payload); +} + +template +void ImageWatcher::handle_notify(uint64_t notify_id, uint64_t handle, + uint64_t notifier_id, bufferlist &bl) { + NotifyMessage notify_message; + if (bl.length() == 0) { + // legacy notification for header updates + notify_message = NotifyMessage(HeaderUpdatePayload()); + } else { + try { + bufferlist::iterator iter = bl.begin(); + ::decode(notify_message, iter); + } catch (const buffer::error &err) { + lderr(m_image_ctx.cct) << this << " error decoding image notification: " + << err.what() << dendl; + return; + } + } + + // if an image refresh is required, refresh before processing the request + if (notify_message.check_for_refresh() && + m_image_ctx.state->is_refresh_required()) { + m_image_ctx.state->refresh(new C_ProcessPayload(this, notify_id, handle, + notify_message.payload)); + } else { + process_payload(notify_id, handle, notify_message.payload); + } +} + +template +void ImageWatcher::handle_error(uint64_t handle, int err) { + lderr(m_image_ctx.cct) << this << " image watch failed: " << handle << ", " + << cpp_strerror(err) << dendl; + + { + Mutex::Locker l(m_owner_client_id_lock); + set_owner_client_id(ClientId()); + } + + Watcher::handle_error(handle, err); +} + +template +void ImageWatcher::handle_rewatch_complete(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; + + { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + // update the lock cookie with the new watch handle + m_image_ctx.exclusive_lock->reacquire_lock(); + } + } + + // image might have been updated while we didn't have active watch + handle_payload(HeaderUpdatePayload(), nullptr); +} + +template +void ImageWatcher::send_notify(const Payload &payload, Context *ctx) { + bufferlist bl; + + ::encode(NotifyMessage(payload), bl); + Watcher::send_notify(bl, nullptr, ctx); +} + +template +void ImageWatcher::RemoteContext::finish(int r) { + m_image_watcher.schedule_async_complete(m_async_request_id, r); +} + +template +void ImageWatcher::C_ResponseMessage::finish(int r) { + CephContext *cct = notify_ack->cct; + ldout(cct, 10) << this << " C_ResponseMessage: r=" << r << dendl; + + ::encode(ResponseMessage(r), notify_ack->out); + notify_ack->complete(0); +} + +} // namespace librbd + +template class librbd::ImageWatcher;