]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: do not invalidate object map if update races with copyup
authorJason Dillaman <dillaman@redhat.com>
Mon, 24 Sep 2018 18:45:09 +0000 (14:45 -0400)
committerJason Dillaman <dillaman@redhat.com>
Thu, 4 Oct 2018 12:04:21 +0000 (08:04 -0400)
The copyup state machine needs to iterate over all object maps to update
the existence for the object. If an snapshot is being removed concurrently,
it's possible to invalidate the object map for the image.

Fixes: http://tracker.ceph.com/issues/24516
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
(cherry picked from commit 5a1cb469879157297ab456261f9335d8b855684f)

Conflicts:
src/librbd/ObjectMap.cc: trivial resolution
src/librbd/ObjectMap.h: trivial resolution
src/librbd/io/CopyupRequest.cc: trivial resolution
src/test/librbd/deep_copy/test_mock_ObjectCopyRequest.cc: trivial resolution
src/test/librbd/test_mock_ObjectMap.cc: trivial resolution

14 files changed:
src/librbd/ObjectMap.cc
src/librbd/ObjectMap.h
src/librbd/deep_copy/ObjectCopyRequest.cc
src/librbd/io/CopyupRequest.cc
src/librbd/io/ObjectRequest.cc
src/librbd/object_map/UpdateRequest.cc
src/librbd/object_map/UpdateRequest.h
src/librbd/operation/TrimRequest.cc
src/test/librbd/deep_copy/test_mock_ObjectCopyRequest.cc
src/test/librbd/io/test_mock_ObjectRequest.cc
src/test/librbd/mock/MockObjectMap.h
src/test/librbd/object_map/test_mock_UpdateRequest.cc
src/test/librbd/operation/test_mock_TrimRequest.cc
src/test/librbd/test_mock_ObjectMap.cc

index b91f42d3cb8b63f8f91a39f91c98f85d92e8703a..2b151275b16a1dd6db45fad9edfe8c9954d99732 100644 (file)
@@ -259,7 +259,7 @@ void ObjectMap<I>::detained_aio_update(UpdateOperation &&op) {
       handle_detained_aio_update(cell, r, on_finish);
     });
   aio_update(CEPH_NOSNAP, op.start_object_no, op.end_object_no, op.new_state,
-             op.current_state, op.parent_trace, ctx);
+             op.current_state, op.parent_trace, op.ignore_enoent, ctx);
 }
 
 template <typename I>
@@ -287,13 +287,14 @@ void ObjectMap<I>::aio_update(uint64_t snap_id, uint64_t start_object_no,
                               uint64_t end_object_no, uint8_t new_state,
                               const boost::optional<uint8_t> &current_state,
                               const ZTracer::Trace &parent_trace,
-                              Context *on_finish) {
+                              bool ignore_enoent, Context *on_finish) {
   assert(m_image_ctx.snap_lock.is_locked());
   assert((m_image_ctx.features & RBD_FEATURE_OBJECT_MAP) != 0);
   assert(m_image_ctx.image_watcher != nullptr);
   assert(m_image_ctx.exclusive_lock == nullptr ||
          m_image_ctx.exclusive_lock->is_lock_owner());
-  assert(snap_id != CEPH_NOSNAP || m_image_ctx.object_map_lock.is_wlocked());
+  assert(snap_id != CEPH_NOSNAP ||
+         m_image_ctx.object_map_lock.is_wlocked());
   assert(start_object_no < end_object_no);
 
   CephContext *cct = m_image_ctx.cct;
@@ -326,7 +327,7 @@ void ObjectMap<I>::aio_update(uint64_t snap_id, uint64_t start_object_no,
 
   auto req = object_map::UpdateRequest<I>::create(
     m_image_ctx, &m_object_map, snap_id, start_object_no, end_object_no,
-    new_state, current_state, parent_trace, on_finish);
+    new_state, current_state, parent_trace, ignore_enoent, on_finish);
   req->send();
 }
 
