]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: localize snap_remove op for mirror snapshots 51166/head
authorChristopher Hoffman <choffman@redhat.com>
Wed, 19 Apr 2023 15:26:27 +0000 (15:26 +0000)
committerChristopher Hoffman <choffman@redhat.com>
Mon, 8 May 2023 14:33:49 +0000 (14:33 +0000)
A client may attempt a lock request not quickly enough to
obtain exclusive lock for operations when another competing
client responds quicker. This can happen when a peer site has
different performance characteristics or latency. Instead of
relying on this unpredictable behavior, localize operation to
primary cluster.

Fixes: https://tracker.ceph.com/issues/59393
Signed-off-by: Christopher Hoffman <choffman@redhat.com>
src/cls/rbd/cls_rbd.cc
src/librbd/api/Mirror.cc
src/librbd/mirror/snapshot/CreatePrimaryRequest.cc
src/librbd/mirror/snapshot/UnlinkPeerRequest.cc
src/librbd/mirror/snapshot/UnlinkPeerRequest.h
src/test/cls_rbd/test_cls_rbd.cc
src/test/librbd/mirror/snapshot/test_mock_CreatePrimaryRequest.cc
src/test/librbd/mirror/snapshot/test_mock_UnlinkPeerRequest.cc
src/test/librbd/test_mirroring.cc
src/test/rbd_mirror/image_replayer/snapshot/test_mock_Replayer.cc
src/tools/rbd_mirror/image_replayer/snapshot/Replayer.cc

index b4efc23ceb60b966164cc9ac300ef52a14c7c27f..229f85d4909588d2df2c9093ef6074047bd09b4c 100644 (file)
@@ -5720,24 +5720,6 @@ int image_snapshot_unlink_peer(cls_method_context_t hctx,
     return -ENOENT;
   }
 
-  if (mirror_ns->mirror_peer_uuids.size() == 1) {
-    // if this is the last peer to unlink and we have at least one additional
-    // newer mirror snapshot, return a special error to inform the caller it
-    // should remove the snapshot instead.
-    auto search_lambda = [snap_id](const cls_rbd_snap& snap_meta) {
-      if (snap_meta.id > snap_id &&
-          std::holds_alternative<cls::rbd::MirrorSnapshotNamespace>(
-                   snap_meta.snapshot_namespace)) {
-        return -EEXIST;
-      }
-      return 0;
-    };
-    r = image::snapshot::iterate(hctx, search_lambda);
-    if (r == -EEXIST) {
-      return -ERESTART;
-    }
-  }
-
   mirror_ns->mirror_peer_uuids.erase(mirror_peer_uuid);
 
   r = image::snapshot::write(hctx, snap_key, std::move(snap));
