]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: implement image state tracker for open/close/refresh/etc
authorJason Dillaman <dillaman@redhat.com>
Wed, 9 Dec 2015 04:07:02 +0000 (23:07 -0500)
committerJason Dillaman <dillaman@redhat.com>
Tue, 15 Dec 2015 01:31:31 +0000 (20:31 -0500)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/librbd/ImageState.cc
src/librbd/ImageState.h

index f2ac1219260a067c07b73396b28efb882ebbc2f1..c43a63124a600082b02332cfe3f780b2f33aa498 100644 (file)
@@ -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
 
 namespace librbd {
 
+using util::create_context_callback;
+
+template <typename I>
+ImageState<I>::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 <typename I>
+ImageState<I>::~ImageState() {
+  assert(m_state == STATE_UNINITIALIZED || m_state == STATE_CLOSED);
+}
+
+template <typename I>
+int ImageState<I>::open() {
+  C_SaferCond ctx;
+  open(&ctx);
+  return ctx.wait();
+}
+
+template <typename I>
+void ImageState<I>::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 <typename I>
-ImageState<I>::ImageState(I &image_ctx) : m_image_ctx(image_ctx) {
+int ImageState<I>::close() {
+  C_SaferCond ctx;
+  close(&ctx);
+
+  int r = ctx.wait();
+  delete m_image_ctx;
+  return r;
+}
+
+template <typename I>
+void ImageState<I>::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 <typename I>
+void ImageState<I>::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 <typename I>
 bool ImageState<I>::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 <typename I>
+int ImageState<I>::refresh() {
+  C_SaferCond refresh_ctx;
+  {
+    RWLock::RLocker owner_lock(m_image_ctx->owner_lock);
+    refresh(&refresh_ctx);
+  }
+  return refresh_ctx.wait();
 }
 
 template <typename I>
 void ImageState<I>::refresh(Context *on_finish) {
-  // TODO simple state machine to restrict to a single in-progress refresh / snap set
-  image::StateRequest<I> *req = image::StateRequest<I>::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 <typename I>
+int ImageState<I>::refresh_if_required() {
+  RWLock::RLocker owner_locker(m_image_ctx->owner_lock);
+  return refresh_if_required(m_image_ctx->owner_lock);
+}
+
+template <typename I>
+int ImageState<I>::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 <typename I>
+void ImageState<I>::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 <typename I>
+bool ImageState<I>::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 <typename I>
+bool ImageState<I>::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 <typename I>
+void ImageState<I>::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 <typename I>
+void ImageState<I>::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 <typename I>
+void ImageState<I>::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 <typename I>
+void ImageState<I>::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 <typename I>
+void ImageState<I>::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<I>, &ImageState<I>::handle_open>(this);
+  image::OpenRequest<I> *req = image::OpenRequest<I>::create(
+    m_image_ctx, ctx);
+
+  m_lock.Unlock();
   req->send();
+  m_lock.Lock();
+}
+
+template <typename I>
+void ImageState<I>::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 <typename I>
+void ImageState<I>::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<I>, &ImageState<I>::handle_close>(this);
+  image::CloseRequest<I> *req = image::CloseRequest<I>::create(
+    m_image_ctx, ctx);
+
+  m_lock.Unlock();
+  req->send();
+  m_lock.Lock();
+}
+
+template <typename I>
+void ImageState<I>::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 <typename I>
+void ImageState<I>::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<I>, &ImageState<I>::handle_refresh>(this);
+  image::RefreshRequest<I> *req = image::RefreshRequest<I>::create(
+    *m_image_ctx, ctx);
+
+  m_lock.Unlock();
+  req->send();
+  m_lock.Lock();
+}
+
+template <typename I>
+void ImageState<I>::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 <typename I>
+void ImageState<I>::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<I>, &ImageState<I>::handle_set_snap>(this);
+  image::SetSnapRequest<I> *req = image::SetSnapRequest<I>::create(
+    *m_image_ctx, action_contexts.first.snap_name, ctx);
+
+  m_lock.Unlock();
+  req->send();
+  m_lock.Lock();
+}
+
+template <typename I>
+void ImageState<I>::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
index b323be77a503f32394fae6d3245e1b3dbb3ddafe..f6a170bd896bc73ea9613958cba11b88033182d2 100644 (file)
@@ -5,8 +5,13 @@
 #define CEPH_LIBRBD_IMAGE_STATE_H
 
 #include "include/int_types.h"
+#include "common/Mutex.h"
+#include <list>
+#include <string>
+#include <utility>
 
 class Context;
+class RWLock;
 
 namespace librbd {
 
@@ -15,14 +20,98 @@ class ImageCtx;
 template <typename ImageCtxT = ImageCtx>
 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<Context *> Contexts;
+  typedef std::pair<Action, Contexts> ActionContexts;
+  typedef std::list<ActionContexts> 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);
 
 };