rbd-mirror: replicate dynamic feature updates

Fixes: http://tracker.ceph.com/issues/16213
Signed-off-by: Mykola Golub <mgolub@mirantis.com>
This commit is contained in:
Mykola Golub 2016-09-14 21:55:16 +03:00
parent 3c270b57bf
commit 93cf63f993
19 changed files with 469 additions and 621 deletions

View File

@ -349,6 +349,18 @@ void ImageWatcher<I>::notify_rename(const std::string &image_name,
notify_lock_owner(std::move(bl), on_finish);
}
template <typename I>
void ImageWatcher<I>::notify_update_features(uint64_t features, bool enabled,
Context *on_finish) {
assert(m_image_ctx.owner_lock.is_locked());
assert(m_image_ctx.exclusive_lock &&
!m_image_ctx.exclusive_lock->is_lock_owner());
bufferlist bl;
::encode(NotifyMessage(UpdateFeaturesPayload(features, enabled)), bl);
notify_lock_owner(std::move(bl), on_finish);
}
template <typename I>
void ImageWatcher<I>::notify_header_update(Context *on_finish) {
ldout(m_image_ctx.cct, 10) << this << ": " << __func__ << dendl;
@ -949,6 +961,28 @@ bool ImageWatcher<I>::handle_payload(const RenamePayload& payload,
return true;
}
template <typename I>
bool ImageWatcher<I>::handle_payload(const UpdateFeaturesPayload& payload,
C_NotifyAck *ack_ctx) {
RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
if (m_image_ctx.exclusive_lock != nullptr) {
int r;
if (m_image_ctx.exclusive_lock->accept_requests(&r)) {
ldout(m_image_ctx.cct, 10) << this << " remote update_features request: "
<< payload.features << " "
<< (payload.enabled ? "enabled" : "disabled")
<< dendl;
m_image_ctx.operations->execute_update_features(
payload.features, payload.enabled, new C_ResponseMessage(ack_ctx), 0);
return false;
} else if (r < 0) {
::encode(ResponseMessage(r), ack_ctx->out);
}
}
return true;
}
template <typename I>
bool ImageWatcher<I>::handle_payload(const UnknownPayload &payload,
C_NotifyAck *ack_ctx) {

View File

@ -47,6 +47,9 @@ public:
ProgressContext &prog_ctx, Context *on_finish);
void notify_rename(const std::string &image_name, Context *on_finish);
void notify_update_features(uint64_t features, bool enabled,
Context *on_finish);
void notify_acquired_lock();
void notify_released_lock();
void notify_request_lock();
@ -303,6 +306,8 @@ private:
C_NotifyAck *ctx);
bool handle_payload(const watch_notify::RenamePayload& payload,
C_NotifyAck *ctx);
bool handle_payload(const watch_notify::UpdateFeaturesPayload& payload,
C_NotifyAck *ctx);
bool handle_payload(const watch_notify::UnknownPayload& payload,
C_NotifyAck *ctx);
void process_payload(uint64_t notify_id, uint64_t handle,

View File

@ -5,6 +5,7 @@
#include "include/rbd_types.h"
#include "include/rados/librados.hpp"
#include "common/errno.h"
#include "librbd/Utils.h"
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
@ -53,6 +54,17 @@ template <typename I>
int MirroringWatcher<I>::notify_image_updated(
librados::IoCtx &io_ctx, cls::rbd::MirrorImageState mirror_image_state,
const std::string &image_id, const std::string &global_image_id) {
C_SaferCond cond;
notify_image_updated(io_ctx, mirror_image_state, image_id, global_image_id,
&cond);
return cond.wait();
}
template <typename I>
int MirroringWatcher<I>::notify_image_updated(
librados::IoCtx &io_ctx, cls::rbd::MirrorImageState mirror_image_state,
const std::string &image_id, const std::string &global_image_id,
Context *on_finish) {
CephContext *cct = reinterpret_cast<CephContext*>(io_ctx.cct());
ldout(cct, 20) << dendl;
@ -60,8 +72,9 @@ int MirroringWatcher<I>::notify_image_updated(
::encode(NotifyMessage{ImageUpdatedPayload{mirror_image_state, image_id,
global_image_id}},
bl);
int r = io_ctx.notify2(RBD_MIRRORING, bl, NOTIFY_TIMEOUT_MS, nullptr);
librados::AioCompletion *comp = util::create_rados_ack_callback(on_finish);
int r = io_ctx.aio_notify(RBD_MIRRORING, comp, bl, NOTIFY_TIMEOUT_MS,
nullptr);
if (r < 0) {
lderr(cct) << ": error encountered sending image updated notification: "
<< cpp_strerror(r) << dendl;

View File

@ -29,6 +29,11 @@ public:
cls::rbd::MirrorImageState mirror_image_state,
const std::string &image_id,
const std::string &global_image_id);
static int notify_image_updated(librados::IoCtx &io_ctx,
cls::rbd::MirrorImageState mirror_image_state,
const std::string &image_id,
const std::string &global_image_id,
Context *on_finish);
virtual void handle_mode_updated(cls::rbd::MirrorMode mirror_mode,
Context *on_ack) = 0;

View File

@ -12,6 +12,8 @@
#include "librbd/ImageWatcher.h"
#include "librbd/ObjectMap.h"
#include "librbd/Utils.h"
#include "librbd/operation/DisableFeaturesRequest.h"
#include "librbd/operation/EnableFeaturesRequest.h"
#include "librbd/operation/FlattenRequest.h"
#include "librbd/operation/RebuildObjectMapRequest.h"
#include "librbd/operation/ObjectMapIterate.h"
@ -1216,6 +1218,84 @@ void Operations<I>::execute_snap_set_limit(const uint64_t limit,
request->send();
}
template <typename I>
int Operations<I>::update_features(uint64_t features, bool enabled) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 5) << this << " " << __func__ << ": features=" << features
<< ", enabled=" << enabled << dendl;
int r = m_image_ctx.state->refresh_if_required();
if (r < 0) {
return r;
}
if (m_image_ctx.read_only) {
return -EROFS;
} else if (m_image_ctx.old_format) {
lderr(cct) << "old-format images do not support features" << dendl;
return -EINVAL;
}
uint64_t disable_mask = (RBD_FEATURES_MUTABLE |
RBD_FEATURES_DISABLE_ONLY);
if ((enabled && (features & RBD_FEATURES_MUTABLE) != features) ||
(!enabled && (features & disable_mask) != features)) {
lderr(cct) << "cannot update immutable features" << dendl;
return -EINVAL;
}
if (features == 0) {
lderr(cct) << "update requires at least one feature" << dendl;
return -EINVAL;
}
{
RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
if (enabled && (features & m_image_ctx.features) != 0) {
lderr(cct) << "one or more requested features are already enabled"
<< dendl;
return -EINVAL;
}
if (!enabled && (features & ~m_image_ctx.features) != 0) {
lderr(cct) << "one or more requested features are already disabled"
<< dendl;
return -EINVAL;
}
}
r = invoke_async_request("update_features", false,
boost::bind(&Operations<I>::execute_update_features,
this, features, enabled, _1, 0),
boost::bind(&ImageWatcher<I>::notify_update_features,
m_image_ctx.image_watcher, features,
enabled, _1));
ldout(cct, 2) << "update_features finished" << dendl;
return r;
}
template <typename I>
void Operations<I>::execute_update_features(uint64_t features, bool enabled,
Context *on_finish,
uint64_t journal_op_tid) {
assert(m_image_ctx.owner_lock.is_locked());
assert(m_image_ctx.exclusive_lock == nullptr ||
m_image_ctx.exclusive_lock->is_lock_owner());
CephContext *cct = m_image_ctx.cct;
ldout(cct, 5) << this << " " << __func__ << ": features=" << features
<< ", enabled=" << enabled << dendl;
if (enabled) {
operation::EnableFeaturesRequest<I> *req =
new operation::EnableFeaturesRequest<I>(
m_image_ctx, on_finish, journal_op_tid, features);
req->send();
} else {
operation::DisableFeaturesRequest<I> *req =
new operation::DisableFeaturesRequest<I>(
m_image_ctx, on_finish, journal_op_tid, features);
req->send();
}
}
template <typename I>
int Operations<I>::prepare_image_update() {
assert(m_image_ctx.owner_lock.is_locked() &&

View File

@ -70,6 +70,10 @@ public:
int snap_set_limit(uint64_t limit);
void execute_snap_set_limit(uint64_t limit, Context *on_finish);
int update_features(uint64_t features, bool enabled);
void execute_update_features(uint64_t features, bool enabled,
Context *on_finish, uint64_t journal_op_tid);
int prepare_image_update();
private:

View File

@ -266,6 +266,21 @@ void RenamePayload::dump(Formatter *f) const {
f->dump_string("image_name", image_name);
}
void UpdateFeaturesPayload::encode(bufferlist &bl) const {
::encode(features, bl);
::encode(enabled, bl);
}
void UpdateFeaturesPayload::decode(__u8 version, bufferlist::iterator &iter) {
::decode(features, iter);
::decode(enabled, iter);
}
void UpdateFeaturesPayload::dump(Formatter *f) const {
f->dump_unsigned("features", features);
f->dump_bool("enabled", enabled);
}
void UnknownPayload::encode(bufferlist &bl) const {
assert(false);
}
@ -339,6 +354,9 @@ void NotifyMessage::decode(bufferlist::iterator& iter) {
case NOTIFY_OP_RENAME:
payload = RenamePayload();
break;
case NOTIFY_OP_UPDATE_FEATURES:
payload = UpdateFeaturesPayload();
break;
default:
payload = UnknownPayload();
break;
@ -367,6 +385,7 @@ void NotifyMessage::generate_test_instances(std::list<NotifyMessage *> &o) {
o.push_back(new NotifyMessage(SnapUnprotectPayload("foo")));
o.push_back(new NotifyMessage(RebuildObjectMapPayload(AsyncRequestId(ClientId(0, 1), 2))));
o.push_back(new NotifyMessage(RenamePayload("foo")));
o.push_back(new NotifyMessage(UpdateFeaturesPayload(1, true)));
}
void ResponseMessage::encode(bufferlist& bl) const {
@ -442,6 +461,9 @@ std::ostream &operator<<(std::ostream &out,
case NOTIFY_OP_RENAME:
out << "Rename";
break;
case NOTIFY_OP_UPDATE_FEATURES:
out << "UpdateFeatures";
break;
default:
out << "Unknown (" << static_cast<uint32_t>(op) << ")";
break;

View File

@ -87,7 +87,8 @@ enum NotifyOp {
NOTIFY_OP_SNAP_RENAME = 11,
NOTIFY_OP_SNAP_PROTECT = 12,
NOTIFY_OP_SNAP_UNPROTECT = 13,
NOTIFY_OP_RENAME = 14
NOTIFY_OP_RENAME = 14,
NOTIFY_OP_UPDATE_FEATURES = 15,
};
struct AcquiredLockPayload {
@ -292,6 +293,22 @@ struct RenamePayload {
void dump(Formatter *f) const;
};
struct UpdateFeaturesPayload {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_UPDATE_FEATURES;
static const bool CHECK_FOR_REFRESH = true;
UpdateFeaturesPayload() : features(0), enabled(false) {}
UpdateFeaturesPayload(uint64_t features_, bool enabled_)
: features(features_), enabled(enabled_) {}
uint64_t features;
bool enabled;
void encode(bufferlist &bl) const;
void decode(__u8 version, bufferlist::iterator &iter);
void dump(Formatter *f) const;
};
struct UnknownPayload {
static const NotifyOp NOTIFY_OP = static_cast<NotifyOp>(-1);
static const bool CHECK_FOR_REFRESH = false;
@ -316,6 +333,7 @@ typedef boost::variant<AcquiredLockPayload,
SnapUnprotectPayload,
RebuildObjectMapPayload,
RenamePayload,
UpdateFeaturesPayload,
UnknownPayload> Payload;
struct NotifyMessage {

View File

@ -32,9 +32,9 @@
#include "librbd/ImageState.h"
#include "librbd/internal.h"
#include "librbd/Journal.h"
#include "librbd/journal/DisabledPolicy.h"
#include "librbd/journal/StandardPolicy.h"
#include "librbd/journal/Types.h"
#include "librbd/mirror/DisableRequest.h"
#include "librbd/mirror/EnableRequest.h"
#include "librbd/MirroringWatcher.h"
#include "librbd/ObjectMap.h"
#include "librbd/Operations.h"
@ -73,91 +73,6 @@ namespace librbd {
namespace {
int remove_object_map(ImageCtx *ictx) {
assert(ictx->snap_lock.is_locked());
CephContext *cct = ictx->cct;
int r;
for (std::map<snap_t, SnapInfo>::iterator it = ictx->snap_info.begin();
it != ictx->snap_info.end(); ++it) {
std::string oid(ObjectMap::object_map_name(ictx->id, it->first));
r = ictx->md_ctx.remove(oid);
if (r < 0 && r != -ENOENT) {
lderr(cct) << "failed to remove object map " << oid << ": "
<< cpp_strerror(r) << dendl;
return r;
}
}
r = ictx->md_ctx.remove(ObjectMap::object_map_name(ictx->id, CEPH_NOSNAP));
if (r < 0 && r != -ENOENT) {
lderr(cct) << "failed to remove object map: " << cpp_strerror(r) << dendl;
return r;
}
return 0;
}
int create_object_map(ImageCtx *ictx) {
assert(ictx->snap_lock.is_locked());
CephContext *cct = ictx->cct;
int r;
uint64_t max_size = ictx->size;
std::vector<uint64_t> snap_ids;
snap_ids.push_back(CEPH_NOSNAP);
for (std::map<snap_t, SnapInfo>::iterator it = ictx->snap_info.begin();
it != ictx->snap_info.end(); ++it) {
max_size = MAX(max_size, it->second.size);
snap_ids.push_back(it->first);
}
if (!ObjectMap::is_compatible(ictx->layout, max_size)) {
lderr(cct) << "image size not compatible with object map" << dendl;
return -EINVAL;
}
for (std::vector<uint64_t>::iterator it = snap_ids.begin();
it != snap_ids.end(); ++it) {
librados::ObjectWriteOperation op;
std::string oid(ObjectMap::object_map_name(ictx->id, *it));
uint64_t snap_size = ictx->get_image_size(*it);
cls_client::object_map_resize(&op, Striper::get_num_objects(ictx->layout, snap_size),
OBJECT_NONEXISTENT);
r = ictx->md_ctx.operate(oid, &op);
if (r < 0) {
lderr(cct) << "failed to create object map " << oid << ": "
<< cpp_strerror(r) << dendl;
return r;
}
}
return 0;
}
int update_all_flags(ImageCtx *ictx, uint64_t flags, uint64_t mask) {
assert(ictx->snap_lock.is_locked());
CephContext *cct = ictx->cct;
std::vector<uint64_t> snap_ids;
snap_ids.push_back(CEPH_NOSNAP);
for (std::map<snap_t, SnapInfo>::iterator it = ictx->snap_info.begin();
it != ictx->snap_info.end(); ++it) {
snap_ids.push_back(it->first);
}
for (size_t i=0; i<snap_ids.size(); ++i) {
librados::ObjectWriteOperation op;
cls_client::set_flags(&op, snap_ids[i], flags, mask);
int r = ictx->md_ctx.operate(ictx->header_oid, &op);
if (r < 0) {
lderr(cct) << "failed to update image flags: " << cpp_strerror(r)
<< dendl;
return r;
}
}
return 0;
}
int validate_pool(IoCtx &io_ctx, CephContext *cct) {
if (!cct->_conf->rbd_validate_pool) {
return 0;
@ -210,79 +125,22 @@ int validate_mirroring_enabled(ImageCtx *ictx) {
return 0;
}
int mirror_image_enable(CephContext *cct, librados::IoCtx &io_ctx,
const std::string &id,
const std::string &global_image_id) {
cls::rbd::MirrorImage mirror_image_internal;
int r = cls_client::mirror_image_get(&io_ctx, id, &mirror_image_internal);
if (r < 0 && r != -ENOENT) {
lderr(cct) << "cannot enable mirroring: " << cpp_strerror(r) << dendl;
return r;
}
if (mirror_image_internal.state == cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
// mirroring is already enabled
return 0;
} else if (r != -ENOENT) {
lderr(cct) << "cannot enable mirroring: currently disabling" << dendl;
return -EINVAL;
}
mirror_image_internal.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED;
if (global_image_id.empty()) {
uuid_d uuid_gen;
uuid_gen.generate_random();
mirror_image_internal.global_image_id = uuid_gen.to_string();
} else {
mirror_image_internal.global_image_id = global_image_id;
}
r = cls_client::mirror_image_set(&io_ctx, id, mirror_image_internal);
if (r < 0) {
lderr(cct) << "cannot enable mirroring: " << cpp_strerror(r) << dendl;
return r;
}
r = MirroringWatcher<>::notify_image_updated(
io_ctx, cls::rbd::MIRROR_IMAGE_STATE_ENABLED, id,
mirror_image_internal.global_image_id);
if (r < 0) {
lderr(cct) << "failed to send update notification: " << cpp_strerror(r)
<< dendl;
}
ldout(cct, 20) << "image mirroring is enabled: global_id=" <<
mirror_image_internal.global_image_id << dendl;
return 0;
}
int mirror_image_enable_internal(ImageCtx *ictx) {
CephContext *cct = ictx->cct;
C_SaferCond cond;
if ((ictx->features & RBD_FEATURE_JOURNALING) == 0) {
lderr(cct) << "cannot enable mirroring: journaling is not enabled"
<< dendl;
lderr(cct) << "cannot enable mirroring: journaling is not enabled" << dendl;
return -EINVAL;
}
bool is_primary;
int r = Journal<>::is_tag_owner(ictx, &is_primary);
if (r < 0) {
lderr(cct) << "cannot enable mirroring: failed to check tag ownership: "
<< cpp_strerror(r) << dendl;
return r;
}
mirror::EnableRequest<ImageCtx> *req =
mirror::EnableRequest<ImageCtx>::create(ictx, &cond);
req->send();
if (!is_primary) {
lderr(cct) <<
"cannot enable mirroring: last journal tag not owned by local cluster"
<< dendl;
return -EINVAL;
}
r = mirror_image_enable(cct, ictx->md_ctx, ictx->id, "");
int r = cond.wait();
if (r < 0) {
lderr(cct) << "cannot enable mirroring: " << cpp_strerror(r) << dendl;
return r;
}
@ -292,121 +150,17 @@ int mirror_image_enable_internal(ImageCtx *ictx) {
int mirror_image_disable_internal(ImageCtx *ictx, bool force,
bool remove=true) {
CephContext *cct = ictx->cct;
C_SaferCond cond;
cls::rbd::MirrorImage mirror_image_internal;
std::vector<snap_info_t> snaps;
std::set<cls::journal::Client> clients;
std::string header_oid;
mirror::DisableRequest<ImageCtx> *req =
mirror::DisableRequest<ImageCtx>::create(ictx, force, remove, &cond);
req->send();
int r = cls_client::mirror_image_get(&ictx->md_ctx, ictx->id,
&mirror_image_internal);
if (r == -ENOENT) {
// mirroring is not enabled for this image
ldout(cct, 20) << "ignoring disable command: mirroring is not enabled "
"for this image" << dendl;
return 0;
} else if (r == -EOPNOTSUPP) {
ldout(cct, 5) << "mirroring not supported by OSD" << dendl;
return r;
} else if (r < 0) {
lderr(cct) << "cannot disable mirroring: " << cpp_strerror(r) << dendl;
return r;
}
bool is_primary;
r = Journal<>::is_tag_owner(ictx, &is_primary);
if (r < 0) {
lderr(cct) << "cannot disable mirroring: failed to check tag ownership: "
<< cpp_strerror(r) << dendl;
return r;
}
if (!is_primary && !force) {
lderr(cct) << "Mirrored image is not the primary, add force option to"
" disable mirroring" << dendl;
return -EINVAL;
}
mirror_image_internal.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING;
r = cls_client::mirror_image_set(&ictx->md_ctx, ictx->id,
mirror_image_internal);
int r = cond.wait();
if (r < 0) {
lderr(cct) << "cannot disable mirroring: " << cpp_strerror(r) << dendl;
return r;
}
r = MirroringWatcher<>::notify_image_updated(
ictx->md_ctx, cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
ictx->id, mirror_image_internal.global_image_id);
if (r < 0) {
lderr(cct) << "failed to send update notification: " << cpp_strerror(r)
<< dendl;
}
header_oid = ::journal::Journaler::header_oid(ictx->id);
while(true) {
clients.clear();
r = cls::journal::client::client_list(ictx->md_ctx, header_oid, &clients);
if (r < 0) {
lderr(cct) << "cannot disable mirroring: " << cpp_strerror(r) << dendl;
return r;
}
assert(clients.size() >= 1);
if (clients.size() == 1) {
// only local journal client remains
break;
}
for (auto client : clients) {
journal::ClientData client_data;
bufferlist::iterator bl = client.data.begin();
::decode(client_data, bl);
journal::ClientMetaType type = client_data.get_client_meta_type();
if (type != journal::ClientMetaType::MIRROR_PEER_CLIENT_META_TYPE) {
continue;
}
journal::MirrorPeerClientMeta client_meta =
boost::get<journal::MirrorPeerClientMeta>(client_data.client_meta);
for (const auto& sync : client_meta.sync_points) {
r = ictx->operations->snap_remove(sync.snap_name.c_str());
if (r < 0 && r != -ENOENT) {
lderr(cct) << "cannot disable mirroring: failed to remove temporary"
" snapshot created by remote peer: " << cpp_strerror(r) << dendl;
return r;
}
}
r = cls::journal::client::client_unregister(ictx->md_ctx, header_oid,
client.id);
if (r < 0 && r != -ENOENT) {
lderr(cct) << "cannot disable mirroring: failed to unregister remote"
" journal client: " << cpp_strerror(r) << dendl;
return r;
}
}
}
if (remove) {
r = cls_client::mirror_image_remove(&ictx->md_ctx, ictx->id);
if (r < 0 && r != -ENOENT) {
lderr(cct) << "failed to remove image from mirroring directory: "
<< cpp_strerror(r) << dendl;
return r;
}
ldout(cct, 20) << "removed image state from rbd_mirroring object" << dendl;
if (is_primary) {
// TODO: send notification to mirroring object about update
}
}
return 0;
}
@ -1603,313 +1357,6 @@ int mirror_image_disable_internal(ImageCtx *ictx, bool force,
return 0;
}
int update_features(ImageCtx *ictx, uint64_t features, bool enabled)
{
int r = ictx->state->refresh_if_required();
if (r < 0) {
return r;
}
CephContext *cct = ictx->cct;
if (ictx->read_only) {
return -EROFS;
} else if (ictx->old_format) {
lderr(cct) << "old-format images do not support features" << dendl;
return -EINVAL;
}
uint64_t disable_mask = (RBD_FEATURES_MUTABLE |
RBD_FEATURES_DISABLE_ONLY);
if ((enabled && (features & RBD_FEATURES_MUTABLE) != features) ||
(!enabled && (features & disable_mask) != features)) {
lderr(cct) << "cannot update immutable features" << dendl;
return -EINVAL;
} else if (features == 0) {
lderr(cct) << "update requires at least one feature" << dendl;
return -EINVAL;
}
rbd_mirror_mode_t mirror_mode = RBD_MIRROR_MODE_DISABLED;
if ((features & RBD_FEATURE_JOURNALING) != 0) {
r = librbd::mirror_mode_get(ictx->md_ctx, &mirror_mode);
if (r < 0) {
lderr(cct) << "error in retrieving pool mirroring status: "
<< cpp_strerror(r) << dendl;
return r;
}
// mark mirroring as disabling and prune all sync snapshots
// before acquiring locks
if (mirror_mode == RBD_MIRROR_MODE_POOL && !enabled) {
r = mirror_image_disable_internal(ictx, false, false);
if (r < 0) {
lderr(cct) << "error disabling image mirroring: "
<< cpp_strerror(r) << dendl;
}
}
}
RWLock::RLocker owner_locker(ictx->owner_lock);
r = ictx->aio_work_queue->block_writes();
BOOST_SCOPE_EXIT_ALL( (ictx) ) {
ictx->aio_work_queue->unblock_writes();
};
if (r < 0) {
return r;
}
// avoid accepting new requests from peers while we manipulate
// the image features
if (ictx->exclusive_lock != nullptr) {
ictx->exclusive_lock->block_requests(0);
}
BOOST_SCOPE_EXIT_ALL( (ictx) ) {
if (ictx->exclusive_lock != nullptr) {
ictx->exclusive_lock->unblock_requests();
}
};
// if disabling journaling, avoid attempting to open the journal
// when acquiring the exclusive lock in case the journal is corrupt
bool disabling_journal = false;
if (!enabled && ((features & RBD_FEATURE_JOURNALING) != 0)) {
RWLock::WLocker snap_locker(ictx->snap_lock);
ictx->set_journal_policy(new journal::DisabledPolicy());
disabling_journal = true;
}
BOOST_SCOPE_EXIT_ALL( (ictx)(disabling_journal) ) {
if (disabling_journal) {
RWLock::WLocker snap_locker(ictx->snap_lock);
ictx->set_journal_policy(new journal::StandardPolicy(ictx));
}
};
// if disabling features w/ exclusive lock supported, we need to
// acquire the lock to temporarily block IO against the image
bool acquired_lock = false;
if (ictx->exclusive_lock != nullptr &&
!ictx->exclusive_lock->is_lock_owner() && !enabled) {
acquired_lock = true;
C_SaferCond lock_ctx;
ictx->exclusive_lock->request_lock(&lock_ctx);
// don't block holding lock since refresh might be required
ictx->owner_lock.put_read();
r = lock_ctx.wait();
ictx->owner_lock.get_read();
if (r < 0) {
lderr(cct) << "failed to lock image: " << cpp_strerror(r) << dendl;
return r;
} else if (ictx->exclusive_lock == nullptr ||
!ictx->exclusive_lock->is_lock_owner()) {
lderr(cct) << "failed to acquire exclusive lock" << dendl;
return -EROFS;
}
}
{
RWLock::WLocker snap_locker(ictx->snap_lock);
uint64_t new_features;
if (enabled) {
if ((features & ictx->features) != 0) {
lderr(cct) << "one or more requested features are already enabled" << dendl;
return -EINVAL;
}
features &= ~ictx->features;
new_features = ictx->features | features;
} else {
if ((features & ~ictx->features) != 0) {
lderr(cct) << "one or more requested features are already disabled" << dendl;
return -EINVAL;
}
features &= ictx->features;
new_features = ictx->features & ~features;
}
bool enable_mirroring = false;
uint64_t features_mask = features;
uint64_t disable_flags = 0;
if (enabled) {
uint64_t enable_flags = 0;
if ((features & RBD_FEATURE_OBJECT_MAP) != 0) {
if ((new_features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) {
lderr(cct) << "cannot enable object map" << dendl;
return -EINVAL;
}
enable_flags |= RBD_FLAG_OBJECT_MAP_INVALID;
features_mask |= RBD_FEATURE_EXCLUSIVE_LOCK;
}
if ((features & RBD_FEATURE_FAST_DIFF) != 0) {
if ((new_features & RBD_FEATURE_OBJECT_MAP) == 0) {
lderr(cct) << "cannot enable fast diff" << dendl;
return -EINVAL;
}
enable_flags |= RBD_FLAG_FAST_DIFF_INVALID;
features_mask |= (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_EXCLUSIVE_LOCK);
}
if ((features & RBD_FEATURE_JOURNALING) != 0) {
if ((new_features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) {
lderr(cct) << "cannot enable journaling" << dendl;
return -EINVAL;
}
features_mask |= RBD_FEATURE_EXCLUSIVE_LOCK;
r = Journal<>::create(ictx->md_ctx, ictx->id, ictx->journal_order,
ictx->journal_splay_width, ictx->journal_pool);
if (r < 0) {
lderr(cct) << "error creating image journal: " << cpp_strerror(r)
<< dendl;
return r;
}
enable_mirroring = (mirror_mode == RBD_MIRROR_MODE_POOL);
}
if (enable_flags != 0) {
r = update_all_flags(ictx, enable_flags, enable_flags);
if (r < 0) {
return r;
}
}
} else {
if ((features & RBD_FEATURE_EXCLUSIVE_LOCK) != 0) {
if ((new_features & RBD_FEATURE_OBJECT_MAP) != 0 ||
(new_features & RBD_FEATURE_JOURNALING) != 0) {
lderr(cct) << "cannot disable exclusive lock" << dendl;
return -EINVAL;
}
features_mask |= (RBD_FEATURE_OBJECT_MAP |
RBD_FEATURE_JOURNALING);
}
if ((features & RBD_FEATURE_OBJECT_MAP) != 0) {
if ((new_features & RBD_FEATURE_FAST_DIFF) != 0) {
lderr(cct) << "cannot disable object map" << dendl;
return -EINVAL;
}
disable_flags |= RBD_FLAG_OBJECT_MAP_INVALID;
r = remove_object_map(ictx);
if (r < 0) {
lderr(cct) << "failed to remove object map" << dendl;
return r;
}
}
if ((features & RBD_FEATURE_FAST_DIFF) != 0) {
disable_flags |= RBD_FLAG_FAST_DIFF_INVALID;
}
if ((features & RBD_FEATURE_JOURNALING) != 0) {
if (mirror_mode == RBD_MIRROR_MODE_IMAGE) {
cls::rbd::MirrorImage mirror_image;
r = cls_client::mirror_image_get(&ictx->md_ctx, ictx->id,
&mirror_image);
if (r < 0 && r != -ENOENT) {
lderr(cct) << "error retrieving mirroring state: "
<< cpp_strerror(r) << dendl;
return r;
}
if (mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
lderr(cct) << "cannot disable journaling: image mirroring "
<< "enabled and mirror pool mode set to image"
<< dendl;
return -EINVAL;
}
} else if (mirror_mode == RBD_MIRROR_MODE_POOL) {
r = cls_client::mirror_image_remove(&ictx->md_ctx, ictx->id);
if (r < 0 && r != -ENOENT) {
lderr(cct) << "failed to remove image from mirroring directory: "
<< cpp_strerror(r) << dendl;
return r;
}
}
if (ictx->journal != nullptr) {
C_SaferCond cond;
ictx->journal->close(&cond);
r = cond.wait();
if (r < 0) {
lderr(cct) << "error closing image journal: " << cpp_strerror(r)
<< dendl;
return r;
}
}
r = Journal<>::remove(ictx->md_ctx, ictx->id);
if (r < 0) {
lderr(cct) << "error removing image journal: " << cpp_strerror(r)
<< dendl;
return r;
}
}
}
ldout(cct, 10) << "update_features: features=" << new_features << ", "
<< "mask=" << features_mask << dendl;
r = librbd::cls_client::set_features(&ictx->md_ctx, ictx->header_oid,
new_features, features_mask);
if (!enabled && r == -EINVAL) {
// NOTE: infernalis OSDs will not accept a mask with new features, so
// re-attempt with a reduced mask.
features_mask &= ~RBD_FEATURE_JOURNALING;
r = librbd::cls_client::set_features(&ictx->md_ctx, ictx->header_oid,
new_features, features_mask);
}
if (r < 0) {
lderr(cct) << "failed to update features: " << cpp_strerror(r)
<< dendl;
return r;
}
if (((ictx->features & RBD_FEATURE_OBJECT_MAP) == 0) &&
((features & RBD_FEATURE_OBJECT_MAP) != 0)) {
r = create_object_map(ictx);
if (r < 0) {
lderr(cct) << "failed to create object map" << dendl;
return r;
}
}
if (disable_flags != 0) {
r = update_all_flags(ictx, 0, disable_flags);
if (r < 0) {
return r;
}
}
if (enable_mirroring) {
ImageCtx *img_ctx = new ImageCtx("", ictx->id, nullptr,
ictx->md_ctx, false);
r = img_ctx->state->open();
if (r < 0) {
lderr(cct) << "error opening image: " << cpp_strerror(r) << dendl;
delete img_ctx;
} else {
r = mirror_image_enable_internal(img_ctx);
if (r < 0) {
lderr(cct) << "error enabling mirroring: " << cpp_strerror(r)
<< dendl;
}
img_ctx->state->close();
}
}
}
ictx->notify_update();
if (ictx->exclusive_lock != nullptr && acquired_lock) {
C_SaferCond lock_ctx;
ictx->exclusive_lock->release_lock(&lock_ctx);
r = lock_ctx.wait();
if (r < 0) {
lderr(cct) << "failed to unlock image: " << cpp_strerror(r) << dendl;
return r;
}
}
return 0;
}
int get_overlap(ImageCtx *ictx, uint64_t *overlap)
{
int r = ictx->state->refresh_if_required();

View File

@ -129,7 +129,6 @@ namespace librbd {
int get_old_format(ImageCtx *ictx, uint8_t *old);
int get_size(ImageCtx *ictx, uint64_t *size);
int get_features(ImageCtx *ictx, uint64_t *features);
int update_features(ImageCtx *ictx, uint64_t features, bool enabled);
int get_overlap(ImageCtx *ictx, uint64_t *overlap);
int get_parent_info(ImageCtx *ictx, std::string *parent_pool_name,
std::string *parent_name, std::string *parent_snap_name);

View File

@ -329,7 +329,7 @@ typedef boost::variant<AioDiscardEvent,
FlattenEvent,
DemoteEvent,
SnapLimitEvent,
UpdateFeaturesEvent,
UpdateFeaturesEvent,
UnknownEvent> Event;
struct EventEntry {

View File

@ -750,7 +750,7 @@ namespace librbd {
{
ImageCtx *ictx = reinterpret_cast<ImageCtx *>(ctx);
tracepoint(librbd, update_features_enter, ictx, features, enabled);
int r = librbd::update_features(ictx, features, enabled);
int r = ictx->operations->update_features(features, enabled);
tracepoint(librbd, update_features_exit, r);
return r;
}
@ -2164,7 +2164,7 @@ extern "C" int rbd_update_features(rbd_image_t image, uint64_t features,
librbd::ImageCtx *ictx = reinterpret_cast<librbd::ImageCtx *>(image);
bool features_enabled = enabled != 0;
tracepoint(librbd, update_features_enter, ictx, features, features_enabled);
int r = librbd::update_features(ictx, features, features_enabled);
int r = ictx->operations->update_features(features, features_enabled);
tracepoint(librbd, update_features_exit, r);
return r;
}

View File

@ -519,22 +519,26 @@ TEST_F(TestMockImageRefreshRequest, DisableExclusiveLock) {
mock_image_ctx.journal = &mock_journal;
}
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING, false));
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
false));
}
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_FAST_DIFF , false));
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_FAST_DIFF,
false));
}
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_OBJECT_MAP , false));
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_OBJECT_MAP,
false));
}
if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_EXCLUSIVE_LOCK , false));
if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_EXCLUSIVE_LOCK,
false));
}
expect_op_work_queue(mock_image_ctx);
expect_test_features(mock_image_ctx);
@ -565,20 +569,24 @@ TEST_F(TestMockImageRefreshRequest, DisableExclusiveLockWhileAcquiringLock) {
MockExclusiveLock mock_exclusive_lock;
mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING, false));
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
false));
}
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_FAST_DIFF , false));
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_FAST_DIFF,
false));
}
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_OBJECT_MAP , false));
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_OBJECT_MAP,
false));
}
if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_EXCLUSIVE_LOCK , false));
if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_EXCLUSIVE_LOCK,
false));
}
expect_op_work_queue(mock_image_ctx);
@ -605,11 +613,13 @@ TEST_F(TestMockImageRefreshRequest, JournalDisabledByPolicy) {
ASSERT_EQ(0, open_image(m_image_name, &ictx));
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_FAST_DIFF , false));
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_FAST_DIFF,
false));
}
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_OBJECT_MAP , false));
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_OBJECT_MAP,
false));
}
MockRefreshImageCtx mock_image_ctx(*ictx);
@ -646,12 +656,14 @@ TEST_F(TestMockImageRefreshRequest, EnableJournalWithExclusiveLock) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_FAST_DIFF , false));
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_FAST_DIFF,
false));
}
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_OBJECT_MAP , false));
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_OBJECT_MAP,
false));
}
MockRefreshImageCtx mock_image_ctx(*ictx);
@ -690,12 +702,14 @@ TEST_F(TestMockImageRefreshRequest, EnableJournalWithoutExclusiveLock) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_FAST_DIFF , false));
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_FAST_DIFF,
false));
}
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_OBJECT_MAP , false));
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_OBJECT_MAP,
false));
}
MockRefreshImageCtx mock_image_ctx(*ictx);
@ -742,8 +756,9 @@ TEST_F(TestMockImageRefreshRequest, DisableJournal) {
MockJournal *mock_journal = new MockJournal();
mock_image_ctx.journal = mock_journal;
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING , false));
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
false));
}
expect_op_work_queue(mock_image_ctx);
@ -772,8 +787,9 @@ TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithExclusiveLock) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING , false));
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
false));
}
MockRefreshImageCtx mock_image_ctx(*ictx);
@ -808,8 +824,9 @@ TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithoutExclusiveLock) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING , false));
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
false));
}
MockRefreshImageCtx mock_image_ctx(*ictx);
@ -855,12 +872,14 @@ TEST_F(TestMockImageRefreshRequest, DisableObjectMap) {
mock_image_ctx.journal = &mock_journal;
}
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_FAST_DIFF , false));
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_FAST_DIFF,
false));
}
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_OBJECT_MAP , false));
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_OBJECT_MAP,
false));
}
expect_op_work_queue(mock_image_ctx);
@ -886,8 +905,9 @@ TEST_F(TestMockImageRefreshRequest, OpenObjectMapError) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING , false));
if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
false));
}
MockRefreshImageCtx mock_image_ctx(*ictx);