index ee5897eddfc30e8991bf38c7ec76361bce452f96..2cfad0d32753220c276dba1a1ecec72d99a811c8 100644 (file)
@@ -1654,7 +1654,7 @@ int Mirror<I>::peer_site_remove(librados::IoCtx& io_ctx,
       for (auto snap_id : snap_ids) {
         C_SaferCond cond;
         auto req = mirror::snapshot::UnlinkPeerRequest<I>::create(
-          img_ctx, snap_id, uuid, &cond);
+          img_ctx, snap_id, uuid, true, &cond);
         req->send();
         r = cond.wait();
         if (r == -ENOENT) {
index ae8c045e16ba3404ac6e2767f043ac944264ac07..fd6f9b5023d2a4cdb866f9a520418d628c350695 100644 (file)
@@ -239,7 +239,8 @@ void CreatePrimaryRequest<I>::unlink_peer() {
   auto ctx = create_context_callback<
     CreatePrimaryRequest<I>,
     &CreatePrimaryRequest<I>::handle_unlink_peer>(this);
-  auto req = UnlinkPeerRequest<I>::create(m_image_ctx, snap_id, peer_uuid, ctx);
+  auto req = UnlinkPeerRequest<I>::create(m_image_ctx, snap_id, peer_uuid, true,
+                                          ctx);
   req->send();
 }
 
index b28591c00e8bfed5387990aa3765ead510c4d64a..35313f6277981bb6876724dcc3b316043aea2d91 100644 (file)
@@ -62,18 +62,19 @@ void UnlinkPeerRequest<I>::unlink_peer() {
 
   m_image_ctx->image_lock.lock_shared();
   int r = -ENOENT;
-  cls::rbd::MirrorSnapshotNamespace* mirror_ns = nullptr;
-  m_newer_mirror_snapshots = false;
+  cls::rbd::SnapshotNamespace snap_namespace;
+  std::string snap_name;
+  bool have_newer_mirror_snapshot = false;
   for (auto snap_it = m_image_ctx->snap_info.find(m_snap_id);
        snap_it != m_image_ctx->snap_info.end(); ++snap_it) {
     if (snap_it->first == m_snap_id) {
       r = 0;
-      mirror_ns = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
-        &snap_it->second.snap_namespace);
+      snap_namespace = snap_it->second.snap_namespace;
+      snap_name = snap_it->second.name;
     } else if (std::holds_alternative<cls::rbd::MirrorSnapshotNamespace>(
                  snap_it->second.snap_namespace)) {
       ldout(cct, 15) << "located newer mirror snapshot" << dendl;
-      m_newer_mirror_snapshots = true;
+      have_newer_mirror_snapshot = true;
       break;
     }
   }
@@ -85,6 +86,8 @@ void UnlinkPeerRequest<I>::unlink_peer() {
     return;
   }
 
+  auto mirror_ns = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
+    &snap_namespace);
   if (mirror_ns == nullptr) {
     lderr(cct) << "not mirror snapshot (snap_id=" << m_snap_id << ")" << dendl;
     m_image_ctx->image_lock.unlock_shared();
@@ -94,12 +97,32 @@ void UnlinkPeerRequest<I>::unlink_peer() {
 
   // if there is or will be no more peers in the mirror snapshot and we have
   // a more recent mirror snapshot, remove the older one
-  if ((mirror_ns->mirror_peer_uuids.count(m_mirror_peer_uuid) == 0) ||
-      (mirror_ns->mirror_peer_uuids.size() <= 1U && m_newer_mirror_snapshots)) {
+  if ((mirror_ns->mirror_peer_uuids.empty() ||
+       (mirror_ns->mirror_peer_uuids.size() == 1 &&
+        mirror_ns->mirror_peer_uuids.count(m_mirror_peer_uuid) != 0)) &&
+      have_newer_mirror_snapshot) {
+    if (m_allow_remove) {
+      m_image_ctx->image_lock.unlock_shared();
+      remove_snapshot(snap_namespace, snap_name);
+      return;
+    } else {
+      ldout(cct, 15) << "skipping removal of snapshot: snap_id=" << m_snap_id
+                     << ", mirror_peer_uuid=" << m_mirror_peer_uuid
+                     << ", mirror_peer_uuids=" << mirror_ns->mirror_peer_uuids
+                     << dendl;
+    }
+  }
+
+  if (mirror_ns->mirror_peer_uuids.count(m_mirror_peer_uuid) == 0) {
+    ldout(cct, 15) << "no peer to unlink: snap_id=" << m_snap_id
+                   << ", mirror_peer_uuid=" << m_mirror_peer_uuid
+                   << ", mirror_peer_uuids=" << mirror_ns->mirror_peer_uuids
+                   << dendl;
     m_image_ctx->image_lock.unlock_shared();
-    remove_snapshot();
+    finish(0);
     return;
   }
+
   m_image_ctx->image_lock.unlock_shared();
 
   ldout(cct, 15) << "snap_id=" << m_snap_id << ", "
@@ -120,6 +143,10 @@ void UnlinkPeerRequest<I>::handle_unlink_peer(int r) {
   ldout(cct, 15) << "r=" << r << dendl;
 
   if (r == -ERESTART || r == -ENOENT) {
+    if (r == -ERESTART) {
+      ldout(cct, 15) << "unlinking last peer not supported" << dendl;
+      m_allow_remove = true;
+    }
     refresh_image();
     return;
   }
@@ -161,44 +188,12 @@ void UnlinkPeerRequest<I>::handle_notify_update(int r) {
 }
 
 template <typename I>
-void UnlinkPeerRequest<I>::remove_snapshot() {
+void UnlinkPeerRequest<I>::remove_snapshot(
+    const cls::rbd::SnapshotNamespace& snap_namespace,
+    const std::string& snap_name) {
   CephContext *cct = m_image_ctx->cct;
   ldout(cct, 15) << dendl;
 
-  cls::rbd::SnapshotNamespace snap_namespace;
-  std::string snap_name;
-  int r = 0;
-  {
-    std::shared_lock image_locker{m_image_ctx->image_lock};
-
-    auto snap_info = m_image_ctx->get_snap_info(m_snap_id);
-    if (!snap_info) {
-      r = -ENOENT;
-    } else {
-      snap_namespace = snap_info->snap_namespace;
-      snap_name = snap_info->name;
-    }
-  }
-
-  if (r == -ENOENT) {
-    ldout(cct, 15) << "failed to locate snapshot " << m_snap_id << dendl;
-    finish(0);
-    return;
-  }
-
-  auto info = std::get<cls::rbd::MirrorSnapshotNamespace>(
-    snap_namespace);
-
-  info.mirror_peer_uuids.erase(m_mirror_peer_uuid);
-  if (!info.mirror_peer_uuids.empty() || !m_newer_mirror_snapshots) {
-    ldout(cct, 15) << "skipping removal of snapshot: "
-                   << "snap_id=" << m_snap_id << ": "
-                   << "mirror_peer_uuid=" << m_mirror_peer_uuid << ", "
-                   << "mirror_peer_uuids=" << info.mirror_peer_uuids << dendl;
-    finish(0);
-    return;
-  }
-
   auto ctx = create_context_callback<
     UnlinkPeerRequest<I>, &UnlinkPeerRequest<I>::handle_remove_snapshot>(this);
   m_image_ctx->operations->snap_remove(snap_namespace, snap_name, ctx);
index 9ef47269d87609e51e3ab85d271c59b89e5f127a..192b40d6e5e676a6357e365695868f5b3fb386fc 100644 (file)
@@ -5,6 +5,7 @@
 #define CEPH_LIBRBD_MIRROR_SNAPSHOT_UNLINK_PEER_REQUEST_H
 
 #include "include/buffer.h"
+#include "cls/rbd/cls_rbd_client.h"
 
 #include <string>
 #include <set>
@@ -23,15 +24,17 @@ class UnlinkPeerRequest {
 public:
   static UnlinkPeerRequest *create(ImageCtxT *image_ctx, uint64_t snap_id,
                                    const std::string &mirror_peer_uuid,
-                                   Context *on_finish) {
+                                   bool allow_remove, Context *on_finish) {
     return new UnlinkPeerRequest(image_ctx, snap_id, mirror_peer_uuid,
-                                 on_finish);
+                                 allow_remove, on_finish);
   }
 
   UnlinkPeerRequest(ImageCtxT *image_ctx, uint64_t snap_id,
-                    const std::string &mirror_peer_uuid, Context *on_finish)
+                    const std::string &mirror_peer_uuid, bool allow_remove,
+                    Context *on_finish)
     : m_image_ctx(image_ctx), m_snap_id(snap_id),
-      m_mirror_peer_uuid(mirror_peer_uuid), m_on_finish(on_finish) {
+      m_mirror_peer_uuid(mirror_peer_uuid), m_allow_remove(allow_remove),
+      m_on_finish(on_finish) {
   }
 
   void send();
@@ -67,10 +70,9 @@ private:
   ImageCtxT *m_image_ctx;
   uint64_t m_snap_id;
   std::string m_mirror_peer_uuid;
+  bool m_allow_remove;
   Context *m_on_finish;
 
-  bool m_newer_mirror_snapshots = false;
-
   void refresh_image();
   void handle_refresh_image(int r);
 
@@ -80,7 +82,8 @@ private:
   void notify_update();
   void handle_notify_update(int r);
 
-  void remove_snapshot();
+  void remove_snapshot(const cls::rbd::SnapshotNamespace& snap_namespace,
+                       const std::string& snap_name);
   void handle_remove_snapshot(int r);
 
   void finish(int r);
index 4d6f9bd89b0080da417ec8d7fa2fe48503714100..2f553a0f43385a7fc6237e14d43fcec73aed0ea8 100644 (file)
@@ -2275,14 +2275,14 @@ TEST_F(TestClsRbd, mirror_snapshot) {
   ASSERT_EQ(1U, sn->mirror_peer_uuids.size());
   ASSERT_EQ(1U, sn->mirror_peer_uuids.count("peer2"));
 
-  ASSERT_EQ(-ERESTART,
-            mirror_image_snapshot_unlink_peer(&ioctx, oid, 1, "peer2"));
+  ASSERT_EQ(0, mirror_image_snapshot_unlink_peer(&ioctx, oid, 1, "peer2"));
+  ASSERT_EQ(-ENOENT, mirror_image_snapshot_unlink_peer(&ioctx, oid, 1,
+                                                       "peer2"));
   ASSERT_EQ(0, snapshot_get(&ioctx, oid, 1, &snap));
   sn = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
     &snap.snapshot_namespace);
   ASSERT_NE(nullptr, sn);
-  ASSERT_EQ(1U, sn->mirror_peer_uuids.size());
-  ASSERT_EQ(1U, sn->mirror_peer_uuids.count("peer2"));
+  ASSERT_EQ(0U, sn->mirror_peer_uuids.size());
 
   ASSERT_EQ(0, snapshot_get(&ioctx, oid, 2, &snap));
   auto nsn = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
index 1875678c91ac5fc5de56a62b143d440160efc9d3..22692e15f28040fa0ca8d2caf770b8e488997d54 100644 (file)
@@ -58,15 +58,18 @@ template <>
 struct UnlinkPeerRequest<MockTestImageCtx> {
   uint64_t snap_id = CEPH_NOSNAP;
   std::string mirror_peer_uuid;
+  bool allow_remove;
   Context* on_finish = nullptr;
   static UnlinkPeerRequest* s_instance;
   static UnlinkPeerRequest *create(MockTestImageCtx *image_ctx,
                                    uint64_t snap_id,
                                    const std::string &mirror_peer_uuid,
+                                   bool allow_remove,
                                    Context *on_finish) {
     ceph_assert(s_instance != nullptr);
     s_instance->snap_id = snap_id;
     s_instance->mirror_peer_uuid = mirror_peer_uuid;
+    s_instance->allow_remove = allow_remove;
     s_instance->on_finish = on_finish;
     return s_instance;
   }
@@ -175,13 +178,15 @@ public:
   void expect_unlink_peer(MockTestImageCtx &mock_image_ctx,
                           MockUnlinkPeerRequest &mock_unlink_peer_request,
                           uint64_t snap_id, const std::string &peer_uuid,
-                          bool is_linked, bool complete, int r) {
+                          bool is_linked, bool complete, bool allow_remove,
+                          int r) {
     EXPECT_CALL(mock_unlink_peer_request, send())
       .WillOnce(Invoke([&mock_image_ctx, &mock_unlink_peer_request,
-                        snap_id, peer_uuid, is_linked, complete, r]() {
+                        snap_id, peer_uuid, is_linked, complete, allow_remove, r]() {
                          ASSERT_EQ(mock_unlink_peer_request.mirror_peer_uuid,
                                    peer_uuid);
                          ASSERT_EQ(mock_unlink_peer_request.snap_id, snap_id);
+                         ASSERT_EQ(mock_unlink_peer_request.allow_remove, allow_remove);
                          if (r == 0) {
                            auto it = mock_image_ctx.snap_info.find(snap_id);
                            ASSERT_NE(it, mock_image_ctx.snap_info.end());
@@ -325,7 +330,7 @@ TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkIncomplete) {
   auto it = mock_image_ctx.snap_info.rbegin();
   auto snap_id = it->first;
   expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
-                     true, false, 0);
+                     true, false, true, 0);
   C_SaferCond ctx;
   auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
                                           0U, 0U, nullptr, &ctx);
@@ -362,7 +367,7 @@ TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkPeer) {
   auto it = mock_image_ctx.snap_info.rbegin();
   auto snap_id = it->first;
   expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
-                     true, true, 0);
+                     true, true, true, 0);
   C_SaferCond ctx;
   auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
                                           0U, 0U, nullptr, &ctx);
@@ -397,7 +402,7 @@ TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkNoPeer) {
   auto it = mock_image_ctx.snap_info.rbegin();
   auto snap_id = it->first;
   expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid",
-                     false, true, 0);
+                     false, true, true, 0);
 
   C_SaferCond ctx;
   auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
@@ -438,9 +443,9 @@ TEST_F(TestMockMirrorSnapshotCreatePrimaryRequest, SuccessUnlinkMultiplePeers) {
   auto it = mock_image_ctx.snap_info.rbegin();
   auto snap_id = it->first;
   expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid1",
-                     true, true, 0);
+                     true, true, true, 0);
   expect_unlink_peer(mock_image_ctx, mock_unlink_peer_request, snap_id, "uuid2",
-                     true, true, 0);
+                     true, true, true, 0);
   C_SaferCond ctx;
   auto req = new MockCreatePrimaryRequest(&mock_image_ctx, "gid", CEPH_NOSNAP,
                                           0U, 0U, nullptr, &ctx);
index eebd9fd81819f1fa4135a1042e3b03ddf47afdaf..869bdecff479f6fb32ed3b1ee296abf1ac83d57e 100644 (file)
@@ -149,7 +149,7 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, Success) {
 
   C_SaferCond ctx;
   auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer1_uuid",
-                                       &ctx);
+                                       true, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
@@ -177,7 +177,37 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RemoveSnapshot) {
 
   C_SaferCond ctx;
   auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
-                                       &ctx);
+                                       true, &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RemoveSnapshotNotAllowed) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorSnapshotNamespace ns{
+    cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer_uuid"},
+    "", CEPH_NOSNAP};
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+  snap_create(mock_image_ctx, ns, "mirror_snap2");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, true);
+  expect_refresh_image(mock_image_ctx, 0);
+  expect_unlink_peer(mock_image_ctx, snap_id, "peer_uuid", 0);
+  expect_notify_update(mock_image_ctx, 0);
+  expect_refresh_image(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+                                       false, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
@@ -206,7 +236,35 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, SnapshotRemoveEmptyPeers) {
 
   C_SaferCond ctx;
   auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
-                                       &ctx);
+                                       true, &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, SnapshotRemoveEmptyPeersNotAllowed) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorSnapshotNamespace ns{
+    cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {},
+    "", CEPH_NOSNAP};
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+  ns.mirror_peer_uuids = {"peer_uuid"};
+  snap_create(mock_image_ctx, ns, "mirror_snap2");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, true);
+  expect_refresh_image(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+                                       false, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
@@ -227,7 +285,8 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, SnapshotDNE) {
   expect_refresh_image(mock_image_ctx, 0);
 
   C_SaferCond ctx;
-  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, 123, "peer_uuid", &ctx);
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, 123, "peer_uuid",
+                                       true, &ctx);
   req->send();
   ASSERT_EQ(-ENOENT, ctx.wait());
 }
