bool rollback = false;
BOOST_SCOPE_EXIT_ALL(ictx, &mirror_image_internal, &rollback) {
if (rollback) {
+ // restore the mask bit for treating the non-primary feature as read-only
+ ictx->image_lock.lock();
+ ictx->read_only_mask |= IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
+ ictx->image_lock.unlock();
+
+ ictx->state->handle_update_notification();
+
+ // attempt to restore the image state
CephContext *cct = ictx->cct;
mirror_image_internal.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED;
int r = cls_client::mirror_image_set(&ictx->md_ctx, ictx->id,
image_locker.unlock();
if (mirror_image_internal.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ // don't let the non-primary feature bit prevent image updates
+ ictx->image_lock.lock();
+ ictx->read_only_mask &= ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
+ ictx->image_lock.unlock();
+
+ r = ictx->state->refresh();
+ if (r < 0) {
+ rollback = true;
+ return r;
+ }
+
// remove any snapshot-based mirroring image-meta from image
std::string mirror_uuid;
r = uuid_get(ictx->md_ctx, &mirror_uuid);
ldout(cct, 20) << "ictx=" << ictx << ", "
<< "force=" << force << dendl;
- auto on_refresh = new LambdaContext([ictx, force, on_finish](int r) {
+ // don't let the non-primary feature bit prevent image updates
+ ictx->image_lock.lock();
+ ictx->read_only_mask &= ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
+ ictx->image_lock.unlock();
+
+ auto on_promote = new LambdaContext([ictx, on_finish](int r) {
+ ictx->image_lock.lock();
+ ictx->read_only_mask |= IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
+ ictx->image_lock.unlock();
+
+ ictx->state->handle_update_notification();
+ on_finish->complete(r);
+ });
+
+ auto on_refresh = new LambdaContext([ictx, force, on_promote](int r) {
if (r < 0) {
lderr(ictx->cct) << "refresh failed: " << cpp_strerror(r) << dendl;
- on_finish->complete(r);
+ on_promote->complete(r);
return;
}
- auto req = mirror::PromoteRequest<>::create(*ictx, force, on_finish);
+ auto req = mirror::PromoteRequest<>::create(*ictx, force, on_promote);
req->send();
});
- if (ictx->state->is_refresh_required()) {
- ictx->state->refresh(on_refresh);
- } else {
- on_refresh->complete(0);
- }
+ ictx->state->refresh(on_refresh);
}
template <typename I>
template <typename I>
void EnableRequest<I>::get_tag_owner() {
- if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT ||
- !m_non_primary_global_image_id.empty()) {
+ if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
create_primary_snapshot();
return;
+ } else if (!m_non_primary_global_image_id.empty()) {
+ image_state_update();
+ return;
}
ldout(m_cct, 10) << dendl;
template <typename I>
void EnableRequest<I>::create_primary_snapshot() {
if (!m_non_primary_global_image_id.empty()) {
- image_state_update();
+ // special case for rbd-mirror creating a non-primary image
+ enable_non_primary_feature();
return;
}
void EnableRequest<I>::handle_create_primary_snapshot(int r) {
ldout(m_cct, 10) << "r=" << r << dendl;
+ if (r < 0) {
+ lderr(m_cct) << "failed to create initial primary snapshot: "
+ << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
+
+ image_state_update();
+}
+
+template <typename I>
+void EnableRequest<I>::enable_non_primary_feature() {
+ if (m_mirror_image.mode != cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ image_state_update();
+ return;
+ }
+
+ ldout(m_cct, 10) << dendl;
+
+ // ensure image is flagged with non-primary feature so that
+ // standard RBD clients cannot write to it.
+ librados::ObjectWriteOperation op;
+ cls_client::set_features(&op, RBD_FEATURE_NON_PRIMARY,
+ RBD_FEATURE_NON_PRIMARY);
+
+ auto aio_comp = create_rados_callback<
+ EnableRequest<I>,
+ &EnableRequest<I>::handle_enable_non_primary_feature>(this);
+ int r = m_io_ctx.aio_operate(util::header_name(m_image_id), aio_comp, &op);
+ ceph_assert(r == 0);
+ aio_comp->release();
+}
+
+template <typename I>
+void EnableRequest<I>::handle_enable_non_primary_feature(int r) {
+ ldout(m_cct, 10) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(m_cct) << "failed to enable non-primary feature: "
+ << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
+
image_state_update();
}
* v (skip if not needed) *
* CREATE_PRIMARY_SNAPSHOT * * *
* | *
+ * v (skip if not needed) *
+ * ENABLE_NON_PRIMARY_FEATURE *
+ * | *
* v *
* IMAGE_STATE_UPDATE * * * * * *
* | *
void create_primary_snapshot();
void handle_create_primary_snapshot(int r);
+ void enable_non_primary_feature();
+ void handle_enable_non_primary_feature(int r);
+
void image_state_update();
void handle_image_state_update(int r);
namespace snapshot {
using librbd::util::create_context_callback;
+using librbd::util::create_rados_callback;
template <typename I>
void DemoteRequest<I>::send() {
+ enable_non_primary_feature();
+}
+
+template <typename I>
+void DemoteRequest<I>::enable_non_primary_feature() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ // ensure image is flagged with non-primary feature so that
+ // standard RBD clients cannot write to it.
+ librados::ObjectWriteOperation op;
+ cls_client::set_features(&op, RBD_FEATURE_NON_PRIMARY,
+ RBD_FEATURE_NON_PRIMARY);
+
+ auto aio_comp = create_rados_callback<
+ DemoteRequest<I>,
+ &DemoteRequest<I>::handle_enable_non_primary_feature>(this);
+ int r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, aio_comp,
+ &op);
+ ceph_assert(r == 0);
+ aio_comp->release();
+}
+
+template <typename I>
+void DemoteRequest<I>::handle_enable_non_primary_feature(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to enable non-primary feature: "
+ << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
+
create_snapshot();
}
* <start>
* |
* v
+ * ENABLE_NON_PRIMARY_FEATURE
+ * |
+ * v
* CREATE_SNAPSHOT
* |
* v
std::string m_global_image_id;
Context *m_on_finish;
+ void enable_non_primary_feature();
+ void handle_enable_non_primary_feature(int r);
+
void create_snapshot();
void handle_create_snapshot(int r);
using librbd::util::create_async_context_callback;
using librbd::util::create_context_callback;
+using librbd::util::create_rados_callback;
template <typename I>
void PromoteRequest<I>::send() {
return;
}
+ disable_non_primary_feature();
+}
+
+template <typename I>
+void PromoteRequest<I>::disable_non_primary_feature() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ // remove the non-primary feature flag so that the image can be
+ // R/W by standard RBD clients
+ librados::ObjectWriteOperation op;
+ cls_client::set_features(&op, 0U, RBD_FEATURE_NON_PRIMARY);
+
+ auto aio_comp = create_rados_callback<
+ PromoteRequest<I>,
+ &PromoteRequest<I>::handle_disable_non_primary_feature>(this);
+ int r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, aio_comp,
+ &op);
+ ceph_assert(r == 0);
+ aio_comp->release();
+}
+
+template <typename I>
+void PromoteRequest<I>::handle_disable_non_primary_feature(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to disable non-primary feature: "
+ << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
+
release_exclusive_lock();
}
* CREATE_PROMOTE_SNAPSHOT <--------------------/
* |
* v
+ * DISABLE_NON_PRIMARY_FEATURE
+ * |
+ * v
* RELEASE_EXCLUSIVE_LOCK (skip if not needed)
* |
* v
void create_promote_snapshot();
void handle_create_promote_snapshot(int r);
+ void disable_non_primary_feature();
+ void handle_disable_non_primary_feature(int r);
+
void release_exclusive_lock();
void handle_release_exclusive_lock(int r);