From: Ricardo Dias Date: Fri, 4 Nov 2016 02:34:39 +0000 (+0000) Subject: rbd: Changed exclusive-lock implementation to use the new managed-lock X-Git-Tag: v12.0.0~225^2~2^2~1 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=2914eef50d6901b89a720f813ffe3c5e7379c0e3;p=ceph.git rbd: Changed exclusive-lock implementation to use the new managed-lock Fixes: http://tracker.ceph.com/issues/17016 Signed-off-by: Ricardo Dias --- diff --git a/src/librbd/AioImageRequestWQ.cc b/src/librbd/AioImageRequestWQ.cc index 8565932924e7..91b3dc784c8b 100644 --- a/src/librbd/AioImageRequestWQ.cc +++ b/src/librbd/AioImageRequestWQ.cc @@ -460,7 +460,7 @@ void AioImageRequestWQ::queue(AioImageRequest<> *req) { ThreadPool::PointerWQ >::queue(req); if (lock_required) { - m_image_ctx.exclusive_lock->request_lock(nullptr); + m_image_ctx.exclusive_lock->acquire_lock(nullptr); } } @@ -487,7 +487,7 @@ void AioImageRequestWQ::handle_refreshed(int r, AioImageRequest<> *req) { // we acquire the lock RWLock::RLocker owner_locker(m_image_ctx.owner_lock); if (is_lock_required() && is_lock_request_needed()) { - m_image_ctx.exclusive_lock->request_lock(nullptr); + m_image_ctx.exclusive_lock->acquire_lock(nullptr); } } diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index ff58d68eb169..cb72553b2ccd 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -35,10 +35,10 @@ set(librbd_internal_srcs managed_lock/ReleaseRequest.cc managed_lock/ReacquireRequest.cc ManagedLock.cc - exclusive_lock/AcquireRequest.cc + exclusive_lock/PreAcquireRequest.cc + exclusive_lock/PostAcquireRequest.cc + exclusive_lock/PreReleaseRequest.cc exclusive_lock/AutomaticPolicy.cc - exclusive_lock/ReacquireRequest.cc - exclusive_lock/ReleaseRequest.cc exclusive_lock/StandardPolicy.cc image/CloseRequest.cc image/CreateRequest.cc diff --git a/src/librbd/ExclusiveLock.cc b/src/librbd/ExclusiveLock.cc index 58fc21e78a08..f0eed9c03ee1 100644 --- a/src/librbd/ExclusiveLock.cc +++ b/src/librbd/ExclusiveLock.cc @@ -2,126 +2,83 @@ // vim: ts=8 sw=2 smarttab #include "librbd/ExclusiveLock.h" -#include "cls/lock/cls_lock_client.h" -#include "common/dout.h" -#include "common/errno.h" #include "librbd/AioImageRequestWQ.h" -#include "librbd/ImageCtx.h" -#include "librbd/ImageState.h" #include "librbd/ImageWatcher.h" +#include "librbd/ImageState.h" +#include "librbd/exclusive_lock/PreAcquireRequest.h" +#include "librbd/exclusive_lock/PostAcquireRequest.h" +#include "librbd/exclusive_lock/PreReleaseRequest.h" #include "librbd/Utils.h" -#include "librbd/exclusive_lock/AcquireRequest.h" -#include "librbd/exclusive_lock/ReacquireRequest.h" -#include "librbd/exclusive_lock/ReleaseRequest.h" -#include +#include "common/Mutex.h" +#include "common/dout.h" #define dout_subsys ceph_subsys_rbd #undef dout_prefix -#define dout_prefix *_dout << "librbd::ExclusiveLock: " +#define dout_prefix *_dout << "librbd::ExclusiveLock: " << this << " " << __func__ namespace librbd { using namespace exclusive_lock; -namespace { - -const std::string WATCHER_LOCK_COOKIE_PREFIX = "auto"; - -template -struct C_SendRequest : public Context { - R* request; - explicit C_SendRequest(R* request) : request(request) { - } - virtual void finish(int r) override { - request->send(); - } -}; - -} // anonymous namespace - template -const std::string ExclusiveLock::WATCHER_LOCK_TAG("internal"); +using ML = ManagedLock; template ExclusiveLock::ExclusiveLock(I &image_ctx) - : m_image_ctx(image_ctx), - m_lock(util::unique_lock_name("librbd::ExclusiveLock::m_lock", this)), - m_state(STATE_UNINITIALIZED), m_watch_handle(0) { -} - -template -ExclusiveLock::~ExclusiveLock() { - assert(m_state == STATE_UNINITIALIZED || m_state == STATE_SHUTDOWN); -} - -template -bool ExclusiveLock::is_lock_owner() const { - Mutex::Locker locker(m_lock); - - bool lock_owner; - switch (m_state) { - case STATE_LOCKED: - case STATE_POST_ACQUIRING: - case STATE_REACQUIRING: - case STATE_PRE_RELEASING: - case STATE_PRE_SHUTTING_DOWN: - lock_owner = true; - break; - default: - lock_owner = false; - break; - } - - ldout(m_image_ctx.cct, 20) << this << " " << __func__ << "=" << lock_owner - << dendl; - return lock_owner; + : ML(image_ctx.md_ctx, image_ctx.op_work_queue, image_ctx.header_oid, + image_ctx.image_watcher), + m_image_ctx(image_ctx), m_pre_post_callback(nullptr), + m_shutting_down(false) { + ML::m_state = ML::STATE_UNINITIALIZED; } template bool ExclusiveLock::accept_requests(int *ret_val) const { - Mutex::Locker locker(m_lock); + Mutex::Locker locker(ML::m_lock); - bool accept_requests = (!is_shutdown() && m_state == STATE_LOCKED && + bool accept_requests = (!ML::is_shutdown_locked() && + ML::m_state == ML::STATE_LOCKED && m_request_blocked_count == 0); *ret_val = m_request_blocked_ret_val; - ldout(m_image_ctx.cct, 20) << this << " " << __func__ << "=" - << accept_requests << dendl; + ldout(m_image_ctx.cct, 20) << "=" << accept_requests << dendl; return accept_requests; } template void ExclusiveLock::block_requests(int r) { - Mutex::Locker locker(m_lock); + Mutex::Locker locker(ML::m_lock); + m_request_blocked_count++; if (m_request_blocked_ret_val == 0) { m_request_blocked_ret_val = r; } - ldout(m_image_ctx.cct, 20) << this << " " << __func__ << dendl; + ldout(m_image_ctx.cct, 20) << dendl; } template void ExclusiveLock::unblock_requests() { - Mutex::Locker locker(m_lock); + Mutex::Locker locker(ML::m_lock); + assert(m_request_blocked_count > 0); m_request_blocked_count--; if (m_request_blocked_count == 0) { m_request_blocked_ret_val = 0; } - ldout(m_image_ctx.cct, 20) << this << " " << __func__ << dendl; + ldout(m_image_ctx.cct, 20) << dendl; } template void ExclusiveLock::init(uint64_t features, Context *on_init) { assert(m_image_ctx.owner_lock.is_locked()); - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; + ldout(m_image_ctx.cct, 10) << dendl; { - Mutex::Locker locker(m_lock); - assert(m_state == STATE_UNINITIALIZED); - m_state = STATE_INITIALIZING; + Mutex::Locker locker(ML::m_lock); + assert(ML::m_state == ML::STATE_UNINITIALIZED); + ML::m_state = ML::STATE_INITIALIZING; } m_image_ctx.aio_work_queue->block_writes(new C_InitComplete(this, on_init)); @@ -132,602 +89,210 @@ void ExclusiveLock::init(uint64_t features, Context *on_init) { template void ExclusiveLock::shut_down(Context *on_shut_down) { - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; + ldout(m_image_ctx.cct, 10) << dendl; - { - Mutex::Locker locker(m_lock); - assert(!is_shutdown()); - execute_action(ACTION_SHUT_DOWN, on_shut_down); - } + ML::shut_down(on_shut_down); // if stalled in request state machine -- abort handle_peer_notification(); } -template -void ExclusiveLock::try_lock(Context *on_tried_lock) { - int r = 0; - { - Mutex::Locker locker(m_lock); - assert(m_image_ctx.owner_lock.is_locked()); - if (is_shutdown()) { - r = -ESHUTDOWN; - } else if (m_state != STATE_LOCKED || !m_actions_contexts.empty()) { - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; - execute_action(ACTION_TRY_LOCK, on_tried_lock); - return; - } - } - - on_tried_lock->complete(r); -} - -template -void ExclusiveLock::request_lock(Context *on_locked) { - int r = 0; - { - Mutex::Locker locker(m_lock); - assert(m_image_ctx.owner_lock.is_locked()); - if (is_shutdown()) { - r = -ESHUTDOWN; - } else if (m_state != STATE_LOCKED || !m_actions_contexts.empty()) { - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; - execute_action(ACTION_REQUEST_LOCK, on_locked); - return; - } - } - - if (on_locked != nullptr) { - on_locked->complete(r); - } -} - -template -void ExclusiveLock::release_lock(Context *on_released) { - int r = 0; - { - Mutex::Locker locker(m_lock); - assert(m_image_ctx.owner_lock.is_locked()); - if (is_shutdown()) { - r = -ESHUTDOWN; - } else if (m_state != STATE_UNLOCKED || !m_actions_contexts.empty()) { - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; - execute_action(ACTION_RELEASE_LOCK, on_released); - return; - } - } - - on_released->complete(r); -} - -template -void ExclusiveLock::reacquire_lock(Context *on_reacquired) { - { - Mutex::Locker locker(m_lock); - assert(m_image_ctx.owner_lock.is_locked()); - - if (m_state == STATE_WAITING_FOR_REGISTER) { - // restart the acquire lock process now that watch is valid - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << ": " - << "woke up waiting acquire" << dendl; - Action active_action = get_active_action(); - assert(active_action == ACTION_TRY_LOCK || - active_action == ACTION_REQUEST_LOCK); - execute_next_action(); - } else if (!is_shutdown() && - (m_state == STATE_LOCKED || - m_state == STATE_ACQUIRING || - m_state == STATE_POST_ACQUIRING || - m_state == STATE_WAITING_FOR_PEER)) { - // interlock the lock operation with other image state ops - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; - execute_action(ACTION_REACQUIRE_LOCK, on_reacquired); - return; - } - } - - // ignore request if shutdown or not in a locked-related state - if (on_reacquired != nullptr) { - on_reacquired->complete(0); - } -} - template void ExclusiveLock::handle_peer_notification() { - Mutex::Locker locker(m_lock); - if (m_state != STATE_WAITING_FOR_PEER) { + Mutex::Locker locker(ML::m_lock); + if (ML::m_state != ML::STATE_WAITING_FOR_LOCK) { return; } - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; - assert(get_active_action() == ACTION_REQUEST_LOCK); - execute_next_action(); + ldout(m_image_ctx.cct, 10) << dendl; + assert(ML::get_active_action() == ML::ACTION_ACQUIRE_LOCK); + ML::execute_next_action(); } template -std::string ExclusiveLock::encode_lock_cookie() const { - assert(m_lock.is_locked()); - - assert(m_watch_handle != 0); - std::ostringstream ss; - ss << WATCHER_LOCK_COOKIE_PREFIX << " " << m_watch_handle; - return ss.str(); -} - -template -bool ExclusiveLock::decode_lock_cookie(const std::string &tag, - uint64_t *handle) { - std::string prefix; - std::istringstream ss(tag); - if (!(ss >> prefix >> *handle) || prefix != WATCHER_LOCK_COOKIE_PREFIX) { - return false; - } - return true; -} +void ExclusiveLock::handle_init_complete() { + ldout(m_image_ctx.cct, 10) << dendl; -template -bool ExclusiveLock::is_transition_state() const { - switch (m_state) { - case STATE_INITIALIZING: - case STATE_ACQUIRING: - case STATE_WAITING_FOR_PEER: - case STATE_WAITING_FOR_REGISTER: - case STATE_POST_ACQUIRING: - case STATE_REACQUIRING: - case STATE_PRE_RELEASING: - case STATE_RELEASING: - case STATE_PRE_SHUTTING_DOWN: - case STATE_SHUTTING_DOWN: - return true; - case STATE_UNINITIALIZED: - case STATE_UNLOCKED: - case STATE_LOCKED: - case STATE_SHUTDOWN: - break; - } - return false; + Mutex::Locker locker(ML::m_lock); + ML::m_state = ML::STATE_UNLOCKED; } template -void ExclusiveLock::append_context(Action action, Context *ctx) { - assert(m_lock.is_locked()); - - for (auto &action_ctxs : m_actions_contexts) { - if (action == action_ctxs.first) { - if (ctx != nullptr) { - action_ctxs.second.push_back(ctx); - } - return; - } - } - - Contexts contexts; - if (ctx != nullptr) { - contexts.push_back(ctx); - } - m_actions_contexts.push_back({action, std::move(contexts)}); -} +void ExclusiveLock::shutdown_handler(int r, Context *on_finish) { + ldout(m_image_ctx.cct, 10) << dendl; -template -void ExclusiveLock::execute_action(Action action, Context *ctx) { - assert(m_lock.is_locked()); - - append_context(action, ctx); - if (!is_transition_state()) { - execute_next_action(); + { + RWLock::WLocker owner_locker(m_image_ctx.owner_lock); + m_image_ctx.aio_work_queue->clear_require_lock_on_read(); + m_image_ctx.exclusive_lock = nullptr; } -} -template -void ExclusiveLock::execute_next_action() { - assert(m_lock.is_locked()); - assert(!m_actions_contexts.empty()); - switch (get_active_action()) { - case ACTION_TRY_LOCK: - case ACTION_REQUEST_LOCK: - send_acquire_lock(); - break; - case ACTION_REACQUIRE_LOCK: - send_reacquire_lock(); - break; - case ACTION_RELEASE_LOCK: - send_release_lock(); - break; - case ACTION_SHUT_DOWN: - send_shutdown(); - break; - default: - assert(false); - break; - } + m_image_ctx.aio_work_queue->unblock_writes(); + m_image_ctx.image_watcher->flush(on_finish); } template -typename ExclusiveLock::Action ExclusiveLock::get_active_action() const { - assert(m_lock.is_locked()); - assert(!m_actions_contexts.empty()); - return m_actions_contexts.front().first; +void ExclusiveLock::pre_acquire_lock_handler(Context *on_finish) { + PreAcquireRequest *req = PreAcquireRequest::create(m_image_ctx, + on_finish); + m_image_ctx.op_work_queue->queue(new FunctionContext([req](int r) { + req->send(); + })); } template -void ExclusiveLock::complete_active_action(State next_state, int r) { - assert(m_lock.is_locked()); - assert(!m_actions_contexts.empty()); - - ActionContexts action_contexts(std::move(m_actions_contexts.front())); - m_actions_contexts.pop_front(); - m_state = next_state; - - m_lock.Unlock(); - for (auto ctx : action_contexts.second) { - ctx->complete(r); - } - m_lock.Lock(); +void ExclusiveLock::post_acquire_lock_handler(int r, Context *on_finish) { + ldout(m_image_ctx.cct, 10) << ": r=" << r << dendl; - if (!is_transition_state() && !m_actions_contexts.empty()) { - execute_next_action(); - } -} -template -bool ExclusiveLock::is_shutdown() const { - assert(m_lock.is_locked()); + if (r < 0) { + ML::m_lock.Lock(); + assert(ML::m_state == ML::STATE_ACQUIRING); - return ((m_state == STATE_SHUTDOWN) || - (!m_actions_contexts.empty() && - m_actions_contexts.back().first == ACTION_SHUT_DOWN)); -} + // PostAcquire state machine will not run, so we need complete prepare + m_image_ctx.state->handle_prepare_lock_complete(); -template -void ExclusiveLock::handle_init_complete() { - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; + typename ML::Action action = ML::get_active_action(); + if (action == ML::ACTION_ACQUIRE_LOCK && r < 0 && r != -EBLACKLISTED) { + ML::m_state = ML::STATE_WAITING_FOR_LOCK; + ML::m_lock.Unlock(); - Mutex::Locker locker(m_lock); - m_state = STATE_UNLOCKED; -} + // request the lock from a peer + m_image_ctx.image_watcher->notify_request_lock(); -template -void ExclusiveLock::send_acquire_lock() { - assert(m_lock.is_locked()); - if (m_state == STATE_LOCKED) { - complete_active_action(STATE_LOCKED, 0); - return; - } + return; + } - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << this << " " << __func__ << dendl; - m_state = STATE_ACQUIRING; + ML::m_lock.Unlock(); + if (r == -EAGAIN) { + r = 0; + } - m_watch_handle = m_image_ctx.image_watcher->get_watch_handle(); - if (m_watch_handle == 0) { - lderr(cct) << "image watcher not registered - delaying request" << dendl; - m_state = STATE_WAITING_FOR_REGISTER; + on_finish->complete(r); return; } - m_cookie = encode_lock_cookie(); + Mutex::Locker locker(ML::m_lock); + m_pre_post_callback = on_finish; + using EL = ExclusiveLock; + PostAcquireRequest *req = PostAcquireRequest::create(m_image_ctx, + util::create_context_callback(this), + util::create_context_callback(this)); - using el = ExclusiveLock; - AcquireRequest* req = AcquireRequest::create( - m_image_ctx, m_cookie, - util::create_context_callback(this), - util::create_context_callback(this)); - m_image_ctx.op_work_queue->queue(new C_SendRequest >(req), - 0); + m_image_ctx.op_work_queue->queue(new FunctionContext([req](int r) { + req->send(); + })); } template -void ExclusiveLock::handle_acquiring_lock(int r) { - Mutex::Locker locker(m_lock); - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; +void ExclusiveLock::handle_post_acquiring_lock(int r) { + ldout(m_image_ctx.cct, 10) << dendl; + + Mutex::Locker locker(ML::m_lock); assert(r == 0); - assert(m_state == STATE_ACQUIRING); + assert(ML::m_state == ML::STATE_ACQUIRING); // lock is owned at this point - m_state = STATE_POST_ACQUIRING; + ML::m_state = ML::STATE_POST_ACQUIRING; } template -void ExclusiveLock::handle_acquire_lock(int r) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; - - if (r == -EBUSY || r == -EAGAIN) { - ldout(cct, 5) << "unable to acquire exclusive lock" << dendl; - } else if (r < 0) { - lderr(cct) << "failed to acquire exclusive lock:" << cpp_strerror(r) - << dendl; - } else { - ldout(cct, 5) << "successfully acquired exclusive lock" << dendl; - } +void ExclusiveLock::handle_post_acquired_lock(int r) { + ldout(m_image_ctx.cct, 10) << ": r=" << r << dendl; + Context *on_finish = nullptr; { - m_lock.Lock(); - assert(m_state == STATE_ACQUIRING || - m_state == STATE_POST_ACQUIRING); + Mutex::Locker locker(ML::m_lock); + assert(ML::m_state == ML::STATE_ACQUIRING || + ML::m_state == ML::STATE_POST_ACQUIRING); - Action action = get_active_action(); - assert(action == ACTION_TRY_LOCK || action == ACTION_REQUEST_LOCK); - if (action == ACTION_REQUEST_LOCK && r < 0 && r != -EBLACKLISTED && - r != -EPERM) { - m_state = STATE_WAITING_FOR_PEER; - m_lock.Unlock(); - - // request the lock from a peer - m_image_ctx.image_watcher->notify_request_lock(); - return; - } - m_lock.Unlock(); + assert (m_pre_post_callback != nullptr); + std::swap(m_pre_post_callback, on_finish); } - State next_state = (r < 0 ? STATE_UNLOCKED : STATE_LOCKED); - if (r == -EAGAIN) { - r = 0; - } - - if (next_state == STATE_LOCKED) { + if (r >= 0) { m_image_ctx.image_watcher->notify_acquired_lock(); m_image_ctx.aio_work_queue->clear_require_lock_on_read(); m_image_ctx.aio_work_queue->unblock_writes(); } - Mutex::Locker locker(m_lock); - complete_active_action(next_state, r); + on_finish->complete(r); } template -void ExclusiveLock::send_reacquire_lock() { - assert(m_lock.is_locked()); - - CephContext *cct = m_image_ctx.cct; - if (m_state != STATE_LOCKED) { - complete_active_action(m_state, 0); - return; - } - - m_watch_handle = m_image_ctx.image_watcher->get_watch_handle(); - if (m_watch_handle == 0) { - // watch (re)failed while recovering - lderr(cct) << this << " " << __func__ << ": " - << "aborting reacquire due to invalid watch handle" << dendl; - complete_active_action(STATE_LOCKED, 0); - return; - } +void ExclusiveLock::pre_release_lock_handler(bool shutting_down, + Context *on_finish) { + ldout(m_image_ctx.cct, 10) << dendl; + Mutex::Locker locker(ML::m_lock); - m_new_cookie = encode_lock_cookie(); - if (m_cookie == m_new_cookie) { - ldout(cct, 10) << this << " " << __func__ << ": " - << "skipping reacquire since cookie still valid" << dendl; - complete_active_action(STATE_LOCKED, 0); - return; - } + m_shutting_down = shutting_down; - ldout(cct, 10) << this << " " << __func__ << dendl; - m_state = STATE_REACQUIRING; + using EL = ExclusiveLock; + PreReleaseRequest *req = PreReleaseRequest::create(m_image_ctx, + util::create_context_callback(this), + on_finish, shutting_down); - using el = ExclusiveLock; - ReacquireRequest* req = ReacquireRequest::create( - m_image_ctx, m_cookie, m_new_cookie, - util::create_context_callback(this)); - req->send(); + m_image_ctx.op_work_queue->queue(new FunctionContext([req](int r) { + req->send(); + })); } template -void ExclusiveLock::handle_reacquire_lock(int r) { - Mutex::Locker locker(m_lock); +void ExclusiveLock::handle_pre_releasing_lock(int r) { + ldout(m_image_ctx.cct, 10) << dendl; - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; + Mutex::Locker locker(ML::m_lock); - assert(m_state == STATE_REACQUIRING); - if (r < 0) { - if (r == -EOPNOTSUPP) { - ldout(cct, 10) << this << " " << __func__ << ": " - << "updating lock is not supported" << dendl; - } else { - lderr(cct) << this << " " << __func__ << ": " - << "failed to update lock cookie: " << cpp_strerror(r) - << dendl; - } + assert(r == 0); + if (!m_shutting_down) { + assert(ML::m_state == ML::STATE_PRE_RELEASING); - if (!is_shutdown()) { - // queue a release and re-acquire of the lock since cookie cannot - // be updated on older OSDs - execute_action(ACTION_RELEASE_LOCK, nullptr); - - assert(!m_actions_contexts.empty()); - ActionContexts &action_contexts(m_actions_contexts.front()); - - // reacquire completes when the request lock completes - Contexts contexts; - std::swap(contexts, action_contexts.second); - if (contexts.empty()) { - execute_action(ACTION_REQUEST_LOCK, nullptr); - } else { - for (auto ctx : contexts) { - ctx = new FunctionContext([ctx, r](int acquire_ret_val) { - if (acquire_ret_val >= 0) { - acquire_ret_val = r; - } - ctx->complete(acquire_ret_val); - }); - execute_action(ACTION_REQUEST_LOCK, ctx); - } - } - } + // all IO and ops should be blocked/canceled by this point + ML::m_state = ML::STATE_RELEASING; } else { - m_cookie = m_new_cookie; - } + assert(ML::m_state == ML::STATE_PRE_SHUTTING_DOWN); - complete_active_action(STATE_LOCKED, 0); -} - -template -void ExclusiveLock::send_release_lock() { - assert(m_lock.is_locked()); - if (m_state == STATE_UNLOCKED) { - complete_active_action(STATE_UNLOCKED, 0); - return; + // all IO and ops should be blocked/canceled by this point + ML::m_state = ML::STATE_SHUTTING_DOWN; } - - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; - m_state = STATE_PRE_RELEASING; - - using el = ExclusiveLock; - ReleaseRequest* req = ReleaseRequest::create( - m_image_ctx, m_cookie, - util::create_context_callback(this), - util::create_context_callback(this), - false); - m_image_ctx.op_work_queue->queue(new C_SendRequest >(req), - 0); -} - -template -void ExclusiveLock::handle_releasing_lock(int r) { - Mutex::Locker locker(m_lock); - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; - - assert(r == 0); - assert(m_state == STATE_PRE_RELEASING); - - // all IO and ops should be blocked/canceled by this point - m_state = STATE_RELEASING; } template -void ExclusiveLock::handle_release_lock(int r) { - bool lock_request_needed = false; - { - Mutex::Locker locker(m_lock); - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << ": r=" << r - << dendl; +void ExclusiveLock::post_release_lock_handler(bool shutting_down, int r, + Context *on_finish) { + ldout(m_image_ctx.cct, 10) << ": r=" << r << " shutting_down=" + << shutting_down << dendl; + if (!shutting_down) { + { + Mutex::Locker locker(ML::m_lock); + assert(ML::m_state == ML::STATE_PRE_RELEASING || + ML::m_state == ML::STATE_RELEASING); + } - assert(m_state == STATE_PRE_RELEASING || - m_state == STATE_RELEASING); if (r >= 0) { - m_lock.Unlock(); m_image_ctx.image_watcher->notify_released_lock(); - lock_request_needed = m_image_ctx.aio_work_queue->is_lock_request_needed(); - m_lock.Lock(); - - m_cookie = ""; - m_watch_handle = 0; + if (m_image_ctx.aio_work_queue->is_lock_request_needed()) { + // if we have blocked IO -- re-request the lock + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + ML::acquire_lock(nullptr); + } } - complete_active_action(r < 0 ? STATE_LOCKED : STATE_UNLOCKED, r); - } - - if (r >= 0 && lock_request_needed) { - // if we have blocked IO -- re-request the lock - RWLock::RLocker owner_locker(m_image_ctx.owner_lock); - request_lock(nullptr); - } -} - -template -void ExclusiveLock::send_shutdown() { - assert(m_lock.is_locked()); - if (m_state == STATE_UNLOCKED) { - m_state = STATE_SHUTTING_DOWN; - m_image_ctx.op_work_queue->queue(util::create_context_callback< - ExclusiveLock, &ExclusiveLock::handle_shutdown>(this), 0); - return; - } - - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; - assert(m_state == STATE_LOCKED); - m_state = STATE_PRE_SHUTTING_DOWN; - - m_lock.Unlock(); - m_image_ctx.op_work_queue->queue(new C_ShutDownRelease(this), 0); - m_lock.Lock(); -} - -template -void ExclusiveLock::send_shutdown_release() { - std::string cookie; - { - Mutex::Locker locker(m_lock); - cookie = m_cookie; - } - - using el = ExclusiveLock; - ReleaseRequest* req = ReleaseRequest::create( - m_image_ctx, cookie, - util::create_context_callback(this), - util::create_context_callback(this), - true); - req->send(); -} - -template -void ExclusiveLock::handle_shutdown_releasing(int r) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; - - assert(r == 0); - assert(m_state == STATE_PRE_SHUTTING_DOWN); - - // all IO and ops should be blocked/canceled by this point - m_state = STATE_SHUTTING_DOWN; -} - -template -void ExclusiveLock::handle_shutdown_released(int r) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; - - { - RWLock::WLocker owner_locker(m_image_ctx.owner_lock); - m_image_ctx.aio_work_queue->clear_require_lock_on_read(); - m_image_ctx.exclusive_lock = nullptr; - } - - if (r < 0) { - lderr(cct) << "failed to shut down exclusive lock: " << cpp_strerror(r) - << dendl; } else { - m_image_ctx.aio_work_queue->unblock_writes(); - } - - m_image_ctx.image_watcher->notify_released_lock(); - complete_shutdown(r); -} - -template -void ExclusiveLock::handle_shutdown(int r) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; - - { - RWLock::WLocker owner_locker(m_image_ctx.owner_lock); - m_image_ctx.aio_work_queue->clear_require_lock_on_read(); - m_image_ctx.exclusive_lock = nullptr; - } - - m_image_ctx.aio_work_queue->unblock_writes(); - m_image_ctx.image_watcher->flush(util::create_context_callback< - ExclusiveLock, &ExclusiveLock::complete_shutdown>(this)); -} + { + RWLock::WLocker owner_locker(m_image_ctx.owner_lock); + m_image_ctx.aio_work_queue->clear_require_lock_on_read(); + m_image_ctx.exclusive_lock = nullptr; + } -template -void ExclusiveLock::complete_shutdown(int r) { - ActionContexts action_contexts; - { - Mutex::Locker locker(m_lock); - assert(m_lock.is_locked()); - assert(m_actions_contexts.size() == 1); + if (r >= 0) { + m_image_ctx.aio_work_queue->unblock_writes(); + } - action_contexts = std::move(m_actions_contexts.front()); - m_actions_contexts.pop_front(); - m_state = STATE_SHUTDOWN; + m_image_ctx.image_watcher->notify_released_lock(); } - // expect to be destroyed after firing callback - for (auto ctx : action_contexts.second) { - ctx->complete(r); - } + on_finish->complete(r); } } // namespace librbd diff --git a/src/librbd/ExclusiveLock.h b/src/librbd/ExclusiveLock.h index c313a8959819..1d2a59fa1caf 100644 --- a/src/librbd/ExclusiveLock.h +++ b/src/librbd/ExclusiveLock.h @@ -4,31 +4,20 @@ #ifndef CEPH_LIBRBD_EXCLUSIVE_LOCK_H #define CEPH_LIBRBD_EXCLUSIVE_LOCK_H -#include "include/int_types.h" -#include "include/Context.h" -#include "include/rados/librados.hpp" -#include "common/Mutex.h" -#include -#include -#include +#include "librbd/ManagedLock.h" +#include "librbd/ImageCtx.h" namespace librbd { -class ImageCtx; - template -class ExclusiveLock { +class ExclusiveLock : public ManagedLock { public: - static const std::string WATCHER_LOCK_TAG; - static ExclusiveLock *create(ImageCtxT &image_ctx) { return new ExclusiveLock(image_ctx); } ExclusiveLock(ImageCtxT &image_ctx); - ~ExclusiveLock(); - bool is_lock_owner() const; bool accept_requests(int *ret_val) const; void block_requests(int r); @@ -37,15 +26,16 @@ public: void init(uint64_t features, Context *on_init); void shut_down(Context *on_shutdown); - void try_lock(Context *on_tried_lock); - void request_lock(Context *on_locked); - void release_lock(Context *on_released); - - void reacquire_lock(Context *on_reacquired = nullptr); - void handle_peer_notification(); - static bool decode_lock_cookie(const std::string &cookie, uint64_t *handle); +protected: + virtual void shutdown_handler(int r, Context *on_finish); + virtual void pre_acquire_lock_handler(Context *on_finish); + virtual void post_acquire_lock_handler(int r, Context *on_finish); + virtual void pre_release_lock_handler(bool shutting_down, + Context *on_finish); + virtual void post_release_lock_handler(bool shutting_down, int r, + Context *on_finish); private: @@ -86,34 +76,6 @@ private: * * @endverbatim */ - enum State { - STATE_UNINITIALIZED, - STATE_UNLOCKED, - STATE_LOCKED, - STATE_INITIALIZING, - STATE_ACQUIRING, - STATE_POST_ACQUIRING, - STATE_WAITING_FOR_PEER, - STATE_WAITING_FOR_REGISTER, - STATE_REACQUIRING, - STATE_PRE_RELEASING, - STATE_RELEASING, - STATE_PRE_SHUTTING_DOWN, - STATE_SHUTTING_DOWN, - STATE_SHUTDOWN - }; - - enum Action { - ACTION_TRY_LOCK, - ACTION_REQUEST_LOCK, - ACTION_REACQUIRE_LOCK, - ACTION_RELEASE_LOCK, - ACTION_SHUT_DOWN - }; - - typedef std::list Contexts; - typedef std::pair ActionContexts; - typedef std::list ActionsContexts; struct C_InitComplete : public Context { ExclusiveLock *exclusive_lock; @@ -129,65 +91,19 @@ private: } }; - struct C_ShutDownRelease : public Context { - ExclusiveLock *exclusive_lock; - C_ShutDownRelease(ExclusiveLock *exclusive_lock) - : exclusive_lock(exclusive_lock) { - } - virtual void finish(int r) override { - exclusive_lock->send_shutdown_release(); - } - }; - - ImageCtxT &m_image_ctx; - - mutable Mutex m_lock; - State m_state; - std::string m_cookie; - std::string m_new_cookie; - uint64_t m_watch_handle; - - ActionsContexts m_actions_contexts; + ImageCtxT& m_image_ctx; + Context *m_pre_post_callback; + bool m_shutting_down; uint32_t m_request_blocked_count = 0; int m_request_blocked_ret_val = 0; - std::string encode_lock_cookie() const; - - bool is_transition_state() const; - - void append_context(Action action, Context *ctx); - void execute_action(Action action, Context *ctx); - void execute_next_action(); - - Action get_active_action() const; - void complete_active_action(State next_state, int r); - - bool is_shutdown() const; - void handle_init_complete(); - - void send_acquire_lock(); - void handle_acquiring_lock(int r); - void handle_acquire_lock(int r); - - void send_reacquire_lock(); - void handle_reacquire_lock(int r); - - void send_release_lock(); - void handle_releasing_lock(int r); - void handle_release_lock(int r); - - void send_shutdown(); - void send_shutdown_release(); - void handle_shutdown_releasing(int r); - void handle_shutdown_released(int r); - void handle_shutdown(int r); - void complete_shutdown(int r); + void handle_post_acquiring_lock(int r); + void handle_post_acquired_lock(int r); + void handle_pre_releasing_lock(int r); }; } // namespace librbd -extern template class librbd::ExclusiveLock; - #endif // CEPH_LIBRBD_EXCLUSIVE_LOCK_H diff --git a/src/librbd/Operations.cc b/src/librbd/Operations.cc index b83e1c6f4e58..2297b9f731ba 100644 --- a/src/librbd/Operations.cc +++ b/src/librbd/Operations.cc @@ -212,9 +212,9 @@ struct C_InvokeAsyncRequest : public Context { // current lock owner doesn't support op -- try to perform // the action locally request_lock = false; - image_ctx.exclusive_lock->request_lock(ctx); + image_ctx.exclusive_lock->acquire_lock(ctx); } else { - image_ctx.exclusive_lock->try_lock(ctx); + image_ctx.exclusive_lock->try_acquire_lock(ctx); } owner_lock.put_read(); } @@ -1212,7 +1212,7 @@ int Operations::snap_set_limit(uint64_t limit) { !m_image_ctx.exclusive_lock->is_lock_owner()) { C_SaferCond lock_ctx; - m_image_ctx.exclusive_lock->request_lock(&lock_ctx); + m_image_ctx.exclusive_lock->acquire_lock(&lock_ctx); r = lock_ctx.wait(); if (r < 0) { return r; @@ -1369,7 +1369,7 @@ int Operations::metadata_set(const std::string &key, !m_image_ctx.exclusive_lock->is_lock_owner()) { C_SaferCond lock_ctx; - m_image_ctx.exclusive_lock->request_lock(&lock_ctx); + m_image_ctx.exclusive_lock->acquire_lock(&lock_ctx); r = lock_ctx.wait(); if (r < 0) { return r; @@ -1424,7 +1424,7 @@ int Operations::metadata_remove(const std::string &key) { !m_image_ctx.exclusive_lock->is_lock_owner()) { C_SaferCond lock_ctx; - m_image_ctx.exclusive_lock->request_lock(&lock_ctx); + m_image_ctx.exclusive_lock->acquire_lock(&lock_ctx); r = lock_ctx.wait(); if (r < 0) { return r; @@ -1469,7 +1469,7 @@ int Operations::prepare_image_update() { if (m_image_ctx.exclusive_lock != nullptr && (!m_image_ctx.exclusive_lock->is_lock_owner() || !m_image_ctx.exclusive_lock->accept_requests(&r))) { - m_image_ctx.exclusive_lock->try_lock(&ctx); + m_image_ctx.exclusive_lock->try_acquire_lock(&ctx); trying_lock = true; } } diff --git a/src/librbd/exclusive_lock/AcquireRequest.cc b/src/librbd/exclusive_lock/AcquireRequest.cc deleted file mode 100644 index 1887e74c589c..000000000000 --- a/src/librbd/exclusive_lock/AcquireRequest.cc +++ /dev/null @@ -1,622 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#include "librbd/exclusive_lock/AcquireRequest.h" -#include "cls/lock/cls_lock_client.h" -#include "cls/lock/cls_lock_types.h" -#include "common/dout.h" -#include "common/errno.h" -#include "common/WorkQueue.h" -#include "include/stringify.h" -#include "librbd/ExclusiveLock.h" -#include "librbd/ImageCtx.h" -#include "librbd/ImageState.h" -#include "librbd/ImageWatcher.h" -#include "librbd/Journal.h" -#include "librbd/ObjectMap.h" -#include "librbd/Utils.h" -#include "librbd/image/RefreshRequest.h" -#include "librbd/journal/Policy.h" - -#define dout_subsys ceph_subsys_rbd -#undef dout_prefix -#define dout_prefix *_dout << "librbd::exclusive_lock::AcquireRequest: " - -namespace librbd { -namespace exclusive_lock { - -using util::create_async_context_callback; -using util::create_context_callback; -using util::create_rados_ack_callback; -using util::create_rados_safe_callback; - -namespace { - -template -struct C_BlacklistClient : public Context { - I &image_ctx; - std::string locker_address; - Context *on_finish; - - C_BlacklistClient(I &image_ctx, const std::string &locker_address, - Context *on_finish) - : image_ctx(image_ctx), locker_address(locker_address), - on_finish(on_finish) { - } - - virtual void finish(int r) override { - librados::Rados rados(image_ctx.md_ctx); - r = rados.blacklist_add(locker_address, - image_ctx.blacklist_expire_seconds); - on_finish->complete(r); - } -}; - -} // anonymous namespace - -template -AcquireRequest* AcquireRequest::create(I &image_ctx, - const std::string &cookie, - Context *on_acquire, - Context *on_finish) { - return new AcquireRequest(image_ctx, cookie, on_acquire, on_finish); -} - -template -AcquireRequest::AcquireRequest(I &image_ctx, const std::string &cookie, - Context *on_acquire, Context *on_finish) - : m_image_ctx(image_ctx), m_cookie(cookie), m_on_acquire(on_acquire), - m_on_finish(create_async_context_callback(image_ctx, on_finish)), - m_object_map(nullptr), m_journal(nullptr), m_error_result(0) { -} - -template -AcquireRequest::~AcquireRequest() { - if (!m_prepare_lock_completed) { - m_image_ctx.state->handle_prepare_lock_complete(); - } - delete m_on_acquire; -} - -template -void AcquireRequest::send() { - send_prepare_lock(); -} - -template -void AcquireRequest::send_prepare_lock() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - // acquire the lock if the image is not busy performing other actions - Context *ctx = create_context_callback< - AcquireRequest, &AcquireRequest::handle_prepare_lock>(this); - m_image_ctx.state->prepare_lock(ctx); -} - -template -Context *AcquireRequest::handle_prepare_lock(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - send_flush_notifies(); - return nullptr; -} - -template -void AcquireRequest::send_flush_notifies() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = AcquireRequest; - Context *ctx = create_context_callback( - this); - m_image_ctx.image_watcher->flush(ctx); -} - -template -Context *AcquireRequest::handle_flush_notifies(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - assert(*ret_val == 0); - send_lock(); - return nullptr; -} - -template -void AcquireRequest::send_lock() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - librados::ObjectWriteOperation op; - rados::cls::lock::lock(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, m_cookie, - ExclusiveLock<>::WATCHER_LOCK_TAG, "", utime_t(), 0); - - using klass = AcquireRequest; - librados::AioCompletion *rados_completion = - create_rados_safe_callback(this); - int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, - rados_completion, &op); - assert(r == 0); - rados_completion->release(); -} - -template -Context *AcquireRequest::handle_lock(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val == 0) { - return send_refresh(); - } else if (*ret_val != -EBUSY) { - lderr(cct) << "failed to lock: " << cpp_strerror(*ret_val) << dendl; - return m_on_finish; - } - - send_get_lockers(); - return nullptr; -} - -template -Context *AcquireRequest::send_refresh() { - if (!m_image_ctx.state->is_refresh_required()) { - return send_open_object_map(); - } - - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = AcquireRequest; - Context *ctx = create_async_context_callback( - m_image_ctx, create_context_callback(this)); - - // ImageState is blocked waiting for lock to complete -- safe to directly - // refresh - image::RefreshRequest *req = image::RefreshRequest::create( - m_image_ctx, true, ctx); - req->send(); - return nullptr; -} - -template -Context *AcquireRequest::handle_refresh(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val == -ERESTART) { - // next issued IO or op will (re)-refresh the image and shut down lock - ldout(cct, 5) << ": exclusive lock dynamically disabled" << dendl; - *ret_val = 0; - } else if (*ret_val < 0) { - lderr(cct) << "failed to refresh image: " << cpp_strerror(*ret_val) - << dendl; - m_error_result = *ret_val; - send_unlock(); - return nullptr; - } - - return send_open_object_map(); -} - -template -Context *AcquireRequest::send_open_journal() { - // alert caller that we now own the exclusive lock - m_on_acquire->complete(0); - m_on_acquire = nullptr; - - bool journal_enabled; - { - RWLock::RLocker snap_locker(m_image_ctx.snap_lock); - journal_enabled = (m_image_ctx.test_features(RBD_FEATURE_JOURNALING, - m_image_ctx.snap_lock) && - !m_image_ctx.get_journal_policy()->journal_disabled()); - } - if (!journal_enabled) { - apply(); - return m_on_finish; - } - - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = AcquireRequest; - Context *ctx = create_context_callback( - this); - m_journal = m_image_ctx.create_journal(); - - // journal playback requires object map (if enabled) and itself - apply(); - - m_journal->open(ctx); - return nullptr; -} - -template -Context *AcquireRequest::handle_open_journal(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val < 0) { - lderr(cct) << "failed to open journal: " << cpp_strerror(*ret_val) << dendl; - m_error_result = *ret_val; - send_close_journal(); - return nullptr; - } - - send_allocate_journal_tag(); - return nullptr; -} - -template -void AcquireRequest::send_allocate_journal_tag() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - RWLock::RLocker snap_locker(m_image_ctx.snap_lock); - using klass = AcquireRequest; - Context *ctx = create_context_callback< - klass, &klass::handle_allocate_journal_tag>(this); - m_image_ctx.get_journal_policy()->allocate_tag_on_lock(ctx); -} - -template -Context *AcquireRequest::handle_allocate_journal_tag(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val < 0) { - lderr(cct) << "failed to allocate journal tag: " << cpp_strerror(*ret_val) - << dendl; - m_error_result = *ret_val; - send_close_journal(); - return nullptr; - } - return m_on_finish; -} - -template -void AcquireRequest::send_close_journal() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = AcquireRequest; - Context *ctx = create_context_callback( - this); - m_journal->close(ctx); -} - -template -Context *AcquireRequest::handle_close_journal(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val < 0) { - lderr(cct) << "failed to close journal: " << cpp_strerror(*ret_val) - << dendl; - } - - send_close_object_map(); - return nullptr; -} - -template -Context *AcquireRequest::send_open_object_map() { - if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP)) { - return send_open_journal(); - } - - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = AcquireRequest; - Context *ctx = create_context_callback( - this); - - m_object_map = m_image_ctx.create_object_map(CEPH_NOSNAP); - m_object_map->open(ctx); - return nullptr; -} - -template -Context *AcquireRequest::handle_open_object_map(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val < 0) { - lderr(cct) << "failed to open object map: " << cpp_strerror(*ret_val) - << dendl; - - *ret_val = 0; - delete m_object_map; - m_object_map = nullptr; - } - - return send_open_journal(); -} - -template -void AcquireRequest::send_close_object_map() { - if (m_object_map == nullptr) { - send_unlock(); - return; - } - - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = AcquireRequest; - Context *ctx = create_context_callback< - klass, &klass::handle_close_object_map>(this); - m_object_map->close(ctx); -} - -template -Context *AcquireRequest::handle_close_object_map(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - // object map should never result in an error - assert(*ret_val == 0); - send_unlock(); - return nullptr; -} - -template -void AcquireRequest::send_unlock() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - librados::ObjectWriteOperation op; - rados::cls::lock::unlock(&op, RBD_LOCK_NAME, m_cookie); - - using klass = AcquireRequest; - librados::AioCompletion *rados_completion = - create_rados_safe_callback(this); - int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, - rados_completion, &op); - assert(r == 0); - rados_completion->release(); -} - -template -Context *AcquireRequest::handle_unlock(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val < 0) { - lderr(cct) << "failed to unlock image: " << cpp_strerror(*ret_val) << dendl; - } - - revert(ret_val); - return m_on_finish; -} - -template -void AcquireRequest::send_get_lockers() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - librados::ObjectReadOperation op; - rados::cls::lock::get_lock_info_start(&op, RBD_LOCK_NAME); - - using klass = AcquireRequest; - librados::AioCompletion *rados_completion = - create_rados_ack_callback(this); - m_out_bl.clear(); - int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, - rados_completion, &op, &m_out_bl); - assert(r == 0); - rados_completion->release(); -} - -template -Context *AcquireRequest::handle_get_lockers(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - std::map lockers; - ClsLockType lock_type = LOCK_NONE; - std::string lock_tag; - if (*ret_val == 0) { - bufferlist::iterator it = m_out_bl.begin(); - *ret_val = rados::cls::lock::get_lock_info_finish(&it, &lockers, - &lock_type, &lock_tag); - } - - if (*ret_val < 0) { - lderr(cct) << "failed to retrieve lockers: " << cpp_strerror(*ret_val) - << dendl; - return m_on_finish; - } - - if (lockers.empty()) { - ldout(cct, 20) << "no lockers detected" << dendl; - send_lock(); - return nullptr; - } - - if (lock_tag != ExclusiveLock<>::WATCHER_LOCK_TAG) { - ldout(cct, 5) <<"locked by external mechanism: tag=" << lock_tag << dendl; - *ret_val = -EBUSY; - return m_on_finish; - } - - if (lock_type == LOCK_SHARED) { - ldout(cct, 5) << "shared lock type detected" << dendl; - *ret_val = -EBUSY; - return m_on_finish; - } - - std::map::iterator iter = lockers.begin(); - if (!ExclusiveLock<>::decode_lock_cookie(iter->first.cookie, - &m_locker_handle)) { - ldout(cct, 5) << "locked by external mechanism: " - << "cookie=" << iter->first.cookie << dendl; - *ret_val = -EBUSY; - return m_on_finish; - } - - m_locker_entity = iter->first.locker; - m_locker_cookie = iter->first.cookie; - m_locker_address = stringify(iter->second.addr); - if (m_locker_cookie.empty() || m_locker_address.empty()) { - ldout(cct, 20) << "no valid lockers detected" << dendl; - send_lock(); - return nullptr; - } - - ldout(cct, 10) << "retrieved exclusive locker: " - << m_locker_entity << "@" << m_locker_address << dendl; - send_get_watchers(); - return nullptr; -} - -template -void AcquireRequest::send_get_watchers() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - librados::ObjectReadOperation op; - op.list_watchers(&m_watchers, &m_watchers_ret_val); - - using klass = AcquireRequest; - librados::AioCompletion *rados_completion = - create_rados_ack_callback(this); - m_out_bl.clear(); - int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, - rados_completion, &op, &m_out_bl); - assert(r == 0); - rados_completion->release(); -} - -template -Context *AcquireRequest::handle_get_watchers(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val == 0) { - *ret_val = m_watchers_ret_val; - } - if (*ret_val < 0) { - lderr(cct) << "failed to retrieve watchers: " << cpp_strerror(*ret_val) - << dendl; - return m_on_finish; - } - - for (auto &watcher : m_watchers) { - if ((strncmp(m_locker_address.c_str(), - watcher.addr, sizeof(watcher.addr)) == 0) && - (m_locker_handle == watcher.cookie)) { - ldout(cct, 10) << "lock owner is still alive" << dendl; - - *ret_val = -EAGAIN; - return m_on_finish; - } - } - - send_blacklist(); - return nullptr; -} - -template -void AcquireRequest::send_blacklist() { - if (!m_image_ctx.blacklist_on_break_lock) { - send_break_lock(); - return; - } - - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - // TODO: need async version of RadosClient::blacklist_add - using klass = AcquireRequest; - Context *ctx = create_context_callback( - this); - m_image_ctx.op_work_queue->queue(new C_BlacklistClient(m_image_ctx, - m_locker_address, - ctx), 0); -} - -template -Context *AcquireRequest::handle_blacklist(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val < 0) { - lderr(cct) << "failed to blacklist lock owner: " << cpp_strerror(*ret_val) - << dendl; - return m_on_finish; - } - send_break_lock(); - return nullptr; -} - -template -void AcquireRequest::send_break_lock() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - librados::ObjectWriteOperation op; - rados::cls::lock::break_lock(&op, RBD_LOCK_NAME, m_locker_cookie, - m_locker_entity); - - using klass = AcquireRequest; - librados::AioCompletion *rados_completion = - create_rados_safe_callback(this); - int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, - rados_completion, &op); - assert(r == 0); - rados_completion->release(); -} - -template -Context *AcquireRequest::handle_break_lock(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val == -ENOENT) { - *ret_val = 0; - } else if (*ret_val < 0) { - lderr(cct) << "failed to break lock: " << cpp_strerror(*ret_val) << dendl; - return m_on_finish; - } - - send_lock(); - return nullptr; -} - -template -void AcquireRequest::apply() { - { - RWLock::WLocker snap_locker(m_image_ctx.snap_lock); - assert(m_image_ctx.object_map == nullptr); - m_image_ctx.object_map = m_object_map; - - assert(m_image_ctx.journal == nullptr); - m_image_ctx.journal = m_journal; - } - - m_prepare_lock_completed = true; - m_image_ctx.state->handle_prepare_lock_complete(); -} - -template -void AcquireRequest::revert(int *ret_val) { - RWLock::WLocker snap_locker(m_image_ctx.snap_lock); - m_image_ctx.object_map = nullptr; - m_image_ctx.journal = nullptr; - - delete m_object_map; - delete m_journal; - - assert(m_error_result < 0); - *ret_val = m_error_result; -} - -} // namespace exclusive_lock -} // namespace librbd - -template class librbd::exclusive_lock::AcquireRequest; diff --git a/src/librbd/exclusive_lock/AcquireRequest.h b/src/librbd/exclusive_lock/AcquireRequest.h deleted file mode 100644 index 64f078b08c25..000000000000 --- a/src/librbd/exclusive_lock/AcquireRequest.h +++ /dev/null @@ -1,156 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#ifndef CEPH_LIBRBD_EXCLUSIVE_LOCK_ACQUIRE_REQUEST_H -#define CEPH_LIBRBD_EXCLUSIVE_LOCK_ACQUIRE_REQUEST_H - -#include "include/int_types.h" -#include "include/buffer.h" -#include "librbd/ImageCtx.h" -#include "msg/msg_types.h" -#include - -class Context; - -namespace librbd { - -template class Journal; - -namespace exclusive_lock { - -template -class AcquireRequest { -public: - static AcquireRequest* create(ImageCtxT &image_ctx, const std::string &cookie, - Context *on_acquire, Context *on_finish); - - ~AcquireRequest(); - void send(); - -private: - - /** - * @verbatim - * - * - * | - * v - * PREPARE_LOCK - * | - * v - * FLUSH_NOTIFIES - * | - * | /-----------------------------------------------------------\ - * | | | - * | | (no lockers) | - * | | . . . . . . . . . . . . . . . . . . . . . . | - * | | . . | - * | v v (EBUSY) . | - * \--> LOCK_IMAGE * * * * * * * * > GET_LOCKERS . . . . | - * | | | - * v v | - * REFRESH (skip if not GET_WATCHERS | - * | needed) | | - * v v | - * OPEN_OBJECT_MAP (skip if BLACKLIST (skip if blacklist | - * | disabled) | disabled) | - * v v | - * OPEN_JOURNAL (skip if BREAK_LOCK | - * | * disabled) | | - * | * \-----------------------------/ - * | * * * * * * * * - * v * - * ALLOCATE_JOURNAL_TAG * - * | * * - * | * * - * | v v - * | CLOSE_JOURNAL - * | | - * | v - * | CLOSE_OBJECT_MAP - * | | - * | v - * | UNLOCK_IMAGE - * | | - * v | - * <----------/ - * - * @endverbatim - */ - - AcquireRequest(ImageCtxT &image_ctx, const std::string &cookie, - Context *on_acquire, Context *on_finish); - - ImageCtxT &m_image_ctx; - std::string m_cookie; - Context *m_on_acquire; - Context *m_on_finish; - - bufferlist m_out_bl; - - std::list m_watchers; - int m_watchers_ret_val; - - decltype(m_image_ctx.object_map) m_object_map; - decltype(m_image_ctx.journal) m_journal; - - entity_name_t m_locker_entity; - std::string m_locker_cookie; - std::string m_locker_address; - uint64_t m_locker_handle; - - int m_error_result; - bool m_prepare_lock_completed = false; - - void send_prepare_lock(); - Context *handle_prepare_lock(int *ret_val); - - void send_flush_notifies(); - Context *handle_flush_notifies(int *ret_val); - - void send_lock(); - Context *handle_lock(int *ret_val); - - Context *send_refresh(); - Context *handle_refresh(int *ret_val); - - Context *send_open_journal(); - Context *handle_open_journal(int *ret_val); - - void send_allocate_journal_tag(); - Context *handle_allocate_journal_tag(int *ret_val); - - Context *send_open_object_map(); - Context *handle_open_object_map(int *ret_val); - - void send_close_journal(); - Context *handle_close_journal(int *ret_val); - - void send_close_object_map(); - Context *handle_close_object_map(int *ret_val); - - void send_unlock(); - Context *handle_unlock(int *ret_val); - - void send_get_lockers(); - Context *handle_get_lockers(int *ret_val); - - void send_get_watchers(); - Context *handle_get_watchers(int *ret_val); - - void send_blacklist(); - Context *handle_blacklist(int *ret_val); - - void send_break_lock(); - Context *handle_break_lock(int *ret_val); - - void apply(); - void revert(int *ret_val); -}; - -} // namespace exclusive_lock -} // namespace librbd - -extern template class librbd::exclusive_lock::AcquireRequest; - -#endif // CEPH_LIBRBD_EXCLUSIVE_LOCK_ACQUIRE_REQUEST_H diff --git a/src/librbd/exclusive_lock/PostAcquireRequest.cc b/src/librbd/exclusive_lock/PostAcquireRequest.cc new file mode 100644 index 000000000000..ed5c466c2eaf --- /dev/null +++ b/src/librbd/exclusive_lock/PostAcquireRequest.cc @@ -0,0 +1,303 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/exclusive_lock/PostAcquireRequest.h" +#include "cls/lock/cls_lock_client.h" +#include "cls/lock/cls_lock_types.h" +#include "common/dout.h" +#include "common/errno.h" +#include "common/WorkQueue.h" +#include "include/stringify.h" +#include "librbd/ExclusiveLock.h" +#include "librbd/ManagedLock.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageState.h" +#include "librbd/ImageWatcher.h" +#include "librbd/Journal.h" +#include "librbd/ObjectMap.h" +#include "librbd/Utils.h" +#include "librbd/image/RefreshRequest.h" +#include "librbd/journal/Policy.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::exclusive_lock::PostAcquireRequest: " + +namespace librbd { +namespace exclusive_lock { + +using util::create_async_context_callback; +using util::create_context_callback; +using util::create_rados_ack_callback; +using util::create_rados_safe_callback; + + +template +PostAcquireRequest* PostAcquireRequest::create(I &image_ctx, + Context *on_acquire, + Context *on_finish) { + return new PostAcquireRequest(image_ctx, on_acquire, on_finish); +} + +template +PostAcquireRequest::PostAcquireRequest(I &image_ctx, Context *on_acquire, + Context *on_finish) + : m_image_ctx(image_ctx), + m_on_acquire(on_acquire), + m_on_finish(create_async_context_callback(image_ctx, on_finish)), + m_object_map(nullptr), m_journal(nullptr), m_error_result(0) { +} + +template +PostAcquireRequest::~PostAcquireRequest() { + if (!m_prepare_lock_completed) { + m_image_ctx.state->handle_prepare_lock_complete(); + } + delete m_on_acquire; +} + +template +void PostAcquireRequest::send() { + send_refresh(); +} + +template +void PostAcquireRequest::send_refresh() { + if (!m_image_ctx.state->is_refresh_required()) { + send_open_object_map(); + return; + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PostAcquireRequest; + Context *ctx = create_async_context_callback( + m_image_ctx, create_context_callback(this)); + + // ImageState is blocked waiting for lock to complete -- safe to directly + // refresh + image::RefreshRequest *req = image::RefreshRequest::create( + m_image_ctx, true, ctx); + req->send(); +} + +template +void PostAcquireRequest::handle_refresh(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + if (r == -ERESTART) { + // next issued IO or op will (re)-refresh the image and shut down lock + ldout(cct, 5) << ": exclusive lock dynamically disabled" << dendl; + r = 0; + } else if (r < 0) { + lderr(cct) << "failed to refresh image: " << cpp_strerror(r) << dendl; + save_result(r); + revert(); + finish(); + return; + } + + send_open_object_map(); +} + +template +void PostAcquireRequest::send_open_journal() { + // alert caller that we now own the exclusive lock + m_on_acquire->complete(0); + m_on_acquire = nullptr; + + bool journal_enabled; + { + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + journal_enabled = (m_image_ctx.test_features(RBD_FEATURE_JOURNALING, + m_image_ctx.snap_lock) && + !m_image_ctx.get_journal_policy()->journal_disabled()); + } + if (!journal_enabled) { + apply(); + finish(); + return; + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PostAcquireRequest; + Context *ctx = create_context_callback( + this); + m_journal = m_image_ctx.create_journal(); + + // journal playback requires object map (if enabled) and itself + apply(); + + m_journal->open(ctx); +} + +template +void PostAcquireRequest::handle_open_journal(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + save_result(r); + if (r < 0) { + lderr(cct) << "failed to open journal: " << cpp_strerror(r) << dendl; + send_close_journal(); + return; + } + + send_allocate_journal_tag(); +} + +template +void PostAcquireRequest::send_allocate_journal_tag() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + RWLock::RLocker snap_locker(m_image_ctx.snap_lock); + using klass = PostAcquireRequest; + Context *ctx = create_context_callback< + klass, &klass::handle_allocate_journal_tag>(this); + m_image_ctx.get_journal_policy()->allocate_tag_on_lock(ctx); +} + +template +void PostAcquireRequest::handle_allocate_journal_tag(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + save_result(r); + if (r < 0) { + lderr(cct) << "failed to allocate journal tag: " << cpp_strerror(r) + << dendl; + send_close_journal(); + return; + } + + finish(); +} + +template +void PostAcquireRequest::send_close_journal() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PostAcquireRequest; + Context *ctx = create_context_callback( + this); + m_journal->close(ctx); +} + +template +void PostAcquireRequest::handle_close_journal(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + save_result(r); + if (r < 0) { + lderr(cct) << "failed to close journal: " << cpp_strerror(r) << dendl; + } + + send_close_object_map(); +} + +template +void PostAcquireRequest::send_open_object_map() { + if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP)) { + send_open_journal(); + return; + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PostAcquireRequest; + Context *ctx = create_context_callback( + this); + + m_object_map = m_image_ctx.create_object_map(CEPH_NOSNAP); + m_object_map->open(ctx); +} + +template +void PostAcquireRequest::handle_open_object_map(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to open object map: " << cpp_strerror(r) << dendl; + + r = 0; + delete m_object_map; + m_object_map = nullptr; + } + + send_open_journal(); +} + +template +void PostAcquireRequest::send_close_object_map() { + if (m_object_map == nullptr) { + revert(); + finish(); + return; + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PostAcquireRequest; + Context *ctx = create_context_callback< + klass, &klass::handle_close_object_map>(this); + m_object_map->close(ctx); +} + +template +void PostAcquireRequest::handle_close_object_map(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + // object map should never result in an error + assert(r == 0); + revert(); + finish(); +} + +template +void PostAcquireRequest::apply() { + { + RWLock::WLocker snap_locker(m_image_ctx.snap_lock); + assert(m_image_ctx.object_map == nullptr); + m_image_ctx.object_map = m_object_map; + + assert(m_image_ctx.journal == nullptr); + m_image_ctx.journal = m_journal; + } + + m_prepare_lock_completed = true; + m_image_ctx.state->handle_prepare_lock_complete(); +} + +template +void PostAcquireRequest::revert() { + RWLock::WLocker snap_locker(m_image_ctx.snap_lock); + m_image_ctx.object_map = nullptr; + m_image_ctx.journal = nullptr; + + delete m_object_map; + delete m_journal; + + assert(m_error_result < 0); +} + +template +void PostAcquireRequest::finish() { + m_on_finish->complete(m_error_result); + delete this; +} + +} // namespace exclusive_lock +} // namespace librbd + +template class librbd::exclusive_lock::PostAcquireRequest; diff --git a/src/librbd/exclusive_lock/PostAcquireRequest.h b/src/librbd/exclusive_lock/PostAcquireRequest.h new file mode 100644 index 000000000000..92d2da9abcdf --- /dev/null +++ b/src/librbd/exclusive_lock/PostAcquireRequest.h @@ -0,0 +1,114 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_EXCLUSIVE_LOCK_POST_ACQUIRE_REQUEST_H +#define CEPH_LIBRBD_EXCLUSIVE_LOCK_POST_ACQUIRE_REQUEST_H + +#include "include/int_types.h" +#include "include/buffer.h" +#include "librbd/ImageCtx.h" +#include "msg/msg_types.h" +#include + +class Context; + +namespace librbd { + +template class Journal; +template class ManagedLock; + +namespace exclusive_lock { + +template +class PostAcquireRequest { +public: + static PostAcquireRequest* create(ImageCtxT &image_ctx, Context *on_acquire, + Context *on_finish); + + ~PostAcquireRequest(); + void send(); + +private: + + /** + * @verbatim + * + * + * | + * | + * v + * REFRESH (skip if not + * | needed) + * v + * OPEN_OBJECT_MAP (skip if + * | disabled) + * v + * OPEN_JOURNAL (skip if + * | * disabled) + * | * + * | * * * * * * * * + * v * + * ALLOCATE_JOURNAL_TAG * + * | * * + * | * * + * | v v + * | CLOSE_JOURNAL + * | | + * | v + * | CLOSE_OBJECT_MAP + * | | + * v | + * <----------/ + * + * @endverbatim + */ + + PostAcquireRequest(ImageCtxT &image_ctx, Context *on_acquire, + Context *on_finish); + + ImageCtxT &m_image_ctx; + Context *m_on_acquire; + Context *m_on_finish; + + decltype(m_image_ctx.object_map) m_object_map; + decltype(m_image_ctx.journal) m_journal; + + bool m_prepare_lock_completed = false; + int m_error_result; + + void send_refresh(); + void handle_refresh(int r); + + void send_open_journal(); + void handle_open_journal(int r); + + void send_allocate_journal_tag(); + void handle_allocate_journal_tag(int r); + + void send_open_object_map(); + void handle_open_object_map(int r); + + void send_close_journal(); + void handle_close_journal(int r); + + void send_close_object_map(); + void handle_close_object_map(int r); + + void apply(); + void revert(); + + void finish(); + + void save_result(int result) { + if (m_error_result == 0 && result < 0) { + m_error_result = result; + } + } +}; + +} // namespace exclusive_lock +} // namespace librbd + +extern template class librbd::exclusive_lock::PostAcquireRequest; + +#endif // CEPH_LIBRBD_EXCLUSIVE_LOCK_POST_ACQUIRE_REQUEST_H diff --git a/src/librbd/exclusive_lock/PreAcquireRequest.cc b/src/librbd/exclusive_lock/PreAcquireRequest.cc new file mode 100644 index 000000000000..25ef4809b2dd --- /dev/null +++ b/src/librbd/exclusive_lock/PreAcquireRequest.cc @@ -0,0 +1,95 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/exclusive_lock/PreAcquireRequest.h" +#include "librbd/Utils.h" +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageWatcher.h" +#include "librbd/ImageState.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::exclusive_lock::PreAcquireRequest: " + +namespace librbd { +namespace exclusive_lock { + +using util::create_async_context_callback; +using util::create_context_callback; +using util::create_rados_ack_callback; +using util::create_rados_safe_callback; + + +template +PreAcquireRequest* PreAcquireRequest::create(I &image_ctx, + Context *on_finish) { + return new PreAcquireRequest(image_ctx, on_finish); +} + +template +PreAcquireRequest::PreAcquireRequest(I &image_ctx, Context *on_finish) + : m_image_ctx(image_ctx), + m_on_finish(create_async_context_callback(image_ctx, on_finish)), + m_error_result(0) { +} + +template +PreAcquireRequest::~PreAcquireRequest() { +} + +template +void PreAcquireRequest::send() { + send_prepare_lock(); +} + +template +void PreAcquireRequest::send_prepare_lock() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + // acquire the lock if the image is not busy performing other actions + Context *ctx = create_context_callback< + PreAcquireRequest, &PreAcquireRequest::handle_prepare_lock>(this); + m_image_ctx.state->prepare_lock(ctx); +} + +template +void PreAcquireRequest::handle_prepare_lock(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + send_flush_notifies(); +} + +template +void PreAcquireRequest::send_flush_notifies() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PreAcquireRequest; + Context *ctx = create_context_callback( + this); + m_image_ctx.image_watcher->flush(ctx); +} + +template +void PreAcquireRequest::handle_flush_notifies(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + assert(r == 0); + finish(); +} + +template +void PreAcquireRequest::finish() { + m_on_finish->complete(m_error_result); + delete this; +} + +} // namespace exclusive_lock +} // namespace librbd + +template class librbd::exclusive_lock::PreAcquireRequest; diff --git a/src/librbd/exclusive_lock/PreAcquireRequest.h b/src/librbd/exclusive_lock/PreAcquireRequest.h new file mode 100644 index 000000000000..15d4b2c12b0d --- /dev/null +++ b/src/librbd/exclusive_lock/PreAcquireRequest.h @@ -0,0 +1,75 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_EXCLUSIVE_LOCK_PRE_ACQUIRE_REQUEST_H +#define CEPH_LIBRBD_EXCLUSIVE_LOCK_PRE_ACQUIRE_REQUEST_H + +#include "include/int_types.h" +#include "include/buffer.h" +#include "librbd/ImageCtx.h" +#include "msg/msg_types.h" +#include + +class Context; + +namespace librbd { + +namespace exclusive_lock { + +template +class PreAcquireRequest { +public: + static PreAcquireRequest* create(ImageCtxT &image_ctx, Context *on_finish); + + ~PreAcquireRequest(); + void send(); + +private: + + /** + * @verbatim + * + * + * | + * v + * PREPARE_LOCK + * | + * v + * FLUSH_NOTIFIES + * | + * | + * | + v + * + * + * @endverbatim + */ + + PreAcquireRequest(ImageCtxT &image_ctx, Context *on_finish); + + ImageCtxT &m_image_ctx; + Context *m_on_finish; + + int m_error_result; + + void send_prepare_lock(); + void handle_prepare_lock(int r); + + void send_flush_notifies(); + void handle_flush_notifies(int r); + + void finish(); + + void save_result(int result) { + if (m_error_result == 0 && result < 0) { + m_error_result = result; + } + } +}; + +} // namespace exclusive_lock +} // namespace librbd + +extern template class librbd::exclusive_lock::PreAcquireRequest; + +#endif // CEPH_LIBRBD_EXCLUSIVE_LOCK_ACQUIRE_REQUEST_H diff --git a/src/librbd/exclusive_lock/PreReleaseRequest.cc b/src/librbd/exclusive_lock/PreReleaseRequest.cc new file mode 100644 index 000000000000..c03838f3c680 --- /dev/null +++ b/src/librbd/exclusive_lock/PreReleaseRequest.cc @@ -0,0 +1,247 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/exclusive_lock/PreReleaseRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "librbd/AioImageRequestWQ.h" +#include "librbd/ExclusiveLock.h" +#include "librbd/ManagedLock.h" +#include "librbd/ImageState.h" +#include "librbd/ImageWatcher.h" +#include "librbd/Journal.h" +#include "librbd/ObjectMap.h" +#include "librbd/Utils.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::exclusive_lock::PreReleaseRequest: " + +namespace librbd { +namespace exclusive_lock { + +using util::create_async_context_callback; +using util::create_context_callback; + +template +PreReleaseRequest* PreReleaseRequest::create(I &image_ctx, + Context *on_releasing, + Context *on_finish, + bool shutting_down) { + return new PreReleaseRequest(image_ctx, on_releasing, on_finish, + shutting_down); +} + +template +PreReleaseRequest::PreReleaseRequest(I &image_ctx, Context *on_releasing, + Context *on_finish, bool shutting_down) + : m_image_ctx(image_ctx), m_on_releasing(on_releasing), + m_on_finish(create_async_context_callback(image_ctx, on_finish)), + m_shutting_down(shutting_down), m_error_result(0), m_object_map(nullptr), + m_journal(nullptr) { +} + +template +PreReleaseRequest::~PreReleaseRequest() { + if (!m_shutting_down) { + m_image_ctx.state->handle_prepare_lock_complete(); + } + delete m_on_releasing; +} + +template +void PreReleaseRequest::send() { + send_prepare_lock(); +} + +template +void PreReleaseRequest::send_prepare_lock() { + if (m_shutting_down) { + send_cancel_op_requests(); + return; + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + // release the lock if the image is not busy performing other actions + Context *ctx = create_context_callback< + PreReleaseRequest, &PreReleaseRequest::handle_prepare_lock>(this); + m_image_ctx.state->prepare_lock(ctx); +} + +template +void PreReleaseRequest::handle_prepare_lock(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + send_cancel_op_requests(); +} + +template +void PreReleaseRequest::send_cancel_op_requests() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PreReleaseRequest; + Context *ctx = create_context_callback< + klass, &klass::handle_cancel_op_requests>(this); + m_image_ctx.cancel_async_requests(ctx); +} + +template +void PreReleaseRequest::handle_cancel_op_requests(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + assert(r == 0); + + send_block_writes(); +} + +template +void PreReleaseRequest::send_block_writes() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PreReleaseRequest; + Context *ctx = create_context_callback< + klass, &klass::handle_block_writes>(this); + + { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { + m_image_ctx.aio_work_queue->set_require_lock_on_read(); + } + m_image_ctx.aio_work_queue->block_writes(ctx); + } +} + +template +void PreReleaseRequest::handle_block_writes(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + save_result(r); + if (r < 0) { + m_image_ctx.aio_work_queue->unblock_writes(); + finish(); + return; + } + + send_image_flush_notifies(); +} + +template +void PreReleaseRequest::send_image_flush_notifies() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PreReleaseRequest; + Context *ctx = + create_context_callback(this); + m_image_ctx.image_watcher->flush(ctx); +} + +template +void PreReleaseRequest::handle_image_flush_notifies(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + assert(r == 0); + send_close_journal(); +} + +template +void PreReleaseRequest::send_close_journal() { + { + RWLock::WLocker snap_locker(m_image_ctx.snap_lock); + std::swap(m_journal, m_image_ctx.journal); + } + + if (m_journal == nullptr) { + send_close_object_map(); + return; + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PreReleaseRequest; + Context *ctx = create_context_callback( + this); + m_journal->close(ctx); +} + +template +void PreReleaseRequest::handle_close_journal(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + if (r < 0) { + // error implies some journal events were not flushed -- continue + lderr(cct) << "failed to close journal: " << cpp_strerror(r) << dendl; + } + + delete m_journal; + + send_close_object_map(); +} + +template +void PreReleaseRequest::send_close_object_map() { + { + RWLock::WLocker snap_locker(m_image_ctx.snap_lock); + std::swap(m_object_map, m_image_ctx.object_map); + } + + if (m_object_map == nullptr) { + send_unlock(); + return; + } + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + using klass = PreReleaseRequest; + Context *ctx = create_context_callback< + klass, &klass::handle_close_object_map>(this); + m_object_map->close(ctx); +} + +template +void PreReleaseRequest::handle_close_object_map(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << ": r=" << r << dendl; + + // object map shouldn't return errors + assert(r == 0); + delete m_object_map; + + send_unlock(); +} + +template +void PreReleaseRequest::send_unlock() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << __func__ << dendl; + + if (m_on_releasing != nullptr) { + // alert caller that we no longer own the exclusive lock + m_on_releasing->complete(0); + m_on_releasing = nullptr; + } + + finish(); +} + +template +void PreReleaseRequest::finish() { + m_on_finish->complete(m_error_result); + delete this; +} + +} // namespace exclusive_lock +} // namespace librbd + +template class librbd::exclusive_lock::PreReleaseRequest; diff --git a/src/librbd/exclusive_lock/PreReleaseRequest.h b/src/librbd/exclusive_lock/PreReleaseRequest.h new file mode 100644 index 000000000000..b51a5b7cd16f --- /dev/null +++ b/src/librbd/exclusive_lock/PreReleaseRequest.h @@ -0,0 +1,105 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_EXCLUSIVE_LOCK_PRE_RELEASE_REQUEST_H +#define CEPH_LIBRBD_EXCLUSIVE_LOCK_PRE_RELEASE_REQUEST_H + +#include "librbd/ImageCtx.h" +#include + +class Context; + +namespace librbd { + +struct ImageCtx; +template class ManagedLock; +template class Journal; + +namespace exclusive_lock { + +template +class PreReleaseRequest { +public: + static PreReleaseRequest* create(ImageCtxT &image_ctx, Context *on_releasing, + Context *on_finish, bool shutting_down); + + ~PreReleaseRequest(); + void send(); + +private: + /** + * @verbatim + * + * + * | + * v + * PREPARE_LOCK + * | + * v + * CANCEL_OP_REQUESTS + * | + * v + * BLOCK_WRITES + * | + * v + * FLUSH_NOTIFIES . . . . . . . . . . . . . . + * | . + * v . + * CLOSE_JOURNAL . + * | (journal disabled, . + * v object map enabled) . + * CLOSE_OBJECT_MAP < . . . . . . . . . . . . + * | . + * v (object map disabled) . + * < . . . . . . . . . . . . . . . . . + * + * @endverbatim + */ + + PreReleaseRequest(ImageCtxT &image_ctx, Context *on_releasing, + Context *on_finish, bool shutting_down); + + ImageCtxT &m_image_ctx; + Context *m_on_releasing; + Context *m_on_finish; + bool m_shutting_down; + + int m_error_result; + + decltype(m_image_ctx.object_map) m_object_map; + decltype(m_image_ctx.journal) m_journal; + + void send_prepare_lock(); + void handle_prepare_lock(int r); + + void send_cancel_op_requests(); + void handle_cancel_op_requests(int r); + + void send_block_writes(); + void handle_block_writes(int r); + + void send_image_flush_notifies(); + void handle_image_flush_notifies(int r); + + void send_close_journal(); + void handle_close_journal(int r); + + void send_close_object_map(); + void handle_close_object_map(int r); + + void send_unlock(); + + void finish(); + + void save_result(int result) { + if (m_error_result == 0 && result < 0) { + m_error_result = result; + } + } + +}; + +} // namespace exclusive_lock +} // namespace librbd + +#endif // CEPH_LIBRBD_EXCLUSIVE_LOCK_PRE_RELEASE_REQUEST_H diff --git a/src/librbd/exclusive_lock/ReacquireRequest.cc b/src/librbd/exclusive_lock/ReacquireRequest.cc deleted file mode 100644 index e847f65bf877..000000000000 --- a/src/librbd/exclusive_lock/ReacquireRequest.cc +++ /dev/null @@ -1,72 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#include "librbd/exclusive_lock/ReacquireRequest.h" -#include "cls/lock/cls_lock_client.h" -#include "cls/lock/cls_lock_types.h" -#include "common/dout.h" -#include "common/errno.h" -#include "librbd/ExclusiveLock.h" -#include "librbd/ImageCtx.h" -#include "librbd/Utils.h" - -#define dout_subsys ceph_subsys_rbd -#undef dout_prefix -#define dout_prefix *_dout << "librbd::exclusive_lock::ReacquireRequest: " \ - << this << ": " << __func__ - -namespace librbd { -namespace exclusive_lock { - -using librbd::util::create_rados_safe_callback; - -template -ReacquireRequest::ReacquireRequest(I &image_ctx, - const std::string &old_cookie, - const std::string &new_cookie, - Context *on_finish) - : m_image_ctx(image_ctx), m_old_cookie(old_cookie), m_new_cookie(new_cookie), - m_on_finish(on_finish) { -} - -template -void ReacquireRequest::send() { - set_cookie(); -} - -template -void ReacquireRequest::set_cookie() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << dendl; - - librados::ObjectWriteOperation op; - rados::cls::lock::set_cookie(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, m_old_cookie, - ExclusiveLock<>::WATCHER_LOCK_TAG, m_new_cookie); - - librados::AioCompletion *rados_completion = create_rados_safe_callback< - ReacquireRequest, &ReacquireRequest::handle_set_cookie>(this); - int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, - rados_completion, &op); - assert(r == 0); - rados_completion->release(); -} - -template -void ReacquireRequest::handle_set_cookie(int r) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << ": r=" << r << dendl; - - if (r == -EOPNOTSUPP) { - ldout(cct, 10) << ": OSD doesn't support updating lock" << dendl; - } else if (r < 0) { - lderr(cct) << ": failed to update lock: " << cpp_strerror(r) << dendl; - } - - m_on_finish->complete(r); - delete this; -} - -} // namespace exclusive_lock -} // namespace librbd - -template class librbd::exclusive_lock::ReacquireRequest; diff --git a/src/librbd/exclusive_lock/ReacquireRequest.h b/src/librbd/exclusive_lock/ReacquireRequest.h deleted file mode 100644 index 2718befa45bf..000000000000 --- a/src/librbd/exclusive_lock/ReacquireRequest.h +++ /dev/null @@ -1,63 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#ifndef CEPH_LIBRBD_EXCLUSIVE_LOCK_REACQUIRE_REQUEST_H -#define CEPH_LIBRBD_EXCLUSIVE_LOCK_REACQUIRE_REQUEST_H - -#include "include/int_types.h" -#include - -class Context; - -namespace librbd { - -class ImageCtx; - -namespace exclusive_lock { - -template -class ReacquireRequest { -public: - - static ReacquireRequest *create(ImageCtxT &image_ctx, - const std::string &old_cookie, - const std::string &new_cookie, - Context *on_finish) { - return new ReacquireRequest(image_ctx, old_cookie, new_cookie, on_finish); - } - - ReacquireRequest(ImageCtxT &image_ctx, const std::string &old_cookie, - const std::string &new_cookie, Context *on_finish); - - void send(); - -private: - /** - * @verbatim - * - * - * | - * v - * SET_COOKIE - * | - * v - * - * - * @endverbatim - */ - ImageCtxT &m_image_ctx; - std::string m_old_cookie; - std::string m_new_cookie; - Context *m_on_finish; - - void set_cookie(); - void handle_set_cookie(int r); - -}; - -} // namespace exclusive_lock -} // namespace librbd - -extern template class librbd::exclusive_lock::ReacquireRequest; - -#endif // CEPH_LIBRBD_EXCLUSIVE_LOCK_REACQUIRE_REQUEST_H diff --git a/src/librbd/exclusive_lock/ReleaseRequest.cc b/src/librbd/exclusive_lock/ReleaseRequest.cc deleted file mode 100644 index 52343f85dfed..000000000000 --- a/src/librbd/exclusive_lock/ReleaseRequest.cc +++ /dev/null @@ -1,272 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#include "librbd/exclusive_lock/ReleaseRequest.h" -#include "cls/lock/cls_lock_client.h" -#include "cls/lock/cls_lock_types.h" -#include "common/dout.h" -#include "common/errno.h" -#include "librbd/AioImageRequestWQ.h" -#include "librbd/ExclusiveLock.h" -#include "librbd/ImageState.h" -#include "librbd/ImageWatcher.h" -#include "librbd/Journal.h" -#include "librbd/ObjectMap.h" -#include "librbd/Utils.h" - -#define dout_subsys ceph_subsys_rbd -#undef dout_prefix -#define dout_prefix *_dout << "librbd::exclusive_lock::ReleaseRequest: " - -namespace librbd { -namespace exclusive_lock { - -using util::create_async_context_callback; -using util::create_context_callback; -using util::create_rados_safe_callback; - -template -ReleaseRequest* ReleaseRequest::create(I &image_ctx, - const std::string &cookie, - Context *on_releasing, - Context *on_finish, - bool shutting_down) { - return new ReleaseRequest(image_ctx, cookie, on_releasing, on_finish, - shutting_down); -} - -template -ReleaseRequest::ReleaseRequest(I &image_ctx, const std::string &cookie, - Context *on_releasing, Context *on_finish, - bool shutting_down) - : m_image_ctx(image_ctx), m_cookie(cookie), m_on_releasing(on_releasing), - m_on_finish(create_async_context_callback(image_ctx, on_finish)), - m_shutting_down(shutting_down), m_object_map(nullptr), m_journal(nullptr) { -} - -template -ReleaseRequest::~ReleaseRequest() { - if (!m_shutting_down) { - m_image_ctx.state->handle_prepare_lock_complete(); - } - delete m_on_releasing; -} - -template -void ReleaseRequest::send() { - send_prepare_lock(); -} - -template -void ReleaseRequest::send_prepare_lock() { - if (m_shutting_down) { - send_cancel_op_requests(); - return; - } - - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - // release the lock if the image is not busy performing other actions - Context *ctx = create_context_callback< - ReleaseRequest, &ReleaseRequest::handle_prepare_lock>(this); - m_image_ctx.state->prepare_lock(ctx); -} - -template -Context *ReleaseRequest::handle_prepare_lock(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - send_cancel_op_requests(); - return nullptr; -} - -template -void ReleaseRequest::send_cancel_op_requests() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = ReleaseRequest; - Context *ctx = create_context_callback< - klass, &klass::handle_cancel_op_requests>(this); - m_image_ctx.cancel_async_requests(ctx); -} - -template -Context *ReleaseRequest::handle_cancel_op_requests(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - assert(*ret_val == 0); - - send_block_writes(); - return nullptr; -} - -template -void ReleaseRequest::send_block_writes() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = ReleaseRequest; - Context *ctx = create_context_callback< - klass, &klass::handle_block_writes>(this); - - { - RWLock::RLocker owner_locker(m_image_ctx.owner_lock); - if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) { - m_image_ctx.aio_work_queue->set_require_lock_on_read(); - } - m_image_ctx.aio_work_queue->block_writes(ctx); - } -} - -template -Context *ReleaseRequest::handle_block_writes(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val < 0) { - m_image_ctx.aio_work_queue->unblock_writes(); - return m_on_finish; - } - - send_flush_notifies(); - return nullptr; -} - -template -void ReleaseRequest::send_flush_notifies() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = ReleaseRequest; - Context *ctx = create_context_callback< - klass, &klass::handle_flush_notifies>(this); - m_image_ctx.image_watcher->flush(ctx); -} - -template -Context *ReleaseRequest::handle_flush_notifies(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - assert(*ret_val == 0); - send_close_journal(); - return nullptr; -} - -template -void ReleaseRequest::send_close_journal() { - { - RWLock::WLocker snap_locker(m_image_ctx.snap_lock); - std::swap(m_journal, m_image_ctx.journal); - } - - if (m_journal == nullptr) { - send_close_object_map(); - return; - } - - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = ReleaseRequest; - Context *ctx = create_context_callback( - this); - m_journal->close(ctx); -} - -template -Context *ReleaseRequest::handle_close_journal(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val < 0) { - // error implies some journal events were not flushed -- continue - lderr(cct) << "failed to close journal: " << cpp_strerror(*ret_val) - << dendl; - } - - delete m_journal; - - send_close_object_map(); - return nullptr; -} - -template -void ReleaseRequest::send_close_object_map() { - { - RWLock::WLocker snap_locker(m_image_ctx.snap_lock); - std::swap(m_object_map, m_image_ctx.object_map); - } - - if (m_object_map == nullptr) { - send_unlock(); - return; - } - - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - using klass = ReleaseRequest; - Context *ctx = create_context_callback< - klass, &klass::handle_close_object_map>(this); - m_object_map->close(ctx); -} - -template -Context *ReleaseRequest::handle_close_object_map(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - // object map shouldn't return errors - assert(*ret_val == 0); - delete m_object_map; - - send_unlock(); - return nullptr; -} - -template -void ReleaseRequest::send_unlock() { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << dendl; - - if (m_on_releasing != nullptr) { - // alert caller that we no longer own the exclusive lock - m_on_releasing->complete(0); - m_on_releasing = nullptr; - } - - librados::ObjectWriteOperation op; - rados::cls::lock::unlock(&op, RBD_LOCK_NAME, m_cookie); - - using klass = ReleaseRequest; - librados::AioCompletion *rados_completion = - create_rados_safe_callback(this); - int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, - rados_completion, &op); - assert(r == 0); - rados_completion->release(); -} - -template -Context *ReleaseRequest::handle_unlock(int *ret_val) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - - if (*ret_val < 0 && *ret_val != -ENOENT) { - lderr(cct) << "failed to unlock: " << cpp_strerror(*ret_val) << dendl; - } - - // treat errors as the image is unlocked - *ret_val = 0; - return m_on_finish; -} - -} // namespace exclusive_lock -} // namespace librbd - -template class librbd::exclusive_lock::ReleaseRequest; diff --git a/src/librbd/exclusive_lock/ReleaseRequest.h b/src/librbd/exclusive_lock/ReleaseRequest.h deleted file mode 100644 index 7c070ef69035..000000000000 --- a/src/librbd/exclusive_lock/ReleaseRequest.h +++ /dev/null @@ -1,103 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#ifndef CEPH_LIBRBD_EXCLUSIVE_LOCK_RELEASE_REQUEST_H -#define CEPH_LIBRBD_EXCLUSIVE_LOCK_RELEASE_REQUEST_H - -#include "librbd/ImageCtx.h" -#include - -class Context; - -namespace librbd { - -class ImageCtx; -template class Journal; - -namespace exclusive_lock { - -template -class ReleaseRequest { -public: - static ReleaseRequest* create(ImageCtxT &image_ctx, const std::string &cookie, - Context *on_releasing, Context *on_finish, - bool shutting_down); - - ~ReleaseRequest(); - void send(); - -private: - /** - * @verbatim - * - * - * | - * v - * PREPARE_LOCK - * | - * v - * CANCEL_OP_REQUESTS - * | - * v - * BLOCK_WRITES - * | - * v - * FLUSH_NOTIFIES . . . . . . . . . . . . . . - * | . - * v . - * CLOSE_JOURNAL . - * | (journal disabled, . - * v object map enabled) . - * CLOSE_OBJECT_MAP < . . . . . . . . . . . . - * | . - * v (object map disabled) . - * UNLOCK < . . . . . . . . . . . . . . . . . - * | - * v - * - * - * @endverbatim - */ - - ReleaseRequest(ImageCtxT &image_ctx, const std::string &cookie, - Context *on_releasing, Context *on_finish, - bool shutting_down); - - ImageCtxT &m_image_ctx; - std::string m_cookie; - Context *m_on_releasing; - Context *m_on_finish; - bool m_shutting_down; - - decltype(m_image_ctx.object_map) m_object_map; - decltype(m_image_ctx.journal) m_journal; - - void send_prepare_lock(); - Context *handle_prepare_lock(int *ret_val); - - void send_cancel_op_requests(); - Context *handle_cancel_op_requests(int *ret_val); - - void send_block_writes(); - Context *handle_block_writes(int *ret_val); - - void send_flush_notifies(); - Context *handle_flush_notifies(int *ret_val); - - void send_close_journal(); - Context *handle_close_journal(int *ret_val); - - void send_close_object_map(); - Context *handle_close_object_map(int *ret_val); - - void send_unlock(); - Context *handle_unlock(int *ret_val); - -}; - -} // namespace exclusive_lock -} // namespace librbd - -extern template class librbd::exclusive_lock::ReleaseRequest; - -#endif // CEPH_LIBRBD_EXCLUSIVE_LOCK_RELEASE_REQUEST_H diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc index 7815bc09ce3e..bfe226bdb326 100644 --- a/src/librbd/internal.cc +++ b/src/librbd/internal.cc @@ -1438,7 +1438,7 @@ int mirror_image_disable_internal(ImageCtx *ictx, bool force, return 0; } - ictx->exclusive_lock->request_lock(&lock_ctx); + ictx->exclusive_lock->acquire_lock(&lock_ctx); } int r = lock_ctx.wait(); @@ -2580,7 +2580,7 @@ int mirror_image_disable_internal(ImageCtx *ictx, bool force, }; C_SaferCond lock_ctx; - ictx->exclusive_lock->request_lock(&lock_ctx); + ictx->exclusive_lock->acquire_lock(&lock_ctx); // don't block holding lock since refresh might be required ictx->owner_lock.put_read(); diff --git a/src/librbd/managed_lock/AcquireRequest.cc b/src/librbd/managed_lock/AcquireRequest.cc index 591f8201be18..5084180a517f 100644 --- a/src/librbd/managed_lock/AcquireRequest.cc +++ b/src/librbd/managed_lock/AcquireRequest.cc @@ -139,7 +139,7 @@ void AcquireRequest::handle_get_lockers(int r) { std::map lockers; - ClsLockType lock_type; + ClsLockType lock_type = LOCK_NONE; std::string lock_tag; if (r == 0) { diff --git a/src/librbd/operation/DisableFeaturesRequest.cc b/src/librbd/operation/DisableFeaturesRequest.cc index bd9edf6edcc9..b7e9bc473c4c 100644 --- a/src/librbd/operation/DisableFeaturesRequest.cc +++ b/src/librbd/operation/DisableFeaturesRequest.cc @@ -144,7 +144,7 @@ void DisableFeaturesRequest::send_acquire_exclusive_lock() { !image_ctx.exclusive_lock->is_lock_owner()) { m_acquired_lock = true; - image_ctx.exclusive_lock->request_lock(ctx); + image_ctx.exclusive_lock->acquire_lock(ctx); return; } } diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index e3d6dbbb6ee0..b84abaf68105 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -31,9 +31,9 @@ set(unittest_librbd_srcs test_mock_ExclusiveLock.cc test_mock_Journal.cc test_mock_ObjectMap.cc - exclusive_lock/test_mock_AcquireRequest.cc - exclusive_lock/test_mock_ReacquireRequest.cc - exclusive_lock/test_mock_ReleaseRequest.cc + exclusive_lock/test_mock_PreAcquireRequest.cc + exclusive_lock/test_mock_PostAcquireRequest.cc + exclusive_lock/test_mock_PreReleaseRequest.cc image/test_mock_RefreshRequest.cc journal/test_mock_OpenRequest.cc journal/test_mock_PromoteRequest.cc diff --git a/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc b/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc deleted file mode 100644 index ee4548821cc9..000000000000 --- a/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc +++ /dev/null @@ -1,924 +0,0 @@ -// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#include "test/librbd/test_mock_fixture.h" -#include "test/librbd/test_support.h" -#include "test/librbd/mock/MockImageCtx.h" -#include "test/librbd/mock/MockImageState.h" -#include "test/librbd/mock/MockJournal.h" -#include "test/librbd/mock/MockJournalPolicy.h" -#include "test/librbd/mock/MockObjectMap.h" -#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" -#include "test/librados_test_stub/MockTestMemRadosClient.h" -#include "cls/lock/cls_lock_ops.h" -#include "librbd/ExclusiveLock.h" -#include "librbd/exclusive_lock/AcquireRequest.h" -#include "librbd/image/RefreshRequest.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include -#include - -namespace librbd { -namespace { - -struct MockTestImageCtx : public librbd::MockImageCtx { - MockTestImageCtx(librbd::ImageCtx &image_ctx) - : librbd::MockImageCtx(image_ctx) { - } -}; - -} // anonymous namespace - -namespace image { - -template<> -struct RefreshRequest { - static RefreshRequest *s_instance; - Context *on_finish; - - static RefreshRequest *create(librbd::MockTestImageCtx &image_ctx, - bool acquire_lock_refresh, - Context *on_finish) { - EXPECT_TRUE(acquire_lock_refresh); - assert(s_instance != nullptr); - s_instance->on_finish = on_finish; - return s_instance; - } - - RefreshRequest() { - s_instance = this; - } - MOCK_METHOD0(send, void()); -}; - -RefreshRequest *RefreshRequest::s_instance = nullptr; - -} // namespace image -} // namespace librbd - -// template definitions -#include "librbd/Journal.cc" -#include "librbd/exclusive_lock/AcquireRequest.cc" -template class librbd::exclusive_lock::AcquireRequest; - -namespace librbd { -namespace exclusive_lock { - -using ::testing::_; -using ::testing::DoAll; -using ::testing::InSequence; -using ::testing::Invoke; -using ::testing::Return; -using ::testing::SetArgPointee; -using ::testing::StrEq; -using ::testing::WithArg; - -static const std::string TEST_COOKIE("auto 123"); - -class TestMockExclusiveLockAcquireRequest : public TestMockFixture { -public: - typedef AcquireRequest MockAcquireRequest; - typedef ExclusiveLock MockExclusiveLock; - typedef librbd::image::RefreshRequest MockRefreshRequest; - - void expect_test_features(MockTestImageCtx &mock_image_ctx, uint64_t features, - bool enabled) { - EXPECT_CALL(mock_image_ctx, test_features(features)) - .WillOnce(Return(enabled)); - } - - void expect_test_features(MockTestImageCtx &mock_image_ctx, uint64_t features, - RWLock &lock, bool enabled) { - EXPECT_CALL(mock_image_ctx, test_features(features, _)) - .WillOnce(Return(enabled)); - } - - void expect_lock(MockTestImageCtx &mock_image_ctx, int r) { - EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), - exec(mock_image_ctx.header_oid, _, StrEq("lock"), StrEq("lock"), _, _, _)) - .WillOnce(Return(r)); - } - - void expect_unlock(MockTestImageCtx &mock_image_ctx, int r) { - EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), - exec(mock_image_ctx.header_oid, _, StrEq("lock"), StrEq("unlock"), _, _, _)) - .WillOnce(Return(r)); - } - - void expect_is_refresh_required(MockTestImageCtx &mock_image_ctx, bool required) { - EXPECT_CALL(*mock_image_ctx.state, is_refresh_required()) - .WillOnce(Return(required)); - } - - void expect_refresh(MockTestImageCtx &mock_image_ctx, - MockRefreshRequest &mock_refresh_request, int r) { - EXPECT_CALL(mock_refresh_request, send()) - .WillOnce(FinishRequest(&mock_refresh_request, r, - &mock_image_ctx)); - } - - void expect_create_object_map(MockTestImageCtx &mock_image_ctx, - MockObjectMap *mock_object_map) { - EXPECT_CALL(mock_image_ctx, create_object_map(_)) - .WillOnce(Return(mock_object_map)); - } - - void expect_open_object_map(MockTestImageCtx &mock_image_ctx, - MockObjectMap &mock_object_map, int r) { - EXPECT_CALL(mock_object_map, open(_)) - .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_close_object_map(MockTestImageCtx &mock_image_ctx, - MockObjectMap &mock_object_map) { - EXPECT_CALL(mock_object_map, close(_)) - .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_create_journal(MockTestImageCtx &mock_image_ctx, - MockJournal *mock_journal) { - EXPECT_CALL(mock_image_ctx, create_journal()) - .WillOnce(Return(mock_journal)); - } - - void expect_open_journal(MockTestImageCtx &mock_image_ctx, - MockJournal &mock_journal, int r) { - EXPECT_CALL(mock_journal, open(_)) - .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_close_journal(MockTestImageCtx &mock_image_ctx, - MockJournal &mock_journal) { - EXPECT_CALL(mock_journal, close(_)) - .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_get_journal_policy(MockTestImageCtx &mock_image_ctx, - MockJournalPolicy &mock_journal_policy) { - EXPECT_CALL(mock_image_ctx, get_journal_policy()) - .WillOnce(Return(&mock_journal_policy)); - } - - void expect_journal_disabled(MockJournalPolicy &mock_journal_policy, - bool disabled) { - EXPECT_CALL(mock_journal_policy, journal_disabled()) - .WillOnce(Return(disabled)); - } - - void expect_allocate_journal_tag(MockTestImageCtx &mock_image_ctx, - MockJournalPolicy &mock_journal_policy, - int r) { - EXPECT_CALL(mock_journal_policy, allocate_tag_on_lock(_)) - .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_get_lock_info(MockTestImageCtx &mock_image_ctx, int r, - const entity_name_t &locker_entity, - const std::string &locker_address, - const std::string &locker_cookie, - const std::string &lock_tag, - ClsLockType lock_type) { - auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), - exec(mock_image_ctx.header_oid, _, StrEq("lock"), - StrEq("get_info"), _, _, _)); - if (r < 0 && r != -ENOENT) { - expect.WillOnce(Return(r)); - } else { - entity_name_t entity(locker_entity); - entity_addr_t entity_addr; - entity_addr.parse(locker_address.c_str(), NULL); - - cls_lock_get_info_reply reply; - if (r != -ENOENT) { - reply.lockers = decltype(reply.lockers){ - {rados::cls::lock::locker_id_t(entity, locker_cookie), - rados::cls::lock::locker_info_t(utime_t(), entity_addr, "")}}; - reply.tag = lock_tag; - reply.lock_type = lock_type; - } - - bufferlist bl; - ::encode(reply, bl, CEPH_FEATURES_SUPPORTED_DEFAULT); - - std::string str(bl.c_str(), bl.length()); - expect.WillOnce(DoAll(WithArg<5>(CopyInBufferlist(str)), Return(0))); - } - } - - void expect_list_watchers(MockTestImageCtx &mock_image_ctx, int r, - const std::string &address, uint64_t watch_handle) { - auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), - list_watchers(mock_image_ctx.header_oid, _)); - if (r < 0) { - expect.WillOnce(Return(r)); - } else { - obj_watch_t watcher; - strcpy(watcher.addr, (address + ":0/0").c_str()); - watcher.cookie = watch_handle; - - std::list watchers; - watchers.push_back(watcher); - - expect.WillOnce(DoAll(SetArgPointee<1>(watchers), Return(0))); - } - } - - void expect_blacklist_add(MockTestImageCtx &mock_image_ctx, int r) { - EXPECT_CALL(get_mock_rados_client(), blacklist_add(_, _)) - .WillOnce(Return(r)); - } - - void expect_break_lock(MockTestImageCtx &mock_image_ctx, int r) { - EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), - exec(mock_image_ctx.header_oid, _, StrEq("lock"), StrEq("break_lock"), _, _, _)) - .WillOnce(Return(r)); - } - - void expect_flush_notifies(MockTestImageCtx &mock_image_ctx) { - EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_)) - .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_prepare_lock(MockTestImageCtx &mock_image_ctx) { - EXPECT_CALL(*mock_image_ctx.state, prepare_lock(_)) - .WillOnce(Invoke([](Context *on_ready) { - on_ready->complete(0); - })); - } - - void expect_handle_prepare_lock_complete(MockTestImageCtx &mock_image_ctx) { - EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete()); - } - -}; - -TEST_F(TestMockExclusiveLockAcquireRequest, Success) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, 0); - expect_is_refresh_required(mock_image_ctx, false); - - MockObjectMap mock_object_map; - expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true); - expect_create_object_map(mock_image_ctx, &mock_object_map); - expect_open_object_map(mock_image_ctx, mock_object_map, 0); - - MockJournal mock_journal; - MockJournalPolicy mock_journal_policy; - expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, - mock_image_ctx.snap_lock, true); - expect_get_journal_policy(mock_image_ctx, mock_journal_policy); - expect_journal_disabled(mock_journal_policy, false); - expect_create_journal(mock_image_ctx, &mock_journal); - expect_handle_prepare_lock_complete(mock_image_ctx); - expect_open_journal(mock_image_ctx, mock_journal, 0); - expect_get_journal_policy(mock_image_ctx, mock_journal_policy); - expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0); - - C_SaferCond acquire_ctx; - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - &acquire_ctx, &ctx); - req->send(); - ASSERT_EQ(0, acquire_ctx.wait()); - ASSERT_EQ(0, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, SuccessRefresh) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - MockRefreshRequest mock_refresh_request; - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, 0); - expect_is_refresh_required(mock_image_ctx, true); - expect_refresh(mock_image_ctx, mock_refresh_request, 0); - - MockObjectMap mock_object_map; - expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false); - expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, - mock_image_ctx.snap_lock, false); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond acquire_ctx; - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - &acquire_ctx, &ctx); - req->send(); - ASSERT_EQ(0, acquire_ctx.wait()); - ASSERT_EQ(0, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, SuccessJournalDisabled) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, 0); - expect_is_refresh_required(mock_image_ctx, false); - - MockObjectMap mock_object_map; - expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true); - expect_create_object_map(mock_image_ctx, &mock_object_map); - expect_open_object_map(mock_image_ctx, mock_object_map, 0); - - expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, - mock_image_ctx.snap_lock, false); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond acquire_ctx; - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - &acquire_ctx, &ctx); - req->send(); - ASSERT_EQ(0, acquire_ctx.wait()); - ASSERT_EQ(0, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, SuccessObjectMapDisabled) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, 0); - expect_is_refresh_required(mock_image_ctx, false); - - expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false); - - MockJournal mock_journal; - MockJournalPolicy mock_journal_policy; - expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, - mock_image_ctx.snap_lock, true); - expect_get_journal_policy(mock_image_ctx, mock_journal_policy); - expect_journal_disabled(mock_journal_policy, false); - expect_create_journal(mock_image_ctx, &mock_journal); - expect_handle_prepare_lock_complete(mock_image_ctx); - expect_open_journal(mock_image_ctx, mock_journal, 0); - expect_get_journal_policy(mock_image_ctx, mock_journal_policy); - expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0); - - C_SaferCond acquire_ctx; - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - &acquire_ctx, &ctx); - req->send(); - ASSERT_EQ(0, acquire_ctx.wait()); - ASSERT_EQ(0, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, RefreshError) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - MockRefreshRequest mock_refresh_request; - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, 0); - expect_is_refresh_required(mock_image_ctx, true); - expect_refresh(mock_image_ctx, mock_refresh_request, -EINVAL); - expect_unlock(mock_image_ctx, 0); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond *acquire_ctx = new C_SaferCond(); - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - acquire_ctx, &ctx); - req->send(); - ASSERT_EQ(-EINVAL, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, RefreshLockDisabled) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - MockRefreshRequest mock_refresh_request; - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, 0); - expect_is_refresh_required(mock_image_ctx, true); - expect_refresh(mock_image_ctx, mock_refresh_request, -ERESTART); - - MockObjectMap mock_object_map; - expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false); - expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, - mock_image_ctx.snap_lock, false); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond acquire_ctx; - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - &acquire_ctx, &ctx); - req->send(); - ASSERT_EQ(0, acquire_ctx.wait()); - ASSERT_EQ(0, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, JournalError) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, 0); - expect_is_refresh_required(mock_image_ctx, false); - - MockObjectMap *mock_object_map = new MockObjectMap(); - expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true); - expect_create_object_map(mock_image_ctx, mock_object_map); - expect_open_object_map(mock_image_ctx, *mock_object_map, 0); - - MockJournal *mock_journal = new MockJournal(); - MockJournalPolicy mock_journal_policy; - expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, - mock_image_ctx.snap_lock, true); - expect_get_journal_policy(mock_image_ctx, mock_journal_policy); - expect_journal_disabled(mock_journal_policy, false); - expect_create_journal(mock_image_ctx, mock_journal); - expect_handle_prepare_lock_complete(mock_image_ctx); - expect_open_journal(mock_image_ctx, *mock_journal, -EINVAL); - expect_close_journal(mock_image_ctx, *mock_journal); - expect_close_object_map(mock_image_ctx, *mock_object_map); - expect_unlock(mock_image_ctx, 0); - - C_SaferCond acquire_ctx; - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - &acquire_ctx, &ctx); - req->send(); - ASSERT_EQ(-EINVAL, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, AllocateJournalTagError) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, 0); - expect_is_refresh_required(mock_image_ctx, false); - - MockObjectMap *mock_object_map = new MockObjectMap(); - expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true); - expect_create_object_map(mock_image_ctx, mock_object_map); - expect_open_object_map(mock_image_ctx, *mock_object_map, 0); - - MockJournal *mock_journal = new MockJournal(); - MockJournalPolicy mock_journal_policy; - expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, - mock_image_ctx.snap_lock, true); - expect_get_journal_policy(mock_image_ctx, mock_journal_policy); - expect_journal_disabled(mock_journal_policy, false); - expect_create_journal(mock_image_ctx, mock_journal); - expect_handle_prepare_lock_complete(mock_image_ctx); - expect_open_journal(mock_image_ctx, *mock_journal, 0); - expect_get_journal_policy(mock_image_ctx, mock_journal_policy); - expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, -EPERM); - expect_close_journal(mock_image_ctx, *mock_journal); - expect_close_object_map(mock_image_ctx, *mock_object_map); - expect_unlock(mock_image_ctx, 0); - - C_SaferCond acquire_ctx; - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - &acquire_ctx, &ctx); - req->send(); - ASSERT_EQ(-EPERM, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, LockBusy) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); - expect_list_watchers(mock_image_ctx, 0, "dead client", 123); - expect_blacklist_add(mock_image_ctx, 0); - expect_break_lock(mock_image_ctx, 0); - expect_lock(mock_image_ctx, -ENOENT); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-ENOENT, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoError) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, -EINVAL, entity_name_t::CLIENT(1), "", - "", "", LOCK_EXCLUSIVE); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EINVAL, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoEmpty) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, -ENOENT, entity_name_t::CLIENT(1), "", - "", "", LOCK_EXCLUSIVE); - expect_lock(mock_image_ctx, -EINVAL); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EINVAL, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoExternalTag) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", "external tag", LOCK_EXCLUSIVE); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EBUSY, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoShared) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_SHARED); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EBUSY, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoExternalCookie) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "external cookie", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EBUSY, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, GetWatchersError) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); - expect_list_watchers(mock_image_ctx, -EINVAL, "dead client", 123); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EINVAL, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, GetWatchersAlive) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); - expect_list_watchers(mock_image_ctx, 0, "1.2.3.4", 123); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EAGAIN, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, BlacklistDisabled) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - mock_image_ctx.blacklist_on_break_lock = false; - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); - expect_list_watchers(mock_image_ctx, 0, "dead client", 123); - expect_break_lock(mock_image_ctx, 0); - expect_lock(mock_image_ctx, -ENOENT); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-ENOENT, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, BlacklistError) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); - expect_list_watchers(mock_image_ctx, 0, "dead client", 123); - expect_blacklist_add(mock_image_ctx, -EINVAL); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EINVAL, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, BreakLockMissing) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); - expect_list_watchers(mock_image_ctx, 0, "dead client", 123); - expect_blacklist_add(mock_image_ctx, 0); - expect_break_lock(mock_image_ctx, -ENOENT); - expect_lock(mock_image_ctx, -EINVAL); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EINVAL, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, BreakLockError) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); - expect_list_watchers(mock_image_ctx, 0, "dead client", 123); - expect_blacklist_add(mock_image_ctx, 0); - expect_break_lock(mock_image_ctx, -EINVAL); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EINVAL, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, OpenObjectMapError) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, 0); - expect_is_refresh_required(mock_image_ctx, false); - - MockObjectMap *mock_object_map = new MockObjectMap(); - expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true); - expect_create_object_map(mock_image_ctx, mock_object_map); - expect_open_object_map(mock_image_ctx, *mock_object_map, -EFBIG); - - MockJournal mock_journal; - MockJournalPolicy mock_journal_policy; - expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, - mock_image_ctx.snap_lock, true); - expect_get_journal_policy(mock_image_ctx, mock_journal_policy); - expect_journal_disabled(mock_journal_policy, false); - expect_create_journal(mock_image_ctx, &mock_journal); - expect_handle_prepare_lock_complete(mock_image_ctx); - expect_open_journal(mock_image_ctx, mock_journal, 0); - expect_get_journal_policy(mock_image_ctx, mock_journal_policy); - expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0); - - C_SaferCond acquire_ctx; - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - &acquire_ctx, &ctx); - req->send(); - ASSERT_EQ(0, acquire_ctx.wait()); - ASSERT_EQ(0, ctx.wait()); - ASSERT_EQ(nullptr, mock_image_ctx.object_map); -} - -} // namespace exclusive_lock -} // namespace librbd diff --git a/src/test/librbd/exclusive_lock/test_mock_PostAcquireRequest.cc b/src/test/librbd/exclusive_lock/test_mock_PostAcquireRequest.cc new file mode 100644 index 000000000000..26b20e0e1287 --- /dev/null +++ b/src/test/librbd/exclusive_lock/test_mock_PostAcquireRequest.cc @@ -0,0 +1,486 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/mock/MockImageState.h" +#include "test/librbd/mock/MockJournal.h" +#include "test/librbd/mock/MockJournalPolicy.h" +#include "test/librbd/mock/MockObjectMap.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librados_test_stub/MockTestMemRadosClient.h" +#include "librbd/exclusive_lock/PostAcquireRequest.h" +#include "librbd/image/RefreshRequest.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include + +namespace librbd { +namespace { + +struct MockTestImageCtx : public librbd::MockImageCtx { + MockTestImageCtx(librbd::ImageCtx &image_ctx) + : librbd::MockImageCtx(image_ctx) { + } +}; + +inline ImageCtx &get_image_ctx(MockTestImageCtx &image_ctx) { + return *(image_ctx.image_ctx); +} + +} // anonymous namespace + +namespace image { + +template<> +struct RefreshRequest { + static RefreshRequest *s_instance; + Context *on_finish; + + static RefreshRequest *create(librbd::MockTestImageCtx &image_ctx, + bool acquire_lock_refresh, + Context *on_finish) { + EXPECT_TRUE(acquire_lock_refresh); + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + RefreshRequest() { + s_instance = this; + } + MOCK_METHOD0(send, void()); +}; + +RefreshRequest *RefreshRequest::s_instance = nullptr; + +} // namespace image +} // namespace librbd + +// template definitions +#include "librbd/Journal.cc" + +#include "librbd/exclusive_lock/PostAcquireRequest.cc" +template class librbd::exclusive_lock::PostAcquireRequest; + +ACTION_P3(FinishRequest2, request, r, mock) { + mock->image_ctx->op_work_queue->queue(request->on_finish, r); +} + + +namespace librbd { +namespace exclusive_lock { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::StrEq; +using ::testing::WithArg; + +static const std::string TEST_COOKIE("auto 123"); + +class TestMockExclusiveLockPostAcquireRequest : public TestMockFixture { +public: + typedef PostAcquireRequest MockPostAcquireRequest; + typedef librbd::image::RefreshRequest MockRefreshRequest; + + void expect_test_features(MockTestImageCtx &mock_image_ctx, uint64_t features, + bool enabled) { + EXPECT_CALL(mock_image_ctx, test_features(features)) + .WillOnce(Return(enabled)); + } + + void expect_test_features(MockTestImageCtx &mock_image_ctx, uint64_t features, + RWLock &lock, bool enabled) { + EXPECT_CALL(mock_image_ctx, test_features(features, _)) + .WillOnce(Return(enabled)); + } + + void expect_is_refresh_required(MockTestImageCtx &mock_image_ctx, bool required) { + EXPECT_CALL(*mock_image_ctx.state, is_refresh_required()) + .WillOnce(Return(required)); + } + + void expect_refresh(MockTestImageCtx &mock_image_ctx, + MockRefreshRequest &mock_refresh_request, int r) { + EXPECT_CALL(mock_refresh_request, send()) + .WillOnce(FinishRequest2(&mock_refresh_request, r, + &mock_image_ctx)); + } + + void expect_create_object_map(MockTestImageCtx &mock_image_ctx, + MockObjectMap *mock_object_map) { + EXPECT_CALL(mock_image_ctx, create_object_map(_)) + .WillOnce(Return(mock_object_map)); + } + + void expect_open_object_map(MockTestImageCtx &mock_image_ctx, + MockObjectMap &mock_object_map, int r) { + EXPECT_CALL(mock_object_map, open(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_close_object_map(MockTestImageCtx &mock_image_ctx, + MockObjectMap &mock_object_map) { + EXPECT_CALL(mock_object_map, close(_)) + .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_create_journal(MockTestImageCtx &mock_image_ctx, + MockJournal *mock_journal) { + EXPECT_CALL(mock_image_ctx, create_journal()) + .WillOnce(Return(mock_journal)); + } + + void expect_open_journal(MockTestImageCtx &mock_image_ctx, + MockJournal &mock_journal, int r) { + EXPECT_CALL(mock_journal, open(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_close_journal(MockTestImageCtx &mock_image_ctx, + MockJournal &mock_journal) { + EXPECT_CALL(mock_journal, close(_)) + .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_get_journal_policy(MockTestImageCtx &mock_image_ctx, + MockJournalPolicy &mock_journal_policy) { + EXPECT_CALL(mock_image_ctx, get_journal_policy()) + .WillOnce(Return(&mock_journal_policy)); + } + + void expect_journal_disabled(MockJournalPolicy &mock_journal_policy, + bool disabled) { + EXPECT_CALL(mock_journal_policy, journal_disabled()) + .WillOnce(Return(disabled)); + } + + void expect_allocate_journal_tag(MockTestImageCtx &mock_image_ctx, + MockJournalPolicy &mock_journal_policy, + int r) { + EXPECT_CALL(mock_journal_policy, allocate_tag_on_lock(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_handle_prepare_lock_complete(MockTestImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete()); + } + +}; + +TEST_F(TestMockExclusiveLockPostAcquireRequest, Success) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_is_refresh_required(mock_image_ctx, false); + + MockObjectMap mock_object_map; + expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true); + expect_create_object_map(mock_image_ctx, &mock_object_map); + expect_open_object_map(mock_image_ctx, mock_object_map, 0); + + MockJournal mock_journal; + MockJournalPolicy mock_journal_policy; + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, + mock_image_ctx.snap_lock, true); + expect_get_journal_policy(mock_image_ctx, mock_journal_policy); + expect_journal_disabled(mock_journal_policy, false); + expect_create_journal(mock_image_ctx, &mock_journal); + expect_handle_prepare_lock_complete(mock_image_ctx); + expect_open_journal(mock_image_ctx, mock_journal, 0); + expect_get_journal_policy(mock_image_ctx, mock_journal_policy); + expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0); + + C_SaferCond acquire_ctx; + C_SaferCond ctx; + MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx, + &acquire_ctx, + &ctx); + req->send(); + ASSERT_EQ(0, acquire_ctx.wait()); + ASSERT_EQ(0, ctx.wait()); + } + +TEST_F(TestMockExclusiveLockPostAcquireRequest, SuccessRefresh) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockRefreshRequest mock_refresh_request; + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_is_refresh_required(mock_image_ctx, true); + expect_refresh(mock_image_ctx, mock_refresh_request, 0); + + MockObjectMap mock_object_map; + expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false); + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, + mock_image_ctx.snap_lock, false); + expect_handle_prepare_lock_complete(mock_image_ctx); + + C_SaferCond acquire_ctx; + C_SaferCond ctx; + MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx, + &acquire_ctx, + &ctx); + req->send(); + ASSERT_EQ(0, acquire_ctx.wait()); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPostAcquireRequest, SuccessJournalDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_is_refresh_required(mock_image_ctx, false); + + MockObjectMap mock_object_map; + expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true); + expect_create_object_map(mock_image_ctx, &mock_object_map); + expect_open_object_map(mock_image_ctx, mock_object_map, 0); + + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, + mock_image_ctx.snap_lock, false); + expect_handle_prepare_lock_complete(mock_image_ctx); + + C_SaferCond acquire_ctx; + C_SaferCond ctx; + MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx, + &acquire_ctx, + &ctx); + req->send(); + ASSERT_EQ(0, acquire_ctx.wait()); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPostAcquireRequest, SuccessObjectMapDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_is_refresh_required(mock_image_ctx, false); + + expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false); + + MockJournal mock_journal; + MockJournalPolicy mock_journal_policy; + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, + mock_image_ctx.snap_lock, true); + expect_get_journal_policy(mock_image_ctx, mock_journal_policy); + expect_journal_disabled(mock_journal_policy, false); + expect_create_journal(mock_image_ctx, &mock_journal); + expect_handle_prepare_lock_complete(mock_image_ctx); + expect_open_journal(mock_image_ctx, mock_journal, 0); + expect_get_journal_policy(mock_image_ctx, mock_journal_policy); + expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0); + + C_SaferCond acquire_ctx; + C_SaferCond ctx; + MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx, + &acquire_ctx, + &ctx); + req->send(); + ASSERT_EQ(0, acquire_ctx.wait()); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPostAcquireRequest, RefreshError) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockRefreshRequest mock_refresh_request; + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_is_refresh_required(mock_image_ctx, true); + expect_refresh(mock_image_ctx, mock_refresh_request, -EINVAL); + expect_handle_prepare_lock_complete(mock_image_ctx); + + C_SaferCond *acquire_ctx = new C_SaferCond(); + C_SaferCond ctx; + MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx, + acquire_ctx, + &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPostAcquireRequest, RefreshLockDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockRefreshRequest mock_refresh_request; + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_is_refresh_required(mock_image_ctx, true); + expect_refresh(mock_image_ctx, mock_refresh_request, -ERESTART); + + MockObjectMap mock_object_map; + expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false); + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, + mock_image_ctx.snap_lock, false); + expect_handle_prepare_lock_complete(mock_image_ctx); + + C_SaferCond acquire_ctx; + C_SaferCond ctx; + MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx, + &acquire_ctx, + &ctx); + req->send(); + ASSERT_EQ(0, acquire_ctx.wait()); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPostAcquireRequest, JournalError) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_is_refresh_required(mock_image_ctx, false); + + MockObjectMap *mock_object_map = new MockObjectMap(); + expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true); + expect_create_object_map(mock_image_ctx, mock_object_map); + expect_open_object_map(mock_image_ctx, *mock_object_map, 0); + + MockJournal *mock_journal = new MockJournal(); + MockJournalPolicy mock_journal_policy; + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, + mock_image_ctx.snap_lock, true); + expect_get_journal_policy(mock_image_ctx, mock_journal_policy); + expect_journal_disabled(mock_journal_policy, false); + expect_create_journal(mock_image_ctx, mock_journal); + expect_handle_prepare_lock_complete(mock_image_ctx); + expect_open_journal(mock_image_ctx, *mock_journal, -EINVAL); + expect_close_journal(mock_image_ctx, *mock_journal); + expect_close_object_map(mock_image_ctx, *mock_object_map); + + C_SaferCond acquire_ctx; + C_SaferCond ctx; + MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx, + &acquire_ctx, + &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPostAcquireRequest, AllocateJournalTagError) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_is_refresh_required(mock_image_ctx, false); + + MockObjectMap *mock_object_map = new MockObjectMap(); + expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true); + expect_create_object_map(mock_image_ctx, mock_object_map); + expect_open_object_map(mock_image_ctx, *mock_object_map, 0); + + MockJournal *mock_journal = new MockJournal(); + MockJournalPolicy mock_journal_policy; + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, + mock_image_ctx.snap_lock, true); + expect_get_journal_policy(mock_image_ctx, mock_journal_policy); + expect_journal_disabled(mock_journal_policy, false); + expect_create_journal(mock_image_ctx, mock_journal); + expect_handle_prepare_lock_complete(mock_image_ctx); + expect_open_journal(mock_image_ctx, *mock_journal, 0); + expect_get_journal_policy(mock_image_ctx, mock_journal_policy); + expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, -EPERM); + expect_close_journal(mock_image_ctx, *mock_journal); + expect_close_object_map(mock_image_ctx, *mock_object_map); + + C_SaferCond acquire_ctx; + C_SaferCond ctx; + MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx, + &acquire_ctx, + &ctx); + req->send(); + ASSERT_EQ(-EPERM, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPostAcquireRequest, OpenObjectMapError) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_is_refresh_required(mock_image_ctx, false); + + MockObjectMap *mock_object_map = new MockObjectMap(); + expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true); + expect_create_object_map(mock_image_ctx, mock_object_map); + expect_open_object_map(mock_image_ctx, *mock_object_map, -EFBIG); + + MockJournal mock_journal; + MockJournalPolicy mock_journal_policy; + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, + mock_image_ctx.snap_lock, true); + expect_get_journal_policy(mock_image_ctx, mock_journal_policy); + expect_journal_disabled(mock_journal_policy, false); + expect_create_journal(mock_image_ctx, &mock_journal); + expect_handle_prepare_lock_complete(mock_image_ctx); + expect_open_journal(mock_image_ctx, mock_journal, 0); + expect_get_journal_policy(mock_image_ctx, mock_journal_policy); + expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0); + + C_SaferCond acquire_ctx; + C_SaferCond ctx; + MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx, + &acquire_ctx, + &ctx); + req->send(); + ASSERT_EQ(0, acquire_ctx.wait()); + ASSERT_EQ(0, ctx.wait()); + ASSERT_EQ(nullptr, mock_image_ctx.object_map); +} + +} // namespace exclusive_lock +} // namespace librbd diff --git a/src/test/librbd/exclusive_lock/test_mock_PreAcquireRequest.cc b/src/test/librbd/exclusive_lock/test_mock_PreAcquireRequest.cc new file mode 100644 index 000000000000..9a01123e9d71 --- /dev/null +++ b/src/test/librbd/exclusive_lock/test_mock_PreAcquireRequest.cc @@ -0,0 +1,92 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librados_test_stub/MockTestMemRadosClient.h" +#include "librbd/exclusive_lock/PreAcquireRequest.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include + +namespace librbd { +namespace { + +struct MockTestImageCtx : public librbd::MockImageCtx { + MockTestImageCtx(librbd::ImageCtx &image_ctx) + : librbd::MockImageCtx(image_ctx) { + } +}; + +inline ImageCtx &get_image_ctx(MockTestImageCtx &image_ctx) { + return *(image_ctx.image_ctx); +} + +} // anonymous namespace +} // namespace librbd + +// template definitions +#include "librbd/exclusive_lock/PreAcquireRequest.cc" +template class librbd::exclusive_lock::PreAcquireRequest; + +namespace librbd { +namespace exclusive_lock { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::StrEq; +using ::testing::WithArg; + +static const std::string TEST_COOKIE("auto 123"); + +class TestMockExclusiveLockPreAcquireRequest : public TestMockFixture { +public: + typedef PreAcquireRequest MockPreAcquireRequest; + + void expect_flush_notifies(MockTestImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_)) + .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_prepare_lock(MockTestImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.state, prepare_lock(_)) + .WillOnce(Invoke([](Context *on_ready) { + on_ready->complete(0); + })); + } + + void expect_handle_prepare_lock_complete(MockTestImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete()); + } + +}; + +TEST_F(TestMockExclusiveLockPreAcquireRequest, Success) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_prepare_lock(mock_image_ctx); + expect_flush_notifies(mock_image_ctx); + + C_SaferCond ctx; + MockPreAcquireRequest *req = MockPreAcquireRequest::create(mock_image_ctx, + &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + } + +} // namespace exclusive_lock +} // namespace librbd diff --git a/src/test/librbd/exclusive_lock/test_mock_PreReleaseRequest.cc b/src/test/librbd/exclusive_lock/test_mock_PreReleaseRequest.cc new file mode 100644 index 000000000000..3c5e8c8e72b5 --- /dev/null +++ b/src/test/librbd/exclusive_lock/test_mock_PreReleaseRequest.cc @@ -0,0 +1,249 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/mock/MockJournal.h" +#include "test/librbd/mock/MockObjectMap.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "librbd/exclusive_lock/PreReleaseRequest.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include + +// template definitions +#include "librbd/exclusive_lock/PreReleaseRequest.cc" +template class librbd::exclusive_lock::PreReleaseRequest; + +namespace librbd { + +using librbd::ManagedLock; + +namespace exclusive_lock { + +namespace { + +struct MockContext : public Context { + MOCK_METHOD1(complete, void(int)); + MOCK_METHOD1(finish, void(int)); +}; + +} // anonymous namespace + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::StrEq; + +static const std::string TEST_COOKIE("auto 123"); + +class TestMockExclusiveLockPreReleaseRequest : public TestMockFixture { +public: + typedef PreReleaseRequest MockPreReleaseRequest; + + void expect_complete_context(MockContext &mock_context, int r) { + EXPECT_CALL(mock_context, complete(r)); + } + + void expect_test_features(MockImageCtx &mock_image_ctx, uint64_t features, + bool enabled) { + EXPECT_CALL(mock_image_ctx, test_features(features)) + .WillOnce(Return(enabled)); + } + + void expect_set_require_lock_on_read(MockImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.aio_work_queue, set_require_lock_on_read()); + } + + void expect_block_writes(MockImageCtx &mock_image_ctx, int r) { + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, + ((mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0)); + if ((mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0) { + expect_set_require_lock_on_read(mock_image_ctx); + } + EXPECT_CALL(*mock_image_ctx.aio_work_queue, block_writes(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_unblock_writes(MockImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.aio_work_queue, unblock_writes()); + } + + void expect_cancel_op_requests(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(mock_image_ctx, cancel_async_requests(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_close_journal(MockImageCtx &mock_image_ctx, + MockJournal &mock_journal, int r) { + EXPECT_CALL(mock_journal, close(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_close_object_map(MockImageCtx &mock_image_ctx, + MockObjectMap &mock_object_map) { + EXPECT_CALL(mock_object_map, close(_)) + .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_flush_notifies(MockImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_)) + .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_prepare_lock(MockImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.state, prepare_lock(_)) + .WillOnce(Invoke([](Context *on_ready) { + on_ready->complete(0); + })); + } + + void expect_handle_prepare_lock_complete(MockImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete()); + } + +}; + +TEST_F(TestMockExclusiveLockPreReleaseRequest, Success) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + + expect_prepare_lock(mock_image_ctx); + expect_cancel_op_requests(mock_image_ctx, 0); + expect_block_writes(mock_image_ctx, 0); + expect_flush_notifies(mock_image_ctx); + + MockJournal *mock_journal = new MockJournal(); + mock_image_ctx.journal = mock_journal; + expect_close_journal(mock_image_ctx, *mock_journal, -EINVAL); + + MockObjectMap *mock_object_map = new MockObjectMap(); + mock_image_ctx.object_map = mock_object_map; + expect_close_object_map(mock_image_ctx, *mock_object_map); + + MockContext mock_releasing_ctx; + expect_complete_context(mock_releasing_ctx, 0); + expect_handle_prepare_lock_complete(mock_image_ctx); + + C_SaferCond ctx; + MockPreReleaseRequest *req = MockPreReleaseRequest::create(mock_image_ctx, + &mock_releasing_ctx, + &ctx, false); + req->send(); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPreReleaseRequest, SuccessJournalDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + expect_block_writes(mock_image_ctx, 0); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_prepare_lock(mock_image_ctx); + expect_cancel_op_requests(mock_image_ctx, 0); + expect_flush_notifies(mock_image_ctx); + + MockObjectMap *mock_object_map = new MockObjectMap(); + mock_image_ctx.object_map = mock_object_map; + expect_close_object_map(mock_image_ctx, *mock_object_map); + + expect_handle_prepare_lock_complete(mock_image_ctx); + + C_SaferCond release_ctx; + C_SaferCond ctx; + MockPreReleaseRequest *req = MockPreReleaseRequest::create(mock_image_ctx, + &release_ctx, &ctx, + false); + req->send(); + ASSERT_EQ(0, release_ctx.wait()); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPreReleaseRequest, SuccessObjectMapDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + expect_block_writes(mock_image_ctx, 0); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_cancel_op_requests(mock_image_ctx, 0); + expect_flush_notifies(mock_image_ctx); + + C_SaferCond release_ctx; + C_SaferCond ctx; + MockPreReleaseRequest *req = MockPreReleaseRequest::create(mock_image_ctx, + &release_ctx, &ctx, + true); + req->send(); + ASSERT_EQ(0, release_ctx.wait()); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPreReleaseRequest, BlockWritesError) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_cancel_op_requests(mock_image_ctx, 0); + expect_block_writes(mock_image_ctx, -EINVAL); + expect_unblock_writes(mock_image_ctx); + + C_SaferCond ctx; + MockPreReleaseRequest *req = MockPreReleaseRequest::create(mock_image_ctx, + nullptr, &ctx, + true); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockPreReleaseRequest, UnlockError) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_cancel_op_requests(mock_image_ctx, 0); + expect_block_writes(mock_image_ctx, 0); + expect_flush_notifies(mock_image_ctx); + + C_SaferCond ctx; + MockPreReleaseRequest *req = MockPreReleaseRequest::create(mock_image_ctx, + nullptr, &ctx, + true); + req->send(); + ASSERT_EQ(0, ctx.wait()); +} + +} // namespace exclusive_lock +} // namespace librbd diff --git a/src/test/librbd/exclusive_lock/test_mock_ReacquireRequest.cc b/src/test/librbd/exclusive_lock/test_mock_ReacquireRequest.cc deleted file mode 100644 index 1ca9f780bf78..000000000000 --- a/src/test/librbd/exclusive_lock/test_mock_ReacquireRequest.cc +++ /dev/null @@ -1,101 +0,0 @@ -// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#include "test/librbd/test_mock_fixture.h" -#include "test/librbd/test_support.h" -#include "test/librbd/mock/MockImageCtx.h" -#include "test/librbd/mock/MockImageState.h" -#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" -#include "test/librados_test_stub/MockTestMemRadosClient.h" -#include "cls/lock/cls_lock_ops.h" -#include "librbd/ExclusiveLock.h" -#include "librbd/exclusive_lock/ReacquireRequest.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include -#include - -// template definitions -#include "librbd/exclusive_lock/ReacquireRequest.cc" -template class librbd::exclusive_lock::ReacquireRequest; - -namespace librbd { -namespace exclusive_lock { - -using ::testing::_; -using ::testing::InSequence; -using ::testing::Return; -using ::testing::StrEq; - -class TestMockExclusiveLockReacquireRequest : public TestMockFixture { -public: - typedef ReacquireRequest MockReacquireRequest; - typedef ExclusiveLock MockExclusiveLock; - - void expect_set_cookie(MockImageCtx &mock_image_ctx, int r) { - EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), - exec(mock_image_ctx.header_oid, _, StrEq("lock"), - StrEq("set_cookie"), _, _, _)) - .WillOnce(Return(r)); - } -}; - -TEST_F(TestMockExclusiveLockReacquireRequest, Success) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockImageCtx mock_image_ctx(*ictx); - - InSequence seq; - expect_set_cookie(mock_image_ctx, 0); - - C_SaferCond ctx; - MockReacquireRequest *req = MockReacquireRequest::create(mock_image_ctx, - "old cookie", - "new cookie", &ctx); - req->send(); - ASSERT_EQ(0, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockReacquireRequest, NotSupported) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockImageCtx mock_image_ctx(*ictx); - - InSequence seq; - expect_set_cookie(mock_image_ctx, -EOPNOTSUPP); - - C_SaferCond ctx; - MockReacquireRequest *req = MockReacquireRequest::create(mock_image_ctx, - "old cookie", - "new cookie", &ctx); - req->send(); - ASSERT_EQ(-EOPNOTSUPP, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockReacquireRequest, Error) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockImageCtx mock_image_ctx(*ictx); - - InSequence seq; - expect_set_cookie(mock_image_ctx, -EBUSY); - - C_SaferCond ctx; - MockReacquireRequest *req = MockReacquireRequest::create(mock_image_ctx, - "old cookie", - "new cookie", &ctx); - req->send(); - ASSERT_EQ(-EBUSY, ctx.wait()); -} - -} // namespace exclusive_lock -} // namespace librbd diff --git a/src/test/librbd/exclusive_lock/test_mock_ReleaseRequest.cc b/src/test/librbd/exclusive_lock/test_mock_ReleaseRequest.cc deleted file mode 100644 index e2fb8420e240..000000000000 --- a/src/test/librbd/exclusive_lock/test_mock_ReleaseRequest.cc +++ /dev/null @@ -1,258 +0,0 @@ -// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#include "test/librbd/test_mock_fixture.h" -#include "test/librbd/test_support.h" -#include "test/librbd/mock/MockImageCtx.h" -#include "test/librbd/mock/MockJournal.h" -#include "test/librbd/mock/MockObjectMap.h" -#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" -#include "librbd/exclusive_lock/ReleaseRequest.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include - -// template definitions -#include "librbd/exclusive_lock/ReleaseRequest.cc" -template class librbd::exclusive_lock::ReleaseRequest; - -namespace librbd { -namespace exclusive_lock { - -namespace { - -struct MockContext : public Context { - MOCK_METHOD1(complete, void(int)); - MOCK_METHOD1(finish, void(int)); -}; - -} // anonymous namespace - -using ::testing::_; -using ::testing::InSequence; -using ::testing::Invoke; -using ::testing::Return; -using ::testing::StrEq; - -static const std::string TEST_COOKIE("auto 123"); - -class TestMockExclusiveLockReleaseRequest : public TestMockFixture { -public: - typedef ReleaseRequest MockReleaseRequest; - - void expect_complete_context(MockContext &mock_context, int r) { - EXPECT_CALL(mock_context, complete(r)); - } - - void expect_test_features(MockImageCtx &mock_image_ctx, uint64_t features, - bool enabled) { - EXPECT_CALL(mock_image_ctx, test_features(features)) - .WillOnce(Return(enabled)); - } - - void expect_set_require_lock_on_read(MockImageCtx &mock_image_ctx) { - EXPECT_CALL(*mock_image_ctx.aio_work_queue, set_require_lock_on_read()); - } - - void expect_block_writes(MockImageCtx &mock_image_ctx, int r) { - expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, - ((mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0)); - if ((mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0) { - expect_set_require_lock_on_read(mock_image_ctx); - } - EXPECT_CALL(*mock_image_ctx.aio_work_queue, block_writes(_)) - .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_unblock_writes(MockImageCtx &mock_image_ctx) { - EXPECT_CALL(*mock_image_ctx.aio_work_queue, unblock_writes()); - } - - void expect_cancel_op_requests(MockImageCtx &mock_image_ctx, int r) { - EXPECT_CALL(mock_image_ctx, cancel_async_requests(_)) - .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_unlock(MockImageCtx &mock_image_ctx, int r) { - EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), - exec(mock_image_ctx.header_oid, _, StrEq("lock"), StrEq("unlock"), _, _, _)) - .WillOnce(Return(r)); - } - - void expect_close_journal(MockImageCtx &mock_image_ctx, - MockJournal &mock_journal, int r) { - EXPECT_CALL(mock_journal, close(_)) - .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_close_object_map(MockImageCtx &mock_image_ctx, - MockObjectMap &mock_object_map) { - EXPECT_CALL(mock_object_map, close(_)) - .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_flush_notifies(MockImageCtx &mock_image_ctx) { - EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_)) - .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); - } - - void expect_prepare_lock(MockImageCtx &mock_image_ctx) { - EXPECT_CALL(*mock_image_ctx.state, prepare_lock(_)) - .WillOnce(Invoke([](Context *on_ready) { - on_ready->complete(0); - })); - } - - void expect_handle_prepare_lock_complete(MockImageCtx &mock_image_ctx) { - EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete()); - } - -}; - -TEST_F(TestMockExclusiveLockReleaseRequest, Success) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_cancel_op_requests(mock_image_ctx, 0); - expect_block_writes(mock_image_ctx, 0); - expect_flush_notifies(mock_image_ctx); - - MockJournal *mock_journal = new MockJournal(); - mock_image_ctx.journal = mock_journal; - expect_close_journal(mock_image_ctx, *mock_journal, -EINVAL); - - MockObjectMap *mock_object_map = new MockObjectMap(); - mock_image_ctx.object_map = mock_object_map; - expect_close_object_map(mock_image_ctx, *mock_object_map); - - MockContext mock_releasing_ctx; - expect_complete_context(mock_releasing_ctx, 0); - expect_unlock(mock_image_ctx, 0); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockReleaseRequest *req = MockReleaseRequest::create(mock_image_ctx, - TEST_COOKIE, - &mock_releasing_ctx, - &ctx, false); - req->send(); - ASSERT_EQ(0, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockReleaseRequest, SuccessJournalDisabled) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockImageCtx mock_image_ctx(*ictx); - expect_block_writes(mock_image_ctx, 0); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_cancel_op_requests(mock_image_ctx, 0); - expect_flush_notifies(mock_image_ctx); - - MockObjectMap *mock_object_map = new MockObjectMap(); - mock_image_ctx.object_map = mock_object_map; - expect_close_object_map(mock_image_ctx, *mock_object_map); - - expect_unlock(mock_image_ctx, 0); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond release_ctx; - C_SaferCond ctx; - MockReleaseRequest *req = MockReleaseRequest::create(mock_image_ctx, - TEST_COOKIE, - &release_ctx, &ctx, - false); - req->send(); - ASSERT_EQ(0, release_ctx.wait()); - ASSERT_EQ(0, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockReleaseRequest, SuccessObjectMapDisabled) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockImageCtx mock_image_ctx(*ictx); - expect_block_writes(mock_image_ctx, 0); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_cancel_op_requests(mock_image_ctx, 0); - expect_flush_notifies(mock_image_ctx); - - expect_unlock(mock_image_ctx, 0); - - C_SaferCond release_ctx; - C_SaferCond ctx; - MockReleaseRequest *req = MockReleaseRequest::create(mock_image_ctx, - TEST_COOKIE, - &release_ctx, &ctx, - true); - req->send(); - ASSERT_EQ(0, release_ctx.wait()); - ASSERT_EQ(0, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockReleaseRequest, BlockWritesError) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_cancel_op_requests(mock_image_ctx, 0); - expect_block_writes(mock_image_ctx, -EINVAL); - expect_unblock_writes(mock_image_ctx); - - C_SaferCond ctx; - MockReleaseRequest *req = MockReleaseRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx, - true); - req->send(); - ASSERT_EQ(-EINVAL, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockReleaseRequest, UnlockError) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_cancel_op_requests(mock_image_ctx, 0); - expect_block_writes(mock_image_ctx, 0); - expect_flush_notifies(mock_image_ctx); - - expect_unlock(mock_image_ctx, -EINVAL); - - C_SaferCond ctx; - MockReleaseRequest *req = MockReleaseRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx, - true); - req->send(); - ASSERT_EQ(0, ctx.wait()); -} - -} // namespace exclusive_lock -} // namespace librbd diff --git a/src/test/librbd/journal/test_Replay.cc b/src/test/librbd/journal/test_Replay.cc index d27b7274eb42..23a99ef07b3a 100644 --- a/src/test/librbd/journal/test_Replay.cc +++ b/src/test/librbd/journal/test_Replay.cc @@ -30,7 +30,7 @@ public: C_SaferCond lock_ctx; { RWLock::WLocker owner_locker(ictx->owner_lock); - ictx->exclusive_lock->request_lock(&lock_ctx); + ictx->exclusive_lock->acquire_lock(&lock_ctx); } int r = lock_ctx.wait(); if (r < 0) { diff --git a/src/test/librbd/mock/MockExclusiveLock.h b/src/test/librbd/mock/MockExclusiveLock.h index 6e3621ad82bd..c6ae51cdffec 100644 --- a/src/test/librbd/mock/MockExclusiveLock.h +++ b/src/test/librbd/mock/MockExclusiveLock.h @@ -23,7 +23,7 @@ struct MockExclusiveLock { MOCK_METHOD1(block_requests, void(int)); MOCK_METHOD0(unblock_requests, void()); - MOCK_METHOD1(request_lock, void(Context *)); + MOCK_METHOD1(acquire_lock, void(Context *)); MOCK_METHOD1(release_lock, void(Context *)); }; diff --git a/src/test/librbd/test_ImageWatcher.cc b/src/test/librbd/test_ImageWatcher.cc index c2f7d8f12cf9..a0bf3ea85bc5 100644 --- a/src/test/librbd/test_ImageWatcher.cc +++ b/src/test/librbd/test_ImageWatcher.cc @@ -295,64 +295,6 @@ struct RebuildObjectMapTask { } }; -TEST_F(TestImageWatcher, NotifyRequestLock) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - ASSERT_EQ(0, register_image_watch(*ictx)); - - m_notify_acks = {{NOTIFY_OP_REQUEST_LOCK, {}}}; - ictx->image_watcher->notify_request_lock(); - - C_SaferCond ctx; - ictx->image_watcher->flush(&ctx); - ctx.wait(); - - ASSERT_TRUE(wait_for_notifies(*ictx)); - - NotifyOps expected_notify_ops; - expected_notify_ops += NOTIFY_OP_REQUEST_LOCK; - ASSERT_EQ(expected_notify_ops, m_notifies); -} - -TEST_F(TestImageWatcher, NotifyReleasedLock) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - ASSERT_EQ(0, register_image_watch(*ictx)); - - m_notify_acks = {{NOTIFY_OP_RELEASED_LOCK, {}}}; - ictx->image_watcher->notify_released_lock(); - - ASSERT_TRUE(wait_for_notifies(*ictx)); - - NotifyOps expected_notify_ops; - expected_notify_ops += NOTIFY_OP_RELEASED_LOCK; - ASSERT_EQ(expected_notify_ops, m_notifies); -} - -TEST_F(TestImageWatcher, NotifyAcquiredLock) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - ASSERT_EQ(0, register_image_watch(*ictx)); - - m_notify_acks = {{NOTIFY_OP_ACQUIRED_LOCK, {}}}; - ictx->image_watcher->notify_acquired_lock(); - - ASSERT_TRUE(wait_for_notifies(*ictx)); - - NotifyOps expected_notify_ops; - expected_notify_ops += NOTIFY_OP_ACQUIRED_LOCK; - ASSERT_EQ(expected_notify_ops, m_notifies); -} - TEST_F(TestImageWatcher, NotifyHeaderUpdate) { REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); diff --git a/src/test/librbd/test_ObjectMap.cc b/src/test/librbd/test_ObjectMap.cc index 65d59ffb954b..ac933e521297 100644 --- a/src/test/librbd/test_ObjectMap.cc +++ b/src/test/librbd/test_ObjectMap.cc @@ -34,7 +34,7 @@ TEST_F(TestObjectMap, RefreshInvalidatesWhenCorrupt) { C_SaferCond lock_ctx; { RWLock::WLocker owner_locker(ictx->owner_lock); - ictx->exclusive_lock->try_lock(&lock_ctx); + ictx->exclusive_lock->try_acquire_lock(&lock_ctx); } ASSERT_EQ(0, lock_ctx.wait()); @@ -57,7 +57,7 @@ TEST_F(TestObjectMap, RefreshInvalidatesWhenTooSmall) { C_SaferCond lock_ctx; { RWLock::WLocker owner_locker(ictx->owner_lock); - ictx->exclusive_lock->try_lock(&lock_ctx); + ictx->exclusive_lock->try_acquire_lock(&lock_ctx); } ASSERT_EQ(0, lock_ctx.wait()); @@ -81,7 +81,7 @@ TEST_F(TestObjectMap, InvalidateFlagOnDisk) { C_SaferCond lock_ctx; { RWLock::WLocker owner_locker(ictx->owner_lock); - ictx->exclusive_lock->try_lock(&lock_ctx); + ictx->exclusive_lock->try_acquire_lock(&lock_ctx); } ASSERT_EQ(0, lock_ctx.wait()); diff --git a/src/test/librbd/test_internal.cc b/src/test/librbd/test_internal.cc index a540ba6b917a..d97cda99b890 100644 --- a/src/test/librbd/test_internal.cc +++ b/src/test/librbd/test_internal.cc @@ -100,7 +100,7 @@ TEST_F(TestInternal, IsExclusiveLockOwner) { C_SaferCond ctx; { RWLock::WLocker l(ictx->owner_lock); - ictx->exclusive_lock->try_lock(&ctx); + ictx->exclusive_lock->try_acquire_lock(&ctx); } ASSERT_EQ(0, ctx.wait()); ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner)); @@ -317,7 +317,7 @@ TEST_F(TestInternal, CancelAsyncResize) { C_SaferCond ctx; { RWLock::WLocker l(ictx->owner_lock); - ictx->exclusive_lock->try_lock(&ctx); + ictx->exclusive_lock->try_acquire_lock(&ctx); } ASSERT_EQ(0, ctx.wait()); @@ -360,7 +360,7 @@ TEST_F(TestInternal, MultipleResize) { C_SaferCond ctx; { RWLock::WLocker l(ictx->owner_lock); - ictx->exclusive_lock->try_lock(&ctx); + ictx->exclusive_lock->try_acquire_lock(&ctx); } RWLock::RLocker owner_locker(ictx->owner_lock); diff --git a/src/test/librbd/test_mock_ExclusiveLock.cc b/src/test/librbd/test_mock_ExclusiveLock.cc index 5d6eb17458ed..5b7ec332d510 100644 --- a/src/test/librbd/test_mock_ExclusiveLock.cc +++ b/src/test/librbd/test_mock_ExclusiveLock.cc @@ -5,9 +5,13 @@ #include "test/librbd/test_support.h" #include "test/librbd/mock/MockImageCtx.h" #include "librbd/ExclusiveLock.h" -#include "librbd/exclusive_lock/AcquireRequest.h" -#include "librbd/exclusive_lock/ReacquireRequest.h" -#include "librbd/exclusive_lock/ReleaseRequest.h" +#include "librbd/ManagedLock.h" +#include "librbd/exclusive_lock/PreAcquireRequest.h" +#include "librbd/exclusive_lock/PostAcquireRequest.h" +#include "librbd/exclusive_lock/PreReleaseRequest.h" +#include "librbd/managed_lock/AcquireRequest.h" +#include "librbd/managed_lock/ReacquireRequest.h" +#include "librbd/managed_lock/ReleaseRequest.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include @@ -17,21 +21,33 @@ namespace librbd { namespace { struct MockExclusiveLockImageCtx : public MockImageCtx { + ContextWQ *op_work_queue; + MockExclusiveLockImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { + op_work_queue = image_ctx.op_work_queue; } }; } // anonymous namespace +namespace watcher { +template <> +struct Traits { + typedef librbd::MockImageWatcher Watcher; +}; +} + namespace exclusive_lock { +using librbd::ImageWatcher; + template struct BaseRequest { static std::list s_requests; Context *on_lock_unlock = nullptr; Context *on_finish = nullptr; - static T* create(MockExclusiveLockImageCtx &image_ctx, const std::string &cookie, + static T* create(MockExclusiveLockImageCtx &image_ctx, Context *on_lock_unlock, Context *on_finish, bool shutting_down = false) { assert(!s_requests.empty()); @@ -50,6 +66,52 @@ struct BaseRequest { template std::list BaseRequest::s_requests; +template <> +struct PreAcquireRequest : public BaseRequest > { + static PreAcquireRequest *create( + MockExclusiveLockImageCtx &image_ctx, Context *on_finish) { + return BaseRequest::create(image_ctx, nullptr, on_finish); + } + MOCK_METHOD0(send, void()); +}; + +template <> +struct PostAcquireRequest : public BaseRequest > { + MOCK_METHOD0(send, void()); +}; + +template <> +struct PreReleaseRequest : public BaseRequest > { + MOCK_METHOD0(send, void()); +}; + +} // namespace exclusive_lock + +namespace managed_lock { + +template +struct BaseRequest { + static std::list s_requests; + Context *on_finish = nullptr; + + static T* create(librados::IoCtx& ioctx, MockImageWatcher *watcher, + ContextWQ *work_queue, const std::string& oid, + const std::string& cookie, Context *on_finish) { + assert(!s_requests.empty()); + T* req = s_requests.front(); + req->on_finish = on_finish; + s_requests.pop_front(); + return req; + } + + BaseRequest() { + s_requests.push_back(reinterpret_cast(this)); + } +}; + +template +std::list BaseRequest::s_requests; + template <> struct AcquireRequest : public BaseRequest > { MOCK_METHOD0(send, void()); @@ -57,11 +119,10 @@ struct AcquireRequest : public BaseRequest struct ReacquireRequest : public BaseRequest > { - static ReacquireRequest* create(MockExclusiveLockImageCtx &image_ctx, - const std::string &cookie, - const std::string &new_cookie, - Context *on_finish) { - return BaseRequest::create(image_ctx, cookie, nullptr, on_finish); + static ReacquireRequest* create(librados::IoCtx &ioctx, const std::string& oid, + const string& old_cookie, const std::string& new_cookie, + Context *on_finish) { + return BaseRequest::create(ioctx, nullptr, nullptr, oid, new_cookie, on_finish); } MOCK_METHOD0(send, void()); @@ -72,19 +133,27 @@ struct ReleaseRequest : public BaseRequest; +#include "librbd/ManagedLock.cc" +template class librbd::ManagedLock; + ACTION_P(FinishLockUnlock, request) { if (request->on_lock_unlock != nullptr) { request->on_lock_unlock->complete(0); } } +ACTION_P2(CompleteRequest, request, ret) { + request->on_finish->complete(ret); +} + namespace librbd { using ::testing::_; @@ -96,14 +165,17 @@ using ::testing::Return; class TestMockExclusiveLock : public TestMockFixture { public: typedef ExclusiveLock MockExclusiveLock; - typedef exclusive_lock::AcquireRequest MockAcquireRequest; - typedef exclusive_lock::ReacquireRequest MockReacquireRequest; - typedef exclusive_lock::ReleaseRequest MockReleaseRequest; + typedef exclusive_lock::PreAcquireRequest MockPreAcquireRequest; + typedef exclusive_lock::PostAcquireRequest MockPostAcquireRequest; + typedef exclusive_lock::PreReleaseRequest MockPreReleaseRequest; + typedef managed_lock::AcquireRequest MockManagedAcquireRequest; + typedef managed_lock::ReacquireRequest MockManagedReacquireRequest; + typedef managed_lock::ReleaseRequest MockManagedReleaseRequest; void expect_get_watch_handle(MockExclusiveLockImageCtx &mock_image_ctx, uint64_t watch_handle = 1234567890) { EXPECT_CALL(*mock_image_ctx.image_watcher, get_watch_handle()) - .WillRepeatedly(Return(watch_handle)); + .WillOnce(Return(watch_handle)); } void expect_set_require_lock_on_read(MockExclusiveLockImageCtx &mock_image_ctx) { @@ -128,24 +200,41 @@ public: } void expect_acquire_lock(MockExclusiveLockImageCtx &mock_image_ctx, - MockAcquireRequest &acquire_request, int r) { + MockPreAcquireRequest &pre_acquire_request, + MockManagedAcquireRequest &managed_acquire_request, + MockPostAcquireRequest *post_acquire_request, + int pre_r, int managed_r, int post_r) { + expect_get_watch_handle(mock_image_ctx); - EXPECT_CALL(acquire_request, send()) - .WillOnce(DoAll(FinishLockUnlock(&acquire_request), - FinishRequest(&acquire_request, r, &mock_image_ctx))); - if (r == 0) { + + EXPECT_CALL(pre_acquire_request, send()) + .WillOnce(CompleteRequest(&pre_acquire_request, pre_r)); + EXPECT_CALL(managed_acquire_request, send()) + .WillOnce(CompleteRequest(&managed_acquire_request, managed_r)); + if (managed_r == 0) { + assert(post_acquire_request != nullptr); + EXPECT_CALL(*post_acquire_request, send()) + .WillOnce(DoAll(FinishLockUnlock(post_acquire_request), + CompleteRequest(post_acquire_request, post_r))); + } + + if (pre_r == 0 && managed_r == 0 && post_r == 0) { expect_notify_acquired_lock(mock_image_ctx); expect_unblock_writes(mock_image_ctx); } } void expect_release_lock(MockExclusiveLockImageCtx &mock_image_ctx, - MockReleaseRequest &release_request, int r, - bool shutting_down = false) { - EXPECT_CALL(release_request, send()) - .WillOnce(DoAll(FinishLockUnlock(&release_request), - FinishRequest(&release_request, r, &mock_image_ctx))); - if (r == 0) { + MockPreReleaseRequest &pre_release_request, + MockManagedReleaseRequest &managed_release_request, + int pre_r, int managed_r, bool shutting_down = false) { + EXPECT_CALL(pre_release_request, send()) + .WillOnce(DoAll(FinishLockUnlock(&pre_release_request), + CompleteRequest(&pre_release_request, pre_r))); + EXPECT_CALL(managed_release_request, send()) + .WillOnce(CompleteRequest(&managed_release_request, managed_r)); + + if (pre_r == 0 && managed_r == 0) { if (shutting_down) { expect_unblock_writes(mock_image_ctx); } @@ -155,11 +244,12 @@ public: } void expect_reacquire_lock(MockExclusiveLockImageCtx &mock_image_ctx, - MockReacquireRequest &mock_reacquire_request, + MockManagedReacquireRequest &mock_reacquire_request, int r) { - expect_get_watch_handle(mock_image_ctx, 98765); + EXPECT_CALL(*mock_image_ctx.image_watcher, get_watch_handle()) + .WillOnce(Return(98765)); EXPECT_CALL(mock_reacquire_request, send()) - .WillOnce(FinishRequest(&mock_reacquire_request, r, &mock_image_ctx)); + .WillOnce(CompleteRequest(&mock_reacquire_request, r)); } void expect_notify_request_lock(MockExclusiveLockImageCtx &mock_image_ctx, @@ -171,12 +261,12 @@ public: void expect_notify_acquired_lock(MockExclusiveLockImageCtx &mock_image_ctx) { EXPECT_CALL(*mock_image_ctx.image_watcher, notify_acquired_lock()) - .Times(1); + .Times(1); } void expect_notify_released_lock(MockExclusiveLockImageCtx &mock_image_ctx) { EXPECT_CALL(*mock_image_ctx.image_watcher, notify_released_lock()) - .Times(1); + .Times(1); } void expect_is_lock_request_needed(MockExclusiveLockImageCtx &mock_image_ctx, bool ret) { @@ -204,7 +294,7 @@ public: C_SaferCond ctx; { RWLock::WLocker owner_locker(mock_image_ctx.owner_lock); - exclusive_lock.try_lock(&ctx); + exclusive_lock.try_acquire_lock(&ctx); } return ctx.wait(); } @@ -213,7 +303,7 @@ public: C_SaferCond ctx; { RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); - exclusive_lock.request_lock(&ctx); + exclusive_lock.acquire_lock(&ctx); } return ctx.wait(); } @@ -257,23 +347,34 @@ TEST_F(TestMockExclusiveLock, StateTransitions) { expect_block_writes(mock_image_ctx); ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); - MockAcquireRequest try_lock_acquire; - expect_acquire_lock(mock_image_ctx, try_lock_acquire, 0); + MockPreAcquireRequest try_lock_pre_acquire; + MockManagedAcquireRequest try_lock_managed_acquire; + MockPostAcquireRequest try_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire, + try_lock_managed_acquire, &try_lock_post_acquire, 0, 0, 0); ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock)); ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock)); - MockReleaseRequest request_release; - expect_release_lock(mock_image_ctx, request_release, 0); + MockPreReleaseRequest pre_request_release; + MockManagedReleaseRequest managed_request_release; + expect_release_lock(mock_image_ctx, pre_request_release, + managed_request_release, 0, 0); ASSERT_EQ(0, when_release_lock(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); - MockAcquireRequest request_lock_acquire; - expect_acquire_lock(mock_image_ctx, request_lock_acquire, 0); + MockPreAcquireRequest request_lock_pre_acquire; + MockManagedAcquireRequest request_lock_managed_acquire; + MockPostAcquireRequest request_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire, + request_lock_managed_acquire, &request_lock_post_acquire, + 0, 0, 0); ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock)); ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock)); - MockReleaseRequest shutdown_release; - expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + MockPreReleaseRequest shutdown_pre_release; + MockManagedReleaseRequest shutdown_managed_release; + expect_release_lock(mock_image_ctx, shutdown_pre_release, + shutdown_managed_release, 0, 0, true); ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); } @@ -292,13 +393,18 @@ TEST_F(TestMockExclusiveLock, TryLockLockedState) { expect_block_writes(mock_image_ctx); ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); - MockAcquireRequest try_lock_acquire; - expect_acquire_lock(mock_image_ctx, try_lock_acquire, 0); + MockPreAcquireRequest try_lock_pre_acquire; + MockManagedAcquireRequest try_lock_managed_acquire; + MockPostAcquireRequest try_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire, + try_lock_managed_acquire, &try_lock_post_acquire, 0, 0, 0); ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock)); ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock)); - MockReleaseRequest shutdown_release; - expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + MockPreReleaseRequest shutdown_pre_release; + MockManagedReleaseRequest shutdown_managed_release; + expect_release_lock(mock_image_ctx, shutdown_pre_release, + shutdown_managed_release, 0, 0, true); ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); } @@ -316,8 +422,10 @@ TEST_F(TestMockExclusiveLock, TryLockAlreadyLocked) { expect_block_writes(mock_image_ctx); ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); - MockAcquireRequest try_lock_acquire; - expect_acquire_lock(mock_image_ctx, try_lock_acquire, -EAGAIN); + MockPreAcquireRequest try_lock_pre_acquire; + MockManagedAcquireRequest try_lock_managed_acquire; + expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire, + try_lock_managed_acquire, nullptr, 0, -EAGAIN, 0); ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); @@ -340,8 +448,10 @@ TEST_F(TestMockExclusiveLock, TryLockBusy) { expect_block_writes(mock_image_ctx); ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); - MockAcquireRequest try_lock_acquire; - expect_acquire_lock(mock_image_ctx, try_lock_acquire, -EBUSY); + MockPreAcquireRequest try_lock_pre_acquire; + MockManagedAcquireRequest try_lock_managed_acquire; + expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire, + try_lock_managed_acquire, nullptr, 0, -EBUSY, 0); ASSERT_EQ(-EBUSY, when_try_lock(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); @@ -362,11 +472,12 @@ TEST_F(TestMockExclusiveLock, TryLockError) { InSequence seq; expect_block_writes(mock_image_ctx); - - MockAcquireRequest try_lock_acquire; - expect_acquire_lock(mock_image_ctx, try_lock_acquire, -EINVAL); - ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); + + MockPreAcquireRequest try_lock_pre_acquire; + MockManagedAcquireRequest try_lock_managed_acquire; + expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire, + try_lock_managed_acquire, nullptr, 0, -EINVAL, 0); ASSERT_EQ(-EINVAL, when_try_lock(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); @@ -389,14 +500,19 @@ TEST_F(TestMockExclusiveLock, RequestLockLockedState) { expect_block_writes(mock_image_ctx); ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); - MockAcquireRequest try_lock_acquire; - expect_acquire_lock(mock_image_ctx, try_lock_acquire, 0); + MockPreAcquireRequest try_lock_pre_acquire; + MockManagedAcquireRequest try_lock_managed_acquire; + MockPostAcquireRequest try_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire, + try_lock_managed_acquire, &try_lock_post_acquire, 0, 0, 0); ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock)); - - MockReleaseRequest shutdown_release; - expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock)); ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock)); + MockPreReleaseRequest shutdown_pre_release; + MockManagedReleaseRequest shutdown_managed_release; + expect_release_lock(mock_image_ctx, shutdown_pre_release, + shutdown_managed_release, 0, 0, true); ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); } @@ -415,9 +531,10 @@ TEST_F(TestMockExclusiveLock, RequestLockBlacklist) { ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); // will abort after seeing blacklist error (avoid infinite request loop) - MockAcquireRequest request_lock_acquire; - expect_acquire_lock(mock_image_ctx, request_lock_acquire, -EBLACKLISTED); - expect_notify_request_lock(mock_image_ctx, exclusive_lock); + MockPreAcquireRequest request_pre_acquire; + MockManagedAcquireRequest request_managed_acquire; + expect_acquire_lock(mock_image_ctx, request_pre_acquire, + request_managed_acquire, nullptr, 0, -EBLACKLISTED, 0); ASSERT_EQ(-EBLACKLISTED, when_request_lock(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); @@ -441,17 +558,25 @@ TEST_F(TestMockExclusiveLock, RequestLockBusy) { ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); // will repeat until successfully acquires the lock - MockAcquireRequest request_lock_acquire1; - expect_acquire_lock(mock_image_ctx, request_lock_acquire1, -EBUSY); + MockPreAcquireRequest request_lock_pre_acquire1; + MockManagedAcquireRequest request_lock_managed_acquire1; + expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire1, + request_lock_managed_acquire1, nullptr, 0, -EBUSY, 0); expect_notify_request_lock(mock_image_ctx, exclusive_lock); - MockAcquireRequest request_lock_acquire2; - expect_acquire_lock(mock_image_ctx, request_lock_acquire2, 0); + MockPreAcquireRequest request_lock_pre_acquire2; + MockManagedAcquireRequest request_lock_managed_acquire2; + MockPostAcquireRequest request_lock_post_acquire2; + expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire2, + request_lock_managed_acquire2, + &request_lock_post_acquire2, 0, 0, 0); ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock)); ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock)); - MockReleaseRequest shutdown_release; - expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + MockPreReleaseRequest shutdown_pre_release; + MockManagedReleaseRequest shutdown_managed_release; + expect_release_lock(mock_image_ctx, shutdown_pre_release, + shutdown_managed_release, 0, 0, true); ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); } @@ -470,17 +595,57 @@ TEST_F(TestMockExclusiveLock, RequestLockError) { ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); // will repeat until successfully acquires the lock - MockAcquireRequest request_lock_acquire1; - expect_acquire_lock(mock_image_ctx, request_lock_acquire1, -EINVAL); + MockPreAcquireRequest request_lock_pre_acquire1; + MockManagedAcquireRequest request_lock_managed_acquire1; + expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire1, + request_lock_managed_acquire1, nullptr, 0, -EINVAL, 0); expect_notify_request_lock(mock_image_ctx, exclusive_lock); - MockAcquireRequest request_lock_acquire2; - expect_acquire_lock(mock_image_ctx, request_lock_acquire2, 0); + MockPreAcquireRequest request_lock_pre_acquire2; + MockManagedAcquireRequest request_lock_managed_acquire2; + MockPostAcquireRequest request_lock_post_acquire2; + expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire2, + request_lock_managed_acquire2, + &request_lock_post_acquire2, 0, 0, 0); ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock)); ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock)); - MockReleaseRequest shutdown_release; - expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + MockPreReleaseRequest shutdown_pre_release; + MockManagedReleaseRequest shutdown_managed_release; + expect_release_lock(mock_image_ctx, shutdown_pre_release, + shutdown_managed_release, 0, 0, true); + ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); +} + +TEST_F(TestMockExclusiveLock, RequestLockJournalEPERM) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockExclusiveLockImageCtx mock_image_ctx(*ictx); + MockExclusiveLock exclusive_lock(mock_image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_block_writes(mock_image_ctx); + ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); + + // will abort after seeing perm error (avoid infinite request loop) + MockPreAcquireRequest request_lock_pre_acquire; + MockManagedAcquireRequest request_lock_managed_acquire; + MockPostAcquireRequest request_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire, + request_lock_managed_acquire, + &request_lock_post_acquire, 0, 0, -EPERM); + MockManagedReleaseRequest release_on_error; + EXPECT_CALL(release_on_error, send()) + .WillOnce(CompleteRequest(&release_on_error, 0)); + ASSERT_EQ(-EPERM, when_request_lock(mock_image_ctx, exclusive_lock)); + ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); + + expect_unblock_writes(mock_image_ctx); + expect_flush_notifies(mock_image_ctx); ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); } @@ -519,18 +684,24 @@ TEST_F(TestMockExclusiveLock, ReleaseLockError) { expect_block_writes(mock_image_ctx); ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); - MockAcquireRequest try_lock_acquire; - expect_acquire_lock(mock_image_ctx, try_lock_acquire, 0); + MockPreAcquireRequest try_lock_pre_acquire; + MockManagedAcquireRequest try_lock_managed_acquire; + MockPostAcquireRequest try_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire, + try_lock_managed_acquire, &try_lock_post_acquire, 0, 0, 0); ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock)); - MockReleaseRequest release; - expect_release_lock(mock_image_ctx, release, -EINVAL); - + MockPreReleaseRequest request_pre_release; + MockManagedReleaseRequest request_managed_release; + expect_release_lock(mock_image_ctx, request_pre_release, + request_managed_release, 0, -EINVAL, false); ASSERT_EQ(-EINVAL, when_release_lock(mock_image_ctx, exclusive_lock)); ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock)); - MockReleaseRequest shutdown_release; - expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + MockPreReleaseRequest shutdown_pre_release; + MockManagedReleaseRequest shutdown_managed_release; + expect_release_lock(mock_image_ctx, shutdown_pre_release, + shutdown_managed_release, 0, 0, true); ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); } @@ -549,34 +720,49 @@ TEST_F(TestMockExclusiveLock, ConcurrentRequests) { expect_block_writes(mock_image_ctx); ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); - MockAcquireRequest try_lock_acquire; + MockPreAcquireRequest try_lock_pre_acquire; + MockManagedAcquireRequest try_lock_managed_acquire; + MockPostAcquireRequest try_lock_post_acquire; C_SaferCond wait_for_send_ctx1; expect_get_watch_handle(mock_image_ctx); - EXPECT_CALL(try_lock_acquire, send()) + EXPECT_CALL(try_lock_pre_acquire, send()) + .WillOnce(CompleteRequest(&try_lock_pre_acquire, 0)); + EXPECT_CALL(try_lock_managed_acquire, send()) + .WillOnce(CompleteRequest(&try_lock_managed_acquire, 0)); + EXPECT_CALL(try_lock_post_acquire, send()) .WillOnce(Notify(&wait_for_send_ctx1)); - MockAcquireRequest request_acquire; - expect_acquire_lock(mock_image_ctx, request_acquire, 0); + MockManagedReleaseRequest managed_release_on_error; + EXPECT_CALL(managed_release_on_error, send()) + .WillOnce(CompleteRequest(&managed_release_on_error, 0)); + + MockPreAcquireRequest request_pre_acquire; + MockManagedAcquireRequest request_managed_acquire; + MockPostAcquireRequest request_post_acquire; + expect_acquire_lock(mock_image_ctx, request_pre_acquire, + request_managed_acquire, &request_post_acquire, 0, 0, 0); - MockReleaseRequest release; + MockPreReleaseRequest pre_release; + MockManagedReleaseRequest managed_release; C_SaferCond wait_for_send_ctx2; - EXPECT_CALL(release, send()) - .WillOnce(Notify(&wait_for_send_ctx2)); + EXPECT_CALL(pre_release, send()).WillOnce(Notify(&wait_for_send_ctx2)); + EXPECT_CALL(managed_release, send()) + .WillOnce(CompleteRequest(&managed_release, 0)); expect_notify_released_lock(mock_image_ctx); expect_is_lock_request_needed(mock_image_ctx, false); C_SaferCond try_request_ctx1; { RWLock::WLocker owner_locker(mock_image_ctx.owner_lock); - exclusive_lock.try_lock(&try_request_ctx1); + exclusive_lock.try_acquire_lock(&try_request_ctx1); } C_SaferCond request_lock_ctx1; C_SaferCond request_lock_ctx2; { RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); - exclusive_lock.request_lock(&request_lock_ctx1); - exclusive_lock.request_lock(&request_lock_ctx2); + exclusive_lock.acquire_lock(&request_lock_ctx1); + exclusive_lock.acquire_lock(&request_lock_ctx2); } C_SaferCond release_lock_ctx1; @@ -588,13 +774,13 @@ TEST_F(TestMockExclusiveLock, ConcurrentRequests) { C_SaferCond request_lock_ctx3; { RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); - exclusive_lock.request_lock(&request_lock_ctx3); + exclusive_lock.acquire_lock(&request_lock_ctx3); } // fail the try_lock ASSERT_EQ(0, wait_for_send_ctx1.wait()); - try_lock_acquire.on_lock_unlock->complete(0); - try_lock_acquire.on_finish->complete(-EINVAL); + try_lock_post_acquire.on_lock_unlock->complete(0); + try_lock_post_acquire.on_finish->complete(-EINVAL); ASSERT_EQ(-EINVAL, try_request_ctx1.wait()); // all three pending request locks should complete @@ -604,8 +790,8 @@ TEST_F(TestMockExclusiveLock, ConcurrentRequests) { // proceed with the release ASSERT_EQ(0, wait_for_send_ctx2.wait()); - release.on_lock_unlock->complete(0); - release.on_finish->complete(0); + pre_release.on_lock_unlock->complete(0); + pre_release.on_finish->complete(0); ASSERT_EQ(0, release_lock_ctx1.wait()); expect_unblock_writes(mock_image_ctx); @@ -628,8 +814,11 @@ TEST_F(TestMockExclusiveLock, BlockRequests) { expect_block_writes(mock_image_ctx); ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); - MockAcquireRequest try_lock_acquire; - expect_acquire_lock(mock_image_ctx, try_lock_acquire, 0); + MockPreAcquireRequest try_lock_pre_acquire; + MockManagedAcquireRequest try_lock_managed_acquire; + MockPostAcquireRequest try_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire, + try_lock_managed_acquire, &try_lock_post_acquire, 0, 0, 0); ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock)); ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock)); @@ -645,8 +834,10 @@ TEST_F(TestMockExclusiveLock, BlockRequests) { ASSERT_TRUE(exclusive_lock.accept_requests(&ret_val)); ASSERT_EQ(0, ret_val); - MockReleaseRequest shutdown_release; - expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + MockPreReleaseRequest shutdown_pre_release; + MockManagedReleaseRequest shutdown_managed_release; + expect_release_lock(mock_image_ctx, shutdown_pre_release, + shutdown_managed_release, 0, 0, true); ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); } @@ -675,13 +866,19 @@ TEST_F(TestMockExclusiveLock, RequestLockWatchNotRegistered) { }), Return(0))); - MockAcquireRequest request_lock_acquire; - expect_acquire_lock(mock_image_ctx, request_lock_acquire, 0); + MockPreAcquireRequest request_lock_pre_acquire; + MockManagedAcquireRequest request_lock_managed_acquire; + MockPostAcquireRequest request_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire, + request_lock_managed_acquire, + &request_lock_post_acquire, 0, 0, 0); ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock)); ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock)); - MockReleaseRequest shutdown_release; - expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + MockPreReleaseRequest shutdown_pre_release; + MockManagedReleaseRequest shutdown_managed_release; + expect_release_lock(mock_image_ctx, shutdown_pre_release, + shutdown_managed_release, 0, 0, true); ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); } @@ -700,22 +897,29 @@ TEST_F(TestMockExclusiveLock, ReacquireLock) { expect_block_writes(mock_image_ctx); ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); - MockAcquireRequest request_lock_acquire; - expect_acquire_lock(mock_image_ctx, request_lock_acquire, 0); + MockPreAcquireRequest request_lock_pre_acquire; + MockManagedAcquireRequest request_lock_managed_acquire; + MockPostAcquireRequest request_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire, + request_lock_managed_acquire, + &request_lock_post_acquire, 0, 0, 0); ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock)); ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock)); - MockReacquireRequest mock_reacquire_request; + EXPECT_CALL(*mock_image_ctx.image_watcher, get_watch_handle()) + .WillOnce(Return(1234567890)); + C_SaferCond reacquire_ctx; - expect_reacquire_lock(mock_image_ctx, mock_reacquire_request, 0); { RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); exclusive_lock.reacquire_lock(&reacquire_ctx); } ASSERT_EQ(0, reacquire_ctx.wait()); - MockReleaseRequest shutdown_release; - expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + MockPreReleaseRequest shutdown_pre_release; + MockManagedReleaseRequest shutdown_managed_release; + expect_release_lock(mock_image_ctx, shutdown_pre_release, + shutdown_managed_release, 0, 0, true); ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); } @@ -734,20 +938,30 @@ TEST_F(TestMockExclusiveLock, ReacquireLockError) { expect_block_writes(mock_image_ctx); ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock)); - MockAcquireRequest request_lock_acquire; - expect_acquire_lock(mock_image_ctx, request_lock_acquire, 0); + MockPreAcquireRequest request_lock_pre_acquire; + MockManagedAcquireRequest request_lock_managed_acquire; + MockPostAcquireRequest request_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire, + request_lock_managed_acquire, + &request_lock_post_acquire, 0, 0, 0); ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock)); ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock)); - MockReacquireRequest mock_reacquire_request; + MockManagedReacquireRequest mock_reacquire_request; C_SaferCond reacquire_ctx; expect_reacquire_lock(mock_image_ctx, mock_reacquire_request, -EOPNOTSUPP); - MockReleaseRequest reacquire_lock_release; - expect_release_lock(mock_image_ctx, reacquire_lock_release, 0, false); + MockPreReleaseRequest reacquire_lock_pre_release; + MockManagedReleaseRequest reacquire_lock_managed_release; + expect_release_lock(mock_image_ctx, reacquire_lock_pre_release, + reacquire_lock_managed_release, 0, 0, false); - MockAcquireRequest reacquire_lock_acquire; - expect_acquire_lock(mock_image_ctx, reacquire_lock_acquire, 0); + MockPreAcquireRequest reacquire_lock_pre_acquire; + MockManagedAcquireRequest reacquire_lock_managed_acquire; + MockPostAcquireRequest reacquire_lock_post_acquire; + expect_acquire_lock(mock_image_ctx, reacquire_lock_pre_acquire, + reacquire_lock_managed_acquire, + &reacquire_lock_post_acquire, 0, 0, 0); { RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); @@ -755,8 +969,10 @@ TEST_F(TestMockExclusiveLock, ReacquireLockError) { } ASSERT_EQ(-EOPNOTSUPP, reacquire_ctx.wait()); - MockReleaseRequest shutdown_release; - expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + MockPreReleaseRequest shutdown_pre_release; + MockManagedReleaseRequest shutdown_managed_release; + expect_release_lock(mock_image_ctx, shutdown_pre_release, + shutdown_managed_release, 0, 0, true); ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); } diff --git a/src/test/rbd_mirror/test_ImageReplayer.cc b/src/test/rbd_mirror/test_ImageReplayer.cc index 58f8deefe0df..ac97ea2e9bf9 100644 --- a/src/test/rbd_mirror/test_ImageReplayer.cc +++ b/src/test/rbd_mirror/test_ImageReplayer.cc @@ -753,7 +753,7 @@ TEST_F(TestImageReplayer, MultipleReplayFailures_SingleEpoch) { for (uint64_t i = 0; i < 10; ++i) { RWLock::RLocker owner_locker(ictx->owner_lock); C_SaferCond request_lock; - ictx->exclusive_lock->request_lock(&request_lock); + ictx->exclusive_lock->acquire_lock(&request_lock); ASSERT_EQ(0, request_lock.wait()); C_SaferCond append_ctx; @@ -801,7 +801,7 @@ TEST_F(TestImageReplayer, MultipleReplayFailures_MultiEpoch) { { RWLock::RLocker owner_locker(ictx->owner_lock); C_SaferCond request_lock; - ictx->exclusive_lock->request_lock(&request_lock); + ictx->exclusive_lock->acquire_lock(&request_lock); ASSERT_EQ(0, request_lock.wait()); C_SaferCond append_ctx; diff --git a/src/test/rbd_mirror/test_ImageSync.cc b/src/test/rbd_mirror/test_ImageSync.cc index 51305755b80c..e5de1065270f 100644 --- a/src/test/rbd_mirror/test_ImageSync.cc +++ b/src/test/rbd_mirror/test_ImageSync.cc @@ -79,7 +79,7 @@ public: C_SaferCond ctx; { RWLock::RLocker owner_locker((*image_ctx)->owner_lock); - (*image_ctx)->exclusive_lock->try_lock(&ctx); + (*image_ctx)->exclusive_lock->try_acquire_lock(&ctx); } ASSERT_EQ(0, ctx.wait()); ASSERT_TRUE((*image_ctx)->exclusive_lock->is_lock_owner()); diff --git a/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.cc b/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.cc index ff1493ddfd42..fbfdbd951b5e 100644 --- a/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.cc +++ b/src/tools/rbd_mirror/image_replayer/OpenLocalImageRequest.cc @@ -158,7 +158,7 @@ void OpenLocalImageRequest::send_lock_image() { OpenLocalImageRequest, &OpenLocalImageRequest::handle_lock_image>( this); - (*m_local_image_ctx)->exclusive_lock->request_lock(ctx); + (*m_local_image_ctx)->exclusive_lock->acquire_lock(ctx); } template