if (m_state == STATE_UNLOCKED) {
m_state = STATE_SHUTTING_DOWN;
m_image_ctx.aio_work_queue->unblock_writes();
- m_image_ctx.op_work_queue->queue(util::create_context_callback<
- ExclusiveLock<I>, &ExclusiveLock<I>::complete_shutdown>(this), 0);
+ m_image_ctx.image_watcher->flush(util::create_context_callback<
+ ExclusiveLock<I>, &ExclusiveLock<I>::complete_shutdown>(this));
return;
}
void ImageCtx::set_image_name(const std::string &image_name) {
// update the name so rename can be invoked repeatedly
- RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
- RWLock::WLocker snap_locker(m_image_ctx.snap_lock);
+ RWLock::RLocker owner_locker(owner_lock);
+ RWLock::WLocker snap_locker(snap_lock);
name = image_name;
if (old_format) {
header_oid = util::old_header_name(image_name);
}
void ImageWatcher::notify_header_update(Context *on_finish) {
+ ldout(m_image_ctx.cct, 10) << this << ": " << __func__ << dendl;
+
// supports legacy (empty buffer) clients
bufferlist bl;
::encode(NotifyMessage(HeaderUpdatePayload()), bl);
void ImageWatcher::notify_request_lock() {
RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
+ RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+ assert(!m_image_ctx.exclusive_lock->is_lock_owner());
+
ldout(m_image_ctx.cct, 10) << this << " notify request lock" << dendl;
bufferlist bl;
void ImageWatcher::handle_request_lock(int r) {
RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
+ RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
+
+ // ExclusiveLock state machine cannot transition
assert(!m_image_ctx.exclusive_lock->is_lock_owner());
if (r == -ETIMEDOUT) {
}
// if an image refresh is required, refresh before processing the request
- if (m_image_ctx.state->is_refresh_required()) {
+ if (notify_message.check_for_refresh() &&
+ m_image_ctx.state->is_refresh_required()) {
m_image_ctx.state->refresh(new C_ProcessPayload(this, notify_id, handle,
notify_message.payload));
} else {
namespace {
+class CheckForRefreshVisitor : public boost::static_visitor<bool> {
+public:
+ template <typename Payload>
+ inline bool operator()(const Payload &payload) const {
+ return Payload::CHECK_FOR_REFRESH;
+ }
+};
+
class EncodePayloadVisitor : public boost::static_visitor<void> {
public:
explicit EncodePayloadVisitor(bufferlist &bl) : m_bl(bl) {}
void UnknownPayload::dump(Formatter *f) const {
}
+bool NotifyMessage::check_for_refresh() const {
+ return boost::apply_visitor(CheckForRefreshVisitor(), payload);
+}
+
void NotifyMessage::encode(bufferlist& bl) const {
ENCODE_START(2, 1, bl);
boost::apply_visitor(EncodePayloadVisitor(bl), payload);
struct AcquiredLockPayload {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_ACQUIRED_LOCK;
+ static const bool CHECK_FOR_REFRESH = true;
ClientId client_id;
struct ReleasedLockPayload {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_RELEASED_LOCK;
+ static const bool CHECK_FOR_REFRESH = true;
ClientId client_id;
struct RequestLockPayload {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_REQUEST_LOCK;
+ static const bool CHECK_FOR_REFRESH = true;
ClientId client_id;
struct HeaderUpdatePayload {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_HEADER_UPDATE;
+ static const bool CHECK_FOR_REFRESH = false;
void encode(bufferlist &bl) const;
void decode(__u8 version, bufferlist::iterator &iter);
struct AsyncProgressPayload : public AsyncRequestPayloadBase {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_ASYNC_PROGRESS;
+ static const bool CHECK_FOR_REFRESH = false;
AsyncProgressPayload() : offset(0), total(0) {}
AsyncProgressPayload(const AsyncRequestId &id, uint64_t offset_, uint64_t total_)
struct AsyncCompletePayload : public AsyncRequestPayloadBase {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_ASYNC_COMPLETE;
+ static const bool CHECK_FOR_REFRESH = false;
AsyncCompletePayload() {}
AsyncCompletePayload(const AsyncRequestId &id, int r)
struct FlattenPayload : public AsyncRequestPayloadBase {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_FLATTEN;
+ static const bool CHECK_FOR_REFRESH = true;
FlattenPayload() {}
FlattenPayload(const AsyncRequestId &id) : AsyncRequestPayloadBase(id) {}
struct ResizePayload : public AsyncRequestPayloadBase {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_RESIZE;
+ static const bool CHECK_FOR_REFRESH = true;
ResizePayload() : size(0) {}
ResizePayload(uint64_t size_, const AsyncRequestId &id)
struct SnapPayloadBase {
public:
+ static const bool CHECK_FOR_REFRESH = true;
+
std::string snap_name;
void encode(bufferlist &bl) const;
struct RebuildObjectMapPayload : public AsyncRequestPayloadBase {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_REBUILD_OBJECT_MAP;
+ static const bool CHECK_FOR_REFRESH = true;
RebuildObjectMapPayload() {}
RebuildObjectMapPayload(const AsyncRequestId &id)
struct RenamePayload {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_RENAME;
+ static const bool CHECK_FOR_REFRESH = true;
RenamePayload() {}
RenamePayload(const std::string _image_name) : image_name(_image_name) {}
struct UnknownPayload {
static const NotifyOp NOTIFY_OP = static_cast<NotifyOp>(-1);
+ static const bool CHECK_FOR_REFRESH = false;
void encode(bufferlist &bl) const;
void decode(__u8 version, bufferlist::iterator &iter);
Payload payload;
+ bool check_for_refresh() const;
+
void encode(bufferlist& bl) const;
void decode(bufferlist::iterator& it);
void dump(Formatter *f) const;
#include "include/stringify.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"
template <typename I>
void AcquireRequest<I>::send() {
+ send_flush_notifies();
+}
+
+template <typename I>
+void AcquireRequest<I>::send_flush_notifies() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << __func__ << dendl;
+
+ using klass = AcquireRequest<I>;
+ Context *ctx = create_context_callback<klass, &klass::handle_flush_notifies>(
+ this);
+ m_image_ctx.image_watcher->flush(ctx);
+}
+
+template <typename I>
+Context *AcquireRequest<I>::handle_flush_notifies(int *ret_val) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << __func__ << dendl;
+
+ assert(*ret_val == 0);
send_lock();
+ return nullptr;
}
template <typename I>
* @verbatim
*
* <start>
+ * |
+ * v
+ * FLUSH_NOTIFIES
+ * |
* | /---------------------------------------------------------\
* | | |
* | | (no lockers) |
int m_error_result;
+ void send_flush_notifies();
+ Context *handle_flush_notifies(int *ret_val);
+
void send_lock();
Context *handle_lock(int *ret_val);
#include "librbd/AioImageRequestWQ.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"
m_on_releasing = nullptr;
}
+ send_flush_notifies();
+ return nullptr;
+}
+
+template <typename I>
+void ReleaseRequest<I>::send_flush_notifies() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << __func__ << dendl;
+
+ using klass = ReleaseRequest<I>;
+ Context *ctx = create_context_callback<
+ klass, &klass::handle_flush_notifies>(this);
+ m_image_ctx.image_watcher->flush(ctx);
+}
+
+template <typename I>
+Context *ReleaseRequest<I>::handle_flush_notifies(int *ret_val) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << __func__ << dendl;
+
+ assert(*ret_val == 0);
send_close_journal();
return nullptr;
}
* BLOCK_WRITES
* |
* v
- * CANCEL_OP_REQUESTS . . . . . . . . . . . .
+ * CANCEL_OP_REQUESTS
+ * |
+ * v
+ * FLUSH_NOTIFIES . . . . . . . . . . . . . .
* | .
* v .
* CLOSE_JOURNAL .
void send_cancel_op_requests();
Context *handle_cancel_op_requests(int *ret_val);
+ void send_flush_notifies();
+ Context *handle_flush_notifies(int *ret_val);
+
void send_close_journal();
Context *handle_close_journal(int *ret_val);
return;
}
- assert(m_aio_notify_flush == nullptr);
- m_aio_notify_flush = on_finish;
+ m_aio_notify_flush_ctxs.push_back(on_finish);
}
void Notifier::notify(bufferlist &bl, bufferlist *out_bl, Context *on_finish) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 20) << __func__ << ": pending=" << m_pending_aio_notifies
<< dendl;
- if (m_pending_aio_notifies == 0 && m_aio_notify_flush != nullptr) {
- m_image_ctx.op_work_queue->queue(m_aio_notify_flush, 0);
+ if (m_pending_aio_notifies == 0) {
+ for (auto ctx : m_aio_notify_flush_ctxs) {
+ m_image_ctx.op_work_queue->queue(ctx, 0);
+ }
+ m_aio_notify_flush_ctxs.clear();
}
}
#include "include/buffer.h"
#include "include/Context.h"
#include "common/Mutex.h"
+#include <list>
namespace librbd {
void notify(bufferlist &bl, bufferlist *out_bl, Context *on_finish);
private:
+ typedef std::list<Context*> Contexts;
+
struct C_AioNotify : public Context {
Notifier *notifier;
Context *on_finish;
Mutex m_aio_notify_lock;
size_t m_pending_aio_notifies = 0;
- Context *m_aio_notify_flush = nullptr;
+ Contexts m_aio_notify_flush_ctxs;
void handle_notify(int r, Context *on_finish);
Context *SnapshotCreateRequest<I>::handle_allocate_snap_id(int *result) {
I &image_ctx = this->m_image_ctx;
CephContext *cct = image_ctx.cct;
- ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl;
+ ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << ", "
+ << "snap_id=" << m_snap_id << dendl;
if (*result < 0) {
save_result(result);
exec(mock_image_ctx.header_oid, _, "lock", "break_lock", _, _, _))
.WillOnce(Return(r));
}
+
+ void expect_flush_notifies(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
};
TEST_F(TestMockExclusiveLockAcquireRequest, Success) {
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, 0);
MockObjectMap mock_object_map;
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, 0);
MockObjectMap mock_object_map;
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, 0);
expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, false);
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, 0);
MockObjectMap *mock_object_map = new MockObjectMap();
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
"auto 123", MockExclusiveLock::WATCHER_LOCK_TAG,
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, -EINVAL, entity_name_t::CLIENT(1), "",
"", "", LOCK_EXCLUSIVE);
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, -ENOENT, entity_name_t::CLIENT(1), "",
"", "", LOCK_EXCLUSIVE);
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
"auto 123", "external tag", LOCK_EXCLUSIVE);
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
"auto 123", MockExclusiveLock::WATCHER_LOCK_TAG,
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
"external cookie", MockExclusiveLock::WATCHER_LOCK_TAG,
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
"auto 123", MockExclusiveLock::WATCHER_LOCK_TAG,
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
"auto 123", MockExclusiveLock::WATCHER_LOCK_TAG,
mock_image_ctx.blacklist_on_break_lock = false;
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
"auto 123", MockExclusiveLock::WATCHER_LOCK_TAG,
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
"auto 123", MockExclusiveLock::WATCHER_LOCK_TAG,
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
"auto 123", MockExclusiveLock::WATCHER_LOCK_TAG,
expect_op_work_queue(mock_image_ctx);
InSequence seq;
+ expect_flush_notifies(mock_image_ctx);
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4",
"auto 123", MockExclusiveLock::WATCHER_LOCK_TAG,
EXPECT_CALL(mock_object_map, close(_))
.WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
}
+
+ void expect_flush_notifies(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
};
TEST_F(TestMockExclusiveLockReleaseRequest, Success) {
InSequence seq;
expect_block_writes(mock_image_ctx, 0);
expect_cancel_op_requests(mock_image_ctx, 0);
+ expect_flush_notifies(mock_image_ctx);
MockJournal *mock_journal = new MockJournal();
mock_image_ctx.journal = mock_journal;
InSequence seq;
expect_cancel_op_requests(mock_image_ctx, 0);
+ expect_flush_notifies(mock_image_ctx);
MockObjectMap *mock_object_map = new MockObjectMap();
mock_image_ctx.object_map = mock_object_map;
InSequence seq;
expect_cancel_op_requests(mock_image_ctx, 0);
+ expect_flush_notifies(mock_image_ctx);
expect_unlock(mock_image_ctx, 0);
InSequence seq;
expect_block_writes(mock_image_ctx, 0);
expect_cancel_op_requests(mock_image_ctx, 0);
+ expect_flush_notifies(mock_image_ctx);
expect_unlock(mock_image_ctx, -EINVAL);
#include "test/librbd/mock/MockObjectMap.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/ImageState.h"
#include "librbd/internal.h"
#include "librbd/Operations.h"
#include "librbd/image/RefreshRequest.h"
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, snap_create(*ictx, "snap"));
+ ASSERT_EQ(0, ictx->state->refresh());
MockImageCtx mock_image_ctx(*ictx);
expect_op_work_queue(mock_image_ctx);
#include "gmock/gmock.h"
+class Context;
+
namespace librbd {
struct MockImageWatcher {
MOCK_METHOD0(unregister_watch, void());
+ MOCK_METHOD1(flush, void(Context *));
MOCK_CONST_METHOD0(get_watch_handle, uint64_t());
m_notify_acks = {{NOTIFY_OP_REQUEST_LOCK, {}}};
ictx->image_watcher->notify_request_lock();
+ C_SaferCond ctx;
+ ictx->image_watcher->flush(&ctx);
+ ctx.wait();
+
ASSERT_TRUE(wait_for_notifies(*ictx));
NotifyOps expected_notify_ops;
.WillRepeatedly(Return(true));
}
+ void expect_flush_notifies(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_))
+ .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+ }
+
int when_init(MockImageCtx &mock_image_ctx,
MockExclusiveLock &exclusive_lock) {
C_SaferCond ctx;
ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
expect_unblock_writes(mock_image_ctx);
+ expect_flush_notifies(mock_image_ctx);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}
ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
expect_unblock_writes(mock_image_ctx);
+ expect_flush_notifies(mock_image_ctx);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}
ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
expect_unblock_writes(mock_image_ctx);
+ expect_flush_notifies(mock_image_ctx);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}
ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
expect_unblock_writes(mock_image_ctx);
+ expect_flush_notifies(mock_image_ctx);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}
ASSERT_EQ(0, when_release_lock(mock_image_ctx, exclusive_lock));
expect_unblock_writes(mock_image_ctx);
+ expect_flush_notifies(mock_image_ctx);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}
ASSERT_EQ(0, release_lock_ctx1.wait());
expect_unblock_writes(mock_image_ctx);
- expect_op_work_queue(mock_image_ctx);
+ expect_flush_notifies(mock_image_ctx);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}