@@ -253,7 +312,33 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, PeerDNE) {
 
   C_SaferCond ctx;
   auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "unknown_peer",
-                                       &ctx);
+                                       true, &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, MultiPeerPeerDNE) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorSnapshotNamespace ns{
+    cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer1_uuid", "peer2_uuid"},
+    "", CEPH_NOSNAP};
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, true);
+  expect_refresh_image(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "unknown_peer",
+                                       true, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
@@ -276,7 +361,7 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, InvalidSnapshot) {
 
   C_SaferCond ctx;
   auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
-                                       &ctx);
+                                       true, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -295,7 +380,8 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RefreshError) {
   expect_refresh_image(mock_image_ctx, -EINVAL);
 
   C_SaferCond ctx;
-  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, 123, "peer_uuid", &ctx);
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, 123, "peer_uuid",
+                                       true, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -321,11 +407,41 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, UnlinkError) {
 
   C_SaferCond ctx;
   auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer1_uuid",
-                                       &ctx);
+                                       true, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
 
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, UnlinkErrorRestart) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorSnapshotNamespace ns{
+    cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer_uuid"},
+    "", CEPH_NOSNAP};
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+  snap_create(mock_image_ctx, ns, "mirror_snap2");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, true);
+  expect_refresh_image(mock_image_ctx, 0);
+  expect_unlink_peer(mock_image_ctx, snap_id, "peer_uuid", -ERESTART);
+  expect_refresh_image(mock_image_ctx, 0);
+  expect_remove_snapshot(mock_image_ctx, snap_id, 0);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+                                       false, &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, NotifyError) {
   REQUIRE_FORMAT_V2();
 
@@ -348,7 +464,7 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, NotifyError) {
 
   C_SaferCond ctx;
   auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer1_uuid",
-                                       &ctx);
+                                       true, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -375,7 +491,7 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RemoveSnapshotError) {
 
   C_SaferCond ctx;
   auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
-                                       &ctx);
+                                       true, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
index 480db94d2bf3f81027436dd9658b76b4da40ddb6..19ba5277d30410be8f7770ceedee9d10cbeece86 100644 (file)
@@ -1269,7 +1269,7 @@ TEST_F(TestMirroring, SnapshotUnlinkPeer)
 
   C_SaferCond cond1;
   auto req = librbd::mirror::snapshot::UnlinkPeerRequest<>::create(
-    ictx, snap_id, peer1_uuid, &cond1);
+    ictx, snap_id, peer1_uuid, true, &cond1);
   req->send();
   ASSERT_EQ(0, cond1.wait());
 