View File

@ -643,6 +643,43 @@ TEST_F(TestJournalReplay, Flatten) {
ASSERT_EQ(-EINVAL, ictx2->operations->flatten(no_op));
}
TEST_F(TestJournalReplay, UpdateFeatures) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, when_acquired_lock(ictx));
uint64_t features = RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF;
bool enabled = !ictx->test_features(features);
// get current commit position
int64_t initial_tag;
int64_t initial_entry;
get_journal_commit_position(ictx, &initial_tag, &initial_entry);
// inject update_features op into journal
inject_into_journal(ictx, librbd::journal::UpdateFeaturesEvent(1, features,
enabled));
close_image(ictx);
// replay journal
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, when_acquired_lock(ictx));
int64_t current_tag;
int64_t current_entry;
get_journal_commit_position(ictx, &current_tag, &current_entry);
ASSERT_EQ(initial_tag + 1, current_tag);
ASSERT_EQ(0, current_entry);
ASSERT_EQ(enabled, ictx->test_features(features));
// verify lock ordering constraints
ASSERT_EQ(0, ictx->operations->update_features(features, !enabled));
}
TEST_F(TestJournalReplay, ObjectPosition) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);

View File

@ -197,6 +197,13 @@ public:
NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void expect_update_features(MockReplayImageCtx &mock_image_ctx, Context **on_finish,
uint64_t features, bool enabled, uint64_t op_tid) {
EXPECT_CALL(*mock_image_ctx.operations, execute_update_features(features, enabled, _, op_tid))
.WillOnce(DoAll(SaveArg<2>(on_finish),
NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void expect_refresh_image(MockReplayImageCtx &mock_image_ctx, bool required,
int r) {
EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
@ -1242,6 +1249,47 @@ TEST_F(TestMockJournalReplay, FlattenEventInvalid) {
ASSERT_EQ(0, on_finish_safe.wait());
}
TEST_F(TestMockJournalReplay, UpdateFeaturesEvent) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
uint64_t features = RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF;
bool enabled = !ictx->test_features(features);
MockReplayImageCtx mock_image_ctx(*ictx);
MockJournalReplay mock_journal_replay(mock_image_ctx);
expect_op_work_queue(mock_image_ctx);
InSequence seq;
Context *on_finish = nullptr;
expect_refresh_image(mock_image_ctx, false, 0);
expect_update_features(mock_image_ctx, &on_finish, features, enabled, 123);
C_SaferCond on_start_ready;
C_SaferCond on_start_safe;
when_process(mock_journal_replay,
EventEntry{UpdateFeaturesEvent(123, features, enabled)},
&on_start_ready, &on_start_safe);
C_SaferCond on_resume;
when_replay_op_ready(mock_journal_replay, 123, &on_resume);
ASSERT_EQ(0, on_start_ready.wait());
C_SaferCond on_finish_ready;
C_SaferCond on_finish_safe;
when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
&on_finish_ready, &on_finish_safe);
ASSERT_EQ(0, on_resume.wait());
wait_for_op_invoked(&on_finish, 0);
ASSERT_EQ(0, on_start_safe.wait());
ASSERT_EQ(0, on_finish_ready.wait());
ASSERT_EQ(0, on_finish_safe.wait());
}
TEST_F(TestMockJournalReplay, UnknownEvent) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);

View File

@ -46,6 +46,9 @@ struct MockOperations {
Context *on_finish));
MOCK_METHOD2(execute_snap_set_limit, void(uint64_t limit,
Context *on_finish));
MOCK_METHOD4(execute_update_features, void(uint64_t features, bool enabled,
Context *on_finish,
uint64_t journal_op_tid));
};
} // namespace librbd

