void ImageState<I>::refresh(Context *on_finish) {
CephContext *cct = m_image_ctx->cct;
ldout(cct, 20) << __func__ << dendl;
+ refresh(false, on_finish);
+}
+
+template <typename I>
+void ImageState<I>::acquire_lock_refresh(Context *on_finish) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << __func__ << dendl;
+ refresh(true, on_finish);
+}
+
+template <typename I>
+void ImageState<I>::refresh(bool acquiring_lock, Context *on_finish) {
m_lock.Lock();
if (is_closed()) {
Action action(ACTION_TYPE_REFRESH);
action.refresh_seq = m_refresh_seq;
+ action.refresh_acquiring_lock = acquiring_lock;
execute_action_unlock(action, on_finish);
}
ldout(cct, 10) << this << " " << __func__ << dendl;
m_state = STATE_REFRESHING;
+ assert(!m_actions_contexts.empty());
+ auto &action_context = m_actions_contexts.front().first;
+ assert(action_context.action_type == ACTION_TYPE_REFRESH);
Context *ctx = create_async_context_callback(
*m_image_ctx, create_context_callback<
ImageState<I>, &ImageState<I>::handle_refresh>(this));
image::RefreshRequest<I> *req = image::RefreshRequest<I>::create(
- *m_image_ctx, ctx);
+ *m_image_ctx, action_context.refresh_acquiring_lock, ctx);
m_lock.Unlock();
req->send();
ActionContexts &action_contexts(m_actions_contexts.front());
assert(action_contexts.first.action_type == ACTION_TYPE_REFRESH);
assert(m_last_refresh <= action_contexts.first.refresh_seq);
- m_last_refresh = action_contexts.first.refresh_seq;
+
+ if (r == -ERESTART) {
+ ldout(cct, 5) << "incomplete refresh: not updating sequence" << dendl;
+ r = 0;
+ } else {
+ m_last_refresh = action_contexts.first.refresh_seq;
+ }
complete_action_unlock(STATE_OPEN, r);
}
bool is_refresh_required() const;
int refresh();
- void refresh(Context *on_finish);
int refresh_if_required();
+ void refresh(Context *on_finish);
+ void acquire_lock_refresh(Context *on_finish);
void snap_set(const std::string &snap_name, Context *on_finish);
struct Action {
ActionType action_type;
- uint64_t refresh_seq;
+ uint64_t refresh_seq = 0;
+ bool refresh_acquiring_lock = false;
std::string snap_name;
- Action(ActionType action_type) : action_type(action_type), refresh_seq(0) {
+ Action(ActionType action_type) : action_type(action_type) {
}
inline bool operator==(const Action &action) const {
if (action_type != action.action_type) {
}
switch (action_type) {
case ACTION_TYPE_REFRESH:
- return refresh_seq == action.refresh_seq;
+ return (refresh_seq == action.refresh_seq &&
+ refresh_acquiring_lock == action.refresh_acquiring_lock);
case ACTION_TYPE_SET_SNAP:
return snap_name == action.snap_name;
default:
bool is_transition_state() const;
bool is_closed() const;
+ void refresh(bool acquiring_lock, Context *on_finish);
+
void append_context(const Action &action, Context *context);
void execute_next_action_unlock();
void execute_action_unlock(const Action &action, Context *context);
using util::create_context_callback;
template <typename I>
-RefreshRequest<I>::RefreshRequest(I &image_ctx, Context *on_finish)
- : m_image_ctx(image_ctx),
+RefreshRequest<I>::RefreshRequest(I &image_ctx, bool acquiring_lock,
+ Context *on_finish)
+ : m_image_ctx(image_ctx), m_acquiring_lock(acquiring_lock),
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) {
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;
+ }
+
send_v2_get_flags();
return nullptr;
}
ldout(cct, 10) << this << " " << __func__ << dendl;
apply();
+
return send_v2_finalize_refresh_parent();
}
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;
expect_init_layout(mock_image_ctx);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
}
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_get_snap_id(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_refresh_parent_finalize(mock_image_ctx, *mock_refresh_parent_request, 0);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_shut_down_exclusive_lock(mock_image_ctx, *mock_exclusive_lock, 0);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
}
+TEST_F(TestMockImageRefreshRequest, DisableExclusiveLockWhileAcquiringLock) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockRefreshImageCtx mock_image_ctx(*ictx);
+ MockRefreshParentRequest mock_refresh_parent_request;
+
+ MockExclusiveLock mock_exclusive_lock;
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+ ASSERT_EQ(0, update_features(ictx,
+ RBD_FEATURE_EXCLUSIVE_LOCK |
+ RBD_FEATURE_OBJECT_MAP |
+ RBD_FEATURE_FAST_DIFF |
+ RBD_FEATURE_JOURNALING, false));
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_test_features(mock_image_ctx);
+
+ // verify that exclusive lock is properly handled when object map
+ // and journaling were never enabled (or active)
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_flags(mock_image_ctx, 0);
+ expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+
+ C_SaferCond ctx;
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, true, &ctx);
+ req->send();
+
+ ASSERT_EQ(-ERESTART, ctx.wait());
+}
TEST_F(TestMockImageRefreshRequest, EnableJournalWithExclusiveLock) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
expect_open_journal(mock_image_ctx, mock_journal, 0);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_set_require_lock_on_read(mock_image_ctx);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_unblock_writes(mock_image_ctx);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_open_object_map(mock_image_ctx, &mock_object_map, 0);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_close_object_map(mock_image_ctx, *mock_object_map, 0);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
expect_open_object_map(mock_image_ctx, mock_object_map, -EFBIG);
C_SaferCond ctx;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+ MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());