index dab91c04cf53a695345c458739d44cac7029bcfd..8b4d67cbbd67ce620a2c8fc12c1bbd6d7d909f5f 100644 (file)
@@ -56,17 +56,19 @@ public:
   template <typename T, void(T::*MF)(int) = &T::complete>
   bool aio_update(uint64_t snap_id, uint64_t start_object_no, uint8_t new_state,
                   const boost::optional<uint8_t> &current_state,
-                  const ZTracer::Trace &parent_trace, T *callback_object) {
+                  const ZTracer::Trace &parent_trace, bool ignore_enoent,
+                  T *callback_object) {
     return aio_update<T, MF>(snap_id, start_object_no, start_object_no + 1,
                              new_state, current_state, parent_trace,
-                             callback_object);
+                             ignore_enoent, callback_object);
   }
 
   template <typename T, void(T::*MF)(int) = &T::complete>
   bool aio_update(uint64_t snap_id, uint64_t start_object_no,
                   uint64_t end_object_no, uint8_t new_state,
                   const boost::optional<uint8_t> &current_state,
-                  const ZTracer::Trace &parent_trace, T *callback_object) {
+                  const ZTracer::Trace &parent_trace, bool ignore_enoent,
+                  T *callback_object) {
     assert(start_object_no < end_object_no);
     if (snap_id == CEPH_NOSNAP) {
       end_object_no = std::min(end_object_no, m_object_map.size());
@@ -88,12 +90,13 @@ public:
 
       UpdateOperation update_operation(start_object_no, end_object_no,
                                        new_state, current_state, parent_trace,
+                                       ignore_enoent,
                                        util::create_context_callback<T, MF>(
                                          callback_object));
       detained_aio_update(std::move(update_operation));
     } else {
       aio_update(snap_id, start_object_no, end_object_no, new_state,
-                 current_state, parent_trace,
+                 current_state, parent_trace, ignore_enoent,
                  util::create_context_callback<T, MF>(callback_object));
     }
     return true;
@@ -110,15 +113,18 @@ private:
     uint8_t new_state;
     boost::optional<uint8_t> current_state;
     ZTracer::Trace parent_trace;
+    bool ignore_enoent;
     Context *on_finish;
 
     UpdateOperation(uint64_t start_object_no, uint64_t end_object_no,
                     uint8_t new_state,
                     const boost::optional<uint8_t> &current_state,
-                    const ZTracer::Trace &parent_trace, Context *on_finish)
+                    const ZTracer::Trace &parent_trace,
+                    bool ignore_enoent, Context *on_finish)
       : start_object_no(start_object_no), end_object_no(end_object_no),
         new_state(new_state), current_state(current_state),
-        parent_trace(parent_trace), on_finish(on_finish) {
+        parent_trace(parent_trace), ignore_enoent(ignore_enoent),
+        on_finish(on_finish) {
     }
   };
 
@@ -137,7 +143,8 @@ private:
   void aio_update(uint64_t snap_id, uint64_t start_object_no,
                   uint64_t end_object_no, uint8_t new_state,
                   const boost::optional<uint8_t> &current_state,
-                  const ZTracer::Trace &parent_trace, Context *on_finish);
+                  const ZTracer::Trace &parent_trace, bool ignore_enoent,
+                  Context *on_finish);
   bool update_required(const ceph::BitVector<2>::Iterator &it,
                        uint8_t new_state);
 
index 19072aee7eb1c34aa5a2425cfbf8adfc1e4a42de..9870b6ddaab2f8af105526d20f22ec58c6d40f00 100644 (file)
@@ -463,7 +463,7 @@ void ObjectCopyRequest<I>::send_update_object_map() {
   m_dst_image_ctx->object_map_lock.get_write();
   bool sent = m_dst_image_ctx->object_map->template aio_update<
     Context, &Context::complete>(dst_snap_id, m_dst_object_number, object_state,
-                                 {}, {}, ctx);
+                                 {}, {}, false, ctx);
   m_dst_image_ctx->object_map_lock.put_write();
   m_dst_image_ctx->snap_lock.put_read();
   m_dst_image_ctx->owner_lock.put_read();
