summaryrefslogtreecommitdiffstats
path: root/src/librbd/image/RefreshRequest.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/librbd/image/RefreshRequest.cc1575
1 files changed, 1575 insertions, 0 deletions
diff --git a/src/librbd/image/RefreshRequest.cc b/src/librbd/image/RefreshRequest.cc
new file mode 100644
index 000000000..24159c55b
--- /dev/null
+++ b/src/librbd/image/RefreshRequest.cc
@@ -0,0 +1,1575 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/ceph_assert.h"
+
+#include "librbd/image/RefreshRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "cls/lock/cls_lock_client.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageWatcher.h"
+#include "librbd/Journal.h"
+#include "librbd/ObjectMap.h"
+#include "librbd/Utils.h"
+#include "librbd/deep_copy/Utils.h"
+#include "librbd/image/GetMetadataRequest.h"
+#include "librbd/image/RefreshParentRequest.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageDispatchSpec.h"
+#include "librbd/io/ImageDispatcherInterface.h"
+#include "librbd/journal/Policy.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::image::RefreshRequest: "
+
+namespace librbd {
+namespace image {
+
+using util::create_rados_callback;
+using util::create_async_context_callback;
+using util::create_context_callback;
+
+template <typename I>
+RefreshRequest<I>::RefreshRequest(I &image_ctx, bool acquiring_lock,
+ bool skip_open_parent, Context *on_finish)
+ : m_image_ctx(image_ctx), m_acquiring_lock(acquiring_lock),
+ m_skip_open_parent_image(skip_open_parent),
+ m_on_finish(create_async_context_callback(m_image_ctx, on_finish)),
+ m_error_result(0), m_flush_aio(false), m_exclusive_lock(nullptr),
+ m_object_map(nullptr), m_journal(nullptr), m_refresh_parent(nullptr) {
+ m_pool_metadata_io_ctx.dup(image_ctx.md_ctx);
+ m_pool_metadata_io_ctx.set_namespace("");
+}
+
+template <typename I>
+RefreshRequest<I>::~RefreshRequest() {
+ // these require state machine to close
+ ceph_assert(m_exclusive_lock == nullptr);
+ ceph_assert(m_object_map == nullptr);
+ ceph_assert(m_journal == nullptr);
+ ceph_assert(m_refresh_parent == nullptr);
+ ceph_assert(!m_blocked_writes);
+}
+
+template <typename I>
+void RefreshRequest<I>::send() {
+ if (m_image_ctx.old_format) {
+ send_v1_read_header();
+ } else {
+ send_v2_get_mutable_metadata();
+ }
+}
+
+template <typename I>
+void RefreshRequest<I>::send_get_migration_header() {
+ if (m_image_ctx.ignore_migrating) {
+ m_migration_spec = {};
+ if (m_image_ctx.old_format) {
+ send_v1_get_snapshots();
+ } else {
+ send_v2_get_metadata();
+ }
+ return;
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::migration_get_start(&op);
+
+ using klass = RefreshRequest<I>;
+ librados::AioCompletion *comp =
+ create_rados_callback<klass, &klass::handle_get_migration_header>(this);
+ m_out_bl.clear();
+ m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
+ &m_out_bl);
+ comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_get_migration_header(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result >= 0) {
+ auto it = m_out_bl.cbegin();
+ *result = cls_client::migration_get_finish(&it, &m_migration_spec);
+ } else if (*result == -ENOENT) {
+ ldout(cct, 5) << this << " " << __func__ << ": no migration header found"
+ << ", retrying" << dendl;
+ send();
+ return nullptr;
+ }
+
+ if (*result < 0) {
+ lderr(cct) << "failed to retrieve migration header: "
+ << cpp_strerror(*result) << dendl;
+ return m_on_finish;
+ }
+
+ switch(m_migration_spec.header_type) {
+ case cls::rbd::MIGRATION_HEADER_TYPE_SRC:
+ if (!m_read_only) {
+ lderr(cct) << "image being migrated" << dendl;
+ *result = -EROFS;
+ return m_on_finish;
+ }
+ ldout(cct, 1) << this << " " << __func__ << ": migrating to: "
+ << m_migration_spec << dendl;
+ break;
+ case cls::rbd::MIGRATION_HEADER_TYPE_DST:
+ ldout(cct, 1) << this << " " << __func__ << ": migrating from: "
+ << m_migration_spec << dendl;
+ switch (m_migration_spec.state) {
+ case cls::rbd::MIGRATION_STATE_PREPARING:
+ ldout(cct, 5) << this << " " << __func__ << ": current migration state: "
+ << m_migration_spec.state << ", retrying" << dendl;
+ send();
+ return nullptr;
+ case cls::rbd::MIGRATION_STATE_PREPARED:
+ case cls::rbd::MIGRATION_STATE_EXECUTING:
+ case cls::rbd::MIGRATION_STATE_EXECUTED:
+ break;
+ case cls::rbd::MIGRATION_STATE_ABORTING:
+ if (!m_read_only) {
+ lderr(cct) << this << " " << __func__ << ": migration is being aborted"
+ << dendl;
+ *result = -EROFS;
+ return m_on_finish;
+ }
+ break;
+ default:
+ lderr(cct) << this << " " << __func__ << ": migration is in an "
+ << "unexpected state" << dendl;
+ *result = -EINVAL;
+ return m_on_finish;
+ }
+ break;
+ default:
+ ldout(cct, 1) << this << " " << __func__ << ": migration type "
+ << m_migration_spec.header_type << dendl;
+ *result = -EBADMSG;
+ return m_on_finish;
+ }
+
+ if (m_image_ctx.old_format) {
+ send_v1_get_snapshots();
+ } else {
+ send_v2_get_metadata();
+ }
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v1_read_header() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ librados::ObjectReadOperation op;
+ op.read(0, 0, nullptr, nullptr);
+
+ using klass = RefreshRequest<I>;
+ librados::AioCompletion *comp = create_rados_callback<
+ klass, &klass::handle_v1_read_header>(this);
+ m_out_bl.clear();
+ int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v1_read_header(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": " << "r=" << *result << dendl;
+
+ rbd_obj_header_ondisk v1_header;
+ bool migrating = false;
+ if (*result < 0) {
+ return m_on_finish;
+ } else if (m_out_bl.length() < sizeof(v1_header)) {
+ lderr(cct) << "v1 header too small" << dendl;
+ *result = -EIO;
+ return m_on_finish;
+ } else if (memcmp(RBD_HEADER_TEXT, m_out_bl.c_str(),
+ sizeof(RBD_HEADER_TEXT)) != 0) {
+ if (memcmp(RBD_MIGRATE_HEADER_TEXT, m_out_bl.c_str(),
+ sizeof(RBD_MIGRATE_HEADER_TEXT)) == 0) {
+ ldout(cct, 1) << this << " " << __func__ << ": migration v1 header detected"
+ << dendl;
+ migrating = true;
+ } else {
+ lderr(cct) << "unrecognized v1 header" << dendl;
+ *result = -ENXIO;
+ return m_on_finish;
+ }
+ }
+
+ {
+ std::shared_lock image_locker{m_image_ctx.image_lock};
+ m_read_only = m_image_ctx.read_only;
+ m_read_only_flags = m_image_ctx.read_only_flags;
+ }
+
+ memcpy(&v1_header, m_out_bl.c_str(), sizeof(v1_header));
+ m_order = v1_header.options.order;
+ m_size = v1_header.image_size;
+ m_object_prefix = v1_header.block_name;
+ if (migrating) {
+ send_get_migration_header();
+ } else {
+ m_migration_spec = {};
+ send_v1_get_snapshots();
+ }
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v1_get_snapshots() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::old_snapshot_list_start(&op);
+
+ using klass = RefreshRequest<I>;
+ librados::AioCompletion *comp = create_rados_callback<
+ klass, &klass::handle_v1_get_snapshots>(this);
+ m_out_bl.clear();
+ int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v1_get_snapshots(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": " << "r=" << *result << dendl;
+
+ std::vector<std::string> snap_names;
+ std::vector<uint64_t> snap_sizes;
+ if (*result >= 0) {
+ auto it = m_out_bl.cbegin();
+ *result = cls_client::old_snapshot_list_finish(&it, &snap_names,
+ &snap_sizes, &m_snapc);
+ }
+
+ if (*result < 0) {
+ lderr(cct) << "failed to retrieve v1 snapshots: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ if (!m_snapc.is_valid()) {
+ lderr(cct) << "v1 image snap context is invalid" << dendl;
+ *result = -EIO;
+ return m_on_finish;
+ }
+
+ m_snap_infos.clear();
+ for (size_t i = 0; i < m_snapc.snaps.size(); ++i) {
+ m_snap_infos.push_back({m_snapc.snaps[i],
+ {cls::rbd::UserSnapshotNamespace{}},
+ snap_names[i], snap_sizes[i], {}, 0});
+ }
+
+ send_v1_get_locks();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v1_get_locks() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ librados::ObjectReadOperation op;
+ rados::cls::lock::get_lock_info_start(&op, RBD_LOCK_NAME);
+
+ using klass = RefreshRequest<I>;
+ librados::AioCompletion *comp = create_rados_callback<
+ klass, &klass::handle_v1_get_locks>(this);
+ m_out_bl.clear();
+ int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v1_get_locks(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": "
+ << "r=" << *result << dendl;
+
+ if (*result >= 0) {
+ auto it = m_out_bl.cbegin();
+ ClsLockType lock_type;
+ *result = rados::cls::lock::get_lock_info_finish(&it, &m_lockers,
+ &lock_type, &m_lock_tag);
+ if (*result >= 0) {
+ m_exclusive_locked = (lock_type == ClsLockType::EXCLUSIVE);
+ }
+ }
+
+ if (*result < 0) {
+ lderr(cct) << "failed to retrieve locks: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ send_v1_apply();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v1_apply() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ // ensure we are not in a rados callback when applying updates
+ using klass = RefreshRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_v1_apply>(this);
+ m_image_ctx.op_work_queue->queue(ctx, 0);
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v1_apply(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ apply();
+ return send_flush_aio();
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_get_mutable_metadata() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ uint64_t snap_id;
+ {
+ std::shared_lock image_locker{m_image_ctx.image_lock};
+ snap_id = m_image_ctx.snap_id;
+ m_read_only = m_image_ctx.read_only;
+ m_read_only_flags = m_image_ctx.read_only_flags;
+ }
+
+ // mask out the non-primary read-only flag since its state can change
+ bool read_only = (
+ ((m_read_only_flags & ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY) != 0) ||
+ (snap_id != CEPH_NOSNAP));
+ librados::ObjectReadOperation op;
+ cls_client::get_size_start(&op, CEPH_NOSNAP);
+ cls_client::get_features_start(&op, read_only);
+ cls_client::get_flags_start(&op, CEPH_NOSNAP);
+ cls_client::get_snapcontext_start(&op);
+ rados::cls::lock::get_lock_info_start(&op, RBD_LOCK_NAME);
+
+ using klass = RefreshRequest<I>;
+ librados::AioCompletion *comp = create_rados_callback<
+ klass, &klass::handle_v2_get_mutable_metadata>(this);
+ m_out_bl.clear();
+ int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_get_mutable_metadata(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": "
+ << "r=" << *result << dendl;
+
+ auto it = m_out_bl.cbegin();
+ if (*result >= 0) {
+ uint8_t order;
+ *result = cls_client::get_size_finish(&it, &m_size, &order);
+ }
+
+ if (*result >= 0) {
+ *result = cls_client::get_features_finish(&it, &m_features,
+ &m_incompatible_features);
+ }
+
+ if (*result >= 0) {
+ *result = cls_client::get_flags_finish(&it, &m_flags);
+ }
+
+ if (*result >= 0) {
+ *result = cls_client::get_snapcontext_finish(&it, &m_snapc);
+ }
+
+ if (*result >= 0) {
+ ClsLockType lock_type;
+ *result = rados::cls::lock::get_lock_info_finish(&it, &m_lockers,
+ &lock_type, &m_lock_tag);
+ if (*result >= 0) {
+ m_exclusive_locked = (lock_type == ClsLockType::EXCLUSIVE);
+ }
+ }
+
+ if (*result < 0) {
+ lderr(cct) << "failed to retrieve mutable metadata: "
+ << cpp_strerror(*result) << dendl;
+ return m_on_finish;
+ }
+
+ uint64_t unsupported = m_incompatible_features & ~RBD_FEATURES_ALL;
+ if (unsupported != 0ULL) {
+ lderr(cct) << "Image uses unsupported features: " << unsupported << dendl;
+ *result = -ENOSYS;
+ return m_on_finish;
+ }
+
+ if (!m_snapc.is_valid()) {
+ lderr(cct) << "image snap context is invalid!" << dendl;
+ *result = -EIO;
+ return m_on_finish;
+ }
+
+ if (m_acquiring_lock && (m_features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) {
+ ldout(cct, 5) << "ignoring dynamically disabled exclusive lock" << dendl;
+ m_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ m_incomplete_update = true;
+ } else {
+ m_incomplete_update = false;
+ }
+
+ if (((m_incompatible_features & RBD_FEATURE_NON_PRIMARY) != 0U) &&
+ ((m_read_only_flags & IMAGE_READ_ONLY_FLAG_NON_PRIMARY) == 0U) &&
+ ((m_image_ctx.read_only_mask & IMAGE_READ_ONLY_FLAG_NON_PRIMARY) != 0U)) {
+ // implies we opened a non-primary image in R/W mode
+ ldout(cct, 5) << "adding non-primary read-only image flag" << dendl;
+ m_read_only_flags |= IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
+ } else if ((((m_incompatible_features & RBD_FEATURE_NON_PRIMARY) == 0U) ||
+ ((m_image_ctx.read_only_mask &
+ IMAGE_READ_ONLY_FLAG_NON_PRIMARY) == 0U)) &&
+ ((m_read_only_flags & IMAGE_READ_ONLY_FLAG_NON_PRIMARY) != 0U)) {
+ ldout(cct, 5) << "removing non-primary read-only image flag" << dendl;
+ m_read_only_flags &= ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
+ }
+ m_read_only = (m_read_only_flags != 0U);
+
+ m_legacy_parent = false;
+ send_v2_get_parent();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_get_parent() {
+ // NOTE: remove support when Mimic is EOLed
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": legacy=" << m_legacy_parent
+ << dendl;
+
+ librados::ObjectReadOperation op;
+ if (!m_legacy_parent) {
+ cls_client::parent_get_start(&op);
+ cls_client::parent_overlap_get_start(&op, CEPH_NOSNAP);
+ } else {
+ cls_client::get_parent_start(&op, CEPH_NOSNAP);
+ }
+
+ auto aio_comp = create_rados_callback<
+ RefreshRequest<I>, &RefreshRequest<I>::handle_v2_get_parent>(this);
+ m_out_bl.clear();
+ m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, aio_comp, &op,
+ &m_out_bl);
+ aio_comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_get_parent(int *result) {
+ // NOTE: remove support when Mimic is EOLed
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ auto it = m_out_bl.cbegin();
+ if (!m_legacy_parent) {
+ if (*result >= 0) {
+ *result = cls_client::parent_get_finish(&it, &m_parent_md.spec);
+ }
+
+ std::optional<uint64_t> parent_overlap;
+ if (*result >= 0) {
+ *result = cls_client::parent_overlap_get_finish(&it, &parent_overlap);
+ }
+
+ if (*result >= 0) {
+ if (parent_overlap) {
+ m_parent_md.overlap = *parent_overlap;
+ m_head_parent_overlap = true;
+ } else {
+ m_parent_md.overlap = 0;
+ m_head_parent_overlap = false;
+ }
+ }
+ } else if (*result >= 0) {
+ *result = cls_client::get_parent_finish(&it, &m_parent_md.spec,
+ &m_parent_md.overlap);
+ m_head_parent_overlap = true;
+ }
+
+ if (*result == -EOPNOTSUPP && !m_legacy_parent) {
+ ldout(cct, 10) << "retrying using legacy parent method" << dendl;
+ m_legacy_parent = true;
+ send_v2_get_parent();
+ return nullptr;
+ } else if (*result < 0) {
+ lderr(cct) << "failed to retrieve parent: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ if ((m_features & RBD_FEATURE_MIGRATING) != 0) {
+ ldout(cct, 1) << "migrating feature set" << dendl;
+ send_get_migration_header();
+ } else {
+ m_migration_spec = {};
+ send_v2_get_metadata();
+ }
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_get_metadata() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ auto ctx = create_context_callback<
+ RefreshRequest<I>, &RefreshRequest<I>::handle_v2_get_metadata>(this);
+ m_metadata.clear();
+ auto req = GetMetadataRequest<I>::create(
+ m_image_ctx.md_ctx, m_image_ctx.header_oid, true,
+ ImageCtx::METADATA_CONF_PREFIX, ImageCtx::METADATA_CONF_PREFIX, 0U,
+ &m_metadata, ctx);
+ req->send();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_get_metadata(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to retrieve metadata: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ send_v2_get_pool_metadata();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_get_pool_metadata() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ auto ctx = create_context_callback<
+ RefreshRequest<I>, &RefreshRequest<I>::handle_v2_get_pool_metadata>(this);
+ auto req = GetMetadataRequest<I>::create(
+ m_pool_metadata_io_ctx, RBD_INFO, true, ImageCtx::METADATA_CONF_PREFIX,
+ ImageCtx::METADATA_CONF_PREFIX, 0U, &m_metadata, ctx);
+ req->send();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_get_pool_metadata(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to retrieve pool metadata: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ bool thread_safe = m_image_ctx.image_watcher->is_unregistered();
+ m_image_ctx.apply_metadata(m_metadata, thread_safe);
+
+ send_v2_get_op_features();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_get_op_features() {
+ if ((m_features & RBD_FEATURE_OPERATIONS) == 0LL) {
+ m_op_features = 0;
+ send_v2_get_group();
+ return;
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::op_features_get_start(&op);
+
+ librados::AioCompletion *comp = create_rados_callback<
+ RefreshRequest<I>, &RefreshRequest<I>::handle_v2_get_op_features>(this);
+ m_out_bl.clear();
+ int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_get_op_features(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": "
+ << "r=" << *result << dendl;
+
+ // -EOPNOTSUPP handler not required since feature bit implies OSD
+ // supports the method
+ if (*result >= 0) {
+ auto it = m_out_bl.cbegin();
+ *result = cls_client::op_features_get_finish(&it, &m_op_features);
+ }
+
+ if (*result < 0) {
+ lderr(cct) << "failed to retrieve op features: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ send_v2_get_group();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_get_group() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::image_group_get_start(&op);
+
+ using klass = RefreshRequest<I>;
+ librados::AioCompletion *comp = create_rados_callback<
+ klass, &klass::handle_v2_get_group>(this);
+ m_out_bl.clear();
+ int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_get_group(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": "
+ << "r=" << *result << dendl;
+
+ if (*result >= 0) {
+ auto it = m_out_bl.cbegin();
+ *result = cls_client::image_group_get_finish(&it, &m_group_spec);
+ }
+
+ if (*result == -EOPNOTSUPP) {
+ m_group_spec = {};
+ } else if (*result < 0) {
+ lderr(cct) << "failed to retrieve group: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ m_legacy_snapshot = LEGACY_SNAPSHOT_DISABLED;
+ send_v2_get_snapshots();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_get_snapshots() {
+ m_snap_infos.resize(m_snapc.snaps.size());
+ m_snap_flags.resize(m_snapc.snaps.size());
+ m_snap_parents.resize(m_snapc.snaps.size());
+ m_snap_protection.resize(m_snapc.snaps.size());
+
+ if (m_snapc.snaps.empty()) {
+ send_v2_refresh_parent();
+ return;
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ librados::ObjectReadOperation op;
+ for (auto snap_id : m_snapc.snaps) {
+ if (m_legacy_snapshot != LEGACY_SNAPSHOT_DISABLED) {
+ /// NOTE: remove after Luminous is retired
+ cls_client::get_snapshot_name_start(&op, snap_id);
+ cls_client::get_size_start(&op, snap_id);
+ if (m_legacy_snapshot != LEGACY_SNAPSHOT_ENABLED_NO_TIMESTAMP) {
+ cls_client::get_snapshot_timestamp_start(&op, snap_id);
+ }
+ } else {
+ cls_client::snapshot_get_start(&op, snap_id);
+ }
+
+ if (m_legacy_parent) {
+ cls_client::get_parent_start(&op, snap_id);
+ } else {
+ cls_client::parent_overlap_get_start(&op, snap_id);
+ }
+
+ cls_client::get_flags_start(&op, snap_id);
+ cls_client::get_protection_status_start(&op, snap_id);
+ }
+
+ using klass = RefreshRequest<I>;
+ librados::AioCompletion *comp = create_rados_callback<
+ klass, &klass::handle_v2_get_snapshots>(this);
+ m_out_bl.clear();
+ int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_get_snapshots(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": " << "r=" << *result << dendl;
+
+ auto it = m_out_bl.cbegin();
+ for (size_t i = 0; i < m_snapc.snaps.size(); ++i) {
+ if (m_legacy_snapshot != LEGACY_SNAPSHOT_DISABLED) {
+ /// NOTE: remove after Luminous is retired
+ std::string snap_name;
+ if (*result >= 0) {
+ *result = cls_client::get_snapshot_name_finish(&it, &snap_name);
+ }
+
+ uint64_t snap_size;
+ if (*result >= 0) {
+ uint8_t order;
+ *result = cls_client::get_size_finish(&it, &snap_size, &order);
+ }
+
+ utime_t snap_timestamp;
+ if (*result >= 0 &&
+ m_legacy_snapshot != LEGACY_SNAPSHOT_ENABLED_NO_TIMESTAMP) {
+ /// NOTE: remove after Jewel is retired
+ *result = cls_client::get_snapshot_timestamp_finish(&it,
+ &snap_timestamp);
+ }
+
+ if (*result >= 0) {
+ m_snap_infos[i] = {m_snapc.snaps[i],
+ {cls::rbd::UserSnapshotNamespace{}},
+ snap_name, snap_size, snap_timestamp, 0};
+ }
+ } else if (*result >= 0) {
+ *result = cls_client::snapshot_get_finish(&it, &m_snap_infos[i]);
+ }
+
+ if (*result >= 0) {
+ if (m_legacy_parent) {
+ *result = cls_client::get_parent_finish(&it, &m_snap_parents[i].spec,
+ &m_snap_parents[i].overlap);
+ } else {
+ std::optional<uint64_t> parent_overlap;
+ *result = cls_client::parent_overlap_get_finish(&it, &parent_overlap);
+ if (*result >= 0) {
+ if (parent_overlap && m_parent_md.spec.pool_id > -1) {
+ m_snap_parents[i].spec = m_parent_md.spec;
+ m_snap_parents[i].overlap = *parent_overlap;
+ } else {
+ m_snap_parents[i] = {};
+ }
+ }
+ }
+ }
+
+ if (*result >= 0) {
+ *result = cls_client::get_flags_finish(&it, &m_snap_flags[i]);
+ }
+
+ if (*result >= 0) {
+ *result = cls_client::get_protection_status_finish(
+ &it, &m_snap_protection[i]);
+ }
+
+ if (*result < 0) {
+ break;
+ }
+ }
+
+ if (*result == -ENOENT && m_enoent_retries++ < MAX_ENOENT_RETRIES) {
+ ldout(cct, 10) << "out-of-sync snapshot state detected, retrying" << dendl;
+ send_v2_get_mutable_metadata();
+ return nullptr;
+ } else if (m_legacy_snapshot == LEGACY_SNAPSHOT_DISABLED &&
+ *result == -EOPNOTSUPP) {
+ ldout(cct, 10) << "retrying using legacy snapshot methods" << dendl;
+ m_legacy_snapshot = LEGACY_SNAPSHOT_ENABLED;
+ send_v2_get_snapshots();
+ return nullptr;
+ } else if (m_legacy_snapshot == LEGACY_SNAPSHOT_ENABLED &&
+ *result == -EOPNOTSUPP) {
+ ldout(cct, 10) << "retrying using legacy snapshot methods (jewel)" << dendl;
+ m_legacy_snapshot = LEGACY_SNAPSHOT_ENABLED_NO_TIMESTAMP;
+ send_v2_get_snapshots();
+ return nullptr;
+ } else if (*result < 0) {
+ lderr(cct) << "failed to retrieve snapshots: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ send_v2_refresh_parent();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_refresh_parent() {
+ {
+ std::shared_lock image_locker{m_image_ctx.image_lock};
+
+ ParentImageInfo parent_md;
+ MigrationInfo migration_info;
+ int r = get_parent_info(m_image_ctx.snap_id, &parent_md, &migration_info);
+ if (!m_skip_open_parent_image && (r < 0 ||
+ RefreshParentRequest<I>::is_refresh_required(m_image_ctx, parent_md,
+ migration_info))) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ using klass = RefreshRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_v2_refresh_parent>(this);
+ m_refresh_parent = RefreshParentRequest<I>::create(
+ m_image_ctx, parent_md, migration_info, ctx);
+ }
+ }
+
+ if (m_refresh_parent != nullptr) {
+ m_refresh_parent->send();
+ } else {
+ send_v2_init_exclusive_lock();
+ }
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_refresh_parent(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result == -ENOENT && m_enoent_retries++ < MAX_ENOENT_RETRIES) {
+ ldout(cct, 10) << "out-of-sync parent info detected, retrying" << dendl;
+ ceph_assert(m_refresh_parent != nullptr);
+ delete m_refresh_parent;
+ m_refresh_parent = nullptr;
+ send_v2_get_mutable_metadata();
+ return nullptr;
+ } else if (*result < 0) {
+ lderr(cct) << "failed to refresh parent image: " << cpp_strerror(*result)
+ << dendl;
+ save_result(result);
+ send_v2_apply();
+ return nullptr;
+ }
+
+ send_v2_init_exclusive_lock();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_init_exclusive_lock() {
+ if ((m_features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0 ||
+ m_read_only || !m_image_ctx.snap_name.empty() ||
+ m_image_ctx.exclusive_lock != nullptr) {
+ send_v2_open_object_map();
+ return;
+ }
+
+ // implies exclusive lock dynamically enabled or image open in-progress
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ // TODO need safe shut down
+ m_exclusive_lock = m_image_ctx.create_exclusive_lock();
+
+ using klass = RefreshRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_v2_init_exclusive_lock>(this);
+
+ std::shared_lock owner_locker{m_image_ctx.owner_lock};
+ m_exclusive_lock->init(m_features, ctx);
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_init_exclusive_lock(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to initialize exclusive lock: "
+ << cpp_strerror(*result) << dendl;
+ save_result(result);
+ }
+
+ // object map and journal will be opened when exclusive lock is
+ // acquired (if features are enabled)
+ send_v2_apply();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_open_journal() {
+ bool journal_disabled = (
+ (m_features & RBD_FEATURE_JOURNALING) == 0 ||
+ m_read_only ||
+ !m_image_ctx.snap_name.empty() ||
+ m_image_ctx.journal != nullptr ||
+ m_image_ctx.exclusive_lock == nullptr ||
+ !m_image_ctx.exclusive_lock->is_lock_owner());
+ bool journal_disabled_by_policy;
+ {
+ std::shared_lock image_locker{m_image_ctx.image_lock};
+ journal_disabled_by_policy = (
+ !journal_disabled &&
+ m_image_ctx.get_journal_policy()->journal_disabled());
+ }
+
+ if (journal_disabled || journal_disabled_by_policy) {
+ // journal dynamically enabled -- doesn't own exclusive lock
+ if ((m_features & RBD_FEATURE_JOURNALING) != 0 &&
+ !journal_disabled_by_policy &&
+ m_image_ctx.exclusive_lock != nullptr &&
+ m_image_ctx.journal == nullptr) {
+ auto ctx = new LambdaContext([this](int) {
+ send_v2_block_writes();
+ });
+ m_image_ctx.exclusive_lock->set_require_lock(
+ true, librbd::io::DIRECTION_BOTH, ctx);
+ return;
+ }
+
+ send_v2_block_writes();
+ return;
+ }
+
+ // implies journal dynamically enabled since ExclusiveLock will init
+ // the journal upon acquiring the lock
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ using klass = RefreshRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_v2_open_journal>(this);
+
+ // TODO need safe close
+ m_journal = m_image_ctx.create_journal();
+ m_journal->open(ctx);
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_open_journal(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to initialize journal: " << cpp_strerror(*result)
+ << dendl;
+ save_result(result);
+ }
+
+ send_v2_block_writes();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_block_writes() {
+ bool disabled_journaling = false;
+ {
+ std::shared_lock image_locker{m_image_ctx.image_lock};
+ disabled_journaling = ((m_features & RBD_FEATURE_EXCLUSIVE_LOCK) != 0 &&
+ (m_features & RBD_FEATURE_JOURNALING) == 0 &&
+ m_image_ctx.journal != nullptr);
+ }
+
+ if (!disabled_journaling) {
+ send_v2_apply();
+ return;
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ // we need to block writes temporarily to avoid in-flight journal
+ // writes
+ m_blocked_writes = true;
+ Context *ctx = create_context_callback<
+ RefreshRequest<I>, &RefreshRequest<I>::handle_v2_block_writes>(this);
+
+ std::shared_lock owner_locker{m_image_ctx.owner_lock};
+ m_image_ctx.io_image_dispatcher->block_writes(ctx);
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_block_writes(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to block writes: " << cpp_strerror(*result)
+ << dendl;
+ save_result(result);
+ }
+ send_v2_apply();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_open_object_map() {
+ if ((m_features & RBD_FEATURE_OBJECT_MAP) == 0 ||
+ m_image_ctx.object_map != nullptr ||
+ (m_image_ctx.snap_name.empty() &&
+ (m_read_only ||
+ m_image_ctx.exclusive_lock == nullptr ||
+ !m_image_ctx.exclusive_lock->is_lock_owner()))) {
+ send_v2_open_journal();
+ return;
+ }
+
+ // implies object map dynamically enabled or image open in-progress
+ // since SetSnapRequest loads the object map for a snapshot and
+ // ExclusiveLock loads the object map for HEAD
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ if (m_image_ctx.snap_name.empty()) {
+ m_object_map = m_image_ctx.create_object_map(CEPH_NOSNAP);
+ } else {
+ for (size_t snap_idx = 0; snap_idx < m_snap_infos.size(); ++snap_idx) {
+ if (m_snap_infos[snap_idx].name == m_image_ctx.snap_name) {
+ m_object_map = m_image_ctx.create_object_map(
+ m_snapc.snaps[snap_idx].val);
+ break;
+ }
+ }
+
+ if (m_object_map == nullptr) {
+ lderr(cct) << "failed to locate snapshot: " << m_image_ctx.snap_name
+ << dendl;
+ send_v2_open_journal();
+ return;
+ }
+ }
+
+ using klass = RefreshRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_v2_open_object_map>(this);
+ m_object_map->open(ctx);
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_open_object_map(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to open object map: " << cpp_strerror(*result)
+ << dendl;
+ m_object_map->put();
+ m_object_map = nullptr;
+
+ if (*result != -EFBIG) {
+ save_result(result);
+ }
+ }
+
+ send_v2_open_journal();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_apply() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ // ensure we are not in a rados callback when applying updates
+ using klass = RefreshRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_v2_apply>(this);
+ m_image_ctx.op_work_queue->queue(ctx, 0);
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_apply(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ apply();
+
+ return send_v2_finalize_refresh_parent();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::send_v2_finalize_refresh_parent() {
+ if (m_refresh_parent == nullptr) {
+ return send_v2_shut_down_exclusive_lock();
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ using klass = RefreshRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_v2_finalize_refresh_parent>(this);
+ m_refresh_parent->finalize(ctx);
+ return nullptr;
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_finalize_refresh_parent(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ ceph_assert(m_refresh_parent != nullptr);
+ delete m_refresh_parent;
+ m_refresh_parent = nullptr;
+
+ return send_v2_shut_down_exclusive_lock();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::send_v2_shut_down_exclusive_lock() {
+ if (m_exclusive_lock == nullptr) {
+ return send_v2_close_journal();
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ // exclusive lock feature was dynamically disabled. in-flight IO will be
+ // flushed and in-flight requests will be canceled before releasing lock
+ using klass = RefreshRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_v2_shut_down_exclusive_lock>(this);
+ m_exclusive_lock->shut_down(ctx);
+ return nullptr;
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_shut_down_exclusive_lock(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to shut down exclusive lock: "
+ << cpp_strerror(*result) << dendl;
+ save_result(result);
+ }
+
+ {
+ std::unique_lock owner_locker{m_image_ctx.owner_lock};
+ ceph_assert(m_image_ctx.exclusive_lock == nullptr);
+ }
+
+ ceph_assert(m_exclusive_lock != nullptr);
+ m_exclusive_lock->put();
+ m_exclusive_lock = nullptr;
+
+ return send_v2_close_journal();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::send_v2_close_journal() {
+ if (m_journal == nullptr) {
+ return send_v2_close_object_map();
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ // journal feature was dynamically disabled
+ using klass = RefreshRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_v2_close_journal>(this);
+ m_journal->close(ctx);
+ return nullptr;
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_close_journal(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result < 0) {
+ save_result(result);
+ lderr(cct) << "failed to close journal: " << cpp_strerror(*result)
+ << dendl;
+ }
+
+ ceph_assert(m_journal != nullptr);
+ m_journal->put();
+ m_journal = nullptr;
+
+ ceph_assert(m_blocked_writes);
+ m_blocked_writes = false;
+
+ m_image_ctx.io_image_dispatcher->unblock_writes();
+ return send_v2_close_object_map();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::send_v2_close_object_map() {
+ if (m_object_map == nullptr) {
+ return send_flush_aio();
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ // object map was dynamically disabled
+ using klass = RefreshRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_v2_close_object_map>(this);
+ m_object_map->close(ctx);
+ return nullptr;
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_close_object_map(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to close object map: " << cpp_strerror(*result)
+ << dendl;
+ }
+
+ ceph_assert(m_object_map != nullptr);
+
+ m_object_map->put();
+ m_object_map = nullptr;
+
+ return send_flush_aio();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::send_flush_aio() {
+ if (m_incomplete_update && m_error_result == 0) {
+ // if this was a partial refresh, notify ImageState
+ m_error_result = -ERESTART;
+ }
+
+ if (m_flush_aio) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ std::shared_lock owner_locker{m_image_ctx.owner_lock};
+ auto ctx = create_context_callback<
+ RefreshRequest<I>, &RefreshRequest<I>::handle_flush_aio>(this);
+ auto aio_comp = io::AioCompletion::create_and_start(
+ ctx, util::get_image_ctx(&m_image_ctx), io::AIO_TYPE_FLUSH);
+ auto req = io::ImageDispatchSpec::create_flush(
+ m_image_ctx, io::IMAGE_DISPATCH_LAYER_REFRESH, aio_comp,
+ io::FLUSH_SOURCE_REFRESH, {});
+ req->send();
+ return nullptr;
+ } else if (m_error_result < 0) {
+ // propagate saved error back to caller
+ Context *ctx = create_context_callback<
+ RefreshRequest<I>, &RefreshRequest<I>::handle_error>(this);
+ m_image_ctx.op_work_queue->queue(ctx, 0);
+ return nullptr;
+ }
+
+ return m_on_finish;
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_flush_aio(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ if (*result < 0) {
+ lderr(cct) << "failed to flush pending AIO: " << cpp_strerror(*result)
+ << dendl;
+ }
+
+ return handle_error(result);
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_error(int *result) {
+ if (m_error_result < 0) {
+ *result = m_error_result;
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+ }
+ return m_on_finish;
+}
+
+template <typename I>
+void RefreshRequest<I>::apply() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 20) << this << " " << __func__ << dendl;
+
+ std::scoped_lock locker{m_image_ctx.owner_lock, m_image_ctx.image_lock};
+
+ m_image_ctx.read_only_flags = m_read_only_flags;
+ m_image_ctx.read_only = m_read_only;
+ m_image_ctx.size = m_size;
+ m_image_ctx.lockers = m_lockers;
+ m_image_ctx.lock_tag = m_lock_tag;
+ m_image_ctx.exclusive_locked = m_exclusive_locked;
+
+ std::map<uint64_t, uint64_t> migration_reverse_snap_seq;
+
+ if (m_image_ctx.old_format) {
+ m_image_ctx.order = m_order;
+ m_image_ctx.features = 0;
+ m_image_ctx.flags = 0;
+ m_image_ctx.op_features = 0;
+ m_image_ctx.operations_disabled = false;
+ m_image_ctx.object_prefix = std::move(m_object_prefix);
+ m_image_ctx.init_layout(m_image_ctx.md_ctx.get_id());
+ } else {
+ // HEAD revision doesn't have a defined overlap so it's only
+ // applicable to snapshots
+ if (!m_head_parent_overlap) {
+ m_parent_md = {};
+ }
+
+ m_image_ctx.features = m_features;
+ m_image_ctx.flags = m_flags;
+ m_image_ctx.op_features = m_op_features;
+ m_image_ctx.operations_disabled = (
+ (m_op_features & ~RBD_OPERATION_FEATURES_ALL) != 0ULL);
+ m_image_ctx.group_spec = m_group_spec;
+
+ bool migration_info_valid;
+ int r = get_migration_info(&m_image_ctx.parent_md,
+ &m_image_ctx.migration_info,
+ &migration_info_valid);
+ ceph_assert(r == 0); // validated in refresh parent step
+
+ if (migration_info_valid) {
+ for (auto it : m_image_ctx.migration_info.snap_map) {
+ migration_reverse_snap_seq[it.second.front()] = it.first;
+ }
+ } else {
+ m_image_ctx.parent_md = m_parent_md;
+ m_image_ctx.migration_info = {};
+ }
+
+ librados::Rados rados(m_image_ctx.md_ctx);
+ int8_t require_osd_release;
+ r = rados.get_min_compatible_osd(&require_osd_release);
+ if (r == 0 && require_osd_release >= CEPH_RELEASE_OCTOPUS) {
+ m_image_ctx.enable_sparse_copyup = true;
+ }
+ }
+
+ for (size_t i = 0; i < m_snapc.snaps.size(); ++i) {
+ std::vector<librados::snap_t>::const_iterator it = std::find(
+ m_image_ctx.snaps.begin(), m_image_ctx.snaps.end(),
+ m_snapc.snaps[i].val);
+ if (it == m_image_ctx.snaps.end()) {
+ m_flush_aio = true;
+ ldout(cct, 20) << "new snapshot id=" << m_snapc.snaps[i].val
+ << " name=" << m_snap_infos[i].name
+ << " size=" << m_snap_infos[i].image_size
+ << dendl;
+ }
+ }
+
+ m_image_ctx.snaps.clear();
+ m_image_ctx.snap_info.clear();
+ m_image_ctx.snap_ids.clear();
+ auto overlap = m_image_ctx.parent_md.overlap;
+ for (size_t i = 0; i < m_snapc.snaps.size(); ++i) {
+ uint64_t flags = m_image_ctx.old_format ? 0 : m_snap_flags[i];
+ uint8_t protection_status = m_image_ctx.old_format ?
+ static_cast<uint8_t>(RBD_PROTECTION_STATUS_UNPROTECTED) :
+ m_snap_protection[i];
+ ParentImageInfo parent;
+ if (!m_image_ctx.old_format) {
+ if (!m_image_ctx.migration_info.empty()) {
+ parent = m_image_ctx.parent_md;
+ auto it = migration_reverse_snap_seq.find(m_snapc.snaps[i].val);
+ if (it != migration_reverse_snap_seq.end()) {
+ parent.spec.snap_id = it->second;
+ parent.overlap = m_snap_infos[i].image_size;
+ } else {
+ overlap = std::min(overlap, m_snap_infos[i].image_size);
+ parent.overlap = overlap;
+ }
+ } else {
+ parent = m_snap_parents[i];
+ }
+ }
+ m_image_ctx.add_snap(m_snap_infos[i].snapshot_namespace,
+ m_snap_infos[i].name, m_snapc.snaps[i].val,
+ m_snap_infos[i].image_size, parent,
+ protection_status, flags,
+ m_snap_infos[i].timestamp);
+ }
+ m_image_ctx.parent_md.overlap = std::min(overlap, m_image_ctx.size);
+ m_image_ctx.snapc = m_snapc;
+
+ if (m_image_ctx.snap_id != CEPH_NOSNAP &&
+ m_image_ctx.get_snap_id(m_image_ctx.snap_namespace,
+ m_image_ctx.snap_name) != m_image_ctx.snap_id) {
+ lderr(cct) << "tried to read from a snapshot that no longer exists: "
+ << m_image_ctx.snap_name << dendl;
+ m_image_ctx.snap_exists = false;
+ }
+
+ if (m_refresh_parent != nullptr) {
+ m_refresh_parent->apply();
+ }
+ if (m_image_ctx.data_ctx.is_valid()) {
+ m_image_ctx.data_ctx.selfmanaged_snap_set_write_ctx(m_image_ctx.snapc.seq,
+ m_image_ctx.snaps);
+ m_image_ctx.rebuild_data_io_context();
+ }
+
+ // handle dynamically enabled / disabled features
+ if (m_image_ctx.exclusive_lock != nullptr &&
+ !m_image_ctx.test_features(RBD_FEATURE_EXCLUSIVE_LOCK,
+ m_image_ctx.image_lock)) {
+ // disabling exclusive lock will automatically handle closing
+ // object map and journaling
+ ceph_assert(m_exclusive_lock == nullptr);
+ m_exclusive_lock = m_image_ctx.exclusive_lock;
+ } else {
+ if (m_exclusive_lock != nullptr) {
+ ceph_assert(m_image_ctx.exclusive_lock == nullptr);
+ std::swap(m_exclusive_lock, m_image_ctx.exclusive_lock);
+ }
+ if (!m_image_ctx.test_features(RBD_FEATURE_JOURNALING,
+ m_image_ctx.image_lock)) {
+ if (!m_image_ctx.clone_copy_on_read && m_image_ctx.journal != nullptr) {
+ m_image_ctx.exclusive_lock->unset_require_lock(io::DIRECTION_READ);
+ }
+ std::swap(m_journal, m_image_ctx.journal);
+ } else if (m_journal != nullptr) {
+ std::swap(m_journal, m_image_ctx.journal);
+ }
+ if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP,
+ m_image_ctx.image_lock) ||
+ m_object_map != nullptr) {
+ std::swap(m_object_map, m_image_ctx.object_map);
+ }
+ }
+}
+
+template <typename I>
+int RefreshRequest<I>::get_parent_info(uint64_t snap_id,
+ ParentImageInfo *parent_md,
+ MigrationInfo *migration_info) {
+ bool migration_info_valid;
+ int r = get_migration_info(parent_md, migration_info, &migration_info_valid);
+ if (r < 0) {
+ return r;
+ }
+
+ if (migration_info_valid) {
+ return 0;
+ } else if (snap_id == CEPH_NOSNAP) {
+ *parent_md = m_parent_md;
+ *migration_info = {};
+ return 0;
+ } else {
+ for (size_t i = 0; i < m_snapc.snaps.size(); ++i) {
+ if (m_snapc.snaps[i].val == snap_id) {
+ *parent_md = m_snap_parents[i];
+ *migration_info = {};
+ return 0;
+ }
+ }
+ }
+ return -ENOENT;
+}
+
+template <typename I>
+int RefreshRequest<I>::get_migration_info(ParentImageInfo *parent_md,
+ MigrationInfo *migration_info,
+ bool* migration_info_valid) {
+ CephContext *cct = m_image_ctx.cct;
+ if (m_migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_DST ||
+ (m_migration_spec.state != cls::rbd::MIGRATION_STATE_PREPARED &&
+ m_migration_spec.state != cls::rbd::MIGRATION_STATE_EXECUTING &&
+ m_migration_spec.state != cls::rbd::MIGRATION_STATE_ABORTING)) {
+ if (m_migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_SRC &&
+ m_migration_spec.pool_id != -1 &&
+ m_migration_spec.state != cls::rbd::MIGRATION_STATE_EXECUTED) {
+ lderr(cct) << this << " " << __func__ << ": invalid migration spec"
+ << dendl;
+ return -EINVAL;
+ }
+
+ *migration_info_valid = false;
+ return 0;
+ }
+
+ if (!m_migration_spec.source_spec.empty()) {
+ // use special pool id just to indicate a parent (migration source image)
+ // exists
+ parent_md->spec.pool_id = std::numeric_limits<int64_t>::max();
+ parent_md->spec.pool_namespace = "";
+ parent_md->spec.image_id = "";
+ } else {
+ parent_md->spec.pool_id = m_migration_spec.pool_id;
+ parent_md->spec.pool_namespace = m_migration_spec.pool_namespace;
+ parent_md->spec.image_id = m_migration_spec.image_id;
+ }
+ parent_md->spec.snap_id = CEPH_NOSNAP;
+ parent_md->overlap = std::min(m_size, m_migration_spec.overlap);
+
+ auto snap_seqs = m_migration_spec.snap_seqs;
+ // If new snapshots have been created on destination image after
+ // migration stared, map the source CEPH_NOSNAP to the earliest of
+ // these snapshots.
+ snapid_t snap_id = snap_seqs.empty() ? 0 : snap_seqs.rbegin()->second;
+ auto it = std::upper_bound(m_snapc.snaps.rbegin(), m_snapc.snaps.rend(),
+ snap_id);
+ if (it != m_snapc.snaps.rend()) {
+ snap_seqs[CEPH_NOSNAP] = *it;
+ } else {
+ snap_seqs[CEPH_NOSNAP] = CEPH_NOSNAP;
+ }
+
+ std::set<uint64_t> snap_ids;
+ for (auto& it : snap_seqs) {
+ snap_ids.insert(it.second);
+ }
+ uint64_t overlap = snap_ids.find(CEPH_NOSNAP) != snap_ids.end() ?
+ parent_md->overlap : 0;
+ for (size_t i = 0; i < m_snapc.snaps.size(); ++i) {
+ if (snap_ids.find(m_snapc.snaps[i].val) != snap_ids.end()) {
+ overlap = std::max(overlap, m_snap_infos[i].image_size);
+ }
+ }
+
+ *migration_info = {m_migration_spec.pool_id, m_migration_spec.pool_namespace,
+ m_migration_spec.image_name, m_migration_spec.image_id,
+ m_migration_spec.source_spec, {}, overlap,
+ m_migration_spec.flatten};
+ *migration_info_valid = true;
+
+ deep_copy::util::compute_snap_map(m_image_ctx.cct, 0, CEPH_NOSNAP, {},
+ snap_seqs, &migration_info->snap_map);
+ return 0;
+}
+
+} // namespace image
+} // namespace librbd
+
+template class librbd::image::RefreshRequest<librbd::ImageCtx>;