From: Jason Dillaman Date: Wed, 9 Dec 2015 04:07:02 +0000 (-0500) Subject: librbd: implement image state tracker for open/close/refresh/etc X-Git-Tag: v10.0.2~35^2~11 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=c00b28b01721e03de3c1e1ee060b9f21c0653eb0;p=ceph.git librbd: implement image state tracker for open/close/refresh/etc Signed-off-by: Jason Dillaman --- diff --git a/src/librbd/ImageState.cc b/src/librbd/ImageState.cc index f2ac1219260a..c43a63124a60 100644 --- a/src/librbd/ImageState.cc +++ b/src/librbd/ImageState.cc @@ -4,7 +4,13 @@ #include "librbd/ImageState.h" #include "common/dout.h" #include "common/errno.h" -#include "librbd/image/StateRequest.h" +#include "common/Cond.h" +#include "librbd/ImageCtx.h" +#include "librbd/Utils.h" +#include "librbd/image/CloseRequest.h" +#include "librbd/image/OpenRequest.h" +#include "librbd/image/RefreshRequest.h" +#include "librbd/image/SetSnapRequest.h" #define dout_subsys ceph_subsys_rbd #undef dout_prefix @@ -12,22 +18,373 @@ namespace librbd { +using util::create_context_callback; + +template +ImageState::ImageState(I *image_ctx) + : m_image_ctx(image_ctx), m_state(STATE_UNINITIALIZED), + m_lock(util::unique_lock_name("librbd::ImageState::m_lock", this)), + m_last_refresh(0), m_refresh_seq(0) { +} + +template +ImageState::~ImageState() { + assert(m_state == STATE_UNINITIALIZED || m_state == STATE_CLOSED); +} + +template +int ImageState::open() { + C_SaferCond ctx; + open(&ctx); + return ctx.wait(); +} + +template +void ImageState::open(Context *on_finish) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << __func__ << dendl; + + Mutex::Locker locker(m_lock); + assert(m_state == STATE_UNINITIALIZED); + + Action action(ACTION_TYPE_OPEN); + action.refresh_seq = m_refresh_seq; + execute_action(action, on_finish); +} + template -ImageState::ImageState(I &image_ctx) : m_image_ctx(image_ctx) { +int ImageState::close() { + C_SaferCond ctx; + close(&ctx); + + int r = ctx.wait(); + delete m_image_ctx; + return r; +} + +template +void ImageState::close(Context *on_finish) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << __func__ << dendl; + + Mutex::Locker locker(m_lock); + assert(!is_closed()); + + Action action(ACTION_TYPE_CLOSE); + action.refresh_seq = m_refresh_seq; + execute_action(action, on_finish); +} + +template +void ImageState::handle_update_notification() { + Mutex::Locker locker(m_lock); + ++m_refresh_seq; + + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << "refresh_seq = " << m_refresh_seq << ", " + << "last_refresh = " << m_last_refresh << dendl; } template bool ImageState::is_refresh_required() const { - // TODO future entry point for AIO ops -- to replace ictx_check call - return false; + Mutex::Locker locker(m_lock); + return (m_last_refresh != m_refresh_seq); +} + +template +int ImageState::refresh() { + C_SaferCond refresh_ctx; + { + RWLock::RLocker owner_lock(m_image_ctx->owner_lock); + refresh(&refresh_ctx); + } + return refresh_ctx.wait(); } template void ImageState::refresh(Context *on_finish) { - // TODO simple state machine to restrict to a single in-progress refresh / snap set - image::StateRequest *req = image::StateRequest::create( - m_image_ctx, on_finish); + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << __func__ << dendl; + + m_lock.Lock(); + if (is_closed()) { + m_lock.Unlock(); + on_finish->complete(0); + return; + } + + Action action(ACTION_TYPE_REFRESH); + action.refresh_seq = m_refresh_seq; + execute_action(action, on_finish); + m_lock.Unlock(); +} + +template +int ImageState::refresh_if_required() { + RWLock::RLocker owner_locker(m_image_ctx->owner_lock); + return refresh_if_required(m_image_ctx->owner_lock); +} + +template +int ImageState::refresh_if_required(const RWLock &) { + assert(m_image_ctx->owner_lock.is_locked()); + + C_SaferCond ctx; + { + Mutex::Locker locker(m_lock); + if (m_last_refresh == m_refresh_seq || is_closed()) { + return 0; + } + + Action action(ACTION_TYPE_REFRESH); + action.refresh_seq = m_refresh_seq; + execute_action(action, &ctx); + } + + return ctx.wait(); +} + +template +void ImageState::snap_set(const std::string &snap_name, Context *on_finish) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << __func__ << ": snap_name=" << snap_name << dendl; + + Mutex::Locker locker(m_lock); + Action action(ACTION_TYPE_SET_SNAP); + action.snap_name = snap_name; + execute_action(action, on_finish); +} + +template +bool ImageState::is_transition_state() const { + switch (m_state) { + case STATE_UNINITIALIZED: + case STATE_OPEN: + case STATE_CLOSED: + return false; + case STATE_OPENING: + case STATE_CLOSING: + case STATE_REFRESHING: + case STATE_SETTING_SNAP: + break; + } + return true; +} + +template +bool ImageState::is_closed() const { + assert(m_lock.is_locked()); + + return ((m_state == STATE_CLOSED) || + (!m_actions_contexts.empty() && + m_actions_contexts.back().first.action_type == ACTION_TYPE_CLOSE)); +} + +template +void ImageState::append_context(const Action &action, Context *context) { + assert(m_lock.is_locked()); + + ActionContexts *action_contexts = nullptr; + for (auto &action_ctxs : m_actions_contexts) { + if (action == action_ctxs.first) { + action_contexts = &action_ctxs; + break; + } + } + + if (action_contexts == nullptr) { + m_actions_contexts.push_back({action, {}}); + action_contexts = &m_actions_contexts.back(); + } + + if (context != nullptr) { + action_contexts->second.push_back(context); + } +} + +template +void ImageState::execute_next_action() { + assert(m_lock.is_locked()); + assert(!m_actions_contexts.empty()); + switch (m_actions_contexts.front().first.action_type) { + case ACTION_TYPE_OPEN: + send_open(); + return; + case ACTION_TYPE_CLOSE: + send_close(); + return; + case ACTION_TYPE_REFRESH: + send_refresh(); + return; + case ACTION_TYPE_SET_SNAP: + send_set_snap(); + return; + } + assert(false); +} + +template +void ImageState::execute_action(const Action &action, Context *on_finish) { + assert(m_lock.is_locked()); + + append_context(action, on_finish); + if (!is_transition_state()) { + execute_next_action(); + } +} + +template +void ImageState::complete_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(); + + if (!is_transition_state() && !m_actions_contexts.empty()) { + execute_next_action(); + } +} + +template +void ImageState::send_open() { + assert(m_lock.is_locked()); + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + m_state = STATE_OPENING; + + Context *ctx = create_context_callback< + ImageState, &ImageState::handle_open>(this); + image::OpenRequest *req = image::OpenRequest::create( + m_image_ctx, ctx); + + m_lock.Unlock(); req->send(); + m_lock.Lock(); +} + +template +void ImageState::handle_open(int r) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to open image: " << cpp_strerror(r) << dendl; + } + + Mutex::Locker locker(m_lock); + complete_action(r < 0 ? STATE_UNINITIALIZED : STATE_OPEN, r); +} + +template +void ImageState::send_close() { + assert(m_lock.is_locked()); + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + m_state = STATE_CLOSING; + + Context *ctx = create_context_callback< + ImageState, &ImageState::handle_close>(this); + image::CloseRequest *req = image::CloseRequest::create( + m_image_ctx, ctx); + + m_lock.Unlock(); + req->send(); + m_lock.Lock(); +} + +template +void ImageState::handle_close(int r) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "error occurred while closing image: " << cpp_strerror(r) + << dendl; + } + + Mutex::Locker locker(m_lock); + complete_action(STATE_CLOSED, r); +} + +template +void ImageState::send_refresh() { + assert(m_lock.is_locked()); + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + m_state = STATE_REFRESHING; + + Context *ctx = create_context_callback< + ImageState, &ImageState::handle_refresh>(this); + image::RefreshRequest *req = image::RefreshRequest::create( + *m_image_ctx, ctx); + + m_lock.Unlock(); + req->send(); + m_lock.Lock(); +} + +template +void ImageState::handle_refresh(int r) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl; + + Mutex::Locker locker(m_lock); + assert(!m_actions_contexts.empty()); + + ActionContexts &action_contexts(m_actions_contexts.front()); + assert(action_contexts.first.action_type == ACTION_TYPE_REFRESH); + assert(m_last_refresh <= action_contexts.first.refresh_seq); + m_last_refresh = action_contexts.first.refresh_seq; + + complete_action(STATE_OPEN, r); +} + +template +void ImageState::send_set_snap() { + assert(m_lock.is_locked()); + + m_state = STATE_SETTING_SNAP; + + assert(!m_actions_contexts.empty()); + ActionContexts &action_contexts(m_actions_contexts.front()); + assert(action_contexts.first.action_type == ACTION_TYPE_SET_SNAP); + + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": " + << "snap_name=" << action_contexts.first.snap_name << dendl; + + Context *ctx = create_context_callback< + ImageState, &ImageState::handle_set_snap>(this); + image::SetSnapRequest *req = image::SetSnapRequest::create( + *m_image_ctx, action_contexts.first.snap_name, ctx); + + m_lock.Unlock(); + req->send(); + m_lock.Lock(); +} + +template +void ImageState::handle_set_snap(int r) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << " r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to set snapshot: " << cpp_strerror(r) << dendl; + } + + Mutex::Locker locker(m_lock); + complete_action(STATE_OPEN, r); } } // namespace librbd diff --git a/src/librbd/ImageState.h b/src/librbd/ImageState.h index b323be77a503..f6a170bd896b 100644 --- a/src/librbd/ImageState.h +++ b/src/librbd/ImageState.h @@ -5,8 +5,13 @@ #define CEPH_LIBRBD_IMAGE_STATE_H #include "include/int_types.h" +#include "common/Mutex.h" +#include +#include +#include class Context; +class RWLock; namespace librbd { @@ -15,14 +20,98 @@ class ImageCtx; template class ImageState { public: - ImageState(ImageCtxT &image_ctx); + ImageState(ImageCtxT *image_ctx); + ~ImageState(); + + int open(); + void open(Context *on_finish); + + int close(); + void close(Context *on_finish); + + void handle_update_notification(); bool is_refresh_required() const; + int refresh(); void refresh(Context *on_finish); + int refresh_if_required(); + int refresh_if_required(const RWLock &owner_lock); + + void snap_set(const std::string &snap_name, Context *on_finish); private: - ImageCtxT &m_image_ctx; + enum State { + STATE_UNINITIALIZED, + STATE_OPEN, + STATE_CLOSED, + STATE_OPENING, + STATE_CLOSING, + STATE_REFRESHING, + STATE_SETTING_SNAP + }; + + enum ActionType { + ACTION_TYPE_OPEN, + ACTION_TYPE_CLOSE, + ACTION_TYPE_REFRESH, + ACTION_TYPE_SET_SNAP + }; + + struct Action { + ActionType action_type; + uint64_t refresh_seq; + std::string snap_name; + + Action(ActionType action_type) : action_type(action_type), refresh_seq(0) { + } + inline bool operator==(const Action &action) const { + if (action_type != action.action_type) { + return false; + } + switch (action_type) { + case ACTION_TYPE_REFRESH: + return refresh_seq == action.refresh_seq; + case ACTION_TYPE_SET_SNAP: + return snap_name == action.snap_name; + default: + return true; + } + } + }; + + typedef std::list Contexts; + typedef std::pair ActionContexts; + typedef std::list ActionsContexts; + + ImageCtxT *m_image_ctx; + State m_state; + + mutable Mutex m_lock; + ActionsContexts m_actions_contexts; + + uint64_t m_last_refresh; + uint64_t m_refresh_seq; + + bool is_transition_state() const; + bool is_closed() const; + + void append_context(const Action &action, Context *context); + void execute_next_action(); + void execute_action(const Action &action, Context *context); + void complete_action(State next_state, int r); + + void send_open(); + void handle_open(int r); + + void send_close(); + void handle_close(int r); + + void send_refresh(); + void handle_refresh(int r); + + void send_set_snap(); + void handle_set_snap(int r); };