@@ -1314,7 +1314,7 @@ TEST_F(TestMirroring, SnapshotUnlinkPeer)
 
   C_SaferCond cond2;
   req = librbd::mirror::snapshot::UnlinkPeerRequest<>::create(
-    ictx, snap_id, peer2_uuid, &cond2);
+    ictx, snap_id, peer2_uuid, true, &cond2);
   req->send();
   ASSERT_EQ(0, cond2.wait());
 
index 26b4d32cc4ffd6bdaef6588d61b55d7f62669c9d..75141e5a7be30409797ce002008b9c017faa17e0 100644 (file)
@@ -218,15 +218,18 @@ template <>
 struct UnlinkPeerRequest<MockTestImageCtx> {
   uint64_t snap_id;
   std::string mirror_peer_uuid;
+  bool allow_remove;
 
   static UnlinkPeerRequest* s_instance;
   static UnlinkPeerRequest*create (MockTestImageCtx *image_ctx,
                                    uint64_t snap_id,
                                    const std::string &mirror_peer_uuid,
+                                   bool allow_remove,
                                    Context *on_finish) {
     ceph_assert(s_instance != nullptr);
     s_instance->snap_id = snap_id;
     s_instance->mirror_peer_uuid = mirror_peer_uuid;
+    s_instance->allow_remove = allow_remove;
     s_instance->on_finish = on_finish;
     return s_instance;
   }
@@ -596,12 +599,13 @@ public:
 
   void expect_unlink_peer(MockUnlinkPeerRequest& mock_unlink_peer_request,
                           uint64_t snap_id, const std::string& mirror_peer_uuid,
-                          int r) {
+                          bool allow_remove, int r) {
     EXPECT_CALL(mock_unlink_peer_request, send())
       .WillOnce(Invoke([this, &req=mock_unlink_peer_request, snap_id,
-                        mirror_peer_uuid, r]() {
+                        mirror_peer_uuid, allow_remove, r]() {
         ASSERT_EQ(snap_id, req.snap_id);
         ASSERT_EQ(mirror_peer_uuid, req.mirror_peer_uuid);
+        ASSERT_EQ(allow_remove, req.allow_remove);
         m_threads->work_queue->queue(req.on_finish, r);
       }));
   }
@@ -868,7 +872,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, SyncSnapshot) {
   expect_notify_update(mock_local_image_ctx);
   MockUnlinkPeerRequest mock_unlink_peer_request;
   expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
-                     0);
+                     false, 0);
   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
 
   // prune non-primary snap1
