]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: consolidate all async operation flush logic
authorJason Dillaman <dillaman@redhat.com>
Thu, 5 Feb 2015 06:20:00 +0000 (01:20 -0500)
committerJosh Durgin <jdurgin@redhat.com>
Mon, 9 Feb 2015 22:40:25 +0000 (14:40 -0800)
librbd has three different methods for flushing asynchronous
operations. These have all been consolidated down to a single,
shared method to fix an existing issue and simplify maintenance
going forward.

Fixes: #10783
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
Reviewed-by: Josh Durgin <jdurgin@redhat.com>
13 files changed:
src/librbd/AioCompletion.cc
src/librbd/AioCompletion.h
src/librbd/AsyncOperation.cc [new file with mode: 0644]
src/librbd/AsyncOperation.h [new file with mode: 0644]
src/librbd/CopyupRequest.cc
src/librbd/CopyupRequest.h
src/librbd/ImageCtx.cc
src/librbd/ImageCtx.h
src/librbd/ImageWatcher.cc
src/librbd/ImageWatcher.h
src/librbd/Makefile.am
src/librbd/internal.cc
src/test/librbd/test_internal.cc

index eedfced96e93311891950a7beedd875a93b07495..4ead667d6dbf47899a0023d9fb07de1a4c81b4a8 100644 (file)
@@ -89,12 +89,7 @@ namespace librbd {
       break;
     }
 
