]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: make "snapshot create" notification be "async" 34243/head
authorMykola Golub <mgolub@suse.com>
Wed, 29 Apr 2020 17:49:17 +0000 (18:49 +0100)
committerMykola Golub <mgolub@suse.com>
Tue, 5 May 2020 16:02:12 +0000 (17:02 +0100)
After adding quiesce/unquiesce callbacks the "snapshot create" request
may run long time.

Signed-off-by: Mykola Golub <mgolub@suse.com>
17 files changed:
src/librbd/ImageWatcher.cc
src/librbd/ImageWatcher.h
src/librbd/Operations.cc
src/librbd/Operations.h
src/librbd/WatchNotifyTypes.cc
src/librbd/WatchNotifyTypes.h
src/librbd/deep_copy/SnapshotCreateRequest.cc
src/librbd/deep_copy/SnapshotCreateRequest.h
src/librbd/journal/Replay.cc
src/librbd/operation/SnapshotCreateRequest.cc
src/librbd/operation/SnapshotCreateRequest.h
src/test/librbd/deep_copy/test_mock_SnapshotCreateRequest.cc
src/test/librbd/journal/test_mock_Replay.cc
src/test/librbd/mock/MockImageWatcher.h
src/test/librbd/mock/MockOperations.h
src/test/librbd/operation/test_mock_SnapshotCreateRequest.cc
src/test/librbd/test_ImageWatcher.cc

index 08bc01e6d7bf745cbeed22c8d04902b6b73d8a19..65c82a8ac4616711853858d7b56cc499547fd130 100644 (file)
@@ -193,15 +193,21 @@ void ImageWatcher<I>::notify_resize(uint64_t request_id, uint64_t size,
 }
 
 template <typename I>
-void ImageWatcher<I>::notify_snap_create(const cls::rbd::SnapshotNamespace &snap_namespace,
+void ImageWatcher<I>::notify_snap_create(uint64_t request_id,
+                                         const cls::rbd::SnapshotNamespace &snap_namespace,
                                         const std::string &snap_name,
+                                         ProgressContext &prog_ctx,
                                          Context *on_finish) {
   ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock));
   ceph_assert(m_image_ctx.exclusive_lock &&
               !m_image_ctx.exclusive_lock->is_lock_owner());
 
-  notify_lock_owner(new SnapCreatePayload(snap_namespace, snap_name),
-                    on_finish);
+  AsyncRequestId async_request_id(get_client_id(), request_id);
+
+  notify_async_request(async_request_id,
+                       new SnapCreatePayload(async_request_id, snap_namespace,
+                                             snap_name),
+                       prog_ctx, on_finish);
 }
 
 template <typename I>
@@ -334,7 +340,9 @@ void ImageWatcher<I>::notify_header_update(librados::IoCtx &io_ctx,
 }
 
 template <typename I>