View File

@ -6,6 +6,7 @@
#include "test/librbd/mock/MockImageCtx.h"
#include "cls/rbd/cls_rbd_client.h"
#include "librbd/AioCompletion.h"
#include "librbd/Operations.h"
#include "librbd/internal.h"
#include "librbd/image/SetFlagsRequest.h"
#include "librbd/mirror/EnableRequest.h"
@ -193,7 +194,7 @@ public:
if (!features_to_disable) {
return;
}
ASSERT_EQ(0, librbd::update_features(ictx, features_to_disable, false));
ASSERT_EQ(0, ictx->operations->update_features(features_to_disable, false));
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_EQ(0U, features & features_to_disable);
}

View File

@ -3782,26 +3782,45 @@ TEST_F(TestLibRBD, UpdateFeatures)
ASSERT_EQ(0, image.update_features(disable_features, false));
}
ASSERT_EQ(0, image.features(&features));
ASSERT_EQ(0U, features & disable_features);
// cannot enable object map nor journaling w/o exclusive lock
ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_OBJECT_MAP, true));
ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_JOURNALING, true));
ASSERT_EQ(0, image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, true));
ASSERT_EQ(0, image.features(&features));
ASSERT_NE(0U, features & RBD_FEATURE_EXCLUSIVE_LOCK);
// cannot enable fast diff w/o object map
ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_FAST_DIFF, true));
ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, true));
ASSERT_EQ(0, image.features(&features));
ASSERT_NE(0U, features & RBD_FEATURE_OBJECT_MAP);
uint64_t expected_flags = RBD_FLAG_OBJECT_MAP_INVALID;
uint64_t flags;
ASSERT_EQ(0, image.get_flags(&flags));
ASSERT_EQ(expected_flags, flags);
ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, false));
ASSERT_EQ(0, image.features(&features));
ASSERT_EQ(0U, features & RBD_FEATURE_OBJECT_MAP);
ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP |
RBD_FEATURE_FAST_DIFF |
RBD_FEATURE_JOURNALING, true));
uint64_t expected_flags = RBD_FLAG_OBJECT_MAP_INVALID |
RBD_FLAG_FAST_DIFF_INVALID;
uint64_t flags;
expected_flags = RBD_FLAG_OBJECT_MAP_INVALID | RBD_FLAG_FAST_DIFF_INVALID;
ASSERT_EQ(0, image.get_flags(&flags));
ASSERT_EQ(expected_flags, flags);
// cannot disable object map w/ fast diff
ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_OBJECT_MAP, false));
ASSERT_EQ(0, image.update_features(RBD_FEATURE_FAST_DIFF, false));
ASSERT_EQ(0, image.features(&features));
ASSERT_EQ(0U, features & RBD_FEATURE_FAST_DIFF);
expected_flags = RBD_FLAG_OBJECT_MAP_INVALID;
ASSERT_EQ(0, image.get_flags(&flags));
@ -3820,6 +3839,7 @@ TEST_F(TestLibRBD, UpdateFeatures)
ASSERT_EQ(0, image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, false));
ASSERT_EQ(0, image.features(&features));
if ((features & RBD_FEATURE_DEEP_FLATTEN) != 0) {
ASSERT_EQ(0, image.update_features(RBD_FEATURE_DEEP_FLATTEN, false));
}