index cb7cbfb93bf34788b182d00ce42c5b8deab03882..44f4ce93418e73266f0278ace67626dd0e1f4025 100644 (file)
@@ -49,7 +49,7 @@ public:
       assert(m_image_ctx.exclusive_lock->is_lock_owner());
       assert(m_image_ctx.object_map != nullptr);
       bool sent = m_image_ctx.object_map->aio_update<Context>(
-        CEPH_NOSNAP, m_object_no, OBJECT_EXISTS, {}, m_trace, this);
+        CEPH_NOSNAP, m_object_no, OBJECT_EXISTS, {}, m_trace, false, this);
       return (sent ? 0 : 1);
     }
 
@@ -66,7 +66,7 @@ public:
     }
 
     bool sent = m_image_ctx.object_map->aio_update<Context>(
-      snap_id, m_object_no, state, {}, m_trace, this);
+      snap_id, m_object_no, state, {}, m_trace, true, this);
     assert(sent);
     return 0;
   }
@@ -346,7 +346,7 @@ bool CopyupRequest<I>::send_object_map_head() {
       if (may_update && (new_state != current_state) &&
           m_ictx->object_map->aio_update<CopyupRequest>(
             CEPH_NOSNAP, m_object_no, new_state, current_state, m_trace,
-            this)) {
+            false, this)) {
         return false;
       }
     }
index 23f38ee6414169ef168bbbe64cad0c1c45261052..3d09b50448283b3cee2c6f568a2021dd9eb0756c 100644 (file)
@@ -450,7 +450,8 @@ void AbstractObjectWriteRequest<I>::pre_write_object_map_update() {
   if (image_ctx->object_map->template aio_update<
         AbstractObjectWriteRequest<I>,
         &AbstractObjectWriteRequest<I>::handle_pre_write_object_map_update>(
-          CEPH_NOSNAP, this->m_object_no, new_state, {}, this->m_trace, this)) {
+          CEPH_NOSNAP, this->m_object_no, new_state, {}, this->m_trace, false,
+          this)) {
     image_ctx->object_map_lock.put_write();
     image_ctx->snap_lock.put_read();
     return;
@@ -592,7 +593,7 @@ void AbstractObjectWriteRequest<I>::post_write_object_map_update() {
         AbstractObjectWriteRequest<I>,
         &AbstractObjectWriteRequest<I>::handle_post_write_object_map_update>(
           CEPH_NOSNAP, this->m_object_no, OBJECT_NONEXISTENT, OBJECT_PENDING,
-          this->m_trace, this)) {
+          this->m_trace, false, this)) {
     image_ctx->object_map_lock.put_write();
     image_ctx->snap_lock.put_read();
     return;
index 36ccc6159aa46ab76e4bac76ac01db1e335ef2fe..6841a16a7de4b4982a5a8d341bbdef82d496c8a9 100644 (file)
@@ -72,6 +72,13 @@ template <typename I>
 void UpdateRequest<I>::handle_update_object_map(int r) {
   ldout(m_image_ctx.cct, 20) << "r=" << r << dendl;
 
+  if (r == -ENOENT && m_ignore_enoent) {
+    r = 0;
+  }
+  if (r < 0 && m_ret_val == 0) {
+    m_ret_val = r;
+  }
+
   {
     RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
     RWLock::WLocker object_map_locker(m_image_ctx.object_map_lock);
@@ -85,7 +92,7 @@ void UpdateRequest<I>::handle_update_object_map(int r) {
   }
 
   // no more batch updates to send
-  complete(r);
+  complete(m_ret_val);
 }
 
 template <typename I>
index cb9804d07c9f0e09dc2f2110ab5fd70c7383d857..f5dcce15310278f5fef8e85587c46925a087e92e 100644 (file)
@@ -28,22 +28,24 @@ public:
                                uint64_t end_object_no, uint8_t new_state,
                                const boost::optional<uint8_t> &current_state,
                                const ZTracer::Trace &parent_trace,
-                               Context *on_finish) {
+                               bool ignore_enoent, Context *on_finish) {
     return new UpdateRequest(image_ctx, object_map, snap_id, start_object_no,
                              end_object_no, new_state, current_state,
-                             parent_trace, on_finish);
+                             parent_trace, ignore_enoent, on_finish);
   }
 
   UpdateRequest(ImageCtx &image_ctx, ceph::BitVector<2> *object_map,
                 uint64_t snap_id, uint64_t start_object_no,
                 uint64_t end_object_no, uint8_t new_state,
                 const boost::optional<uint8_t> &current_state,
-               const ZTracer::Trace &parent_trace, Context *on_finish)
+               const ZTracer::Trace &parent_trace, bool ignore_enoent,
+                Context *on_finish)
     : Request(image_ctx, snap_id, on_finish), m_object_map(*object_map),
       m_start_object_no(start_object_no), m_end_object_no(end_object_no),
       m_update_start_object_no(start_object_no), m_new_state(new_state),
       m_current_state(current_state),