@@ -1096,7 +1100,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSyncDelta) {
   expect_notify_update(mock_local_image_ctx);
   MockUnlinkPeerRequest mock_unlink_peer_request;
   expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
-                     0);
+                     false, 0);
   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
 
   // prune non-primary snap1
@@ -1218,7 +1222,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSyncDeltaDemote) {
   expect_notify_update(mock_local_image_ctx);
   MockUnlinkPeerRequest mock_unlink_peer_request;
   expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
-                     0);
+                     false, 0);
   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
 
   // idle
@@ -1429,7 +1433,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedPendingSyncDelta) {
   expect_notify_update(mock_local_image_ctx);
   MockUnlinkPeerRequest mock_unlink_peer_request;
   expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
-                     0);
+                     false, 0);
   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
 
   // prune non-primary snap1
@@ -1570,7 +1574,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedPendingSyncDeltaDemote)
   expect_notify_update(mock_local_image_ctx);
   MockUnlinkPeerRequest mock_unlink_peer_request;
   expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
-                     0);
+                     false, 0);
   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
 
   // idle
@@ -2650,7 +2654,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UnlinkPeerError) {
   expect_notify_update(mock_local_image_ctx);
   MockUnlinkPeerRequest mock_unlink_peer_request;
   expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
-                     -EINVAL);
+                     false, -EINVAL);
   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
 
   // wake-up replayer