-    {
-      Mutex::Locker l(ictx->aio_lock);
-      assert(ictx->pending_aio != 0);
-      --ictx->pending_aio;
-      ictx->pending_aio_cond.Signal();
-    }
+    async_op.finish_op();
 
     if (complete_cb) {
       complete_cb(rbd_comp, complete_arg);
index e1924d6ca47d6e9cfd4c4a4dc489fc122555a29a..6f6d74357aabb87a481cf99d79008bddddef03ca 100644 (file)
@@ -11,6 +11,7 @@
 #include "include/utime.h"
 #include "include/rbd/librbd.hpp"
 
+#include "librbd/AsyncOperation.h"
 #include "librbd/ImageCtx.h"
 #include "librbd/internal.h"
 
@@ -61,6 +62,8 @@ namespace librbd {
     char *read_buf;
     size_t read_buf_len;
 
+    AsyncOperation async_op;
+
     AioCompletion() : lock("AioCompletion::lock", true),
                      done(false), rval(0), complete_cb(NULL),
                      complete_arg(NULL), rbd_comp(NULL),
@@ -91,8 +94,7 @@ namespace librbd {
         aio_type = t;
         start_time = ceph_clock_now(ictx->cct);
 
-        Mutex::Locker l(ictx->aio_lock);
-        ++ictx->pending_aio;
+       async_op.start_op(*ictx);
       }
     }
 
diff --git a/src/librbd/AsyncOperation.cc b/src/librbd/AsyncOperation.cc
new file mode 100644 (file)
index 0000000..cdb1310
--- /dev/null
@@ -0,0 +1,44 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "librbd/AsyncOperation.h"
+#include "librbd/ImageCtx.h"
+#include "common/dout.h"
+#include "include/assert.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::AsyncOperation: "
+
+namespace librbd {
+
+void AsyncOperation::start_op(ImageCtx &image_ctx) {
+  assert(m_image_ctx == NULL);
+  m_image_ctx = &image_ctx;
+
+  ldout(m_image_ctx->cct, 20) << this << " " << __func__ << dendl; 
+  Mutex::Locker l(m_image_ctx->async_ops_lock);
+  m_image_ctx->async_ops.push_back(&m_xlist_item);
+}
+
+void AsyncOperation::finish_op() {
+  ldout(m_image_ctx->cct, 20) << this << " " << __func__ << dendl;
+  {
+    Mutex::Locker l(m_image_ctx->async_ops_lock);
+    assert(m_xlist_item.remove_myself());
+  }
+
+  while (!m_flush_contexts.empty()) {
+    Context *flush_ctx = m_flush_contexts.front();
+    m_flush_contexts.pop_front();
+
+    ldout(m_image_ctx->cct, 20) << "completed flush: " << flush_ctx << dendl;
+    flush_ctx->complete(0);
+  }
+}
+
+void AsyncOperation::add_flush_context(Context *on_finish) {
+  assert(m_image_ctx->async_ops_lock.is_locked());
+  m_flush_contexts.push_back(on_finish);
+} 
+
+} // namespace librbd
diff --git a/src/librbd/AsyncOperation.h b/src/librbd/AsyncOperation.h
new file mode 100644 (file)
index 0000000..2ca2aec
--- /dev/null
@@ -0,0 +1,44 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef LIBRBD_ASYNC_OPERATION_H
+#define LIBRBD_ASYNC_OPERATION_H
+
+#include "include/assert.h"
+#include "include/xlist.h"
+#include <list>
+
+class Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+class AsyncOperation {
+public:
+
+  AsyncOperation()
+    : m_image_ctx(NULL), m_xlist_item(this)
+  {
+  }
+
+  ~AsyncOperation()
+  {
+    assert(!m_xlist_item.is_on_list());
+  }
+
+  void start_op(ImageCtx &image_ctx);
+  void finish_op();
+
+  void add_flush_context(Context *on_finish);
+
+private:
+
+  ImageCtx *m_image_ctx;
+  xlist<AsyncOperation *>::item m_xlist_item;
+  std::list<Context *> m_flush_contexts;
+
+};
+
+} // namespace librbd
+
+#endif // LIBRBD_ASYNC_OPERATION_H
index fa4fe934ba8b66d2eeafa7f5bf74f67c6ad27628..49fd599cdb1a29edf8fc922ad105a30e118c1ead 100644 (file)
@@ -27,10 +27,12 @@ namespace librbd {
     : m_ictx(ictx), m_oid(oid), m_object_no(objectno),
       m_image_extents(image_extents), m_state(STATE_READ_FROM_PARENT)
   {
+    m_async_op.start_op(*m_ictx);
   }
 
   CopyupRequest::~CopyupRequest() {
     assert(m_pending_requests.empty());
+    m_async_op.finish_op();
   }
 
   ceph::bufferlist& CopyupRequest::get_copyup_data() {
@@ -164,10 +166,6 @@ namespace librbd {
       m_ictx->copyup_list.find(m_object_no);
     assert(it != m_ictx->copyup_list.end());
     m_ictx->copyup_list.erase(it);
-
-    if (m_ictx->copyup_list.empty()) {
-      m_ictx->copyup_list_cond.Signal();
-    }
   }
 
   bool CopyupRequest::send_object_map() {
index 6ec13071bbb9a078a1047e82ef07eebf7a61a68d..92714c2bc9a4426a6772b46cf5710a5cb4c2960d 100644 (file)
@@ -3,6 +3,7 @@
 #ifndef CEPH_LIBRBD_COPYUPREQUEST_H
 #define CEPH_LIBRBD_COPYUPREQUEST_H
 
+#include "librbd/AsyncOperation.h"
 #include "include/int_types.h"
 
 #include "common/Mutex.h"
@@ -54,6 +55,8 @@ namespace librbd {
     ceph::bufferlist m_copyup_data;
     vector<AioRequest *> m_pending_requests;
 
+    AsyncOperation m_async_op;
+
     bool complete_requests(int r);
 
     void complete(int r);
index 9882634108da43750cb912cd1bcf3f857a2e724c..116cb92aa8a1f6426310f717b6af9590b19dc7d5 100644 (file)
@@ -7,8 +7,8 @@
 #include "common/errno.h"
 #include "common/perf_counters.h"
 
+#include "librbd/AsyncOperation.h"
 #include "librbd/internal.h"
-
 #include "librbd/ImageCtx.h"
 #include "librbd/ImageWatcher.h"
 #include "librbd/ObjectMap.h"
@@ -48,9 +48,8 @@ namespace librbd {
       parent_lock("librbd::ImageCtx::parent_lock"),
       refresh_lock("librbd::ImageCtx::refresh_lock"),
       object_map_lock("librbd::ImageCtx::object_map_lock"),
-      aio_lock("librbd::ImageCtx::aio_lock"),
+      async_ops_lock("librbd::ImageCtx::async_ops_lock"),
       copyup_list_lock("librbd::ImageCtx::copyup_list_lock"),
-      copyup_list_cond(),
       extra_read_flags(0),
       old_format(true),
       order(0), size(0), features(0),
@@ -60,7 +59,7 @@ namespace librbd {
       object_cacher(NULL), writeback_handler(NULL), object_set(NULL),
       readahead(),
       total_bytes_read(0), copyup_finisher(NULL),
-      pending_aio(0), object_map(NULL)
+      object_map(NULL)
   {
     md_ctx.dup(p);
     data_ctx.dup(p);
@@ -612,6 +611,7 @@ namespace librbd {
   int ImageCtx::invalidate_cache() {
     if (!object_cacher)
       return 0;
+    flush_async_operations();
     cache_lock.Lock();
     object_cacher->release_set(object_set);
     cache_lock.Unlock();
@@ -623,7 +623,6 @@ namespace librbd {
     } else if (r) {
       lderr(cct) << "flush_cache returned " << r << dendl;
     }
-    wait_for_pending_aio();
     cache_lock.Lock();
     loff_t unclean = object_cacher->release_set(object_set);
     cache_lock.Unlock();
@@ -694,18 +693,21 @@ namespace librbd {
     return len;
   }
 
-  void ImageCtx::wait_for_pending_aio() {
-    Mutex::Locker l(aio_lock);
-    while (pending_aio > 0) {
-      pending_aio_cond.Wait(aio_lock);
-    }
+  void ImageCtx::flush_async_operations() {
+    C_SaferCond *ctx = new C_SaferCond();
+    flush_async_operations(ctx);
+    ctx->wait();
   }
 
-  void ImageCtx::wait_for_pending_copyup() {
-    Mutex::Locker l(copyup_list_lock);
-    while (!copyup_list.empty()) {
-      ldout(cct, 20) << __func__ << " waiting CopyupRequest to be completed" << dendl;
-      copyup_list_cond.Wait(copyup_list_lock);
+  void ImageCtx::flush_async_operations(Context *on_finish) {
+    Mutex::Locker l(async_ops_lock);
+    if (async_ops.empty()) {
+      on_finish->complete(0);
+      return;
     }
+
+    ldout(cct, 20) << "flush async operations: " << on_finish << " "
+                   << "count=" << async_ops.size() << dendl;
+    async_ops.back()->add_flush_context(on_finish);
   }
 }
index 13a5bfc2fffb5a32c67b6c89a452d73998b69de9..9b401163fc7e679eaf63c718fe8883d56d648070 100644 (file)
@@ -20,6 +20,7 @@
 #include "include/rbd/librbd.hpp"
 #include "include/rbd_types.h"
 #include "include/types.h"
+#include "include/xlist.h"
 #include "osdc/ObjectCacher.h"
 
 #include "cls/rbd/cls_rbd_client.h"
@@ -33,8 +34,9 @@ class PerfCounters;
 
 namespace librbd {
 
-  class ImageWatcher;
+  class AsyncOperation;
   class CopyupRequest;
+  class ImageWatcher;
   class ObjectMap;
 
   struct ImageCtx {
@@ -67,7 +69,7 @@ namespace librbd {
     /**
      * Lock ordering:
      * owner_lock, md_lock, cache_lock, snap_lock, parent_lock, refresh_lock,
-     * object_map_lock, aio_lock
+     * object_map_lock, async_op_lock
      */
     RWLock owner_lock; // protects exclusive lock leadership updates
     RWLock md_lock; // protects access to the mutable image metadata that
@@ -78,11 +80,9 @@ namespace librbd {
     RWLock parent_lock; // protects parent_md and parent
     Mutex refresh_lock; // protects refresh_seq and last_refresh
     RWLock object_map_lock; // protects object map updates
-    Mutex aio_lock; // protects pending_aio and pending_aio_cond
+    Mutex async_ops_lock; // protects async_ops
     Mutex copyup_list_lock; // protects copyup_waiting_list
 
-    Cond copyup_list_cond; // protected by copyup_waiting_list_lock
-
     unsigned extra_read_flags;
 
     bool old_format;
@@ -110,8 +110,7 @@ namespace librbd {
     Finisher *copyup_finisher;
     std::map<uint64_t, CopyupRequest*> copyup_list;
 
-    Cond pending_aio_cond;
-    uint64_t pending_aio;
+    xlist<AsyncOperation*> async_ops;
 
     ObjectMap *object_map;
 
@@ -182,8 +181,9 @@ namespace librbd {
                         librados::snap_t in_snap_id);
     uint64_t prune_parent_extents(vector<pair<uint64_t,uint64_t> >& objectx,
                                  uint64_t overlap);
-    void wait_for_pending_aio();
-    void wait_for_pending_copyup();
+
+    void flush_async_operations();
+    void flush_async_operations(Context *on_finish);
   };
 }
 
index a777f92246a7291a014dc2098fe117fc03888b8f..731f14dae408a933f25d60e164ac1fdfe5197215 100644 (file)
@@ -72,7 +72,7 @@ ImageWatcher::ImageWatcher(ImageCtx &image_ctx)
     m_async_request_lock("librbd::ImageWatcher::m_async_request_lock"),
     m_async_request_id(0),
     m_aio_request_lock("librbd::ImageWatcher::m_aio_request_lock"),
-    m_retrying_aio_requests(false), m_retry_aio_context(NULL)
+    m_retry_aio_context(NULL)
 {
   m_finisher->start();
   m_timer->init();
@@ -144,30 +144,6 @@ int ImageWatcher::unregister_watch() {
   return r;
 }
 
-bool ImageWatcher::has_pending_aio_operations() {
-  Mutex::Locker l(m_aio_request_lock);
-  return !m_aio_requests.empty();
-}
-
-void ImageWatcher::flush_aio_operations() {
-  C_SaferCond *ctx = new C_SaferCond();
-  flush_aio_operations(ctx);
-  ctx->wait();
-}
-
-void ImageWatcher::flush_aio_operations(Context *ctx) {
-  Mutex::Locker l(m_aio_request_lock);
-  if (!m_retrying_aio_requests && m_aio_requests.empty()) {
-    ctx->complete(0);
-    return;
-  }
-
-  ldout(m_image_ctx.cct, 20) << "pending flush: " << ctx << " "
-                            << "retrying=" << m_retrying_aio_requests << ", "
-                            << "count=" << m_aio_requests.size() << dendl;
-  m_aio_flush_contexts.push_back(ctx);
-}
-
 int ImageWatcher::try_lock() {
   assert(m_image_ctx.owner_lock.is_wlocked());
   assert(m_lock_owner_state == LOCK_OWNER_STATE_NOT_LOCKED);
@@ -567,9 +543,7 @@ void ImageWatcher::retry_aio_requests() {
   std::vector<AioRequest> lock_request_restarts;
   {
     Mutex::Locker l(m_aio_request_lock);
-    assert(!m_retrying_aio_requests);
     lock_request_restarts.swap(m_aio_requests);
-    m_retrying_aio_requests = true;
   }
 
   for (std::vector<AioRequest>::iterator iter = lock_request_restarts.begin();
@@ -580,7 +554,6 @@ void ImageWatcher::retry_aio_requests() {
   }
 
   Mutex::Locker l(m_aio_request_lock);
-  m_retrying_aio_requests = false;
   while (!m_aio_flush_contexts.empty()) {
     Context *flush_ctx = m_aio_flush_contexts.front();
     m_aio_flush_contexts.pop_front();
index 0261b1307701304efa793d36b55ce37064892503..343722aa883a7db86078e126bfdaa3cca26eb995 100644 (file)
@@ -56,10 +56,6 @@ namespace librbd {
     int register_watch();
     int unregister_watch();
 
-    bool has_pending_aio_operations();
-    void flush_aio_operations();
-    void flush_aio_operations(Context *ctx);
-
     int try_lock();
     int request_lock(const boost::function<int(AioCompletion*)>& restart_op,
                     AioCompletion* c);
@@ -176,7 +172,6 @@ namespace librbd {
     Mutex m_aio_request_lock;
     std::list<Context *> m_aio_flush_contexts;
     std::vector<AioRequest> m_aio_requests;
-    bool m_retrying_aio_requests;
     Context *m_retry_aio_context;
 
     std::string encode_lock_cookie() const;
index 2836c696da0365e9f78ef47728d250a4712f0c74..b94061edf167606747065a3b45edd525c3a96e7c 100644 (file)
@@ -3,6 +3,7 @@ librbd_internal_la_SOURCES = \
        librbd/AioRequest.cc \
        librbd/AsyncFlattenRequest.cc \
        librbd/AsyncObjectThrottle.cc \
+       librbd/AsyncOperation.cc \
        librbd/AsyncRequest.cc \
        librbd/AsyncResizeRequest.cc \
        librbd/AsyncTrimRequest.cc \
@@ -44,6 +45,7 @@ noinst_HEADERS += \
        librbd/AioRequest.h \
        librbd/AsyncFlattenRequest.h \
        librbd/AsyncObjectThrottle.h \
+       librbd/AsyncOperation.h \
        librbd/AsyncRequest.h \
        librbd/AsyncResizeRequest.h \
        librbd/AsyncTrimRequest.h \
index a6df0b9d94f5182a5509dcf79c4eb7c581875d0e..2049553cbfa509ecba91e2eb58f225a46af723fd 100644 (file)
@@ -1665,7 +1665,7 @@ reprotect_and_return_err:
       RWLock::RLocker l(ictx->md_lock);
       original_size = ictx->size;
       if (size < ictx->size) {
-       ictx->wait_for_pending_copyup();
+       ictx->flush_async_operations();
        if (ictx->object_cacher) {
          // need to invalidate since we're deleting objects, and
          // ObjectCacher doesn't track non-existent objects
@@ -2299,10 +2299,7 @@ reprotect_and_return_err:
     // ignore return value, since we may be set to a non-existent
     // snapshot and the user is trying to fix that
     ictx_check(ictx);
-    ictx->wait_for_pending_copyup();
-    if (ictx->image_watcher != NULL) {
-      ictx->image_watcher->flush_aio_operations();
-    }
+    ictx->flush_async_operations();
     if (ictx->object_cacher) {
       // complete pending writes before we're set to a snapshot and
       // get -EROFS for writes
@@ -2369,9 +2366,6 @@ reprotect_and_return_err:
     ldout(ictx->cct, 20) << "close_image " << ictx << dendl;
 
     ictx->readahead.wait_for_pending();
-    if (ictx->image_watcher != NULL) {
-      ictx->image_watcher->flush_aio_operations();
-    }
     if (ictx->object_cacher) {
       ictx->shutdown_cache(); // implicitly flushes
     } else {
@@ -2382,7 +2376,6 @@ reprotect_and_return_err:
       ictx->copyup_finisher->wait_for_empty();
       ictx->copyup_finisher->stop();
     }
-    ictx->wait_for_pending_copyup();
 
     if (ictx->parent) {
       close_image(ictx->parent);
@@ -3099,20 +3092,15 @@ reprotect_and_return_err:
       return r;
     }
 
-    if (ictx->image_watcher != NULL) {
-      ictx->image_watcher->flush_aio_operations();
-    }
     ictx->user_flushed();
 
     c->get();
-    c->init_time(ictx, AIO_TYPE_FLUSH);
 
-    if (ictx->image_watcher != NULL) {
-      C_AioWrite *flush_ctx = new C_AioWrite(cct, c);
-      c->add_request();
-      ictx->image_watcher->flush_aio_operations(flush_ctx);
-    }
+    C_AioWrite *flush_ctx = new C_AioWrite(cct, c);
+    c->add_request();
+    ictx->flush_async_operations(flush_ctx);
 
+    c->init_time(ictx, AIO_TYPE_FLUSH);
     C_AioWrite *req_comp = new C_AioWrite(cct, c);
     c->add_request();
     if (ictx->object_cacher) {
@@ -3148,10 +3136,6 @@ reprotect_and_return_err:
 
   int _flush(ImageCtx *ictx)
   {
-    if (ictx->image_watcher != NULL) {
-      ictx->image_watcher->flush_aio_operations();
-    }
-
     CephContext *cct = ictx->cct;
     int r;
     // flush any outstanding writes
@@ -3159,7 +3143,7 @@ reprotect_and_return_err:
       r = ictx->flush_cache();
     } else {
       r = ictx->data_ctx.aio_flush();
-      ictx->wait_for_pending_aio();
+      ictx->flush_async_operations();
     }
 
     if (r)
@@ -3178,9 +3162,7 @@ reprotect_and_return_err:
       return r;
     }
 
-    if (ictx->image_watcher != NULL) {
-      ictx->image_watcher->flush_aio_operations();
-    }
+    ictx->flush_async_operations();
 
     RWLock::WLocker l(ictx->md_lock);
     r = ictx->invalidate_cache();
index f74e83ec769e853ec7fc880bcae4ffcfc93ef3be..2831db5f86aad88aa9a8fefb3395654e4204ded6 100644 (file)
@@ -1,6 +1,7 @@
 // -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 // vim: ts=8 sw=2 smarttab
 #include "test/librbd/test_fixture.h"
+#include "librbd/AioCompletion.h"
 #include "librbd/ImageWatcher.h"
 #include "librbd/internal.h"
 #include <boost/scope_exit.hpp>
@@ -260,8 +261,7 @@ TEST_F(TestInternal, AioWriteRequestsLock) {
   bool is_owner;
   ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
   ASSERT_FALSE(is_owner);
-
-  ASSERT_TRUE(ictx->image_watcher->has_pending_aio_operations());
+  ASSERT_FALSE(c->is_complete());
 }
 
 TEST_F(TestInternal, AioDiscardRequestsLock) {
@@ -279,6 +279,5 @@ TEST_F(TestInternal, AioDiscardRequestsLock) {
   bool is_owner;
   ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
   ASSERT_FALSE(is_owner);
-
-  ASSERT_TRUE(ictx->image_watcher->has_pending_aio_operations());
+  ASSERT_FALSE(c->is_complete());
 }