-void ImageWatcher<I>::notify_quiesce(uint64_t request_id, Context *on_finish) {
+void ImageWatcher<I>::notify_quiesce(uint64_t request_id,
+                                     ProgressContext &prog_ctx,
+                                     Context *on_finish) {
   ldout(m_image_ctx.cct, 10) << this << " " << __func__ << ": request_id="
                              << request_id << dendl;
 
@@ -343,24 +351,29 @@ void ImageWatcher<I>::notify_quiesce(uint64_t request_id, Context *on_finish) {
   auto attempts = m_image_ctx.config.template get_val<uint64_t>(
     "rbd_quiesce_notification_attempts");
 
-  notify_quiesce(async_request_id, attempts, on_finish);
+  notify_quiesce(async_request_id, attempts, prog_ctx, on_finish);
 }
 
 template <typename I>
 void ImageWatcher<I>::notify_quiesce(const AsyncRequestId &async_request_id,
-                                     size_t attempts, Context *on_finish) {
+                                     size_t attempts, ProgressContext &prog_ctx,
+                                     Context *on_finish) {
   ldout(m_image_ctx.cct, 10) << this << " " << __func__ << ": async_request_id="
                              << async_request_id << " attempts=" << attempts
                              << dendl;
 
   ceph_assert(attempts > 0);
   auto on_notify = new LambdaContext(
-    [this, async_request_id, on_finish, attempts=attempts-1](int r) {
+    [this, async_request_id, &prog_ctx, on_finish, attempts=attempts-1](int r) {
+      auto total_attempts = m_image_ctx.config.template get_val<uint64_t>(
+        "rbd_quiesce_notification_attempts");
+      prog_ctx.update_progress(total_attempts - attempts, total_attempts);
+
       if (r == -ETIMEDOUT) {
         ldout(m_image_ctx.cct, 10) << this << " " << __func__ << ": async_request_id="
                                    << async_request_id << " timed out" << dendl;
         if (attempts > 0) {
-          notify_quiesce(async_request_id, attempts, on_finish);
+          notify_quiesce(async_request_id, attempts, prog_ctx, on_finish);
           return;
         }
       }
@@ -922,14 +935,36 @@ bool ImageWatcher<I>::handle_payload(const SnapCreatePayload &payload,
     }
 
     if (m_image_ctx.exclusive_lock->accept_request(request_type, &r)) {
-      ldout(m_image_ctx.cct, 10) << this << " remote snap_create request: "
-                                << payload.snap_name << dendl;
+      bool new_request;
+      Context *ctx;
+      ProgressContext *prog_ctx;
+      bool complete;
+      if (payload.async_request_id) {
+        r = prepare_async_request(payload.async_request_id, &new_request,
+                                  &ctx, &prog_ctx);
+        encode(ResponseMessage(r), ack_ctx->out);
+        complete = true;
+      } else {
+        new_request = true;
+        prog_ctx = new NoOpProgressContext();
+        ctx = new LambdaContext(
+          [prog_ctx, on_finish=new C_ResponseMessage(ack_ctx)](int r) {
+            delete prog_ctx;
+            on_finish->complete(r);
+          });
+        complete = false;
+      }
+      if (r == 0 && new_request) {
+        ldout(m_image_ctx.cct, 10) << this << " remote snap_create request: "
+                                  << payload.async_request_id << " "
+                                   << payload.snap_namespace << " "
+                                   << payload.snap_name << dendl;
 
-      m_image_ctx.operations->execute_snap_create(payload.snap_namespace,
-                                                 payload.snap_name,
-                                                  new C_ResponseMessage(ack_ctx),
-                                                  0, false);
-      return false;
+        m_image_ctx.operations->execute_snap_create(payload.snap_namespace,
+                                                    payload.snap_name,
+                                                    ctx, 0, false, *prog_ctx);
+      }
+      return complete;
     } else if (r < 0) {
       encode(ResponseMessage(r), ack_ctx->out);
     }
index 203b6763cf607da44d4255880f7eabc9c46d83ee..130ba63541368e532d99112ac18facf62a25edfd 100644 (file)
@@ -34,8 +34,10 @@ public:
                       Context *on_finish);
   void notify_resize(uint64_t request_id, uint64_t size, bool allow_shrink,
                      ProgressContext &prog_ctx, Context *on_finish);
-  void notify_snap_create(const cls::rbd::SnapshotNamespace &snap_namespace,
+  void notify_snap_create(uint64_t request_id,
+                          const cls::rbd::SnapshotNamespace &snap_namespace,
                          const std::string &snap_name,
+                          ProgressContext &prog_ctx,
                          Context *on_finish);
   void notify_snap_rename(const snapid_t &src_snap_id,
                           const std::string &dst_snap_name,
@@ -70,7 +72,8 @@ public:
   static void notify_header_update(librados::IoCtx &io_ctx,
                                    const std::string &oid);
 
-  void notify_quiesce(uint64_t request_id, Context *on_finish);
+  void notify_quiesce(uint64_t request_id, ProgressContext &prog_ctx,
+                      Context *on_finish);
   void notify_unquiesce(uint64_t request_id, Context *on_finish);
 
 private:
@@ -206,7 +209,8 @@ private:
   Context *prepare_unquiesce_request(const watch_notify::AsyncRequestId &request);
 
   void notify_quiesce(const watch_notify::AsyncRequestId &async_request_id,
-                      size_t attempts, Context *on_finish);
+                      size_t attempts, ProgressContext &prog_ctx,
+                      Context *on_finish);
 
   bool handle_payload(const watch_notify::HeaderUpdatePayload& payload,
                       C_NotifyAck *ctx);
index fce2d1d6c2b883471e9fd4ce3a46ba60d0ee2262..3e31c71f8892018352ccec5babfbbfdafb81b2f3 100644 (file)
@@ -730,13 +730,22 @@ void Operations<I>::snap_create(const cls::rbd::SnapshotNamespace &snap_namespac
   }
   m_image_ctx.image_lock.unlock_shared();
 
+  auto prog_ctx = new NoOpProgressContext();
+  on_finish = new LambdaContext(
+    [prog_ctx, on_finish](int r) {
+      delete prog_ctx;
+      on_finish->complete(r);
+    });
+
+  uint64_t request_id = ++m_async_request_seq;
   C_InvokeAsyncRequest<I> *req = new C_InvokeAsyncRequest<I>(
     m_image_ctx, "snap_create", exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL,
     true,
     boost::bind(&Operations<I>::execute_snap_create, this, snap_namespace, snap_name,
-               _1, 0, false),
+               _1, 0, false, boost::ref(*prog_ctx)),
     boost::bind(&ImageWatcher<I>::notify_snap_create, m_image_ctx.image_watcher,
-                snap_namespace, snap_name, _1),
+                request_id, snap_namespace, snap_name, boost::ref(*prog_ctx),
+                _1),
     {-EEXIST}, on_finish);
   req->send();
 }
@@ -746,7 +755,8 @@ void Operations<I>::execute_snap_create(const cls::rbd::SnapshotNamespace &snap_
                                        const std::string &snap_name,
                                         Context *on_finish,
                                         uint64_t journal_op_tid,
-                                        bool skip_object_map) {
+                                        bool skip_object_map,
+                                        ProgressContext &prog_ctx) {
   ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock));
   ceph_assert(m_image_ctx.exclusive_lock == nullptr ||
               m_image_ctx.exclusive_lock->is_lock_owner());
@@ -772,7 +782,8 @@ void Operations<I>::execute_snap_create(const cls::rbd::SnapshotNamespace &snap_
   operation::SnapshotCreateRequest<I> *req =
     new operation::SnapshotCreateRequest<I>(
       m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish),
-      snap_namespace, snap_name, journal_op_tid, request_id, skip_object_map);
+      snap_namespace, snap_name, journal_op_tid, request_id, skip_object_map,
+      prog_ctx);
   req->send();
 }
 
