diff --git a/src/cls/lock/cls_lock.cc b/src/cls/lock/cls_lock.cc index dc61a62663b31..c8f3359cee55a 100644 --- a/src/cls/lock/cls_lock.cc +++ b/src/cls/lock/cls_lock.cc @@ -45,6 +45,7 @@ cls_method_handle_t h_break_lock; cls_method_handle_t h_get_info; cls_method_handle_t h_list_locks; cls_method_handle_t h_assert_locked; +cls_method_handle_t h_set_cookie; #define LOCK_PREFIX "lock." @@ -512,6 +513,94 @@ int assert_locked(cls_method_context_t hctx, bufferlist *in, bufferlist *out) return 0; } +/** + * Update the cookie associated with an object lock + * + * Input: + * @param cls_lock_set_cookie_op request input + * + * Output: + * @param none + * + * @return 0 on success, -errno on failure. + */ +int set_cookie(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "set_cookie"); + + cls_lock_set_cookie_op op; + try { + bufferlist::iterator iter = in->begin(); + ::decode(op, iter); + } catch (const buffer::error& err) { + return -EINVAL; + } + + if (op.type != LOCK_EXCLUSIVE && op.type != LOCK_SHARED) { + return -EINVAL; + } + + if (op.name.empty()) { + return -EINVAL; + } + + // see if there's already a locker + lock_info_t linfo; + int r = read_lock(hctx, op.name, &linfo); + if (r < 0) { + CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str()); + return r; + } + + if (linfo.lockers.empty()) { + CLS_LOG(20, "object not locked"); + return -EBUSY; + } + + if (linfo.lock_type != op.type) { + CLS_LOG(20, "lock type mismatch: current=%s, assert=%s", + cls_lock_type_str(linfo.lock_type), cls_lock_type_str(op.type)); + return -EBUSY; + } + + if (linfo.tag != op.tag) { + CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo.tag.c_str(), + op.tag.c_str()); + return -EBUSY; + } + + entity_inst_t inst; + r = cls_get_request_origin(hctx, &inst); + assert(r == 0); + + locker_id_t id; + id.cookie = op.cookie; + id.locker = inst.name; + + map::iterator iter = linfo.lockers.find(id); + if (iter == linfo.lockers.end()) { + CLS_LOG(20, "not locked by client"); + return -EBUSY; + } + + id.cookie = op.new_cookie; + if (linfo.lockers.count(id) != 0) { + CLS_LOG(20, "lock cookie in-use"); + return -EBUSY; + } + + locker_info_t locker_info(iter->second); + linfo.lockers.erase(iter); + + linfo.lockers[id] = locker_info; + r = write_lock(hctx, op.name, linfo); + if (r < 0) { + CLS_ERR("Could not update lock info: %s", cpp_strerror(r).c_str()); + return r; + } + return 0; +} + void __cls_init() { CLS_LOG(20, "Loaded lock class!"); @@ -535,6 +624,9 @@ void __cls_init() cls_register_cxx_method(h_class, "assert_locked", CLS_METHOD_RD | CLS_METHOD_PROMOTE, assert_locked, &h_assert_locked); + cls_register_cxx_method(h_class, "set_cookie", + CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE, + set_cookie, &h_set_cookie); return; } diff --git a/src/cls/lock/cls_lock_client.cc b/src/cls/lock/cls_lock_client.cc index 30466fbd7e5ba..fc2790b92d756 100644 --- a/src/cls/lock/cls_lock_client.cc +++ b/src/cls/lock/cls_lock_client.cc @@ -189,6 +189,22 @@ namespace rados { rados_op->exec("lock", "assert_locked", in); } + void set_cookie(librados::ObjectWriteOperation *rados_op, + const std::string& name, ClsLockType type, + const std::string& cookie, const std::string& tag, + const std::string& new_cookie) + { + cls_lock_set_cookie_op op; + op.name = name; + op.type = type; + op.cookie = cookie; + op.tag = tag; + op.new_cookie = new_cookie; + bufferlist in; + ::encode(op, in); + rados_op->exec("lock", "set_cookie", in); + } + void Lock::assert_locked_exclusive(ObjectOperation *op) { assert_locked(op, name, LOCK_EXCLUSIVE, cookie, tag); diff --git a/src/cls/lock/cls_lock_client.h b/src/cls/lock/cls_lock_client.h index b60d25e098a9f..13fb3f154b0d3 100644 --- a/src/cls/lock/cls_lock_client.h +++ b/src/cls/lock/cls_lock_client.h @@ -59,6 +59,11 @@ namespace rados { const std::string& cookie, const std::string& tag); + extern void set_cookie(librados::ObjectWriteOperation *rados_op, + const std::string& name, ClsLockType type, + const std::string& cookie, const std::string& tag, + const std::string& new_cookie); + class Lock { std::string name; std::string cookie; diff --git a/src/cls/lock/cls_lock_ops.cc b/src/cls/lock/cls_lock_ops.cc index 7de832623d268..4b9a8a3d6ae85 100644 --- a/src/cls/lock/cls_lock_ops.cc +++ b/src/cls/lock/cls_lock_ops.cc @@ -188,3 +188,24 @@ void cls_lock_assert_op::generate_test_instances(list& o) o.push_back(new cls_lock_assert_op); } +void cls_lock_set_cookie_op::dump(Formatter *f) const +{ + f->dump_string("name", name); + f->dump_string("type", cls_lock_type_str(type)); + f->dump_string("cookie", cookie); + f->dump_string("tag", tag); + f->dump_string("new_cookie", new_cookie); +} + +void cls_lock_set_cookie_op::generate_test_instances(list& o) +{ + cls_lock_set_cookie_op *i = new cls_lock_set_cookie_op; + i->name = "name"; + i->type = LOCK_SHARED; + i->cookie = "cookie"; + i->tag = "tag"; + i->new_cookie = "new cookie"; + o.push_back(i); + o.push_back(new cls_lock_set_cookie_op); +} + diff --git a/src/cls/lock/cls_lock_ops.h b/src/cls/lock/cls_lock_ops.h index 9cfe41c4b9d6a..dbdddfe21407d 100644 --- a/src/cls/lock/cls_lock_ops.h +++ b/src/cls/lock/cls_lock_ops.h @@ -203,4 +203,40 @@ struct cls_lock_assert_op }; WRITE_CLASS_ENCODER(cls_lock_assert_op) +struct cls_lock_set_cookie_op +{ + string name; + ClsLockType type; + string cookie; + string tag; + string new_cookie; + + cls_lock_set_cookie_op() : type(LOCK_NONE) {} + + void encode(bufferlist &bl) const { + ENCODE_START(1, 1, bl); + ::encode(name, bl); + uint8_t t = (uint8_t)type; + ::encode(t, bl); + ::encode(cookie, bl); + ::encode(tag, bl); + ::encode(new_cookie, bl); + ENCODE_FINISH(bl); + } + void decode(bufferlist::iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + ::decode(name, bl); + uint8_t t; + ::decode(t, bl); + type = (ClsLockType)t; + ::decode(cookie, bl); + ::decode(tag, bl); + ::decode(new_cookie, bl); + DECODE_FINISH(bl); + } + void dump(Formatter *f) const; + static void generate_test_instances(list& o); +}; +WRITE_CLASS_ENCODER(cls_lock_set_cookie_op) + #endif diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index 974dcac94b715..1eaa652c974da 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -27,6 +27,7 @@ set(librbd_internal_srcs Operations.cc Utils.cc exclusive_lock/AcquireRequest.cc + exclusive_lock/ReacquireRequest.cc exclusive_lock/ReleaseRequest.cc exclusive_lock/StandardPolicy.cc image/CloseRequest.cc @@ -37,6 +38,7 @@ set(librbd_internal_srcs image/SetSnapRequest.cc image_watcher/Notifier.cc image_watcher/NotifyLockOwner.cc + image_watcher/RewatchRequest.cc journal/RemoveRequest.cc journal/CreateRequest.cc journal/Replay.cc diff --git a/src/librbd/ExclusiveLock.cc b/src/librbd/ExclusiveLock.cc index 1d0248ca43fea..f8b06891ef2cc 100644 --- a/src/librbd/ExclusiveLock.cc +++ b/src/librbd/ExclusiveLock.cc @@ -7,9 +7,11 @@ #include "common/errno.h" #include "librbd/AioImageRequestWQ.h" #include "librbd/ImageCtx.h" +#include "librbd/ImageState.h" #include "librbd/ImageWatcher.h" #include "librbd/Utils.h" #include "librbd/exclusive_lock/AcquireRequest.h" +#include "librbd/exclusive_lock/ReacquireRequest.h" #include "librbd/exclusive_lock/ReleaseRequest.h" #include @@ -25,10 +27,10 @@ namespace { const std::string WATCHER_LOCK_COOKIE_PREFIX = "auto"; -template -struct C_SendReleaseRequest : public Context { - ReleaseRequest* request; - explicit C_SendReleaseRequest(ReleaseRequest* request) : request(request) { +template +struct C_SendRequest : public Context { + R* request; + explicit C_SendRequest(R* request) : request(request) { } virtual void finish(int r) override { request->send(); @@ -60,6 +62,7 @@ bool ExclusiveLock::is_lock_owner() const { 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; @@ -195,17 +198,34 @@ void ExclusiveLock::release_lock(Context *on_released) { } template -void ExclusiveLock::handle_watch_registered() { - Mutex::Locker locker(m_lock); - if (m_state != STATE_WAITING_FOR_REGISTER) { - return; +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)) { + // ignore request if shutdown or not in a locked-related state + ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; + execute_action(ACTION_REACQUIRE_LOCK, on_reacquired); + return; + } } - ldout(m_image_ctx.cct, 10) << this << " " << __func__ << dendl; - Action active_action = get_active_action(); - assert(active_action == ACTION_TRY_LOCK || - active_action == ACTION_REQUEST_LOCK); - execute_next_action(); + if (on_reacquired != nullptr) { + on_reacquired->complete(0); + } } template @@ -224,7 +244,7 @@ template void ExclusiveLock::assert_header_locked(librados::ObjectWriteOperation *op) { Mutex::Locker locker(m_lock); rados::cls::lock::assert_locked(op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, - encode_lock_cookie(), WATCHER_LOCK_TAG); + m_cookie, WATCHER_LOCK_TAG); } template @@ -256,6 +276,7 @@ bool ExclusiveLock::is_transition_state() const { 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: @@ -309,6 +330,9 @@ void ExclusiveLock::execute_next_action() { case ACTION_REQUEST_LOCK: send_acquire_lock(); break; + case ACTION_REACQUIRE_LOCK: + send_reacquire_lock(); + break; case ACTION_RELEASE_LOCK: send_release_lock(); break; @@ -384,15 +408,19 @@ void ExclusiveLock::send_acquire_lock() { return; } + m_cookie = encode_lock_cookie(); + using el = ExclusiveLock; AcquireRequest* req = AcquireRequest::create( - m_image_ctx, encode_lock_cookie(), + m_image_ctx, m_cookie, util::create_context_callback(this), util::create_context_callback(this)); - m_lock.Unlock(); - req->send(); - m_lock.Lock(); + // acquire the lock if the image is not busy performing other actions + m_image_ctx.state->prepare_lock(new FunctionContext([this, req](int r) { + m_image_ctx.op_work_queue->queue( + new C_SendRequest >(req), 0); + })); } template @@ -421,6 +449,7 @@ void ExclusiveLock::handle_acquire_lock(int r) { ldout(cct, 5) << "successfully acquired exclusive lock" << dendl; } + m_image_ctx.state->handle_prepare_lock_complete(); { m_lock.Lock(); assert(m_state == STATE_ACQUIRING || @@ -455,6 +484,73 @@ void ExclusiveLock::handle_acquire_lock(int r) { complete_active_action(next_state, 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; + } + + 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; + } + + ldout(cct, 10) << this << " " << __func__ << dendl; + m_state = STATE_REACQUIRING; + + using el = ExclusiveLock; + ReacquireRequest* req = ReacquireRequest::create( + m_image_ctx, m_cookie, m_new_cookie, + util::create_context_callback(this)); + req->send(); +} + +template +void ExclusiveLock::handle_reacquire_lock(int r) { + Mutex::Locker locker(m_lock); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; + + 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; + } + + if (!is_shutdown()) { + // queue a release and re-acquire of the lock + execute_action(ACTION_RELEASE_LOCK, nullptr); + execute_action(ACTION_REQUEST_LOCK, nullptr); + } + } else { + m_cookie = m_new_cookie; + } + + complete_active_action(STATE_LOCKED, r); +} + template void ExclusiveLock::send_release_lock() { assert(m_lock.is_locked()); @@ -468,12 +564,15 @@ void ExclusiveLock::send_release_lock() { using el = ExclusiveLock; ReleaseRequest* req = ReleaseRequest::create( - m_image_ctx, encode_lock_cookie(), + m_image_ctx, m_cookie, util::create_context_callback(this), util::create_context_callback(this)); - // send in alternate thread context to avoid re-entrant locking - m_image_ctx.op_work_queue->queue(new C_SendReleaseRequest(req), 0); + // release the lock if the image is not busy performing other actions + m_image_ctx.state->prepare_lock(new FunctionContext([this, req](int r) { + m_image_ctx.op_work_queue->queue( + new C_SendRequest >(req), 0); + })); } template @@ -490,6 +589,8 @@ void ExclusiveLock::handle_releasing_lock(int r) { template void ExclusiveLock::handle_release_lock(int r) { + m_image_ctx.state->handle_prepare_lock_complete(); + bool lock_request_needed = false; { Mutex::Locker locker(m_lock); @@ -504,6 +605,7 @@ void ExclusiveLock::handle_release_lock(int r) { lock_request_needed = m_image_ctx.aio_work_queue->is_lock_request_needed(); m_lock.Lock(); + m_cookie = ""; m_watch_handle = 0; } complete_active_action(r < 0 ? STATE_LOCKED : STATE_UNLOCKED, r); @@ -540,7 +642,7 @@ void ExclusiveLock::send_shutdown_release() { std::string cookie; { Mutex::Locker locker(m_lock); - cookie = encode_lock_cookie(); + cookie = m_cookie; } using el = ExclusiveLock; diff --git a/src/librbd/ExclusiveLock.h b/src/librbd/ExclusiveLock.h index 7e66037e87305..5543bbb01f579 100644 --- a/src/librbd/ExclusiveLock.h +++ b/src/librbd/ExclusiveLock.h @@ -41,7 +41,8 @@ class ExclusiveLock { void request_lock(Context *on_locked); void release_lock(Context *on_released); - void handle_watch_registered(); + void reacquire_lock(Context *on_reacquired = nullptr); + void handle_peer_notification(); void assert_header_locked(librados::ObjectWriteOperation *op); @@ -51,6 +52,8 @@ class ExclusiveLock { private: /** + * @verbatim + * * * * > WAITING_FOR_REGISTER --------\ * | * (watch not registered) | * | * | @@ -69,11 +72,21 @@ class ExclusiveLock { * | (release_lock) v * PRE_RELEASING <------------------------ LOCKED * + * + * | + * v + * REACQUIRING -------------------------------------> + * . ^ + * . | + * . . . > ---> ---/ + * * * | * | * v * PRE_SHUTTING_DOWN ---> SHUTTING_DOWN ---> SHUTDOWN ---> + * + * @endverbatim */ enum State { STATE_UNINITIALIZED, @@ -84,16 +97,18 @@ class ExclusiveLock { 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, + STATE_SHUTDOWN }; enum Action { ACTION_TRY_LOCK, ACTION_REQUEST_LOCK, + ACTION_REACQUIRE_LOCK, ACTION_RELEASE_LOCK, ACTION_SHUT_DOWN }; @@ -130,6 +145,8 @@ class ExclusiveLock { 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; @@ -156,6 +173,9 @@ class ExclusiveLock { 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); diff --git a/src/librbd/ImageState.cc b/src/librbd/ImageState.cc index 5b450b0671e57..da9f6d967cf33 100644 --- a/src/librbd/ImageState.cc +++ b/src/librbd/ImageState.cc @@ -318,18 +318,6 @@ template void ImageState::refresh(Context *on_finish) { CephContext *cct = m_image_ctx->cct; ldout(cct, 20) << __func__ << dendl; - refresh(false, on_finish); -} - -template -void ImageState::acquire_lock_refresh(Context *on_finish) { - CephContext *cct = m_image_ctx->cct; - ldout(cct, 20) << __func__ << dendl; - refresh(true, on_finish); -} - -template -void ImageState::refresh(bool acquiring_lock, Context *on_finish) { m_lock.Lock(); if (is_closed()) { @@ -340,7 +328,6 @@ void ImageState::refresh(bool acquiring_lock, Context *on_finish) { Action action(ACTION_TYPE_REFRESH); action.refresh_seq = m_refresh_seq; - action.refresh_acquiring_lock = acquiring_lock; execute_action_unlock(action, on_finish); } @@ -377,6 +364,37 @@ void ImageState::snap_set(const std::string &snap_name, Context *on_finish) { execute_action_unlock(action, on_finish); } +template +void ImageState::prepare_lock(Context *on_ready) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << __func__ << dendl; + + m_lock.Lock(); + if (is_closed()) { + m_lock.Unlock(); + on_ready->complete(-ESHUTDOWN); + return; + } + + Action action(ACTION_TYPE_LOCK); + action.on_ready = on_ready; + execute_action_unlock(action, nullptr); +} + +template +void ImageState::handle_prepare_lock_complete() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << __func__ << dendl; + + m_lock.Lock(); + if (m_state != STATE_PREPARING_LOCK) { + m_lock.Unlock(); + return; + } + + complete_action_unlock(STATE_OPEN, 0); +} + template int ImageState::register_update_watcher(UpdateWatchCtx *watcher, uint64_t *handle) { @@ -426,6 +444,7 @@ bool ImageState::is_transition_state() const { case STATE_CLOSING: case STATE_REFRESHING: case STATE_SETTING_SNAP: + case STATE_PREPARING_LOCK: break; } return true; @@ -479,6 +498,9 @@ void ImageState::execute_next_action_unlock() { case ACTION_TYPE_SET_SNAP: send_set_snap_unlock(); return; + case ACTION_TYPE_LOCK: + send_prepare_lock_unlock(); + return; } assert(false); } @@ -598,7 +620,7 @@ void ImageState::send_refresh_unlock() { *m_image_ctx, create_context_callback< ImageState, &ImageState::handle_refresh>(this)); image::RefreshRequest *req = image::RefreshRequest::create( - *m_image_ctx, action_context.refresh_acquiring_lock, ctx); + *m_image_ctx, false, ctx); m_lock.Unlock(); req->send(); @@ -663,6 +685,30 @@ void ImageState::handle_set_snap(int r) { complete_action_unlock(STATE_OPEN, r); } +template +void ImageState::send_prepare_lock_unlock() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + assert(m_lock.is_locked()); + m_state = STATE_PREPARING_LOCK; + + assert(!m_actions_contexts.empty()); + ActionContexts &action_contexts(m_actions_contexts.front()); + assert(action_contexts.first.action_type == ACTION_TYPE_LOCK); + + Context *on_ready = action_contexts.first.on_ready; + m_lock.Unlock(); + + if (on_ready == nullptr) { + complete_action_unlock(STATE_OPEN, 0); + return; + } + + // wake up the lock handler now that its safe to proceed + on_ready->complete(0); +} + } // namespace librbd template class librbd::ImageState; diff --git a/src/librbd/ImageState.h b/src/librbd/ImageState.h index bad42775d393f..412730e711688 100644 --- a/src/librbd/ImageState.h +++ b/src/librbd/ImageState.h @@ -38,10 +38,12 @@ class ImageState { int refresh(); int refresh_if_required(); void refresh(Context *on_finish); - void acquire_lock_refresh(Context *on_finish); void snap_set(const std::string &snap_name, Context *on_finish); + void prepare_lock(Context *on_ready); + void handle_prepare_lock_complete(); + int register_update_watcher(UpdateWatchCtx *watcher, uint64_t *handle); int unregister_update_watcher(uint64_t handle); void flush_update_watchers(Context *on_finish); @@ -55,21 +57,23 @@ class ImageState { STATE_OPENING, STATE_CLOSING, STATE_REFRESHING, - STATE_SETTING_SNAP + STATE_SETTING_SNAP, + STATE_PREPARING_LOCK }; enum ActionType { ACTION_TYPE_OPEN, ACTION_TYPE_CLOSE, ACTION_TYPE_REFRESH, - ACTION_TYPE_SET_SNAP + ACTION_TYPE_SET_SNAP, + ACTION_TYPE_LOCK }; struct Action { ActionType action_type; uint64_t refresh_seq = 0; - bool refresh_acquiring_lock = false; std::string snap_name; + Context *on_ready = nullptr; Action(ActionType action_type) : action_type(action_type) { } @@ -79,10 +83,11 @@ class ImageState { } switch (action_type) { case ACTION_TYPE_REFRESH: - return (refresh_seq == action.refresh_seq && - refresh_acquiring_lock == action.refresh_acquiring_lock); + return (refresh_seq == action.refresh_seq); case ACTION_TYPE_SET_SNAP: return snap_name == action.snap_name; + case ACTION_TYPE_LOCK: + return false; default: return true; } @@ -107,8 +112,6 @@ class ImageState { bool is_transition_state() const; bool is_closed() const; - void refresh(bool acquiring_lock, Context *on_finish); - void append_context(const Action &action, Context *context); void execute_next_action_unlock(); void execute_action_unlock(const Action &action, Context *context); @@ -126,6 +129,8 @@ class ImageState { void send_set_snap_unlock(); void handle_set_snap(int r); + void send_prepare_lock_unlock(); + }; } // namespace librbd diff --git a/src/librbd/ImageWatcher.cc b/src/librbd/ImageWatcher.cc index 55da07dbcb8d0..58031033aa7e3 100644 --- a/src/librbd/ImageWatcher.cc +++ b/src/librbd/ImageWatcher.cc @@ -13,6 +13,7 @@ #include "librbd/exclusive_lock/Policy.h" #include "librbd/image_watcher/Notifier.h" #include "librbd/image_watcher/NotifyLockOwner.h" +#include "librbd/image_watcher/RewatchRequest.h" #include "include/encoding.h" #include "common/errno.h" #include "common/WorkQueue.h" @@ -118,31 +119,43 @@ void ImageWatcher::handle_register_watch(int r) { template void ImageWatcher::unregister_watch(Context *on_finish) { - ldout(m_image_ctx.cct, 10) << this << " unregistering image watcher" << dendl; + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << this << " unregistering image watcher" << dendl; cancel_async_requests(); - C_Gather *g = new C_Gather(m_image_ctx.cct, create_async_context_callback( - m_image_ctx, on_finish)); - m_task_finisher->cancel_all(g->new_sub()); - + C_Gather *gather_ctx = nullptr; { - RWLock::WLocker l(m_watch_lock); + RWLock::WLocker watch_locker(m_watch_lock); + if (m_watch_state == WATCH_STATE_REWATCHING) { + ldout(cct, 10) << this << " delaying unregister until rewatch completed" + << dendl; + + assert(m_unregister_watch_ctx == nullptr); + m_unregister_watch_ctx = new FunctionContext([this, on_finish](int r) { + unregister_watch(on_finish); + }); + return; + } + + gather_ctx = new C_Gather(m_image_ctx.cct, create_async_context_callback( + m_image_ctx, on_finish)); if (m_watch_state == WATCH_STATE_REGISTERED) { m_watch_state = WATCH_STATE_UNREGISTERED; librados::AioCompletion *aio_comp = create_rados_safe_callback( - new C_UnwatchAndFlush(m_image_ctx.md_ctx, g->new_sub())); + new C_UnwatchAndFlush(m_image_ctx.md_ctx, gather_ctx->new_sub())); int r = m_image_ctx.md_ctx.aio_unwatch(m_watch_handle, aio_comp); assert(r == 0); aio_comp->release(); - g->activate(); - return; } else if (m_watch_state == WATCH_STATE_ERROR) { m_watch_state = WATCH_STATE_UNREGISTERED; } } - g->activate(); + + assert(gather_ctx != nullptr); + m_task_finisher->cancel_all(gather_ctx->new_sub()); + gather_ctx->activate(); } template @@ -998,7 +1011,7 @@ void ImageWatcher::handle_error(uint64_t handle, int err) { m_watch_state = WATCH_STATE_ERROR; FunctionContext *ctx = new FunctionContext( - boost::bind(&ImageWatcher::reregister_watch, this)); + boost::bind(&ImageWatcher::rewatch, this)); m_task_finisher->queue(TASK_CODE_REREGISTER_WATCH, ctx); } } @@ -1010,62 +1023,49 @@ void ImageWatcher::acknowledge_notify(uint64_t notify_id, uint64_t handle, } template -void ImageWatcher::reregister_watch() { +void ImageWatcher::rewatch() { ldout(m_image_ctx.cct, 10) << this << " re-registering image watch" << dendl; - bool releasing_lock = false; - C_SaferCond release_lock_ctx; - { - RWLock::WLocker l(m_image_ctx.owner_lock); - if (m_image_ctx.exclusive_lock != nullptr) { - releasing_lock = true; - m_image_ctx.exclusive_lock->release_lock(&release_lock_ctx); - } + RWLock::WLocker l(m_watch_lock); + if (m_watch_state != WATCH_STATE_ERROR) { + return; } + m_watch_state = WATCH_STATE_REWATCHING; - int r; - if (releasing_lock) { - r = release_lock_ctx.wait(); - if (r == -EBLACKLISTED) { - lderr(m_image_ctx.cct) << this << " client blacklisted" << dendl; - return; - } + Context *ctx = create_context_callback< + ImageWatcher, &ImageWatcher::handle_rewatch>(this); + RewatchRequest *req = RewatchRequest::create(m_image_ctx, m_watch_lock, + &m_watch_ctx, + &m_watch_handle, ctx); + req->send(); +} - assert(r == 0); +template +void ImageWatcher::handle_rewatch(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; + + WatchState next_watch_state = WATCH_STATE_REGISTERED; + if (r < 0) { + next_watch_state = WATCH_STATE_ERROR; } + Context *unregister_watch_ctx = nullptr; { - RWLock::WLocker l(m_watch_lock); - if (m_watch_state != WATCH_STATE_ERROR) { - return; - } + RWLock::WLocker watch_locker(m_watch_lock); + assert(m_watch_state == WATCH_STATE_REWATCHING); + m_watch_state = next_watch_state; - r = m_image_ctx.md_ctx.watch2(m_image_ctx.header_oid, - &m_watch_handle, &m_watch_ctx); - if (r < 0) { - lderr(m_image_ctx.cct) << this << " failed to re-register image watch: " - << cpp_strerror(r) << dendl; - if (r != -ESHUTDOWN) { - FunctionContext *ctx = new FunctionContext(boost::bind( - &ImageWatcher::reregister_watch, this)); - m_task_finisher->add_event_after(TASK_CODE_REREGISTER_WATCH, - RETRY_DELAY_SECONDS, ctx); - } - return; - } + std::swap(unregister_watch_ctx, m_unregister_watch_ctx); - m_watch_state = WATCH_STATE_REGISTERED; + // image might have been updated while we didn't have active watch + handle_payload(HeaderUpdatePayload(), nullptr); } - // if the exclusive lock state machine was paused waiting for the - // watch to be re-registered, wake it up - RWLock::RLocker owner_locker(m_image_ctx.owner_lock); - RWLock::RLocker snap_locker(m_image_ctx.snap_lock); - if (m_image_ctx.exclusive_lock != nullptr) { - m_image_ctx.exclusive_lock->handle_watch_registered(); + // wake up pending unregister request + if (unregister_watch_ctx != nullptr) { + unregister_watch_ctx->complete(0); } - - handle_payload(HeaderUpdatePayload(), NULL); } template diff --git a/src/librbd/ImageWatcher.h b/src/librbd/ImageWatcher.h index f90d2b46b6440..7dd3561c1a4be 100644 --- a/src/librbd/ImageWatcher.h +++ b/src/librbd/ImageWatcher.h @@ -64,7 +64,8 @@ class ImageWatcher { enum WatchState { WATCH_STATE_UNREGISTERED, WATCH_STATE_REGISTERED, - WATCH_STATE_ERROR + WATCH_STATE_ERROR, + WATCH_STATE_REWATCHING }; enum TaskCode { @@ -226,6 +227,7 @@ class ImageWatcher { WatchCtx m_watch_ctx; uint64_t m_watch_handle; WatchState m_watch_state; + Context *m_unregister_watch_ctx = nullptr; TaskFinisher *m_task_finisher; @@ -310,7 +312,8 @@ class ImageWatcher { void handle_error(uint64_t cookie, int err); void acknowledge_notify(uint64_t notify_id, uint64_t handle, bufferlist &out); - void reregister_watch(); + void rewatch(); + void handle_rewatch(int r); }; } // namespace librbd diff --git a/src/librbd/Makefile.am b/src/librbd/Makefile.am index 57585b7c24cc5..271d1236bbc46 100644 --- a/src/librbd/Makefile.am +++ b/src/librbd/Makefile.am @@ -32,6 +32,7 @@ librbd_internal_la_SOURCES = \ librbd/Operations.cc \ librbd/Utils.cc \ librbd/exclusive_lock/AcquireRequest.cc \ + librbd/exclusive_lock/ReacquireRequest.cc \ librbd/exclusive_lock/ReleaseRequest.cc \ librbd/exclusive_lock/StandardPolicy.cc \ librbd/image/CloseRequest.cc \ @@ -42,6 +43,7 @@ librbd_internal_la_SOURCES = \ librbd/image/SetSnapRequest.cc \ librbd/image_watcher/Notifier.cc \ librbd/image_watcher/NotifyLockOwner.cc \ + librbd/image_watcher/RewatchRequest.cc \ librbd/journal/RemoveRequest.cc \ librbd/journal/CreateRequest.cc \ librbd/journal/Replay.cc \ @@ -123,6 +125,7 @@ noinst_HEADERS += \ librbd/WatchNotifyTypes.h \ librbd/exclusive_lock/AcquireRequest.h \ librbd/exclusive_lock/Policy.h \ + librbd/exclusive_lock/ReacquireRequest.h \ librbd/exclusive_lock/ReleaseRequest.h \ librbd/exclusive_lock/StandardPolicy.h \ librbd/image/CloseRequest.h \ @@ -133,6 +136,7 @@ noinst_HEADERS += \ librbd/image/SetSnapRequest.h \ librbd/image_watcher/Notifier.h \ librbd/image_watcher/NotifyLockOwner.h \ + librbd/image_watcher/RewatchRequest.h \ librbd/journal/CreateRequest.h \ librbd/journal/DisabledPolicy.h \ librbd/journal/Policy.h \ diff --git a/src/librbd/exclusive_lock/AcquireRequest.cc b/src/librbd/exclusive_lock/AcquireRequest.cc index 94fee20e058e4..df354178b0426 100644 --- a/src/librbd/exclusive_lock/AcquireRequest.cc +++ b/src/librbd/exclusive_lock/AcquireRequest.cc @@ -15,6 +15,7 @@ #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 @@ -107,7 +108,7 @@ void AcquireRequest::send_lock() { librados::ObjectWriteOperation op; rados::cls::lock::lock(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, m_cookie, - ExclusiveLock::WATCHER_LOCK_TAG, "", utime_t(), 0); + ExclusiveLock<>::WATCHER_LOCK_TAG, "", utime_t(), 0); using klass = AcquireRequest; librados::AioCompletion *rados_completion = @@ -144,8 +145,14 @@ Context *AcquireRequest::send_refresh() { ldout(cct, 10) << __func__ << dendl; using klass = AcquireRequest; - Context *ctx = create_context_callback(this); - m_image_ctx.state->acquire_lock_refresh(ctx); + 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; } @@ -403,7 +410,7 @@ Context *AcquireRequest::handle_get_lockers(int *ret_val) { return nullptr; } - if (lock_tag != ExclusiveLock::WATCHER_LOCK_TAG) { + 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; @@ -417,7 +424,7 @@ Context *AcquireRequest::handle_get_lockers(int *ret_val) { std::map::iterator iter = lockers.begin(); - if (!ExclusiveLock::decode_lock_cookie(iter->first.cookie, + if (!ExclusiveLock<>::decode_lock_cookie(iter->first.cookie, &m_locker_handle)) { ldout(cct, 5) << "locked by external mechanism: " << "cookie=" << iter->first.cookie << dendl; diff --git a/src/librbd/exclusive_lock/ReacquireRequest.cc b/src/librbd/exclusive_lock/ReacquireRequest.cc new file mode 100644 index 0000000000000..e847f65bf8779 --- /dev/null +++ b/src/librbd/exclusive_lock/ReacquireRequest.cc @@ -0,0 +1,72 @@ +// -*- 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 new file mode 100644 index 0000000000000..2718befa45bfa --- /dev/null +++ b/src/librbd/exclusive_lock/ReacquireRequest.h @@ -0,0 +1,63 @@ +// -*- 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/image_watcher/RewatchRequest.cc b/src/librbd/image_watcher/RewatchRequest.cc new file mode 100644 index 0000000000000..4d808f459c5d3 --- /dev/null +++ b/src/librbd/image_watcher/RewatchRequest.cc @@ -0,0 +1,125 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/image_watcher/RewatchRequest.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::image_watcher::RewatchRequest: " \ + << this << ": " << __func__ + +namespace librbd { +namespace image_watcher { + +using librbd::util::create_context_callback; +using librbd::util::create_rados_safe_callback; + +template +RewatchRequest::RewatchRequest(I &image_ctx, RWLock &watch_lock, + librados::WatchCtx2 *watch_ctx, + uint64_t *watch_handle, Context *on_finish) + : m_image_ctx(image_ctx), m_watch_lock(watch_lock), m_watch_ctx(watch_ctx), + m_watch_handle(watch_handle), m_on_finish(on_finish) { +} + +template +void RewatchRequest::send() { + unwatch(); +} + +template +void RewatchRequest::unwatch() { + assert(m_watch_lock.is_wlocked()); + assert(*m_watch_handle != 0); + + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << dendl; + + librados::AioCompletion *aio_comp = create_rados_safe_callback< + RewatchRequest, &RewatchRequest::handle_unwatch>(this); + int r = m_image_ctx.md_ctx.aio_unwatch(*m_watch_handle, aio_comp); + assert(r == 0); + aio_comp->release(); + + *m_watch_handle = 0; +} + +template +void RewatchRequest::handle_unwatch(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << "r=" << r << dendl; + + if (r == -EBLACKLISTED) { + lderr(cct) << "client blacklisted" << dendl; + finish(r); + } else if (r < 0) { + lderr(cct) << "failed to unwatch: " << cpp_strerror(r) << dendl; + } + rewatch(); +} + +template +void RewatchRequest::rewatch() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << dendl; + + librados::AioCompletion *aio_comp = create_rados_safe_callback< + RewatchRequest, &RewatchRequest::handle_rewatch>(this); + int r = m_image_ctx.md_ctx.aio_watch(m_image_ctx.header_oid, aio_comp, + &m_rewatch_handle, m_watch_ctx); + assert(r == 0); + aio_comp->release(); +} + +template +void RewatchRequest::handle_rewatch(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << "r=" << r << dendl; + + if (r == -EBLACKLISTED) { + lderr(cct) << "client blacklisted" << dendl; + finish(r); + return; + } else if (r == -ENOENT) { + ldout(cct, 5) << "image header deleted" << dendl; + finish(r); + return; + } else if (r < 0) { + lderr(cct) << "failed to watch image header: " << cpp_strerror(r) + << dendl; + rewatch(); + return; + } + + { + RWLock::WLocker watch_locker(m_watch_lock); + *m_watch_handle = m_rewatch_handle; + } + + { + RWLock::RLocker owner_locker(m_image_ctx.owner_lock); + if (m_image_ctx.exclusive_lock != nullptr) { + // update the lock cookie with the new watch handle + m_image_ctx.exclusive_lock->reacquire_lock(); + } + } + finish(0); +} + +template +void RewatchRequest::finish(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace image_watcher +} // namespace librbd + +template class librbd::image_watcher::RewatchRequest; diff --git a/src/librbd/image_watcher/RewatchRequest.h b/src/librbd/image_watcher/RewatchRequest.h new file mode 100644 index 0000000000000..dc3a2cdfd5379 --- /dev/null +++ b/src/librbd/image_watcher/RewatchRequest.h @@ -0,0 +1,78 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_IMAGE_WATCHER_REWATCH_REQUEST_H +#define CEPH_LIBRBD_IMAGE_WATCHER_REWATCH_REQUEST_H + +#include "include/int_types.h" +#include "include/rados/librados.hpp" + +struct Context; +struct RWLock; + +namespace librbd { + +class ImageCtx; + +namespace image_watcher { + +template +class RewatchRequest { +public: + + static RewatchRequest *create(ImageCtxT &image_ctx, RWLock &watch_lock, + librados::WatchCtx2 *watch_ctx, + uint64_t *watch_handle, Context *on_finish) { + return new RewatchRequest(image_ctx, watch_lock, watch_ctx, watch_handle, + on_finish); + } + + RewatchRequest(ImageCtxT &image_ctx, RWLock &watch_lock, + librados::WatchCtx2 *watch_ctx, uint64_t *watch_handle, + Context *on_finish); + + void send(); + +private: + /** + * @verbatim + * + * + * | + * v + * UNWATCH + * | + * | . . . . + * | . . (recoverable error) + * v v . + * REWATCH . . . + * | + * v + * + * + * @endverbatim + */ + + ImageCtxT &m_image_ctx; + RWLock &m_watch_lock; + librados::WatchCtx2 *m_watch_ctx; + uint64_t *m_watch_handle; + Context *m_on_finish; + + uint64_t m_rewatch_handle = 0; + + void unwatch(); + void handle_unwatch(int r); + + void rewatch(); + void handle_rewatch(int r); + + void finish(int r); +}; + +} // namespace image_watcher +} // namespace librbd + +extern template class librbd::image_watcher::RewatchRequest; + +#endif // CEPH_LIBRBD_IMAGE_WATCHER_REWATCH_REQUEST_H diff --git a/src/test/Makefile-client.am b/src/test/Makefile-client.am index d3b9613544b48..750037cac8dd0 100644 --- a/src/test/Makefile-client.am +++ b/src/test/Makefile-client.am @@ -390,8 +390,10 @@ unittest_librbd_SOURCES = \ test/librbd/test_mock_Journal.cc \ test/librbd/test_mock_ObjectWatcher.cc \ test/librbd/exclusive_lock/test_mock_AcquireRequest.cc \ + test/librbd/exclusive_lock/test_mock_ReacquireRequest.cc \ test/librbd/exclusive_lock/test_mock_ReleaseRequest.cc \ test/librbd/image/test_mock_RefreshRequest.cc \ + test/librbd/image_watcher/test_mock_RewatchRequest.cc \ test/librbd/journal/test_mock_Replay.cc \ test/librbd/object_map/test_mock_InvalidateRequest.cc \ test/librbd/object_map/test_mock_LockRequest.cc \ diff --git a/src/test/cls_lock/test_cls_lock.cc b/src/test/cls_lock/test_cls_lock.cc index 72dbb8ecad0b7..104081559e152 100644 --- a/src/test/cls_lock/test_cls_lock.cc +++ b/src/test/cls_lock/test_cls_lock.cc @@ -338,3 +338,54 @@ TEST(ClsLock, TestAssertLocked) { ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster)); } + +TEST(ClsLock, TestSetCookie) { + Rados cluster; + std::string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool_pp(pool_name, cluster)); + IoCtx ioctx; + cluster.ioctx_create(pool_name.c_str(), ioctx); + + string oid = "foo"; + string name = "name"; + string tag = "tag"; + string cookie = "cookie"; + string new_cookie = "new cookie"; + librados::ObjectWriteOperation op1; + set_cookie(&op1, name, LOCK_SHARED, cookie, tag, new_cookie); + ASSERT_EQ(-ENOENT, ioctx.operate(oid, &op1)); + + librados::ObjectWriteOperation op2; + lock(&op2, name, LOCK_SHARED, cookie, tag, "", utime_t{}, 0); + ASSERT_EQ(0, ioctx.operate(oid, &op2)); + + librados::ObjectWriteOperation op3; + lock(&op3, name, LOCK_SHARED, "cookie 2", tag, "", utime_t{}, 0); + ASSERT_EQ(0, ioctx.operate(oid, &op3)); + + librados::ObjectWriteOperation op4; + set_cookie(&op4, name, LOCK_SHARED, cookie, tag, cookie); + ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op4)); + + librados::ObjectWriteOperation op5; + set_cookie(&op5, name, LOCK_SHARED, cookie, "wrong tag", new_cookie); + ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op5)); + + librados::ObjectWriteOperation op6; + set_cookie(&op6, name, LOCK_SHARED, "wrong cookie", tag, new_cookie); + ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op6)); + + librados::ObjectWriteOperation op7; + set_cookie(&op7, name, LOCK_EXCLUSIVE, cookie, tag, new_cookie); + ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op7)); + + librados::ObjectWriteOperation op8; + set_cookie(&op8, name, LOCK_SHARED, cookie, tag, "cookie 2"); + ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op8)); + + librados::ObjectWriteOperation op9; + set_cookie(&op9, name, LOCK_SHARED, cookie, tag, new_cookie); + ASSERT_EQ(0, ioctx.operate(oid, &op9)); + + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster)); +} diff --git a/src/test/encoding/types.h b/src/test/encoding/types.h index 386e83ace2b2a..dd6a5afa58ae4 100644 --- a/src/test/encoding/types.h +++ b/src/test/encoding/types.h @@ -384,6 +384,7 @@ TYPE(cls_lock_get_info_op) TYPE_FEATUREFUL(cls_lock_get_info_reply) TYPE(cls_lock_list_locks_reply) TYPE(cls_lock_assert_op) +TYPE(cls_lock_set_cookie_op) #include "cls/replica_log/cls_replica_log_types.h" TYPE(cls_replica_log_item_marker) diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index a85c2b16eda49..aaa9ce55f4f7e 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -30,8 +30,10 @@ set(unittest_librbd_srcs test_mock_Journal.cc test_mock_ObjectWatcher.cc exclusive_lock/test_mock_AcquireRequest.cc + exclusive_lock/test_mock_ReacquireRequest.cc exclusive_lock/test_mock_ReleaseRequest.cc image/test_mock_RefreshRequest.cc + image_watcher/test_mock_RewatchRequest.cc journal/test_mock_Replay.cc object_map/test_mock_InvalidateRequest.cc object_map/test_mock_LockRequest.cc diff --git a/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc b/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc index 4c029521849f6..7037dbf8e8da3 100644 --- a/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc +++ b/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc @@ -13,15 +13,54 @@ #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; +template class librbd::exclusive_lock::AcquireRequest; namespace librbd { namespace exclusive_lock { @@ -38,80 +77,83 @@ static const std::string TEST_COOKIE("auto 123"); class TestMockExclusiveLockAcquireRequest : public TestMockFixture { public: - typedef AcquireRequest MockAcquireRequest; - typedef ExclusiveLock MockExclusiveLock; + typedef AcquireRequest MockAcquireRequest; + typedef ExclusiveLock MockExclusiveLock; + typedef librbd::image::RefreshRequest MockRefreshRequest; - void expect_test_features(MockImageCtx &mock_image_ctx, uint64_t features, + 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(MockImageCtx &mock_image_ctx, uint64_t features, + 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(MockImageCtx &mock_image_ctx, int r) { + 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(MockImageCtx &mock_image_ctx, int 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(MockImageCtx &mock_image_ctx, bool required) { + 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(MockImageCtx &mock_image_ctx, int r) { - EXPECT_CALL(*mock_image_ctx.state, acquire_lock_refresh(_)) - .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + 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(MockImageCtx &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(MockImageCtx &mock_image_ctx, + 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(MockImageCtx &mock_image_ctx, + 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(MockImageCtx &mock_image_ctx, + 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(MockImageCtx &mock_image_ctx, + 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(MockImageCtx &mock_image_ctx, + 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(MockImageCtx &mock_image_ctx, + 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)); @@ -123,14 +165,14 @@ class TestMockExclusiveLockAcquireRequest : public TestMockFixture { .WillOnce(Return(disabled)); } - void expect_allocate_journal_tag(MockImageCtx &mock_image_ctx, + 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(MockImageCtx &mock_image_ctx, int r, + 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, @@ -165,7 +207,7 @@ class TestMockExclusiveLockAcquireRequest : public TestMockFixture { } } - void expect_list_watchers(MockImageCtx &mock_image_ctx, int r, + 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, _)); @@ -183,18 +225,18 @@ class TestMockExclusiveLockAcquireRequest : public TestMockFixture { } } - void expect_blacklist_add(MockImageCtx &mock_image_ctx, int r) { + 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(MockImageCtx &mock_image_ctx, int 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(MockImageCtx &mock_image_ctx) { + 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)); } @@ -206,7 +248,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, Success) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; @@ -246,14 +288,15 @@ TEST_F(TestMockExclusiveLockAcquireRequest, SuccessRefresh) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); + MockRefreshRequest mock_refresh_request; expect_op_work_queue(mock_image_ctx); InSequence seq; 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, 0); + expect_refresh(mock_image_ctx, mock_refresh_request, 0); MockObjectMap mock_object_map; expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false); @@ -276,7 +319,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, SuccessJournalDisabled) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; @@ -308,7 +351,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, SuccessObjectMapDisabled) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; @@ -345,14 +388,15 @@ TEST_F(TestMockExclusiveLockAcquireRequest, RefreshError) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); + MockRefreshRequest mock_refresh_request; expect_op_work_queue(mock_image_ctx); InSequence seq; 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, -EINVAL); + expect_refresh(mock_image_ctx, mock_refresh_request, -EINVAL); expect_unlock(mock_image_ctx, 0); C_SaferCond *acquire_ctx = new C_SaferCond(); @@ -370,7 +414,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, JournalError) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; @@ -410,7 +454,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, AllocateJournalTagError) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; @@ -452,14 +496,14 @@ TEST_F(TestMockExclusiveLockAcquireRequest, LockBusy) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; 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", MockExclusiveLock::WATCHER_LOCK_TAG, + "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); @@ -480,7 +524,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoError) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; @@ -503,7 +547,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoEmpty) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; @@ -527,7 +571,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoExternalTag) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; @@ -550,14 +594,14 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoShared) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; 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", MockExclusiveLock::WATCHER_LOCK_TAG, + "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, LOCK_SHARED); C_SaferCond ctx; @@ -574,14 +618,14 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoExternalCookie) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; 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", MockExclusiveLock::WATCHER_LOCK_TAG, + "external cookie", ExclusiveLock<>::WATCHER_LOCK_TAG, LOCK_EXCLUSIVE); C_SaferCond ctx; @@ -598,14 +642,14 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetWatchersError) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; 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", MockExclusiveLock::WATCHER_LOCK_TAG, + "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, LOCK_EXCLUSIVE); expect_list_watchers(mock_image_ctx, -EINVAL, "dead client", 123); @@ -623,14 +667,14 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetWatchersAlive) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; 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", MockExclusiveLock::WATCHER_LOCK_TAG, + "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, LOCK_EXCLUSIVE); expect_list_watchers(mock_image_ctx, 0, "1.2.3.4", 123); @@ -648,7 +692,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, BlacklistDisabled) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); mock_image_ctx.blacklist_on_break_lock = false; @@ -656,7 +700,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, BlacklistDisabled) { 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", MockExclusiveLock::WATCHER_LOCK_TAG, + "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); @@ -676,14 +720,14 @@ TEST_F(TestMockExclusiveLockAcquireRequest, BlacklistError) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; 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", MockExclusiveLock::WATCHER_LOCK_TAG, + "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); @@ -702,14 +746,14 @@ TEST_F(TestMockExclusiveLockAcquireRequest, BreakLockMissing) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; 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", MockExclusiveLock::WATCHER_LOCK_TAG, + "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); @@ -730,14 +774,14 @@ TEST_F(TestMockExclusiveLockAcquireRequest, BreakLockError) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; 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", MockExclusiveLock::WATCHER_LOCK_TAG, + "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); @@ -757,7 +801,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, OpenObjectMapError) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); - MockImageCtx mock_image_ctx(*ictx); + MockTestImageCtx mock_image_ctx(*ictx); expect_op_work_queue(mock_image_ctx); InSequence seq; diff --git a/src/test/librbd/exclusive_lock/test_mock_ReacquireRequest.cc b/src/test/librbd/exclusive_lock/test_mock_ReacquireRequest.cc new file mode 100644 index 0000000000000..1ca9f780bf78b --- /dev/null +++ b/src/test/librbd/exclusive_lock/test_mock_ReacquireRequest.cc @@ -0,0 +1,101 @@ +// -*- 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/image_watcher/test_mock_RewatchRequest.cc b/src/test/librbd/image_watcher/test_mock_RewatchRequest.cc new file mode 100644 index 0000000000000..5609134c54456 --- /dev/null +++ b/src/test/librbd/image_watcher/test_mock_RewatchRequest.cc @@ -0,0 +1,215 @@ +// -*- 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 "include/rados/librados.hpp" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librados_test_stub/MockTestMemRadosClient.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockExclusiveLock.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "librados/AioCompletionImpl.h" +#include "librbd/image_watcher/RewatchRequest.h" + +namespace librbd { +namespace { + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace +} // namespace librbd + +#include "librbd/image_watcher/RewatchRequest.cc" + +namespace librbd { +namespace image_watcher { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::WithArg; + +struct TestMockImageWatcherRewatchRequest : public TestMockFixture { + typedef RewatchRequest MockRewatchRequest; + + TestMockImageWatcherRewatchRequest() + : m_watch_lock("watch_lock") { + } + + void expect_aio_watch(MockImageCtx &mock_image_ctx, int r) { + librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx( + mock_image_ctx.md_ctx)); + + EXPECT_CALL(mock_io_ctx, aio_watch(mock_image_ctx.header_oid, _, _, _)) + .WillOnce(DoAll(WithArg<1>(Invoke([&mock_image_ctx, &mock_io_ctx, r](librados::AioCompletionImpl *c) { + c->get(); + mock_image_ctx.image_ctx->op_work_queue->queue(new FunctionContext([&mock_io_ctx, c](int r) { + mock_io_ctx.get_mock_rados_client()->finish_aio_completion(c, r); + }), r); + })), + Return(0))); + } + + void expect_aio_unwatch(MockImageCtx &mock_image_ctx, int r) { + librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx( + mock_image_ctx.md_ctx)); + + EXPECT_CALL(mock_io_ctx, aio_unwatch(m_watch_handle, _)) + .WillOnce(DoAll(Invoke([&mock_image_ctx, &mock_io_ctx, r](uint64_t handle, + librados::AioCompletionImpl *c) { + c->get(); + mock_image_ctx.image_ctx->op_work_queue->queue(new FunctionContext([&mock_io_ctx, c](int r) { + mock_io_ctx.get_mock_rados_client()->finish_aio_completion(c, r); + }), r); + }), + Return(0))); + } + + void expect_reacquire_lock(MockExclusiveLock &mock_exclusive_lock) { + EXPECT_CALL(mock_exclusive_lock, reacquire_lock()); + } + + struct WatchCtx : public librados::WatchCtx2 { + virtual void handle_notify(uint64_t, uint64_t, uint64_t, + ceph::bufferlist&) { + assert(false); + } + virtual void handle_error(uint64_t, int) { + assert(false); + } + }; + + RWLock m_watch_lock; + WatchCtx m_watch_ctx; + uint64_t m_watch_handle = 123; +}; + +TEST_F(TestMockImageWatcherRewatchRequest, Success) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_aio_unwatch(mock_image_ctx, 0); + expect_aio_watch(mock_image_ctx, 0); + + MockExclusiveLock mock_exclusive_lock; + if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) { + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + expect_reacquire_lock(mock_exclusive_lock); + } + + C_SaferCond ctx; + MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx, + m_watch_lock, + &m_watch_ctx, + &m_watch_handle, + &ctx); + { + RWLock::WLocker watch_locker(m_watch_lock); + req->send(); + } + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageWatcherRewatchRequest, UnwatchError) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_aio_unwatch(mock_image_ctx, -EINVAL); + expect_aio_watch(mock_image_ctx, 0); + + C_SaferCond ctx; + MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx, + m_watch_lock, + &m_watch_ctx, + &m_watch_handle, + &ctx); + { + RWLock::WLocker watch_locker(m_watch_lock); + req->send(); + } + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageWatcherRewatchRequest, WatchBlacklist) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_aio_unwatch(mock_image_ctx, 0); + expect_aio_watch(mock_image_ctx, -EBLACKLISTED); + + C_SaferCond ctx; + MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx, + m_watch_lock, + &m_watch_ctx, + &m_watch_handle, + &ctx); + { + RWLock::WLocker watch_locker(m_watch_lock); + req->send(); + } + ASSERT_EQ(-EBLACKLISTED, ctx.wait()); +} + +TEST_F(TestMockImageWatcherRewatchRequest, WatchDNE) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_aio_unwatch(mock_image_ctx, 0); + expect_aio_watch(mock_image_ctx, -ENOENT); + + C_SaferCond ctx; + MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx, + m_watch_lock, + &m_watch_ctx, + &m_watch_handle, + &ctx); + { + RWLock::WLocker watch_locker(m_watch_lock); + req->send(); + } + ASSERT_EQ(-ENOENT, ctx.wait()); +} + +TEST_F(TestMockImageWatcherRewatchRequest, WatchError) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_aio_unwatch(mock_image_ctx, 0); + expect_aio_watch(mock_image_ctx, -EINVAL); + expect_aio_watch(mock_image_ctx, 0); + + C_SaferCond ctx; + MockRewatchRequest *req = MockRewatchRequest::create(mock_image_ctx, + m_watch_lock, + &m_watch_ctx, + &m_watch_handle, + &ctx); + { + RWLock::WLocker watch_locker(m_watch_lock); + req->send(); + } + ASSERT_EQ(0, ctx.wait()); +} + +} // namespace image_watcher +} // namespace librbd diff --git a/src/test/librbd/mock/MockExclusiveLock.h b/src/test/librbd/mock/MockExclusiveLock.h index 97f9da12fbc52..83d3058d1d8bd 100644 --- a/src/test/librbd/mock/MockExclusiveLock.h +++ b/src/test/librbd/mock/MockExclusiveLock.h @@ -19,6 +19,8 @@ struct MockExclusiveLock { MOCK_METHOD2(init, void(uint64_t features, Context*)); MOCK_METHOD1(shut_down, void(Context*)); + + MOCK_METHOD0(reacquire_lock, void()); }; } // namespace librbd diff --git a/src/test/librbd/mock/MockImageState.h b/src/test/librbd/mock/MockImageState.h index 3ba1ee5d7dd0e..59c9da8d69db6 100644 --- a/src/test/librbd/mock/MockImageState.h +++ b/src/test/librbd/mock/MockImageState.h @@ -13,7 +13,6 @@ namespace librbd { struct MockImageState { MOCK_CONST_METHOD0(is_refresh_required, bool()); MOCK_METHOD1(refresh, void(Context*)); - MOCK_METHOD1(acquire_lock_refresh, void(Context*)); MOCK_METHOD1(open, void(Context*)); @@ -21,6 +20,9 @@ struct MockImageState { MOCK_METHOD1(close, void(Context*)); MOCK_METHOD2(snap_set, void(const std::string &, Context*)); + + MOCK_METHOD1(prepare_lock, void(Context*)); + MOCK_METHOD0(handle_prepare_lock_complete, void()); }; } // namespace librbd diff --git a/src/test/librbd/test_mock_ExclusiveLock.cc b/src/test/librbd/test_mock_ExclusiveLock.cc index a2003a5d0a78e..821f857b769b9 100644 --- a/src/test/librbd/test_mock_ExclusiveLock.cc +++ b/src/test/librbd/test_mock_ExclusiveLock.cc @@ -4,8 +4,10 @@ #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 "librbd/ExclusiveLock.h" #include "librbd/exclusive_lock/AcquireRequest.h" +#include "librbd/exclusive_lock/ReacquireRequest.h" #include "librbd/exclusive_lock/ReleaseRequest.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -53,6 +55,18 @@ 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); + } + + MOCK_METHOD0(send, void()); +}; + template <> struct ReleaseRequest : public BaseRequest > { MOCK_METHOD0(send, void()); @@ -83,11 +97,13 @@ class TestMockExclusiveLock : public TestMockFixture { public: typedef ExclusiveLock MockExclusiveLock; typedef exclusive_lock::AcquireRequest MockAcquireRequest; + typedef exclusive_lock::ReacquireRequest MockReacquireRequest; typedef exclusive_lock::ReleaseRequest MockReleaseRequest; - void expect_get_watch_handle(MockExclusiveLockImageCtx &mock_image_ctx) { + 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(1234567890)); + .WillRepeatedly(Return(watch_handle)); } void expect_set_require_lock_on_read(MockExclusiveLockImageCtx &mock_image_ctx) { @@ -114,9 +130,11 @@ class TestMockExclusiveLock : public TestMockFixture { void expect_acquire_lock(MockExclusiveLockImageCtx &mock_image_ctx, MockAcquireRequest &acquire_request, int r) { expect_get_watch_handle(mock_image_ctx); + expect_prepare_lock(mock_image_ctx); EXPECT_CALL(acquire_request, send()) .WillOnce(DoAll(FinishLockUnlock(&acquire_request), FinishRequest(&acquire_request, r, &mock_image_ctx))); + expect_handle_prepare_lock_complete(mock_image_ctx); if (r == 0) { expect_notify_acquired_lock(mock_image_ctx); expect_unblock_writes(mock_image_ctx); @@ -126,9 +144,15 @@ class TestMockExclusiveLock : public TestMockFixture { void expect_release_lock(MockExclusiveLockImageCtx &mock_image_ctx, MockReleaseRequest &release_request, int r, bool shutting_down = false) { + if (!shutting_down) { + expect_prepare_lock(mock_image_ctx); + } EXPECT_CALL(release_request, send()) .WillOnce(DoAll(FinishLockUnlock(&release_request), FinishRequest(&release_request, r, &mock_image_ctx))); + if (!shutting_down) { + expect_handle_prepare_lock_complete(mock_image_ctx); + } if (r == 0) { if (shutting_down) { expect_unblock_writes(mock_image_ctx); @@ -138,6 +162,14 @@ class TestMockExclusiveLock : public TestMockFixture { } } + void expect_reacquire_lock(MockExclusiveLockImageCtx &mock_image_ctx, + MockReacquireRequest &mock_reacquire_request, + int r) { + expect_get_watch_handle(mock_image_ctx, 98765); + EXPECT_CALL(mock_reacquire_request, send()) + .WillOnce(FinishRequest(&mock_reacquire_request, r, &mock_image_ctx)); + } + void expect_notify_request_lock(MockExclusiveLockImageCtx &mock_image_ctx, MockExclusiveLock &mock_exclusive_lock) { EXPECT_CALL(*mock_image_ctx.image_watcher, notify_request_lock()) @@ -165,6 +197,17 @@ class TestMockExclusiveLock : public TestMockFixture { .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); } + void expect_prepare_lock(MockExclusiveLockImageCtx &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(MockExclusiveLockImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete()); + } + int when_init(MockExclusiveLockImageCtx &mock_image_ctx, MockExclusiveLock &exclusive_lock) { C_SaferCond ctx; @@ -176,7 +219,7 @@ class TestMockExclusiveLock : public TestMockFixture { } int when_try_lock(MockExclusiveLockImageCtx &mock_image_ctx, - MockExclusiveLock &exclusive_lock) { + MockExclusiveLock &exclusive_lock) { C_SaferCond ctx; { RWLock::WLocker owner_locker(mock_image_ctx.owner_lock); @@ -185,7 +228,7 @@ class TestMockExclusiveLock : public TestMockFixture { return ctx.wait(); } int when_request_lock(MockExclusiveLockImageCtx &mock_image_ctx, - MockExclusiveLock &exclusive_lock) { + MockExclusiveLock &exclusive_lock) { C_SaferCond ctx; { RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); @@ -194,7 +237,7 @@ class TestMockExclusiveLock : public TestMockFixture { return ctx.wait(); } int when_release_lock(MockExclusiveLockImageCtx &mock_image_ctx, - MockExclusiveLock &exclusive_lock) { + MockExclusiveLock &exclusive_lock) { C_SaferCond ctx; { RWLock::WLocker owner_locker(mock_image_ctx.owner_lock); @@ -528,16 +571,20 @@ TEST_F(TestMockExclusiveLock, ConcurrentRequests) { MockAcquireRequest try_lock_acquire; C_SaferCond wait_for_send_ctx1; expect_get_watch_handle(mock_image_ctx); + expect_prepare_lock(mock_image_ctx); EXPECT_CALL(try_lock_acquire, send()) .WillOnce(Notify(&wait_for_send_ctx1)); + expect_handle_prepare_lock_complete(mock_image_ctx); MockAcquireRequest request_acquire; expect_acquire_lock(mock_image_ctx, request_acquire, 0); MockReleaseRequest release; C_SaferCond wait_for_send_ctx2; + expect_prepare_lock(mock_image_ctx); EXPECT_CALL(release, send()) .WillOnce(Notify(&wait_for_send_ctx2)); + expect_handle_prepare_lock_complete(mock_image_ctx); expect_notify_released_lock(mock_image_ctx); expect_is_lock_request_needed(mock_image_ctx, false); @@ -644,11 +691,13 @@ TEST_F(TestMockExclusiveLock, RequestLockWatchNotRegistered) { EXPECT_CALL(*mock_image_ctx.image_watcher, get_watch_handle()) .WillOnce(DoAll(Invoke([&mock_image_ctx, &exclusive_lock]() { mock_image_ctx.image_ctx->op_work_queue->queue( - new FunctionContext([&exclusive_lock](int r) { - exclusive_lock.handle_watch_registered(); + new FunctionContext([&mock_image_ctx, &exclusive_lock](int r) { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + exclusive_lock.reacquire_lock(); })); }), Return(0))); + MockAcquireRequest request_lock_acquire; expect_acquire_lock(mock_image_ctx, request_lock_acquire, 0); ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock)); @@ -660,5 +709,80 @@ TEST_F(TestMockExclusiveLock, RequestLockWatchNotRegistered) { ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); } +TEST_F(TestMockExclusiveLock, ReacquireLock) { + 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)); + + MockAcquireRequest request_lock_acquire; + expect_acquire_lock(mock_image_ctx, request_lock_acquire, 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; + 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); + ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); + ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); +} + +TEST_F(TestMockExclusiveLock, ReacquireLockError) { + 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)); + + MockAcquireRequest request_lock_acquire; + expect_acquire_lock(mock_image_ctx, request_lock_acquire, 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; + 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); + + MockAcquireRequest reacquire_lock_acquire; + expect_acquire_lock(mock_image_ctx, reacquire_lock_acquire, 0); + + { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + exclusive_lock.reacquire_lock(&reacquire_ctx); + } + ASSERT_EQ(-EOPNOTSUPP, reacquire_ctx.wait()); + + MockReleaseRequest shutdown_release; + expect_release_lock(mock_image_ctx, shutdown_release, 0, true); + ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock)); + ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock)); +} + } // namespace librbd diff --git a/src/test/librbd/test_mock_fixture.cc b/src/test/librbd/test_mock_fixture.cc index 3fb246d28d37c..efa85562b1cc6 100644 --- a/src/test/librbd/test_mock_fixture.cc +++ b/src/test/librbd/test_mock_fixture.cc @@ -9,12 +9,10 @@ // template definitions #include "librbd/AsyncRequest.cc" #include "librbd/AsyncObjectThrottle.cc" -#include "librbd/ExclusiveLock.cc" #include "librbd/operation/Request.cc" template class librbd::AsyncRequest; template class librbd::AsyncObjectThrottle; -template class librbd::ExclusiveLock; template class librbd::operation::Request; using ::testing::_;