-      m_trace(util::create_trace(image_ctx, "update object map", parent_trace))
+      m_trace(util::create_trace(image_ctx, "update object map", parent_trace)),
+      m_ignore_enoent(ignore_enoent)
   {
     m_trace.event("start");
   }
@@ -80,6 +82,9 @@ private:
   uint8_t m_new_state;
   boost::optional<uint8_t> m_current_state;
   ZTracer::Trace m_trace;
+  bool m_ignore_enoent;
+
+  int m_ret_val = 0;
 
   void update_object_map();
   void handle_update_object_map(int r);
index 99d144e064b9812ae4d5558fc7ec35675403ea89..bc3cf1184e63b613cccce865938acffbd94814c4 100644 (file)
@@ -200,7 +200,7 @@ void TrimRequest<I>::send_pre_trim() {
       RWLock::WLocker object_map_locker(image_ctx.object_map_lock);
       if (image_ctx.object_map->template aio_update<AsyncRequest<I> >(
             CEPH_NOSNAP, m_delete_start_min, m_num_objects, OBJECT_PENDING,
-            OBJECT_EXISTS, {}, this)) {
+            OBJECT_EXISTS, {}, false, this)) {
         return;
       }
     }
@@ -294,7 +294,7 @@ void TrimRequest<I>::send_post_trim() {
       RWLock::WLocker object_map_locker(image_ctx.object_map_lock);
       if (image_ctx.object_map->template aio_update<AsyncRequest<I> >(
             CEPH_NOSNAP, m_delete_start_min, m_num_objects, OBJECT_NONEXISTENT,
-            OBJECT_PENDING, {}, this)) {
+            OBJECT_PENDING, {}, false, this)) {
         return;
       }
     }