View File

@ -395,7 +395,8 @@ TEST_F(TestImageReplayer, BootstrapErrorNoJournal)
open_remote_image(&ictx);
uint64_t features;
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_EQ(0, librbd::update_features(ictx, RBD_FEATURE_JOURNALING, false));
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
false));
close_image(ictx);
create_replayer<>();
@ -436,7 +437,8 @@ TEST_F(TestImageReplayer, ErrorNoJournal)
open_remote_image(&ictx);
uint64_t features;
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_EQ(0, librbd::update_features(ictx, RBD_FEATURE_JOURNALING, false));
ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
false));
close_image(ictx);
C_SaferCond cond;
@ -913,3 +915,93 @@ TEST_F(TestImageReplayer, Disconnect)
stop();
}
TEST_F(TestImageReplayer, UpdateFeatures)
{
const uint64_t FEATURES_TO_UPDATE =
RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF;
uint64_t features;
librbd::ImageCtx *ictx;
// Make sure the features we will update are disabled initially
open_remote_image(&ictx);
ASSERT_EQ(0, librbd::get_features(ictx, &features));
features &= FEATURES_TO_UPDATE;
if (features) {
ASSERT_EQ(0, ictx->operations->update_features(FEATURES_TO_UPDATE,
false));
}
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_EQ(0U, features & FEATURES_TO_UPDATE);
close_image(ictx);
bootstrap();
open_remote_image(&ictx);
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_EQ(0U, features & FEATURES_TO_UPDATE);
close_image(ictx);
open_local_image(&ictx);
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_EQ(0U, features & FEATURES_TO_UPDATE);
close_image(ictx);
// Start replay and update features
start();
open_remote_image(&ictx);
ASSERT_EQ(0, ictx->operations->update_features(FEATURES_TO_UPDATE,
true));
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_EQ(FEATURES_TO_UPDATE, features & FEATURES_TO_UPDATE);
close_image(ictx);
wait_for_replay_complete();
open_local_image(&ictx);
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_EQ(FEATURES_TO_UPDATE, features & FEATURES_TO_UPDATE);
close_image(ictx);
open_remote_image(&ictx);
ASSERT_EQ(0, ictx->operations->update_features(FEATURES_TO_UPDATE,
false));
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_EQ(0U, features & FEATURES_TO_UPDATE);
close_image(ictx);
wait_for_replay_complete();
open_local_image(&ictx);
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_EQ(0U, features & FEATURES_TO_UPDATE);
close_image(ictx);
// Test update_features error does not stop replication
open_remote_image(&ictx);
ASSERT_EQ(0, librbd::get_features(ictx, &features));
ASSERT_NE(0U, features & RBD_FEATURE_EXCLUSIVE_LOCK);
ASSERT_EQ(-EINVAL, ictx->operations->update_features(RBD_FEATURE_EXCLUSIVE_LOCK,
false));
generate_test_data();
for (int i = 0; i < TEST_IO_COUNT; ++i) {
write_test_data(ictx, m_test_data, TEST_IO_SIZE * i, TEST_IO_SIZE);
}
flush(ictx);
close_image(ictx);
wait_for_replay_complete();
open_local_image(&ictx);
for (int i = 0; i < TEST_IO_COUNT; ++i) {
read_test_data(ictx, m_test_data, TEST_IO_SIZE * i, TEST_IO_SIZE);
}
close_image(ictx);
stop();
}