@@ -2876,7 +2880,8 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteFailover) {
     mock_local_image_ctx, 13, true, 0, 0);
   expect_notify_update(mock_local_image_ctx);
   MockUnlinkPeerRequest mock_unlink_peer_request;
-  expect_unlink_peer(mock_unlink_peer_request, 2, "remote mirror peer uuid", 0);
+  expect_unlink_peer(mock_unlink_peer_request, 2, "remote mirror peer uuid",
+                     false, 0);
   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
 
   // idle
@@ -2976,7 +2981,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UnlinkRemoteSnapshot) {
   expect_is_refresh_required(mock_remote_image_ctx, false);
   MockUnlinkPeerRequest mock_unlink_peer_request;
   expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
-                     0);
+                     false, 0);
 
   // idle
   expect_load_image_meta(mock_image_meta, false, 0);
index b4da280bf1be88d8d1a981096f717eefa0c574ba..3561f6fc699419ddb9b229e63e568f53066615b3 100644 (file)
@@ -1293,7 +1293,7 @@ void Replayer<I>::unlink_peer(uint64_t remote_snap_id) {
     Replayer<I>, &Replayer<I>::handle_unlink_peer>(this);
   auto req = librbd::mirror::snapshot::UnlinkPeerRequest<I>::create(
     m_state_builder->remote_image_ctx, remote_snap_id,
-    m_remote_mirror_peer_uuid, ctx);
+    m_remote_mirror_peer_uuid, false, ctx);
   req->send();
 }