index 1120bad1805aa50d09dfa0ab402a8f1cf76f22e1..a433ba8b51b4bb91a86b29b3ae394df3fd38bfc9 100644 (file)
@@ -283,18 +283,18 @@ public:
                                 librados::snap_t snap_id, uint8_t state,
                                 int r) {
     if (mock_image_ctx.image_ctx->object_map != nullptr) {
-      auto &expect = EXPECT_CALL(mock_object_map, aio_update(snap_id, 0, 1, state, _, _, _));
+      auto &expect = EXPECT_CALL(mock_object_map, aio_update(snap_id, 0, 1, state, _, _, false, _));
       if (r < 0) {
-        expect.WillOnce(DoAll(WithArg<6>(Invoke([this, r](Context *ctx) {
+        expect.WillOnce(DoAll(WithArg<7>(Invoke([this, r](Context *ctx) {
                                   m_work_queue->queue(ctx, r);
                                 })),
                               Return(true)));
       } else {
-        expect.WillOnce(DoAll(WithArg<6>(Invoke([&mock_image_ctx, snap_id, state](Context *ctx) {
+        expect.WillOnce(DoAll(WithArg<7>(Invoke([&mock_image_ctx, snap_id, state](Context *ctx) {
                                   assert(mock_image_ctx.image_ctx->snap_lock.is_locked());
                                   assert(mock_image_ctx.image_ctx->object_map_lock.is_wlocked());
                                   mock_image_ctx.image_ctx->object_map->aio_update<Context>(
-                                    snap_id, 0, 1, state, boost::none, {}, ctx);
+                                    snap_id, 0, 1, state, boost::none, {}, false, ctx);
                                 })),
                               Return(true)));
       }
index 27eff02bd95aa716b9d90b1ea2e72fdbe34d1970..1bd5d60946c5a062048c3acdbc475cf331920ccd 100644 (file)
@@ -215,8 +215,8 @@ struct TestMockIoObjectRequest : public TestMockFixture {
     if (mock_image_ctx.object_map != nullptr) {
       EXPECT_CALL(*mock_image_ctx.object_map,
                   aio_update(CEPH_NOSNAP, start_object, end_object, state,
-                             current_state, _, _))
-        .WillOnce(WithArg<6>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
+                             current_state, _, false, _))
+        .WillOnce(WithArg<7>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
                                if (updated) {
                                  mock_image_ctx.op_work_queue->queue(ctx, ret_val);
                                }
index 61dcedc38c3e1a8d792797253ae600f63cb805af..e756178b3a94644812ca08a6bb9aba679d55a8c7 100644 (file)
@@ -24,30 +24,33 @@ struct MockObjectMap {
   template <typename T, void(T::*MF)(int) = &T::complete>
   bool aio_update(uint64_t snap_id, uint64_t start_object_no, uint8_t new_state,
                   const boost::optional<uint8_t> &current_state,
-                  const ZTracer::Trace &parent_trace, T *callback_object) {
+                  const ZTracer::Trace &parent_trace, bool ignore_enoent,
+                  T *callback_object) {
     return aio_update<T, MF>(snap_id, start_object_no, start_object_no + 1,
                              new_state, current_state, parent_trace,
-                             callback_object);
+                             ignore_enoent, callback_object);
   }
 
   template <typename T, void(T::*MF)(int) = &T::complete>
   bool aio_update(uint64_t snap_id, uint64_t start_object_no,
                   uint64_t end_object_no, uint8_t new_state,
                   const boost::optional<uint8_t> &current_state,
-                  const ZTracer::Trace &parent_trace, T *callback_object) {
+                  const ZTracer::Trace &parent_trace, bool ignore_enoent,
+                  T *callback_object) {
     auto ctx = util::create_context_callback<T, MF>(callback_object);
     bool updated = aio_update(snap_id, start_object_no, end_object_no,
-                              new_state, current_state, parent_trace, ctx);
+                              new_state, current_state, parent_trace,
+                              ignore_enoent, ctx);
     if (!updated) {
       delete ctx;
     }
     return updated;
   }
-  MOCK_METHOD7(aio_update, bool(uint64_t snap_id, uint64_t start_object_no,
+  MOCK_METHOD8(aio_update, bool(uint64_t snap_id, uint64_t start_object_no,
                                 uint64_t end_object_no, uint8_t new_state,
                                 const boost::optional<uint8_t> &current_state,
                                 const ZTracer::Trace &parent_trace,
-                                Context *on_finish));
+                                bool ignore_enoent, Context *on_finish));
 
   MOCK_METHOD2(snapshot_add, void(uint64_t snap_id, Context *on_finish));
   MOCK_METHOD2(snapshot_remove, void(uint64_t snap_id, Context *on_finish));
index 380a0350ad884c9d9f8f74416acce6d31cfa5daf..b7de4d1868ea83386aaf5c56a4423ef1884ccb4e 100644 (file)
@@ -81,7 +81,7 @@ TEST_F(TestMockObjectMapUpdateRequest, UpdateInMemory) {
   C_SaferCond cond_ctx;
   AsyncRequest<> *req = new UpdateRequest<>(
     *ictx, &object_map, CEPH_NOSNAP, 0, object_map.size(), OBJECT_NONEXISTENT,
-    OBJECT_EXISTS, {}, &cond_ctx);
+    OBJECT_EXISTS, {}, false, &cond_ctx);
   {
     RWLock::RLocker snap_locker(ictx->snap_lock);
     RWLock::WLocker object_map_locker(ictx->object_map_lock);
@@ -113,7 +113,7 @@ TEST_F(TestMockObjectMapUpdateRequest, UpdateHeadOnDisk) {
   C_SaferCond cond_ctx;
   AsyncRequest<> *req = new UpdateRequest<>(
     *ictx, &object_map, CEPH_NOSNAP, 0, object_map.size(), OBJECT_NONEXISTENT,
-    OBJECT_EXISTS, {}, &cond_ctx);
+    OBJECT_EXISTS, {}, false, &cond_ctx);
   {
     RWLock::RLocker snap_locker(ictx->snap_lock);
     RWLock::WLocker object_map_locker(ictx->object_map_lock);
@@ -143,7 +143,7 @@ TEST_F(TestMockObjectMapUpdateRequest, UpdateSnapOnDisk) {
   C_SaferCond cond_ctx;
   AsyncRequest<> *req = new UpdateRequest<>(
     *ictx, &object_map, snap_id, 0, object_map.size(), OBJECT_NONEXISTENT,
-    OBJECT_EXISTS, {}, &cond_ctx);
+    OBJECT_EXISTS, {}, false, &cond_ctx);
   {
     RWLock::RLocker snap_locker(ictx->snap_lock);
     RWLock::WLocker object_map_locker(ictx->object_map_lock);
@@ -171,7 +171,7 @@ TEST_F(TestMockObjectMapUpdateRequest, UpdateOnDiskError) {
   C_SaferCond cond_ctx;
   AsyncRequest<> *req = new UpdateRequest<>(
     *ictx, &object_map, CEPH_NOSNAP, 0, object_map.size(), OBJECT_NONEXISTENT,
-    OBJECT_EXISTS, {}, &cond_ctx);
+    OBJECT_EXISTS, {}, false, &cond_ctx);
   {
     RWLock::RLocker snap_locker(ictx->snap_lock);
     RWLock::WLocker object_map_locker(ictx->object_map_lock);
@@ -202,7 +202,7 @@ TEST_F(TestMockObjectMapUpdateRequest, RebuildSnapOnDisk) {
   C_SaferCond cond_ctx;
   AsyncRequest<> *req = new UpdateRequest<>(
     *ictx, &object_map, snap_id, 0, object_map.size(), OBJECT_EXISTS_CLEAN,
-    boost::optional<uint8_t>(), {}, &cond_ctx);
+    boost::optional<uint8_t>(), {}, false, &cond_ctx);
   {
     RWLock::RLocker snap_locker(ictx->snap_lock);
     RWLock::WLocker object_map_locker(ictx->object_map_lock);
@@ -240,7 +240,7 @@ TEST_F(TestMockObjectMapUpdateRequest, BatchUpdate) {
   C_SaferCond cond_ctx;
   AsyncRequest<> *req = new UpdateRequest<>(
     *ictx, &object_map, CEPH_NOSNAP, 0, object_map.size(), OBJECT_NONEXISTENT,
-    OBJECT_EXISTS, {}, &cond_ctx);
+    OBJECT_EXISTS, {}, false, &cond_ctx);
   {
     RWLock::RLocker snap_locker(ictx->snap_lock);
     RWLock::WLocker object_map_locker(ictx->object_map_lock);
@@ -249,5 +249,32 @@ TEST_F(TestMockObjectMapUpdateRequest, BatchUpdate) {
   ASSERT_EQ(0, cond_ctx.wait());
 }
 
+TEST_F(TestMockObjectMapUpdateRequest, IgnoreMissingObjectMap) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+  expect_update(ictx, CEPH_NOSNAP, 0, 1, OBJECT_NONEXISTENT, OBJECT_EXISTS,
+                -ENOENT);
+
+  ceph::BitVector<2> object_map;
+  object_map.resize(1);
+
+  C_SaferCond cond_ctx;
+  AsyncRequest<> *req = new UpdateRequest<>(
+    *ictx, &object_map, CEPH_NOSNAP, 0, object_map.size(), OBJECT_NONEXISTENT,
+    OBJECT_EXISTS, {}, true, &cond_ctx);
+  {
+    RWLock::RLocker snap_locker(ictx->snap_lock);
+    RWLock::WLocker object_map_locker(ictx->object_map_lock);
+    req->send();
+  }
+  ASSERT_EQ(0, cond_ctx.wait());
+
+  expect_unlock_exclusive_lock(*ictx);
+}
+
 } // namespace object_map
 } // namespace librbd
index 0203395929add30b3608b383d28e528a42de17ff..15e9bdd055e29583b39816498e1e8ea526e8bab5 100644 (file)
@@ -137,8 +137,8 @@ public:
     if (mock_image_ctx.object_map != nullptr) {
       EXPECT_CALL(*mock_image_ctx.object_map,
                   aio_update(CEPH_NOSNAP, start_object, end_object, state,
-                             boost::optional<uint8_t>(current_state), _, _))
-        .WillOnce(WithArg<6>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
+                             boost::optional<uint8_t>(current_state), _, false, _))
+        .WillOnce(WithArg<7>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
                                if (updated) {
                                  mock_image_ctx.op_work_queue->queue(ctx, ret_val);
                                }
index 59f21ea4316037ce348e3571bd5ed63b8cdf2e42..aa8009da71baa57f165b33bd539c3c12153bf9f5 100644 (file)
@@ -71,17 +71,18 @@ struct UpdateRequest<MockTestImageCtx> {
                                uint8_t new_state,
                                const boost::optional<uint8_t> &current_state,
                                const ZTracer::Trace &parent_trace,
-                               Context *on_finish) {
+                               bool ignore_enoent, Context *on_finish) {
     assert(s_instance != nullptr);
     s_instance->on_finish = on_finish;
     s_instance->construct(snap_id, start_object_no, end_object_no, new_state,
-                          current_state);
+                          current_state, ignore_enoent);
     return s_instance;
   }
 
-  MOCK_METHOD5(construct, void(uint64_t snap_id, uint64_t start_object_no,
+  MOCK_METHOD6(construct, void(uint64_t snap_id, uint64_t start_object_no,
                                uint64_t end_object_no, uint8_t new_state,
-                               const boost::optional<uint8_t> &current_state));
+                               const boost::optional<uint8_t> &current_state,
+                               bool ignore_enoent));
   MOCK_METHOD0(send, void());
   UpdateRequest() {
     s_instance = this;
@@ -133,10 +134,10 @@ public:
                      uint64_t snap_id, uint64_t start_object_no,
                      uint64_t end_object_no, uint8_t new_state,
                      const boost::optional<uint8_t> &current_state,
-                     Context **on_finish) {
+                     bool ignore_enoent, Context **on_finish) {
     EXPECT_CALL(mock_update_request, construct(snap_id, start_object_no,
                                                end_object_no, new_state,
-                                               current_state))
+                                               current_state, ignore_enoent))
       .Times(1);
     EXPECT_CALL(mock_update_request, send())
       .WillOnce(Invoke([&mock_update_request, on_finish]() {
@@ -163,10 +164,10 @@ TEST_F(TestMockObjectMap, NonDetainedUpdate) {
   MockUpdateRequest mock_update_request;
   Context *finish_update_1;
   expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
-                0, 1, 1, {}, &finish_update_1);
+                0, 1, 1, {}, false, &finish_update_1);
   Context *finish_update_2;
   expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
-                1, 2, 1, {}, &finish_update_2);
+                1, 2, 1, {}, false, &finish_update_2);
 
   MockUnlockRequest mock_unlock_request;
   expect_unlock(mock_image_ctx, mock_unlock_request, 0);
@@ -181,8 +182,8 @@ TEST_F(TestMockObjectMap, NonDetainedUpdate) {
   {
     RWLock::RLocker snap_locker(mock_image_ctx.snap_lock);
     RWLock::WLocker object_map_locker(mock_image_ctx.object_map_lock);
-    mock_object_map.aio_update(CEPH_NOSNAP, 0, 1, {}, {}, &update_ctx1);
-    mock_object_map.aio_update(CEPH_NOSNAP, 1, 1, {}, {}, &update_ctx2);
+    mock_object_map.aio_update(CEPH_NOSNAP, 0, 1, {}, {}, false, &update_ctx1);
+    mock_object_map.aio_update(CEPH_NOSNAP, 1, 1, {}, {}, false, &update_ctx2);
   }
 
   finish_update_2->complete(0);
@@ -213,16 +214,16 @@ TEST_F(TestMockObjectMap, DetainedUpdate) {
   MockUpdateRequest mock_update_request;
   Context *finish_update_1;
   expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
-                1, 4, 1, {}, &finish_update_1);
+                1, 4, 1, {}, false, &finish_update_1);
   Context *finish_update_2 = nullptr;
   expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
-                1, 3, 1, {}, &finish_update_2);
+                1, 3, 1, {}, false, &finish_update_2);
   Context *finish_update_3 = nullptr;
   expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
-                2, 3, 1, {}, &finish_update_3);
+                2, 3, 1, {}, false, &finish_update_3);
   Context *finish_update_4 = nullptr;
   expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
-                0, 2, 1, {}, &finish_update_4);
+                0, 2, 1, {}, false, &finish_update_4);
 
   MockUnlockRequest mock_unlock_request;
   expect_unlock(mock_image_ctx, mock_unlock_request, 0);
@@ -239,10 +240,14 @@ TEST_F(TestMockObjectMap, DetainedUpdate) {
   {
     RWLock::RLocker snap_locker(mock_image_ctx.snap_lock);
     RWLock::WLocker object_map_locker(mock_image_ctx.object_map_lock);
-    mock_object_map.aio_update(CEPH_NOSNAP, 1, 4, 1, {}, {}, &update_ctx1);
-    mock_object_map.aio_update(CEPH_NOSNAP, 1, 3, 1, {}, {}, &update_ctx2);
-    mock_object_map.aio_update(CEPH_NOSNAP, 2, 3, 1, {}, {}, &update_ctx3);
-    mock_object_map.aio_update(CEPH_NOSNAP, 0, 2, 1, {}, {}, &update_ctx4);
+    mock_object_map.aio_update(CEPH_NOSNAP, 1, 4, 1, {}, {}, false,
+                               &update_ctx1);
+    mock_object_map.aio_update(CEPH_NOSNAP, 1, 3, 1, {}, {}, false,
+                               &update_ctx2);
+    mock_object_map.aio_update(CEPH_NOSNAP, 2, 3, 1, {}, {}, false,
+                               &update_ctx3);
+    mock_object_map.aio_update(CEPH_NOSNAP, 0, 2, 1, {}, {}, false,
+                               &update_ctx4);
   }
 
   // updates 2, 3, and 4 are blocked on update 1