index 253c9f926733313c9d92200be0452c36765846ff..7c46dedb3aff65022a75e92f137bd7358b1aa2f7 100644 (file)
@@ -52,7 +52,8 @@ public:
   void execute_snap_create(const cls::rbd::SnapshotNamespace &snap_namespace,
                           const std::string &snap_name,
                           Context *on_finish,
-                           uint64_t journal_op_tid, bool skip_object_map);
+                           uint64_t journal_op_tid, bool skip_object_map,
+                           ProgressContext &prog_ctx);
 
   int snap_rollback(const cls::rbd::SnapshotNamespace& snap_namespace,
                    const std::string& snap_name,
index 39599541b61db52e47f588be14d395df290c9ac4..6712f899a2516d5efea7c9c755af27155a590134 100644 (file)
@@ -193,7 +193,9 @@ void SnapPayloadBase::dump(Formatter *f) const {
 }
 
 void SnapCreatePayload::encode(bufferlist &bl) const {
+  using ceph::encode;
   SnapPayloadBase::encode(bl);
+  encode(async_request_id, bl);
 }
 
 void SnapCreatePayload::decode(__u8 version, bufferlist::const_iterator &iter) {
@@ -202,9 +204,15 @@ void SnapCreatePayload::decode(__u8 version, bufferlist::const_iterator &iter) {
   if (version == 5) {
     decode(snap_namespace, iter);
   }
+  if (version >= 7) {
+    decode(async_request_id, iter);
+  }
 }
 
 void SnapCreatePayload::dump(Formatter *f) const {
+  f->open_object_section("async_request_id");
+  async_request_id.dump(f);
+  f->close_section();
   SnapPayloadBase::dump(f);
 }
 
@@ -288,7 +296,7 @@ bool NotifyMessage::check_for_refresh() const {
 }
 
 void NotifyMessage::encode(bufferlist& bl) const {
-  ENCODE_START(6, 1, bl);
+  ENCODE_START(7, 1, bl);
   encode(static_cast<uint32_t>(payload->get_notify_op()), bl);
   payload->encode(bl);
   ENCODE_FINISH(bl);
@@ -385,7 +393,8 @@ void NotifyMessage::generate_test_instances(std::list<NotifyMessage *> &o) {
   o.push_back(new NotifyMessage(new AsyncCompletePayload(AsyncRequestId(ClientId(0, 1), 2), 3)));
   o.push_back(new NotifyMessage(new FlattenPayload(AsyncRequestId(ClientId(0, 1), 2))));
   o.push_back(new NotifyMessage(new ResizePayload(123, true, AsyncRequestId(ClientId(0, 1), 2))));
-  o.push_back(new NotifyMessage(new SnapCreatePayload(cls::rbd::UserSnapshotNamespace(),
+  o.push_back(new NotifyMessage(new SnapCreatePayload(AsyncRequestId(ClientId(0, 1), 2),
+                                                      cls::rbd::UserSnapshotNamespace(),
                                                       "foo")));
   o.push_back(new NotifyMessage(new SnapRemovePayload(cls::rbd::UserSnapshotNamespace(), "foo")));
   o.push_back(new NotifyMessage(new SnapProtectPayload(cls::rbd::UserSnapshotNamespace(), "foo")));
index c5fc125774391f5fa88118b6cc37970a0b4529e4..e1ac5e1611f4871feaf0d4d59682aca9f920b19b 100644 (file)
@@ -47,6 +47,9 @@ struct AsyncRequestId {
   inline bool operator!=(const AsyncRequestId &rhs) const {
     return (client_id != rhs.client_id || request_id != rhs.request_id);
   }
+  inline operator bool() const {
+    return (*this != AsyncRequestId());
+  }
 };
 
 enum NotifyOp {
@@ -258,10 +261,14 @@ protected:
 };
 
 struct SnapCreatePayload : public SnapPayloadBase {
+  AsyncRequestId async_request_id;
+
   SnapCreatePayload() {}
-  SnapCreatePayload(const cls::rbd::SnapshotNamespace &_snap_namespace,
+  SnapCreatePayload(const AsyncRequestId &id,
+                    const cls::rbd::SnapshotNamespace &snap_namespace,
                    const std::string &name)
-    : SnapPayloadBase(_snap_namespace, name) {}
+    : SnapPayloadBase(snap_namespace, name), async_request_id(id) {
+  }
 
   NotifyOp get_notify_op() const override {
     return NOTIFY_OP_SNAP_CREATE;
index 23ebb1c666d9e665f1a6d20ba3a6d5f1400b7b07..2674da3dd31a88a95afca5bb51dda5affcd76440 100644 (file)
@@ -82,9 +82,8 @@ void SnapshotCreateRequest<I>::send_create_snap() {
     });
   std::shared_lock owner_locker{m_dst_image_ctx->owner_lock};
   m_dst_image_ctx->operations->execute_snap_create(m_snap_namespace,
-                                                   m_snap_name.c_str(),
-                                                   ctx,
-                                                   0U, true);
+                                                   m_snap_name.c_str(), ctx, 0U,
+                                                   true, m_prog_ctx);
 }
 
 template <typename I>
index 8c228063a24f396dbef5b51c57843b3f4baa6642..41f7f54e421e02d3dedd81f279b0867d1bc3c0a3 100644 (file)
@@ -9,6 +9,8 @@
 #include "common/snap_types.h"
 #include "librbd/ImageCtx.h"
 #include "librbd/Types.h"
+#include "librbd/internal.h"
+
 #include <map>
 #include <set>
 #include <string>
@@ -72,6 +74,7 @@ private:
   Context *m_on_finish;
 
   CephContext *m_cct;
+  NoOpProgressContext m_prog_ctx;
 
   void send_set_head();
   void handle_set_head(int r);
index 1652efb653514f0283c9130d7bba60ed713e4e18..f5b30e57bb5f1a79e2b57adc3b41835984ea7cad 100644 (file)
@@ -42,7 +42,8 @@ struct ExecuteOp : public Context {
     image_ctx.operations->execute_snap_create(event.snap_namespace,
                                              event.snap_name,
                                               on_op_complete,
-                                              event.op_tid, false);
+                                              event.op_tid, false,
+                                              no_op_progress_callback);
   }
 
   void execute(const journal::SnapRemoveEvent &_) {
index a8649153a1db28851430f2e3115167764130b828..b16cdf24ffb7def8c7bc593eca2f66ba956b07d5 100644 (file)
@@ -31,10 +31,12 @@ SnapshotCreateRequest<I>::SnapshotCreateRequest(I &image_ctx,
                                                 const std::string &snap_name,
                                                 uint64_t journal_op_tid,
                                                 uint64_t request_id,
-                                                bool skip_object_map)
+                                                bool skip_object_map,
+                                                ProgressContext &prog_ctx)
   : Request<I>(image_ctx, on_finish, journal_op_tid),
     m_snap_namespace(snap_namespace), m_snap_name(snap_name),
-    m_request_id(request_id), m_skip_object_map(skip_object_map) {
+    m_request_id(request_id), m_skip_object_map(skip_object_map),
+    m_prog_ctx(prog_ctx) {
 }
 
 template <typename I>
@@ -59,7 +61,7 @@ void SnapshotCreateRequest<I>::send_notify_quiesce() {
   ldout(cct, 5) << this << " " << __func__ << dendl;
 
   image_ctx.image_watcher->notify_quiesce(
-    m_request_id, create_async_context_callback(
+    m_request_id, m_prog_ctx, create_async_context_callback(
       image_ctx, create_context_callback<SnapshotCreateRequest<I>,
       &SnapshotCreateRequest<I>::handle_notify_quiesce>(this)));
 }
index ea6d67449c9c63f1dc0e1ff54c4e9d7c6716984e..c4840d38995e5ac45c7c94e7bea6114b32e5083b 100644 (file)
@@ -14,6 +14,7 @@ class Context;
 namespace librbd {
 
 class ImageCtx;
+class ProgressContext;
 
 namespace operation {
 
@@ -70,7 +71,8 @@ public:
   SnapshotCreateRequest(ImageCtxT &image_ctx, Context *on_finish,
                         const cls::rbd::SnapshotNamespace &snap_namespace,
                         const std::string &snap_name, uint64_t journal_op_tid,
-                        uint64_t request_id, bool skip_object_map);
+                        uint64_t request_id, bool skip_object_map,
+                        ProgressContext &prog_ctx);
 
 protected:
   void send_op() override;
@@ -89,6 +91,7 @@ private:
   std::string m_snap_name;
   uint64_t m_request_id;
   bool m_skip_object_map;
+  ProgressContext &m_prog_ctx;
 
   int m_ret_val = 0;
 
index 27435b72a254cbd3d35f2b83f151fd6115cdc686..17ed8b71cf144ef68e8efd3221c40f70e49c97f6 100644 (file)
@@ -107,7 +107,8 @@ public:
 
   void expect_snap_create(librbd::MockTestImageCtx &mock_image_ctx,
                           const std::string &snap_name, uint64_t snap_id, int r) {
-    EXPECT_CALL(*mock_image_ctx.operations, execute_snap_create(_, StrEq(snap_name), _, 0, true))
+    EXPECT_CALL(*mock_image_ctx.operations,
+                execute_snap_create(_, StrEq(snap_name), _, 0, true, _))
                   .WillOnce(DoAll(InvokeWithoutArgs([&mock_image_ctx, snap_id, snap_name]() {
                                     inject_snap(mock_image_ctx, snap_id, snap_name);
                                   }),
index 452011146b087adfbeafa309b144b4ab5ab267e3..37a5ce7536fb9c632be9357e93c49b1d97dc083c 100644 (file)
@@ -219,7 +219,7 @@ public:
                           Context **on_finish, const char *snap_name,
                           uint64_t op_tid) {
     EXPECT_CALL(*mock_image_ctx.operations, execute_snap_create(_, StrEq(snap_name), _,
-                                                                op_tid, false))
+                                                                op_tid, false, _))
                   .WillOnce(DoAll(SaveArg<2>(on_finish),
                                   NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
   }
index 8044c9e97653f158d22cf1763668011ed39f67b1..0cca579b297a5b703238af885471d21fd3763a6a 100644 (file)
@@ -10,6 +10,8 @@ class Context;
 
 namespace librbd {
 
+class ProgressContext;
+
 struct MockImageWatcher {
   MOCK_METHOD0(is_registered, bool());
   MOCK_METHOD0(is_unregistered, bool());
@@ -22,8 +24,8 @@ struct MockImageWatcher {
   MOCK_METHOD0(notify_acquired_lock, void());
   MOCK_METHOD0(notify_released_lock, void());
   MOCK_METHOD0(notify_request_lock, void());
-  
-  MOCK_METHOD2(notify_quiesce, void(uint64_t, Context *));
+
+  MOCK_METHOD3(notify_quiesce, void(uint64_t, ProgressContext &, Context *));
   MOCK_METHOD2(notify_unquiesce, void(uint64_t, Context *));
 };
 
index 49876d197c5b39b304cb18827d6658dc5dfeb8c2..ca30e55defe4c8cf7c74f178874ef713491eae80 100644 (file)
@@ -28,11 +28,12 @@ struct MockOperations {
   MOCK_METHOD3(snap_create, void(const cls::rbd::SnapshotNamespace &snapshot_namespace,
                                 const std::string &snap_name,
                                  Context *on_finish));
-  MOCK_METHOD5(execute_snap_create, void(const cls::rbd::SnapshotNamespace &snapshot_namespace,
+  MOCK_METHOD6(execute_snap_create, void(const cls::rbd::SnapshotNamespace &snapshot_namespace,
                                         const std::string &snap_name,
                                          Context *on_finish,
                                          uint64_t journal_op_tid,
-                                         bool skip_object_map));
+                                         bool skip_object_map,
+                                         ProgressContext &prog_ctx));
   MOCK_METHOD3(snap_remove, void(const cls::rbd::SnapshotNamespace &snap_namespace,
                                 const std::string &snap_name,
                                  Context *on_finish));
index e32eda54bc675464d2b93feab961635b6db95c4c..905199d9e72954d28d9f3a9907ff4f56dcf2425a 100644 (file)
@@ -63,8 +63,8 @@ public:
   typedef mirror::snapshot::SetImageStateRequest<MockImageCtx> MockSetImageStateRequest;
 
   void expect_notify_quiesce(MockImageCtx &mock_image_ctx, int r) {
-    EXPECT_CALL(*mock_image_ctx.image_watcher, notify_quiesce(_, _))
-      .WillOnce(WithArg<1>(
+    EXPECT_CALL(*mock_image_ctx.image_watcher, notify_quiesce(_, _, _))
+      .WillOnce(WithArg<2>(
                   CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
   }
 
@@ -184,9 +184,10 @@ TEST_F(TestMockOperationSnapshotCreateRequest, Success) {
   expect_notify_unquiesce(mock_image_ctx, -EINVAL);
 
   C_SaferCond cond_ctx;
+  librbd::NoOpProgressContext prog_ctx;
   MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
     mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
-      "snap1", 0, 0, false);
+    "snap1", 0, 0, false, prog_ctx);
   {
     std::shared_lock owner_locker{mock_image_ctx.owner_lock};
     req->send();
@@ -206,9 +207,10 @@ TEST_F(TestMockOperationSnapshotCreateRequest, NotifyQuiesceError) {
   expect_notify_quiesce(mock_image_ctx, -EINVAL);
 
   C_SaferCond cond_ctx;
+  librbd::NoOpProgressContext prog_ctx;
   MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
     mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
-    "snap1", 0, 0, false);
+    "snap1", 0, 0, false, prog_ctx);
   {
     std::shared_lock owner_locker{mock_image_ctx.owner_lock};
     req->send();
@@ -238,9 +240,10 @@ TEST_F(TestMockOperationSnapshotCreateRequest, AllocateSnapIdError) {
   expect_notify_unquiesce(mock_image_ctx, 0);
 
   C_SaferCond cond_ctx;
+  librbd::NoOpProgressContext prog_ctx;
   MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
     mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
-    "snap1", 0, 0, false);
+    "snap1", 0, 0, false, prog_ctx);
   {
     std::shared_lock owner_locker{mock_image_ctx.owner_lock};
     req->send();
@@ -279,9 +282,10 @@ TEST_F(TestMockOperationSnapshotCreateRequest, CreateSnapStale) {
   expect_notify_unquiesce(mock_image_ctx, 0);
 
   C_SaferCond cond_ctx;
+  librbd::NoOpProgressContext prog_ctx;
   MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
     mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
-    "snap1", 0, 0, false);
+    "snap1", 0, 0, false, prog_ctx);
   {
     std::shared_lock owner_locker{mock_image_ctx.owner_lock};
     req->send();
@@ -312,9 +316,10 @@ TEST_F(TestMockOperationSnapshotCreateRequest, CreateSnapError) {
   expect_notify_unquiesce(mock_image_ctx, 0);
 
   C_SaferCond cond_ctx;
+  librbd::NoOpProgressContext prog_ctx;
   MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
     mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
-    "snap1", 0, 0, false);
+    "snap1", 0, 0, false, prog_ctx);
   {
     std::shared_lock owner_locker{mock_image_ctx.owner_lock};
     req->send();
@@ -345,9 +350,10 @@ TEST_F(TestMockOperationSnapshotCreateRequest, ReleaseSnapIdError) {
   expect_notify_unquiesce(mock_image_ctx, 0);
 
   C_SaferCond cond_ctx;
+  librbd::NoOpProgressContext prog_ctx;
   MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
     mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
-    "snap1", 0, 0, false);
+    "snap1", 0, 0, false, prog_ctx);
   {
     std::shared_lock owner_locker{mock_image_ctx.owner_lock};
     req->send();
@@ -384,9 +390,10 @@ TEST_F(TestMockOperationSnapshotCreateRequest, SkipObjectMap) {
   expect_notify_unquiesce(mock_image_ctx, 0);
 
   C_SaferCond cond_ctx;
+  librbd::NoOpProgressContext prog_ctx;
   MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
     mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
-    "snap1", 0, 0, true);
+    "snap1", 0, 0, true, prog_ctx);
   {
     std::shared_lock owner_locker{mock_image_ctx.owner_lock};
     req->send();
@@ -428,11 +435,12 @@ TEST_F(TestMockOperationSnapshotCreateRequest, SetImageState) {
   expect_notify_unquiesce(mock_image_ctx, 0);
 
   C_SaferCond cond_ctx;
+  librbd::NoOpProgressContext prog_ctx;
   MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
     mock_image_ctx, &cond_ctx,
     cls::rbd::MirrorSnapshotNamespace{
       cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {}, "", CEPH_NOSNAP},
-    "snap1", 0, 0, false);
+    "snap1", 0, 0, false, prog_ctx);
   {
     std::shared_lock owner_locker{mock_image_ctx.owner_lock};
     req->send();
index 5d4824aefa09a951df510a9b654ed42322ec4b8c..96fdafef2e344310ec928949fdd71bbbcf50ad42 100644 (file)
@@ -154,7 +154,7 @@ public:
 
     bufferlist payload = m_notify_payloads[op];
     auto iter = payload.cbegin();
-    
+
     switch (op) {
     case NOTIFY_OP_FLATTEN:
       {
@@ -170,6 +170,13 @@ public:
         *id = payload.async_request_id;
       }
       return true;
+    case NOTIFY_OP_SNAP_CREATE:
+      {
+        SnapCreatePayload payload;
+        payload.decode(7, iter);
+        *id = payload.async_request_id;
+      }
+      return true;
     case NOTIFY_OP_REBUILD_OBJECT_MAP:
       {
         RebuildObjectMapPayload payload;
@@ -275,6 +282,23 @@ struct ResizeTask {
   }
 };
 
+struct SnapCreateTask {
+  librbd::ImageCtx *ictx;
+  ProgressContext *progress_context;
+  int result;
+
+  SnapCreateTask(librbd::ImageCtx *ictx_, ProgressContext *ctx)
+    : ictx(ictx_), progress_context(ctx), result(0) {}
+
+  void operator()() {
+    std::shared_lock l{ictx->owner_lock};
+    C_SaferCond ctx;
+    ictx->image_watcher->notify_snap_create(0, cls::rbd::UserSnapshotNamespace(),
+                                            "snap", *progress_context, &ctx);
+    ASSERT_EQ(0, ctx.wait());
+  }
+};
+
 struct RebuildObjectMapTask {
   librbd::ImageCtx *ictx;
   ProgressContext *progress_context;
@@ -424,15 +448,27 @@ TEST_F(TestImageWatcher, NotifySnapCreate) {
 
   m_notify_acks = {{NOTIFY_OP_SNAP_CREATE, create_response_message(0)}};
 
-  std::shared_lock l{ictx->owner_lock};
-  C_SaferCond notify_ctx;
-  ictx->image_watcher->notify_snap_create(cls::rbd::UserSnapshotNamespace(),
-       "snap", &notify_ctx);
-  ASSERT_EQ(0, notify_ctx.wait());
+  ProgressContext progress_context;
+  SnapCreateTask snap_create_task(ictx, &progress_context);
+  boost::thread thread(boost::ref(snap_create_task));
+
+  ASSERT_TRUE(wait_for_notifies(*ictx));
 
   NotifyOps expected_notify_ops;
   expected_notify_ops += NOTIFY_OP_SNAP_CREATE;
   ASSERT_EQ(expected_notify_ops, m_notifies);
+
+  AsyncRequestId async_request_id;
+  ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_SNAP_CREATE,
+                                       &async_request_id));
+
+  ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 1, 10));
+  ASSERT_TRUE(progress_context.wait(ictx, 1, 10));
+
+  ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+  ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+  ASSERT_EQ(0, snap_create_task.result);
 }
 
 TEST_F(TestImageWatcher, NotifySnapCreateError) {
@@ -449,8 +485,9 @@ TEST_F(TestImageWatcher, NotifySnapCreateError) {
 
   std::shared_lock l{ictx->owner_lock};
   C_SaferCond notify_ctx;
-  ictx->image_watcher->notify_snap_create(cls::rbd::UserSnapshotNamespace(),
-       "snap", &notify_ctx);
+  librbd::NoOpProgressContext prog_ctx;
+  ictx->image_watcher->notify_snap_create(0, cls::rbd::UserSnapshotNamespace(),
+       "snap", prog_ctx, &notify_ctx);
   ASSERT_EQ(-EEXIST, notify_ctx.wait());
 
   NotifyOps expected_notify_ops;