]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
librbd/mirror/snapshot: add async group prepare images
authorRamana Raja <rraja@redhat.com>
Sun, 12 Jan 2025 20:56:20 +0000 (15:56 -0500)
committerIlya Dryomov <idryomov@gmail.com>
Sun, 28 Sep 2025 18:25:00 +0000 (20:25 +0200)
... state machine class.

Signed-off-by: Ramana Raja <rraja@redhat.com>
src/librbd/CMakeLists.txt
src/librbd/mirror/snapshot/GroupPrepareImagesRequest.cc [new file with mode: 0644]
src/librbd/mirror/snapshot/GroupPrepareImagesRequest.h [new file with mode: 0644]

index f41f77388ea3a7f120ab4d56f213130b90c356f4..50c0e6c74051aef8761bc3acd8ae6cf0d54567af 100644 (file)
@@ -162,6 +162,7 @@ set(librbd_internal_srcs
   mirror/snapshot/DemoteRequest.cc
   mirror/snapshot/GetImageStateRequest.cc
   mirror/snapshot/GroupGetInfoRequest.cc
+  mirror/snapshot/GroupPrepareImagesRequest.cc
   mirror/snapshot/ImageMeta.cc
   mirror/snapshot/PromoteRequest.cc
   mirror/snapshot/RemoveImageStateRequest.cc
diff --git a/src/librbd/mirror/snapshot/GroupPrepareImagesRequest.cc b/src/librbd/mirror/snapshot/GroupPrepareImagesRequest.cc
new file mode 100644 (file)
index 0000000..01dfd2e
--- /dev/null
@@ -0,0 +1,431 @@
+// -*- mode:c++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/mirror/snapshot/GroupPrepareImagesRequest.h"
+#include "include/ceph_assert.h"
+#include "common/ceph_context.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "include/Context.h"
+#include "librbd/api/Group.h"
+#include "librbd/internal.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/Utils.h"
+#include "librbd/mirror/Types.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::mirror::snapshot::GroupPrepareImagesRequest: " \
+                           << " " << __func__ << ": "
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+namespace {
+
+const uint32_t MAX_RETURN = 1024;
+
+} // anonymous namespace
+
+using librbd::util::create_rados_callback;
+
+template <typename I>
+GroupPrepareImagesRequest<I>::GroupPrepareImagesRequest(
+    librados::IoCtx& group_ioctx, const std::string& group_id,
+    cls::rbd::MirrorSnapshotState state, uint32_t flags,
+    cls::rbd::GroupSnapshot *group_snap,
+    std::vector<librbd::ImageCtx *> *image_ctxs,
+    std::vector<uint64_t> *quiesce_requests,
+    Context *on_finish)
+     : m_group_ioctx(group_ioctx), m_group_id(group_id),
+       m_state(state), m_flags(flags), m_group_snap(group_snap),
+       m_image_ctxs(image_ctxs), m_quiesce_requests(quiesce_requests),
+       m_on_finish(on_finish) {
+  m_cct = reinterpret_cast<CephContext*>(m_group_ioctx.cct());
+  ldout(m_cct, 10) << "group_id=" << m_group_id
+                   << ", state=" << m_state
+                   << ", flags=" << m_flags
+                   << dendl;
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::send() {
+ ldout(m_cct, 10) << dendl;
+
+ check_snap_create_flags();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::check_snap_create_flags() {
+  ldout(m_cct, 10) << dendl;
+
+  int r = librbd::util::snap_create_flags_api_to_internal(m_cct, m_flags,
+                                                          &m_internal_flags);
+  if (r < 0) {
+    lderr(m_cct) << "checking snap create flags failed" << cpp_strerror(r)
+                 << dendl;
+    finish(r);
+    return;
+  }
+
+  get_mirror_peer_list();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::get_mirror_peer_list() {
+  ldout(m_cct, 10) << dendl;
+
+  m_default_ns_ioctx.dup(m_group_ioctx);
+  m_default_ns_ioctx.set_namespace("");
+
+  librados::ObjectReadOperation op;
+  cls_client::mirror_peer_list_start(&op);
+
+  auto comp = create_rados_callback<
+      GroupPrepareImagesRequest<I>,
+      &GroupPrepareImagesRequest<I>::handle_get_mirror_peer_list>(this);
+
+  m_outbl.clear();
+  int r = m_default_ns_ioctx.aio_operate(RBD_MIRRORING, comp, &op, &m_outbl);
+  ceph_assert(r == 0);
+  comp->release();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::handle_get_mirror_peer_list(int r) {
+  ldout(m_cct, 10) << "r=" << r << dendl;
+
+  std::vector<cls::rbd::MirrorPeer> peers;
+  if (r == 0) {
+    auto it = m_outbl.cbegin();
+    r = cls_client::mirror_peer_list_finish(&it, &peers);
+  }
+
+  if (r < 0) {
+    lderr(m_cct) << "error listing mirror peers" << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  for (auto &peer : peers) {
+    if (peer.mirror_peer_direction == cls::rbd::MIRROR_PEER_DIRECTION_RX) {
+      continue;
+    }
+    m_mirror_peer_uuids.insert(peer.uuid);
+  }
+
+  if (m_mirror_peer_uuids.empty()) {
+    lderr(m_cct) << "no mirror tx peers configured for the pool" << dendl;
+    finish(-EINVAL);
+    return;
+  }
+
+  list_group_images();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::list_group_images() {
+  ldout(m_cct, 10) << dendl;
+
+  librados::ObjectReadOperation op;
+  cls_client::group_image_list_start(&op, m_start_after, MAX_RETURN);
+
+  auto comp = create_rados_callback<
+    GroupPrepareImagesRequest<I>,
+    &GroupPrepareImagesRequest<I>::handle_list_group_images>(this);
+
+  m_outbl.clear();
+  int r = m_group_ioctx.aio_operate(
+    librbd::util::group_header_name(m_group_id), comp, &op, &m_outbl);
+  ceph_assert(r == 0);
+  comp->release();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::handle_list_group_images(int r) {
+  ldout(m_cct, 10) << "r=" << r << dendl;
+
+  std::vector<cls::rbd::GroupImageStatus> images;
+  if (r == 0) {
+    auto iter = m_outbl.cbegin();
+    r = cls_client::group_image_list_finish(&iter, &images);
+  }
+
+  if (r < 0) {
+    lderr(m_cct) << "error listing images in group: " << cpp_strerror(r)
+                 << dendl;
+    finish(r);
+    return;
+  }
+
+  auto image_count = images.size();
+  m_images.insert(m_images.end(), images.begin(), images.end());
+  if (image_count == MAX_RETURN) {
+    m_start_after = images.rbegin()->spec;
+    list_group_images();
+    return;
+  }
+
+  open_group_images();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::open_group_images() {
+  ldout(m_cct, 10) << dendl;
+
+  auto ctx = librbd::util::create_context_callback<
+    GroupPrepareImagesRequest<I>,
+    &GroupPrepareImagesRequest<I>::handle_open_group_images>(this);
+  auto gather_ctx = new C_Gather(m_cct, ctx);
+
+  int r = 0;
+  size_t i = 0;
+  for ( ; i < m_images.size(); i++) {
+    auto &image = m_images[i];
+    librbd::IoCtx image_io_ctx;
+    r = librbd::util::create_ioctx(m_group_ioctx, "image",
+                                   image.spec.pool_id, {},
+                                   &image_io_ctx);
+    if (r < 0) {
+      m_ret_code = r;
+      break;
+    }
+
+    librbd::ImageCtx* image_ctx = new ImageCtx("", image.spec.image_id.c_str(),
+                                              nullptr, image_io_ctx, false);
+
+    image_ctx->state->open(0, gather_ctx->new_sub());
+
+    m_image_ctxs->push_back(image_ctx);
+  }
+
+  gather_ctx->activate();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::handle_open_group_images(int r) {
+  ldout(m_cct, 10) << "r=" << r << dendl;
+
+  if (r < 0 && m_ret_code == 0) {
+    m_ret_code = r;
+  }
+
+  if (m_ret_code < 0) {
+    lderr(m_cct) << "failed to open group images: " << cpp_strerror(m_ret_code)
+                 << dendl;
+    close_images();
+    return;
+  }
+
+  set_snap_metadata();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::set_snap_metadata() {
+  ldout(m_cct, 10) << dendl;
+
+  m_group_snap->snapshot_namespace = cls::rbd::GroupSnapshotNamespaceMirror{
+    m_state, m_mirror_peer_uuids, {}, {}};
+
+  for (auto image_ctx: *m_image_ctxs) {
+    m_group_snap->snaps.emplace_back(image_ctx->md_ctx.get_id(), image_ctx->id,
+                                     CEPH_NOSNAP);
+  }
+
+  std::string group_header_oid = librbd::util::group_header_name(m_group_id);
+  int r = cls_client::group_snap_set(&m_group_ioctx, group_header_oid,
+                                     *m_group_snap);
+  if (r < 0) {
+    lderr(m_cct) << "failed to set group snapshot metadata: " << cpp_strerror(r)
+                 << dendl;
+    m_ret_code = r;
+    close_images();
+    return;
+  }
+
+  if (m_image_ctxs->empty()) {
+    finish(0);
+    return;
+  }
+
+  if ((m_internal_flags & SNAP_CREATE_FLAG_SKIP_NOTIFY_QUIESCE) == 0) {
+    notify_quiesce();
+  } else {
+    acquire_image_exclusive_locks();
+  }
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::notify_quiesce() {
+  ldout(m_cct, 10) << dendl;
+
+  auto ctx = librbd::util::create_context_callback<
+    GroupPrepareImagesRequest<I>,
+    &GroupPrepareImagesRequest<I>::handle_notify_quiesce>(this);
+  auto gather_ctx = new C_Gather(m_cct, ctx);
+
+  int image_count = m_image_ctxs->size();
+  m_quiesce_requests->resize(image_count);
+  for (int i = 0; i < image_count; ++i) {
+    auto ictx = (*m_image_ctxs)[i];
+    ictx->image_watcher->notify_quiesce(&(*m_quiesce_requests)[i], m_prog_ctx,
+                                       gather_ctx->new_sub());
+  }
+
+  gather_ctx->activate();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::handle_notify_quiesce(int r) {
+  ldout(m_cct, 10) << "r=" << r << dendl;
+
+  if (r < 0 &&
+      (m_internal_flags & SNAP_CREATE_FLAG_IGNORE_NOTIFY_QUIESCE_ERROR) == 0) {
+    m_ret_code = r;
+    notify_unquiesce();
+    return;
+  }
+
+  acquire_image_exclusive_locks();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::acquire_image_exclusive_locks() {
+  ldout(m_cct, 10) << dendl;
+
+  auto ctx = librbd::util::create_context_callback<
+    GroupPrepareImagesRequest<I>,
+    &GroupPrepareImagesRequest<I>::handle_acquire_image_exclusive_locks>(this);
+  auto gather_ctx = new C_Gather(m_cct, ctx);
+
+  for (auto ictx: *m_image_ctxs) {
+    std::shared_lock owner_lock{ictx->owner_lock};
+    if (ictx->exclusive_lock != nullptr) {
+      ictx->exclusive_lock->block_requests(-EBUSY);
+      ictx->exclusive_lock->acquire_lock(gather_ctx->new_sub());
+    }
+  }
+
+  gather_ctx->activate();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::handle_acquire_image_exclusive_locks(
+    int r) {
+  ldout(m_cct, 10) << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(m_cct) << "failed to acquire image exclusive locks: "
+                 << cpp_strerror(r) << dendl;
+    m_ret_code = r;
+    if (!m_quiesce_requests->empty()) {
+      notify_unquiesce();
+    } else {
+      remove_snap_metadata();
+    }
+    return;
+  }
+
+  finish(0);
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::notify_unquiesce() {
+  ldout(m_cct, 10) << dendl;
+
+  ceph_assert(m_quiesce_requests->size() == m_image_ctxs->size());
+
+  auto ctx = librbd::util::create_context_callback<
+    GroupPrepareImagesRequest<I>,
+    &GroupPrepareImagesRequest<I>::handle_notify_unquiesce>(this);
+  auto gather_ctx = new C_Gather(m_cct, ctx);
+
+  int image_count = m_image_ctxs->size();
+  for (int i = 0; i < image_count; ++i) {
+    auto ictx = (*m_image_ctxs)[i];
+    ictx->image_watcher->notify_unquiesce((*m_quiesce_requests)[i],
+                                          gather_ctx->new_sub());
+  }
+
+  gather_ctx->activate();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::handle_notify_unquiesce(int r) {
+  ldout(m_cct, 10) << "r=" << r << dendl;
+
+  if (r < 0) {
+    // ignore error
+    lderr(m_cct) << "failed to notify the unquiesce requests: "
+                 << cpp_strerror(r) << dendl;
+  }
+
+  remove_snap_metadata();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::remove_snap_metadata() {
+  ldout(m_cct, 10) << dendl;
+
+  int r = cls_client::group_snap_remove(
+    &m_group_ioctx, librbd::util::group_header_name(m_group_id),
+    m_group_snap->id);
+  if (r < 0) {
+    // ignore error
+    lderr(m_cct) << "failed to remove group snapshot metadata: "
+                 << cpp_strerror(r) << dendl;
+  }
+
+  close_images();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::close_images() {
+  ldout(m_cct, 10) << dendl;
+
+  if (m_image_ctxs->empty()) {
+    finish(m_ret_code);
+    return;
+  }
+
+  auto ctx = librbd::util::create_context_callback<
+    GroupPrepareImagesRequest<I>,
+    &GroupPrepareImagesRequest<I>::handle_close_images>(this);
+  auto gather_ctx = new C_Gather(m_cct, ctx);
+
+  for (auto ictx: *m_image_ctxs) {
+    ictx->state->close(gather_ctx->new_sub());
+  }
+
+  gather_ctx->activate();
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::handle_close_images(int r) {
+  ldout(m_cct, 10) << "r=" << r << dendl;
+
+  if (r < 0) {
+    //ignore error
+    lderr(m_cct) << "failed to close images: " << cpp_strerror(r) << dendl;
+  }
+
+  m_image_ctxs->clear();
+  finish(m_ret_code);
+}
+
+template <typename I>
+void GroupPrepareImagesRequest<I>::finish(int r) {
+  ldout(m_cct, 10) << "r=" << r << dendl;
+
+  m_on_finish->complete(r);
+  delete this;
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+template class librbd::mirror::snapshot::GroupPrepareImagesRequest<librbd::ImageCtx>;
diff --git a/src/librbd/mirror/snapshot/GroupPrepareImagesRequest.h b/src/librbd/mirror/snapshot/GroupPrepareImagesRequest.h
new file mode 100644 (file)
index 0000000..83395c6
--- /dev/null
@@ -0,0 +1,138 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_MIRROR_SNAPSHOT_GROUP_PREPARE_IMAGES_REQUEST_H
+#define CEPH_LIBRBD_MIRROR_SNAPSHOT_GROUP_PREPARE_IMAGES_REQUEST_H
+
+#include "include/buffer.h"
+#include "include/rados/librados.hpp"
+#include "librbd/ImageWatcher.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/internal.h"
+#include "librbd/mirror/snapshot/Types.h"
+
+#include <string>
+#include <set>
+
+struct Context;
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace mirror {
+namespace snapshot {
+
+template <typename ImageCtxT = librbd::ImageCtx>
+class GroupPrepareImagesRequest {
+public:
+  static GroupPrepareImagesRequest *create(
+      librados::IoCtx& group_ioctx, const std::string& group_id,
+      cls::rbd::MirrorSnapshotState state, uint32_t flags,
+      cls::rbd::GroupSnapshot *group_snap,
+      std::vector<librbd::ImageCtx *> *image_ctxs,
+      std::vector<uint64_t> *quiesce_requests,
+      Context *on_finish) {
+    return new GroupPrepareImagesRequest(
+      group_ioctx, group_id, state, flags, group_snap, image_ctxs,
+      quiesce_requests, on_finish);
+  }
+
+  GroupPrepareImagesRequest(
+    librados::IoCtx& group_ioctx, const std::string& group_id,
+    cls::rbd::MirrorSnapshotState state, uint32_t flags,
+    cls::rbd::GroupSnapshot *group_snap,
+    std::vector<librbd::ImageCtx *> *image_ctxs,
+    std::vector<uint64_t> *quiesce_requests,
+    Context *on_finish);
+
+  void send();
+
+private:
+  /**
+   *@verbatim
+   *
+   *                <start>
+   *                   |
+   *                   v                        (on error)
+   *                LIST GROUP IMAGES--------------------------------------------------->
+   *                   |                                                   ^             |
+   *                   v                                                   |             |
+   *                OPEN GROUP IMAGES                                      |             |
+   *                   |          \             (on error)                 |             |
+   *                   v           \------------------------------> CLOSE IMAGES         |
+   *   . . . . . .  SET SNAP METADATA                                      ^             |
+   *   . (skip quiesce |          \             (on error)                 |             |
+   *   .  flag)        v           \------------------------------> REMOVE SNAP METADATA |
+   *   .            NOTIFY QUIESCE REQUESTS                                ^             |
+   *   .               |          \             (on error)                 |             |
+   *   .               v           \------------------------------> NOTIFY UNQUIESCE     |
+   *   . . . . . >  ACQUIRE IMAGE EXCLUSIVE LOCKS                          ^             |
+   *                   |          \             (on error)                 |             |
+   *                   |           \----------------------------------------             |
+   *                   |                                                                 |
+   *                   v                                                                 v
+   *                <finish>-----------------------<--------------------------------------
+   *
+   * @endverbatim
+   */
+
+  librados::IoCtx& m_group_ioctx;
+  std::string m_group_id;
+  cls::rbd::MirrorSnapshotState m_state;
+  const uint32_t m_flags;
+  cls::rbd::GroupSnapshot *m_group_snap;
+  std::vector<ImageCtx *> *m_image_ctxs;
+  std::vector<uint64_t> *m_quiesce_requests;
+  Context *m_on_finish;
+
+  CephContext *m_cct;
+
+  librados::IoCtx m_default_ns_ioctx;
+  uint64_t m_internal_flags;
+  std::set<std::string> m_mirror_peer_uuids;
+  bufferlist m_outbl;
+  NoOpProgressContext m_prog_ctx;
+  int m_ret_code = 0;
+  cls::rbd::GroupImageSpec m_start_after;
+  std::vector<cls::rbd::GroupImageStatus> m_images;
+
+  void check_snap_create_flags();
+
+  void get_mirror_peer_list();
+  void handle_get_mirror_peer_list(int r);
+
+  void list_group_images();
+  void handle_list_group_images(int r);
+
+  void open_group_images();
+  void handle_open_group_images(int r);
+
+  void set_snap_metadata();
+
+  void notify_quiesce();
+  void handle_notify_quiesce(int r);
+
+  void acquire_image_exclusive_locks();
+  void handle_acquire_image_exclusive_locks(int r);
+
+  void finish(int r);
+
+  // cleanup
+  void remove_snap_metadata();
+
+  void notify_unquiesce();
+  void handle_notify_unquiesce(int r);
+
+  void close_images();
+  void handle_close_images(int r);
+
+};
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+extern template class librbd::mirror::snapshot::GroupPrepareImagesRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_MIRROR_SNAPSHOT_GROUP_PREPARE_IMAGES_REQUEST_H