test/librbd/test_support.h \
test/librbd/mock/MockAioImageRequestWQ.h \
test/librbd/mock/MockContextWQ.h \
+ test/librbd/mock/MockExclusiveLock.h \
test/librbd/mock/MockImageCtx.h \
test/librbd/mock/MockImageWatcher.h \
test/librbd/mock/MockJournal.h \
test/librbd/mock/MockObjectMap.h \
+ test/librbd/mock/MockReadahead.h \
test/librbd/object_map/mock/MockInvalidateRequest.h
if LINUX
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
expect_lock(mock_image_ctx, 0);
MockJournal mock_journal;
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
expect_lock(mock_image_ctx, 0);
expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
expect_lock(mock_image_ctx, 0);
MockJournal mock_journal;
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
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,
LOCK_EXCLUSIVE);
expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
- expect_op_work_queue(mock_image_ctx);
expect_blacklist_add(mock_image_ctx, 0);
expect_break_lock(mock_image_ctx, 0);
expect_lock(mock_image_ctx, -ENOENT);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, -EINVAL, entity_name_t::CLIENT(1), "",
"", "", LOCK_EXCLUSIVE);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
expect_lock(mock_image_ctx, -EBUSY);
expect_get_lock_info(mock_image_ctx, -ENOENT, entity_name_t::CLIENT(1), "",
"", "", LOCK_EXCLUSIVE);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
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);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
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,
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
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,
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
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,
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
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,
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
mock_image_ctx.blacklist_on_break_lock = false;
+ InSequence seq;
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,
LOCK_EXCLUSIVE);
expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
- expect_op_work_queue(mock_image_ctx);
expect_break_lock(mock_image_ctx, 0);
expect_lock(mock_image_ctx, -ENOENT);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
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,
LOCK_EXCLUSIVE);
expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
- expect_op_work_queue(mock_image_ctx);
expect_blacklist_add(mock_image_ctx, -EINVAL);
C_SaferCond ctx;
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
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,
LOCK_EXCLUSIVE);
expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
- expect_op_work_queue(mock_image_ctx);
expect_blacklist_add(mock_image_ctx, 0);
expect_break_lock(mock_image_ctx, -ENOENT);
expect_lock(mock_image_ctx, -EINVAL);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
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,
LOCK_EXCLUSIVE);
expect_list_watchers(mock_image_ctx, 0, "dead client", 123);
- expect_op_work_queue(mock_image_ctx);
expect_blacklist_add(mock_image_ctx, 0);
expect_break_lock(mock_image_ctx, -EINVAL);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
expect_cancel_op_requests(mock_image_ctx, 0);
MockJournal *mock_journal = new MockJournal();
expect_close_journal(mock_image_ctx, *mock_journal, -EINVAL);
MockObjectMap *mock_object_map = new MockObjectMap();
- mock_image_ctx.object_map_ptr = mock_object_map;
+ mock_image_ctx.object_map = mock_object_map;
expect_unlock_object_map(mock_image_ctx, *mock_object_map);
expect_unlock(mock_image_ctx, 0);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
expect_cancel_op_requests(mock_image_ctx, 0);
MockObjectMap *mock_object_map = new MockObjectMap();
- mock_image_ctx.object_map_ptr = mock_object_map;
+ mock_image_ctx.object_map = mock_object_map;
expect_unlock_object_map(mock_image_ctx, *mock_object_map);
expect_unlock(mock_image_ctx, 0);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
expect_cancel_op_requests(mock_image_ctx, 0);
expect_unlock(mock_image_ctx, 0);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- InSequence seq;
MockImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+ InSequence seq;
expect_cancel_op_requests(mock_image_ctx, 0);
expect_unlock(mock_image_ctx, -EINVAL);
struct MockAioImageRequestWQ {
MOCK_METHOD1(block_writes, void(Context *));
MOCK_METHOD0(unblock_writes, void());
+
+ MOCK_CONST_METHOD0(writes_empty, bool());
};
} // namespace librbd
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_EXCLUSIVE_LOCK_H
+#define CEPH_TEST_LIBRBD_MOCK_EXCLUSIVE_LOCK_H
+
+#include "include/int_types.h"
+#include "include/rados/librados.hpp"
+#include "gmock/gmock.h"
+
+class Context;
+
+namespace librbd {
+
+struct MockExclusiveLock {
+ MOCK_CONST_METHOD0(is_lock_owner, bool());
+
+ MOCK_METHOD1(assert_header_locked, void(librados::ObjectWriteOperation *));
+
+ MOCK_METHOD1(shut_down, void(Context*));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_EXCLUSIVE_LOCK_H
#include "test/librbd/mock/MockAioImageRequestWQ.h"
#include "test/librbd/mock/MockContextWQ.h"
+#include "test/librbd/mock/MockExclusiveLock.h"
#include "test/librbd/mock/MockImageWatcher.h"
#include "test/librbd/mock/MockJournal.h"
#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librbd/mock/MockReadahead.h"
#include "common/RWLock.h"
#include "librbd/ImageCtx.h"
#include "gmock/gmock.h"
layout(image_ctx.layout),
aio_work_queue(new MockAioImageRequestWQ()),
op_work_queue(new MockContextWQ()),
- image_watcher(NULL), object_map_ptr(NULL), journal(NULL),
+ parent(NULL), image_watcher(NULL), object_map(NULL),
+ exclusive_lock(NULL), journal(NULL),
concurrent_management_ops(image_ctx.concurrent_management_ops),
blacklist_on_break_lock(image_ctx.blacklist_on_break_lock),
blacklist_expire_seconds(image_ctx.blacklist_expire_seconds)
uint64_t in_size, parent_info parent,
uint8_t protection_status, uint64_t flags));
MOCK_METHOD2(rm_snap, void(std::string in_snap_name, librados::snap_t id));
+
MOCK_METHOD1(flush, void(Context *));
+ MOCK_METHOD1(flush_copyup, void(Context *));
+
+ MOCK_METHOD1(shut_down_cache, void(Context *));
MOCK_CONST_METHOD1(test_features, bool(uint64_t test_features));
MOCK_METHOD1(cancel_async_requests, void(Context*));
- MOCK_METHOD0(create_object_map, MockObjectMap*());
+ MOCK_METHOD1(create_object_map, MockObjectMap*(uint64_t));
MOCK_METHOD0(create_journal, MockJournal*());
ImageCtx *image_ctx;
MockAioImageRequestWQ *aio_work_queue;
MockContextWQ *op_work_queue;
- MockImageWatcher *image_watcher;
- MockObjectMap object_map; // TODO replace with ptr
- MockObjectMap *object_map_ptr; // TODO
+ MockReadahead readahead;
+ MockImageCtx *parent;
+
+ MockImageWatcher *image_watcher;
+ MockObjectMap *object_map;
+ MockExclusiveLock *exclusive_lock;
MockJournal *journal;
int concurrent_management_ops;
namespace librbd {
struct MockImageWatcher {
- MOCK_CONST_METHOD0(is_lock_owner, bool());
- MOCK_CONST_METHOD1(is_lock_supported, bool(const RWLock &));
- MOCK_METHOD1(assert_header_locked, void (librados::ObjectWriteOperation *));
+ MOCK_METHOD0(unregister_watch, void());
+ MOCK_CONST_METHOD0(get_watch_handle, uint64_t());
+
+ MOCK_METHOD0(notify_acquired_lock, void());
+ MOCK_METHOD0(notify_released_lock, void());
MOCK_METHOD0(notify_request_lock, void());
};
struct MockObjectMap {
MOCK_CONST_METHOD1(enabled, bool(const RWLock &object_map_lock));
+ MOCK_METHOD1(open, void(Context *on_finish));
+
MOCK_METHOD1(lock, void(Context *on_finish));
MOCK_METHOD1(unlock, void(Context *on_finish));
- MOCK_METHOD2(refresh, void(uint64_t snap_id, Context *on_finish));
MOCK_METHOD2(snapshot_add, void(uint64_t snap_id, Context *on_finish));
MOCK_METHOD2(snapshot_remove, void(uint64_t snap_id, Context *on_finish));
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_READAHEAD_H
+#define CEPH_TEST_LIBRBD_MOCK_READAHEAD_H
+
+#include "include/int_types.h"
+#include "gmock/gmock.h"
+
+class Context;
+
+namespace librbd {
+
+struct MockReadahead {
+ MOCK_METHOD1(set_max_readahead_size, void(uint64_t));
+ MOCK_METHOD1(wait_for_pending, void(Context *));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_READAHEAD_H
(*object_map)[i] = rand() % 3;
}
}
-
- void when_apply_refresh_request(MockImageCtx &mock_image_ctx,
- MockRefreshRequest *req) {
- RWLock::WLocker snap_locker(mock_image_ctx.snap_lock);
- RWLock::WLocker object_map_locker(mock_image_ctx.object_map_lock);
- expect_get_image_size(mock_image_ctx, mock_image_ctx.image_ctx->size);
- req->apply();
- }
};
TEST_F(TestMockObjectMapRefreshRequest, Success) {
InSequence seq;
expect_get_image_size(mock_image_ctx, mock_image_ctx.image_ctx->size);
expect_object_map_load(mock_image_ctx, &on_disk_object_map, 0);
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.image_ctx->size);
req->send();
ASSERT_EQ(0, ctx.wait());
- when_apply_refresh_request(mock_image_ctx, req);
ASSERT_EQ(on_disk_object_map, object_map);
}
MockInvalidateRequest invalidate_request;
expect_invalidate_request(mock_image_ctx, invalidate_request);
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.image_ctx->size);
req->send();
ASSERT_EQ(0, ctx.wait());
-
- when_apply_refresh_request(mock_image_ctx, req);
}
TEST_F(TestMockObjectMapRefreshRequest, LoadCorrupt) {
expect_invalidate_request(mock_image_ctx, invalidate_request);
expect_truncate_request(mock_image_ctx);
expect_object_map_resize(mock_image_ctx, on_disk_object_map.size(), 0);
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.image_ctx->size);
req->send();
ASSERT_EQ(0, ctx.wait());
-
- when_apply_refresh_request(mock_image_ctx, req);
}
TEST_F(TestMockObjectMapRefreshRequest, TooSmall) {
MockInvalidateRequest invalidate_request;
expect_invalidate_request(mock_image_ctx, invalidate_request);
expect_object_map_resize(mock_image_ctx, on_disk_object_map.size(), 0);
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.image_ctx->size);
req->send();
ASSERT_EQ(0, ctx.wait());
-
- when_apply_refresh_request(mock_image_ctx, req);
}
TEST_F(TestMockObjectMapRefreshRequest, TooLarge) {
InSequence seq;
expect_get_image_size(mock_image_ctx, mock_image_ctx.image_ctx->size);
expect_object_map_load(mock_image_ctx, &large_object_map, 0);
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.image_ctx->size);
req->send();
ASSERT_EQ(0, ctx.wait());
-
- when_apply_refresh_request(mock_image_ctx, req);
}
TEST_F(TestMockObjectMapRefreshRequest, ResizeError) {
MockInvalidateRequest invalidate_request;
expect_invalidate_request(mock_image_ctx, invalidate_request);
expect_object_map_resize(mock_image_ctx, on_disk_object_map.size(), -ESTALE);
+ expect_get_image_size(mock_image_ctx, mock_image_ctx.image_ctx->size);
req->send();
ASSERT_EQ(0, ctx.wait());
-
- when_apply_refresh_request(mock_image_ctx, req);
-}
-
-TEST_F(TestMockObjectMapRefreshRequest, StaleRefresh) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
-
- C_SaferCond ctx;
- ceph::BitVector<2> object_map;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &object_map,
- TEST_SNAP_ID, &ctx);
- ASSERT_THROW(when_apply_refresh_request(mock_image_ctx, req),
- ceph::FailedAssertion);
-}
-
-TEST_F(TestMockObjectMapRefreshRequest, Discard) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
-
- C_SaferCond ctx;
- ceph::BitVector<2> object_map;
- MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &object_map,
- TEST_SNAP_ID, &ctx);
- req->discard();
}
} // namespace object_map
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "common/bit_vector.hpp"
#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
#include "librbd/object_map/ResizeRequest.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "common/bit_vector.hpp"
#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
#include "librbd/object_map/SnapshotCreateRequest.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/librbd/test_support.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
#include "librbd/object_map/SnapshotRemoveRequest.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
uint64_t snap_id = ictx->snap_info.rbegin()->first;
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
uint64_t snap_id = ictx->snap_info.rbegin()->first;
expect_load_map(ictx, snap_id, -EINVAL);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
uint64_t snap_id = ictx->snap_info.rbegin()->first;
expect_load_map(ictx, snap_id, 0);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
uint64_t snap_id = ictx->snap_info.rbegin()->first;
expect_load_map(ictx, snap_id, 0);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
uint64_t snap_id = ictx->snap_info.rbegin()->first;
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
uint64_t snap_id = ictx->snap_info.rbegin()->first;
if (ictx->test_features(RBD_FEATURE_FAST_DIFF)) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
uint64_t snap_id = ictx->snap_info.rbegin()->first;
#include "test/librbd/test_mock_fixture.h"
#include "test/librbd/test_support.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "librbd/ImageState.h"
#include "librbd/internal.h"
#include "librbd/ObjectMap.h"
#include "librbd/object_map/SnapshotRollbackRequest.h"
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
uint64_t snap_id = ictx->snap_info.rbegin()->first;
expect_read_map(ictx, snap_id, 0);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
uint64_t snap_id = ictx->snap_info.rbegin()->first;
expect_read_map(ictx, snap_id, -ENOENT);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
uint64_t snap_id = ictx->snap_info.rbegin()->first;
expect_read_map(ictx, snap_id, 0);
#include "test/librbd/test_support.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
#include "librbd/object_map/UpdateRequest.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
ASSERT_EQ(CEPH_NOSNAP, ictx->snap_id);
uint64_t snap_id = ictx->snap_info.rbegin()->first;
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "common/bit_vector.hpp"
#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
#include "librbd/operation/SnapshotCreateRequest.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
}
void expect_verify_lock_ownership(MockImageCtx &mock_image_ctx) {
- EXPECT_CALL(*mock_image_ctx.image_watcher, is_lock_supported(_))
- .WillRepeatedly(Return(true));
- EXPECT_CALL(*mock_image_ctx.image_watcher, is_lock_owner())
- .WillRepeatedly(Return(true));
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
+ .WillRepeatedly(Return(true));
+ }
}
void expect_allocate_snap_id(MockImageCtx &mock_image_ctx, int r) {
}
void expect_snap_create(MockImageCtx &mock_image_ctx, int r) {
- if (!mock_image_ctx.old_format) {
- EXPECT_CALL(*mock_image_ctx.image_watcher, assert_header_locked(_))
+ if (!mock_image_ctx.old_format &&
+ mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, assert_header_locked(_))
.Times(r == -ESTALE ? 2 : 1);
}
}
void expect_object_map_snap_create(MockImageCtx &mock_image_ctx) {
- bool enabled = mock_image_ctx.image_ctx->test_features(RBD_FEATURE_OBJECT_MAP);
- EXPECT_CALL(mock_image_ctx.object_map, enabled(_))
- .WillOnce(Return(enabled));
- if (enabled) {
- EXPECT_CALL(mock_image_ctx.object_map, snapshot_add(_, _))
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.object_map, snapshot_add(_, _))
.WillOnce(WithArg<1>(CompleteContext(
0, mock_image_ctx.image_ctx->op_work_queue)));
}
MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
expect_verify_lock_ownership(mock_image_ctx);
expect_op_work_queue(mock_image_ctx);
MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
expect_verify_lock_ownership(mock_image_ctx);
expect_op_work_queue(mock_image_ctx);
MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
expect_verify_lock_ownership(mock_image_ctx);
expect_op_work_queue(mock_image_ctx);
MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
expect_verify_lock_ownership(mock_image_ctx);
expect_op_work_queue(mock_image_ctx);
MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
expect_verify_lock_ownership(mock_image_ctx);
expect_op_work_queue(mock_image_ctx);
#include "test/librbd/mock/MockImageCtx.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
#include "librbd/internal.h"
#include "librbd/operation/SnapshotProtectRequest.h"
#include "gmock/gmock.h"
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
#include "test/librbd/mock/MockImageCtx.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
#include "librbd/internal.h"
#include "librbd/operation/SnapshotRemoveRequest.h"
#include "gmock/gmock.h"
}
void expect_object_map_snap_remove(MockImageCtx &mock_image_ctx, int r) {
- bool enabled = mock_image_ctx.image_ctx->test_features(RBD_FEATURE_OBJECT_MAP);
- EXPECT_CALL(mock_image_ctx.object_map, enabled(_))
- .WillOnce(Return(enabled));
- if (enabled) {
- EXPECT_CALL(mock_image_ctx.object_map, snapshot_remove(_, _))
+ if (mock_image_ctx.object_map != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.object_map, snapshot_remove(_, _))
.WillOnce(WithArg<1>(CompleteContext(
r, mock_image_ctx.image_ctx->op_work_queue)));
}
return;
}
- EXPECT_CALL(*mock_image_ctx.image_watcher, is_lock_owner())
- .WillRepeatedly(Return(false));
+ if (mock_image_ctx.exclusive_lock != nullptr) {
+ EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
+ .WillRepeatedly(Return(false));
+ }
}
void expect_snap_remove(MockImageCtx &mock_image_ctx, int r) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
expect_op_work_queue(mock_image_ctx);
::testing::InSequence seq;
librbd::NoOpProgressContext prog_ctx;
ASSERT_EQ(0, librbd::flatten(ictx, prog_ctx));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
expect_op_work_queue(mock_image_ctx);
uint64_t snap_id = ictx->snap_info.rbegin()->first;
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
expect_op_work_queue(mock_image_ctx);
::testing::InSequence seq;
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
expect_op_work_queue(mock_image_ctx);
::testing::InSequence seq;
librbd::NoOpProgressContext prog_ctx;
ASSERT_EQ(0, librbd::flatten(ictx, prog_ctx));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
expect_op_work_queue(mock_image_ctx);
uint64_t snap_id = ictx->snap_info.rbegin()->first;
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
expect_op_work_queue(mock_image_ctx);
::testing::InSequence seq;
#include "test/librados_test_stub/MockTestMemRadosClient.h"
#include "include/rados/librados.hpp"
#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
#include "librbd/internal.h"
#include "librbd/operation/SnapshotUnprotectRequest.h"
#include "gmock/gmock.h"
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
- ASSERT_EQ(0, librbd::ictx_check(ictx));
+ ASSERT_EQ(0, ictx->state->refresh_if_required());
MockImageCtx mock_image_ctx(*ictx);
{
}
- struct LockListener : public librbd::ImageWatcher::Listener {
- Mutex lock;
- Cond cond;
- size_t releasing_lock_count;
- size_t lock_updated_count;
- bool lock_owner;
-
- LockListener()
- : lock("lock"), releasing_lock_count(0), lock_updated_count(0),
- lock_owner(false) {
- }
-
- virtual bool handle_requested_lock() {
- return true;
- }
- virtual void handle_lock_updated(
- librbd::ImageWatcher::LockUpdateState state) {
- Mutex::Locker locker(lock);
- ++lock_updated_count;
-
- switch (state) {
- case librbd::ImageWatcher::LOCK_UPDATE_STATE_NOT_SUPPORTED:
- case librbd::ImageWatcher::LOCK_UPDATE_STATE_UNLOCKED:
- case librbd::ImageWatcher::LOCK_UPDATE_STATE_NOTIFICATION:
- lock_owner = false;
- break;
- case librbd::ImageWatcher::LOCK_UPDATE_STATE_RELEASING:
- lock_owner = false;
- ++releasing_lock_count;
- break;
- case librbd::ImageWatcher::LOCK_UPDATE_STATE_LOCKED:
- lock_owner = true;
- break;
- }
- cond.Signal();
- }
- };
-
class WatchCtx : public librados::WatchCtx2 {
public:
WatchCtx(TestImageWatcher &parent) : m_parent(parent), m_handle(0) {}
return 0;
}
- void register_lock_listener(librbd::ImageCtx &ictx) {
- ictx.image_watcher->register_listener(&m_lock_listener);
- }
-
int register_image_watch(librbd::ImageCtx &ictx) {
m_watch_ctx = new WatchCtx(*this);
return m_watch_ctx->watch(ictx);
}
- bool wait_for_releasing_lock(librbd::ImageCtx &ictx) {
- Mutex::Locker locker(m_lock_listener.lock);
- while (m_lock_listener.releasing_lock_count == 0) {
- if (m_lock_listener.cond.WaitInterval(ictx.cct, m_lock_listener.lock,
- utime_t(10, 0)) != 0) {
- return false;
- }
- }
- m_lock_listener.releasing_lock_count = 0;
- return true;
- }
-
- bool wait_for_lock_updated(librbd::ImageCtx &ictx) {
- Mutex::Locker locker(m_lock_listener.lock);
- while (m_lock_listener.lock_updated_count == 0) {
- if (m_lock_listener.cond.WaitInterval(ictx.cct, m_lock_listener.lock,
- utime_t(10, 0)) != 0) {
- return false;
- }
- }
- m_lock_listener.lock_updated_count = 0;
- return true;
- }
-
bool wait_for_notifies(librbd::ImageCtx &ictx) {
Mutex::Locker l(m_callback_lock);
while (m_notifies.size() < m_notify_acks.size()) {
WatchCtx *m_watch_ctx;
- LockListener m_lock_listener;
-
NotifyOps m_notifies;
NotifyOpPayloads m_notify_payloads;
NotifyOpPayloads m_notify_acks;
}
};
-TEST_F(TestImageWatcher, IsLockSupported) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- RWLock::WLocker l(ictx->owner_lock);
- ASSERT_TRUE(ictx->image_watcher);
- ASSERT_TRUE(ictx->image_watcher->is_lock_supported());
-
- ictx->read_only = true;
- ASSERT_FALSE(ictx->image_watcher->is_lock_supported());
- ictx->read_only = false;
-
- ictx->features &= ~RBD_FEATURE_EXCLUSIVE_LOCK;
- ASSERT_FALSE(ictx->image_watcher->is_lock_supported());
- ictx->features |= RBD_FEATURE_EXCLUSIVE_LOCK;
-
- ictx->snap_id = 1234;
- ASSERT_FALSE(ictx->image_watcher->is_lock_supported());
- ictx->snap_id = CEPH_NOSNAP;
-}
-
-TEST_F(TestImageWatcher, TryLock) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- ASSERT_TRUE(ictx->image_watcher);
-
- {
- RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
- }
-
- std::map<rados::cls::lock::locker_id_t,
- rados::cls::lock::locker_info_t> lockers;
- ClsLockType lock_type;
- ASSERT_EQ(0, rados::cls::lock::get_lock_info(&m_ioctx, ictx->header_oid,
- RBD_LOCK_NAME, &lockers,
- &lock_type, NULL));
- ASSERT_EQ(LOCK_EXCLUSIVE, lock_type);
- ASSERT_EQ(1U, lockers.size());
-}
-
-TEST_F(TestImageWatcher, TryLockNotifyAnnounceLocked) {
+TEST_F(TestImageWatcher, NotifyRequestLock) {
REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, register_image_watch(*ictx));
- m_notify_acks = {{NOTIFY_OP_ACQUIRED_LOCK, {}}};
- {
- RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
- }
+ m_notify_acks = {{NOTIFY_OP_REQUEST_LOCK, {}}};
+ ictx->image_watcher->notify_request_lock();
ASSERT_TRUE(wait_for_notifies(*ictx));
NotifyOps expected_notify_ops;
- expected_notify_ops += NOTIFY_OP_ACQUIRED_LOCK;
+ expected_notify_ops += NOTIFY_OP_REQUEST_LOCK;
ASSERT_EQ(expected_notify_ops, m_notifies);
}
-TEST_F(TestImageWatcher, TryLockWithTimedOutOwner) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- // use new Rados connection due to blacklisting
- librados::Rados rados;
- ASSERT_EQ("", connect_cluster_pp(rados));
-
- librados::IoCtx io_ctx;
- ASSERT_EQ(0, rados.ioctx_create(_pool_name.c_str(), io_ctx));
- librbd::ImageCtx *ictx = new librbd::ImageCtx(m_image_name.c_str(), "", NULL,
- io_ctx, false);
- ASSERT_EQ(0, librbd::open_image(ictx));
- ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "auto 1234"));
- librbd::close_image(ictx);
- io_ctx.close();
-
- // no watcher on the locked image means we can break the lock
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
-
- rados.test_blacklist_self(false);
-}
-
-TEST_F(TestImageWatcher, TryLockWithUserExclusiveLock) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked"));
-
- RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(-EBUSY, ictx->image_watcher->try_lock());
- ASSERT_FALSE(ictx->image_watcher->is_lock_owner());
-
- ASSERT_EQ(0, unlock_image());
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
-}
-
-TEST_F(TestImageWatcher, TryLockWithUserSharedLocked) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- ASSERT_EQ(0, lock_image(*ictx, LOCK_SHARED, "manually locked"));
-
- RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(-EBUSY, ictx->image_watcher->try_lock());
- ASSERT_FALSE(ictx->image_watcher->is_lock_owner());
-
- ASSERT_EQ(0, unlock_image());
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
-}
-
-TEST_F(TestImageWatcher, ReleaseLockNotLocked) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->release_lock());
-}
-
-TEST_F(TestImageWatcher, ReleaseLockNotifies) {
+TEST_F(TestImageWatcher, NotifyReleasedLock) {
REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, register_image_watch(*ictx));
- m_notify_acks = {{NOTIFY_OP_ACQUIRED_LOCK, {}}};
- {
- RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
- }
- ASSERT_TRUE(wait_for_notifies(*ictx));
+ m_notify_acks = {{NOTIFY_OP_RELEASED_LOCK, {}}};
+ ictx->image_watcher->notify_released_lock();
- m_notify_acks += std::make_pair(NOTIFY_OP_RELEASED_LOCK, bufferlist());
- {
- RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->release_lock());
- }
ASSERT_TRUE(wait_for_notifies(*ictx));
NotifyOps expected_notify_ops;
- expected_notify_ops += NOTIFY_OP_ACQUIRED_LOCK, NOTIFY_OP_RELEASED_LOCK;
+ expected_notify_ops += NOTIFY_OP_RELEASED_LOCK;
ASSERT_EQ(expected_notify_ops, m_notifies);
}
-TEST_F(TestImageWatcher, ReleaseLockBrokenLock) {
+TEST_F(TestImageWatcher, NotifyAcquiredLock) {
REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
-
- std::map<rados::cls::lock::locker_id_t,
- rados::cls::lock::locker_info_t> lockers;
- ClsLockType lock_type;
- ASSERT_EQ(0, rados::cls::lock::get_lock_info(&m_ioctx, ictx->header_oid,
- RBD_LOCK_NAME, &lockers,
- &lock_type, NULL));
- ASSERT_EQ(1U, lockers.size());
- ASSERT_EQ(0, rados::cls::lock::break_lock(&m_ioctx, ictx->header_oid,
- RBD_LOCK_NAME,
- lockers.begin()->first.cookie,
- lockers.begin()->first.locker));
-
- ASSERT_EQ(0, ictx->image_watcher->release_lock());
-}
-
-TEST_F(TestImageWatcher, RequestLock) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, register_image_watch(*ictx));
- register_lock_listener(*ictx);
m_notify_acks = {{NOTIFY_OP_ACQUIRED_LOCK, {}}};
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
+ ictx->image_watcher->notify_acquired_lock();
ASSERT_TRUE(wait_for_notifies(*ictx));
- NotifyOps expected_notify_ops;
- expected_notify_ops += NOTIFY_OP_ACQUIRED_LOCK;
- ASSERT_EQ(expected_notify_ops, m_notifies);
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
- }
-}
-
-TEST_F(TestImageWatcher, RequestLockFromPeer) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- ASSERT_EQ(0, register_image_watch(*ictx));
- ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
- "auto " + stringify(m_watch_ctx->get_handle())));
-
- register_lock_listener(*ictx);
- m_notify_acks = {{NOTIFY_OP_REQUEST_LOCK, create_response_message(0)}};
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
-
- ASSERT_TRUE(wait_for_notifies(*ictx));
NotifyOps expected_notify_ops;
- expected_notify_ops += NOTIFY_OP_REQUEST_LOCK;
- ASSERT_EQ(expected_notify_ops, m_notifies);
-
- ASSERT_EQ(0, unlock_image());
-
- {
- Mutex::Locker l(m_callback_lock);
- m_notifies.clear();
- m_notify_acks = {{NOTIFY_OP_RELEASED_LOCK,{}}};
- }
-
- bufferlist bl;
- {
- ENCODE_START(1, 1, bl);
- ::encode(NOTIFY_OP_RELEASED_LOCK, bl);
- ENCODE_FINISH(bl);
- }
- ASSERT_EQ(0, m_ioctx.notify2(ictx->header_oid, bl, 5000, NULL));
- ASSERT_TRUE(wait_for_lock_updated(*ictx));
-
- {
- Mutex::Locker l(m_callback_lock);
- m_notifies.clear();
- m_notify_acks = {{NOTIFY_OP_ACQUIRED_LOCK, {}}};
- }
-
- {
- RWLock::RLocker owner_lock(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
-
- ASSERT_TRUE(wait_for_notifies(*ictx));
- expected_notify_ops.clear();
expected_notify_ops += NOTIFY_OP_ACQUIRED_LOCK;
ASSERT_EQ(expected_notify_ops, m_notifies);
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
- }
-}
-
-TEST_F(TestImageWatcher, RequestLockTimedOut) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- ASSERT_EQ(0, register_image_watch(*ictx));
- ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
- "auto " + stringify(m_watch_ctx->get_handle())));
-
- register_lock_listener(*ictx);
- m_notify_acks = {{NOTIFY_OP_REQUEST_LOCK, {}}};
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
-
- ASSERT_TRUE(wait_for_notifies(*ictx));
- NotifyOps expected_notify_ops;
- expected_notify_ops += NOTIFY_OP_REQUEST_LOCK;
- ASSERT_EQ(expected_notify_ops, m_notifies);
-
- // should resend when empty ack returned
- {
- Mutex::Locker l(m_callback_lock);
- m_notifies.clear();
- }
- ASSERT_TRUE(wait_for_notifies(*ictx));
-
- {
- Mutex::Locker l(m_callback_lock);
- ASSERT_EQ(0, unlock_image());
- m_notifies.clear();
- m_notify_acks = {{NOTIFY_OP_ACQUIRED_LOCK, {}}};
- }
-
- ASSERT_TRUE(wait_for_notifies(*ictx));
- ASSERT_TRUE(wait_for_lock_updated(*ictx));
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
- }
-}
-
-TEST_F(TestImageWatcher, RequestLockIgnored) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- ASSERT_EQ(0, register_image_watch(*ictx));
- ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
- "auto " + stringify(m_watch_ctx->get_handle())));
-
- register_lock_listener(*ictx);
- m_notify_acks = {{NOTIFY_OP_REQUEST_LOCK, create_response_message(0)}};
-
- int orig_notify_timeout = ictx->cct->_conf->client_notify_timeout;
- ictx->cct->_conf->set_val("client_notify_timeout", "0");
- BOOST_SCOPE_EXIT( (ictx)(orig_notify_timeout) ) {
- ictx->cct->_conf->set_val("client_notify_timeout",
- stringify(orig_notify_timeout));
- } BOOST_SCOPE_EXIT_END;
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
-
- ASSERT_TRUE(wait_for_notifies(*ictx));
- NotifyOps expected_notify_ops;
- expected_notify_ops += NOTIFY_OP_REQUEST_LOCK;
- ASSERT_EQ(expected_notify_ops, m_notifies);
-
- // after the request times out -- it will be resent
- {
- Mutex::Locker l(m_callback_lock);
- m_notifies.clear();
- }
- ASSERT_TRUE(wait_for_notifies(*ictx));
- ASSERT_EQ(expected_notify_ops, m_notifies);
-
- {
- Mutex::Locker l(m_callback_lock);
- ASSERT_EQ(0, unlock_image());
- m_notifies.clear();
- m_notify_acks = {{NOTIFY_OP_ACQUIRED_LOCK, {}}};
- }
-
- ASSERT_TRUE(wait_for_notifies(*ictx));
- ASSERT_TRUE(wait_for_lock_updated(*ictx));
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
- }
-}
-
-TEST_F(TestImageWatcher, RequestLockTryLockRace) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- ASSERT_EQ(0, register_image_watch(*ictx));
- ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
- "auto " + stringify(m_watch_ctx->get_handle())));
-
- register_lock_listener(*ictx);
- m_notify_acks = {{NOTIFY_OP_REQUEST_LOCK, create_response_message(0)}};
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
-
- ASSERT_TRUE(wait_for_notifies(*ictx));
- NotifyOps expected_notify_ops;
- expected_notify_ops += NOTIFY_OP_REQUEST_LOCK;
- ASSERT_EQ(expected_notify_ops, m_notifies);
-
- {
- Mutex::Locker l(m_callback_lock);
- m_notifies.clear();
- m_notify_acks = {{NOTIFY_OP_RELEASED_LOCK, {}}};
- }
-
- bufferlist bl;
- {
- ENCODE_START(1, 1, bl);
- ::encode(NOTIFY_OP_RELEASED_LOCK, bl);
- ENCODE_FINISH(bl);
- }
- ASSERT_EQ(0, m_ioctx.notify2(ictx->header_oid, bl, 5000, NULL));
-
- // after losing race -- it will re-request
- ASSERT_TRUE(wait_for_notifies(*ictx));
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ASSERT_FALSE(ictx->image_watcher->is_lock_owner());
- }
-
- {
- Mutex::Locker l(m_callback_lock);
- ASSERT_EQ(0, unlock_image());
- m_notifies.clear();
- m_notify_acks = {{NOTIFY_OP_RELEASED_LOCK, {}}};
- }
-
- ASSERT_EQ(0, m_ioctx.notify2(ictx->header_oid, bl, 5000, NULL));
- ASSERT_TRUE(wait_for_lock_updated(*ictx));
-
- {
- Mutex::Locker l(m_callback_lock);
- m_notifies.clear();
- m_notify_acks = {{NOTIFY_OP_ACQUIRED_LOCK, {}}};
- }
-
- {
- RWLock::RLocker owner_lock(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
-
- ASSERT_TRUE(wait_for_lock_updated(*ictx));
- ASSERT_TRUE(wait_for_notifies(*ictx));
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
- }
-}
-
-TEST_F(TestImageWatcher, RequestLockTryLockFailed) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- ASSERT_EQ(0, register_image_watch(*ictx));
- ASSERT_EQ(0, lock_image(*ictx, LOCK_SHARED, "manually 1234"));
-
- register_lock_listener(*ictx);
- m_notify_acks = {{NOTIFY_OP_REQUEST_LOCK, {}}};
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
-
- ASSERT_TRUE(wait_for_notifies(*ictx));
- NotifyOps expected_notify_ops;
- expected_notify_ops += NOTIFY_OP_REQUEST_LOCK;
- ASSERT_EQ(expected_notify_ops, m_notifies);
-
- // should resend when error encountered
- {
- Mutex::Locker l(m_callback_lock);
- m_notifies.clear();
- }
- ASSERT_TRUE(wait_for_notifies(*ictx));
-
- {
- Mutex::Locker l(m_callback_lock);
- ASSERT_EQ(0, unlock_image());
- m_notifies.clear();
- m_notify_acks = {{NOTIFY_OP_ACQUIRED_LOCK, {}}};
- }
-
- ASSERT_TRUE(wait_for_notifies(*ictx));
}
TEST_F(TestImageWatcher, NotifyHeaderUpdate) {
ASSERT_EQ(-ERESTART, flatten_task.result);
}
-TEST_F(TestImageWatcher, PeerRequestsLock) {
- REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- ASSERT_EQ(0, register_image_watch(*ictx));
-
- register_lock_listener(*ictx);
- m_notify_acks = {{NOTIFY_OP_ACQUIRED_LOCK, {}}};
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
-
- ASSERT_TRUE(wait_for_notifies(*ictx));
-
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
- }
-
- // if journaling is enabled, ensure we wait for it to replay since
- // it will block our peer request
- std::string buffer(256, '1');
- ictx->aio_work_queue->write(0, buffer.size(), buffer.c_str(), 0);
-
- {
- Mutex::Locker l(m_callback_lock);
- m_notifies.clear();
- m_notify_acks = {{NOTIFY_OP_RELEASED_LOCK, {}}};
- }
-
- bufferlist bl;
- {
- ENCODE_START(1, 1, bl);
- ::encode(NOTIFY_OP_REQUEST_LOCK, bl);
- ENCODE_FINISH(bl);
- }
- ASSERT_EQ(0, m_ioctx.notify2(ictx->header_oid, bl, 5000, NULL));
-
- ASSERT_TRUE(wait_for_releasing_lock(*ictx));
- ASSERT_TRUE(wait_for_notifies(*ictx));
-}
#include "librbd/AioCompletion.h"
#include "librbd/AioImageRequest.h"
#include "librbd/AioImageRequestWQ.h"
+#include "librbd/ExclusiveLock.h"
#include "librbd/ImageCtx.h"
#include "librbd/ImageWatcher.h"
#include "librbd/Journal.h"
class TestJournalReplay : public TestFixture {
public:
- struct Listener : public librbd::ImageWatcher::Listener{
- Mutex lock;
- Cond cond;
-
- Listener() : lock("TestJournalReplay::Listener::lock") {
- }
- virtual bool handle_requested_lock() {
- return true;
- }
- virtual void handle_releasing_lock() {
- }
- virtual void handle_lock_updated(
- librbd::ImageWatcher::LockUpdateState state) {
- Mutex::Locker locker(lock);
- cond.Signal();
- }
- };
-
- void wait_for_lock_owner(librbd::ImageCtx *ictx) {
- Listener listener;
- ictx->image_watcher->register_listener(&listener);
+ int when_acquired_lock(librbd::ImageCtx *ictx) {
+ C_SaferCond lock_ctx;
{
- RWLock::RLocker owner_locker(ictx->owner_lock);
- while (!ictx->image_watcher->is_lock_owner()) {
- ictx->owner_lock.put_read();
- listener.lock.Lock();
- listener.cond.Wait(listener.lock);
- listener.lock.Unlock();
- ictx->owner_lock.get_read();
- }
+ RWLock::WLocker owner_locker(ictx->owner_lock);
+ ictx->exclusive_lock->request_lock(&lock_ctx);
}
- ictx->image_watcher->unregister_listener(&listener);
+ return lock_ctx.wait();
}
};
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ictx->features &= ~RBD_FEATURE_JOURNALING;
- ASSERT_EQ(0, ictx->close_journal(true));
std::string payload(4096, '1');
librbd::AioCompletion *aio_comp = new librbd::AioCompletion();
// inject a discard operation into the journal
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- {
- RWLock::WLocker owner_locker(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
- wait_for_lock_owner(ictx);
-
- ictx->journal->open();
- ictx->journal->wait_for_journal_ready();
+ ASSERT_EQ(0, when_acquired_lock(ictx));
librbd::journal::EventEntry event_entry(
librbd::journal::AioDiscardEvent(0, payload.size()));
RWLock::RLocker owner_locker(ictx->owner_lock);
ictx->journal->append_io_event(NULL, event_entry, requests, 0, 0, true);
}
- ASSERT_EQ(0, ictx->journal->close());
// re-open the journal so that it replays the new entry
- ictx->journal->open();
- ictx->journal->wait_for_journal_ready();
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
aio_comp = new librbd::AioCompletion();
ictx->aio_work_queue->aio_read(aio_comp, 0, read_payload.size(),
// inject a write operation into the journal
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- {
- RWLock::WLocker owner_locker(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
- wait_for_lock_owner(ictx);
-
- ictx->journal->open();
- ictx->journal->wait_for_journal_ready();
+ ASSERT_EQ(0, when_acquired_lock(ictx));
std::string payload(4096, '1');
bufferlist payload_bl;
RWLock::RLocker owner_locker(ictx->owner_lock);
ictx->journal->append_io_event(NULL, event_entry, requests, 0, 0, true);
}
- ASSERT_EQ(0, ictx->journal->close());
// re-open the journal so that it replays the new entry
- ictx->journal->open();
- ictx->journal->wait_for_journal_ready();
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
std::string read_payload(4096, '\0');
librbd::AioCompletion *aio_comp = new librbd::AioCompletion();
// inject a flush operation into the journal
librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- {
- RWLock::WLocker owner_locker(ictx->owner_lock);
- ictx->image_watcher->request_lock();
- }
- wait_for_lock_owner(ictx);
- ictx->journal->open();
- ictx->journal->wait_for_journal_ready();
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
librbd::journal::AioFlushEvent aio_flush_event;
librbd::journal::EventEntry event_entry(aio_flush_event);
RWLock::RLocker owner_locker(ictx->owner_lock);
ictx->journal->append_io_event(NULL, event_entry, requests, 0, 0, true);
}
- ASSERT_EQ(0, ictx->journal->close());
// start an AIO write op
librbd::Journal *journal = ictx->journal;
ictx->journal = journal;
// re-open the journal so that it replays the new entry
- ictx->journal->open();
- ictx->journal->wait_for_journal_ready();
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, when_acquired_lock(ictx));
ASSERT_TRUE(aio_comp->is_complete());
ASSERT_EQ(0, aio_comp->wait_for_complete());
// vim: ts=8 sw=2 smarttab
#include "test/librbd/test_fixture.h"
#include "test/librbd/test_support.h"
+#include "librbd/ExclusiveLock.h"
#include "librbd/ImageCtx.h"
#include "librbd/ImageWatcher.h"
#include "librbd/internal.h"
class TestObjectMap : public TestFixture {
public:
+
+ int when_open_object_map(librbd::ImageCtx *ictx) {
+ C_SaferCond ctx;
+ librbd::ObjectMap object_map(*ictx, ictx->snap_id);
+ object_map.open(&ctx);
+ return ctx.wait();
+ }
};
TEST_F(TestObjectMap, RefreshInvalidatesWhenCorrupt) {
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_FALSE(ictx->test_flags(RBD_FLAG_OBJECT_MAP_INVALID));
+ C_SaferCond lock_ctx;
{
RWLock::WLocker owner_locker(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
+ ictx->exclusive_lock->try_lock(&lock_ctx);
}
+ ASSERT_EQ(0, lock_ctx.wait());
std::string oid = librbd::ObjectMap::object_map_name(ictx->id, CEPH_NOSNAP);
bufferlist bl;
bl.append("corrupt");
ASSERT_EQ(0, ictx->data_ctx.write_full(oid, bl));
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- RWLock::WLocker snap_locker(ictx->snap_lock);
- ictx->object_map.refresh(CEPH_NOSNAP);
- }
+ ASSERT_EQ(0, when_open_object_map(ictx));
ASSERT_TRUE(ictx->test_flags(RBD_FLAG_OBJECT_MAP_INVALID));
}
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_FALSE(ictx->test_flags(RBD_FLAG_OBJECT_MAP_INVALID));
+ C_SaferCond lock_ctx;
{
RWLock::WLocker owner_locker(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
+ ictx->exclusive_lock->try_lock(&lock_ctx);
}
+ ASSERT_EQ(0, lock_ctx.wait());
librados::ObjectWriteOperation op;
librbd::cls_client::object_map_resize(&op, 0, OBJECT_NONEXISTENT);
std::string oid = librbd::ObjectMap::object_map_name(ictx->id, CEPH_NOSNAP);
ASSERT_EQ(0, ictx->data_ctx.operate(oid, &op));
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- RWLock::WLocker snap_locker(ictx->snap_lock);
- ictx->object_map.refresh(CEPH_NOSNAP);
- }
+ ASSERT_EQ(0, when_open_object_map(ictx));
ASSERT_TRUE(ictx->test_flags(RBD_FLAG_OBJECT_MAP_INVALID));
}
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_FALSE(ictx->test_flags(RBD_FLAG_OBJECT_MAP_INVALID));
+ C_SaferCond lock_ctx;
{
RWLock::WLocker owner_locker(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
+ ictx->exclusive_lock->try_lock(&lock_ctx);
}
+ ASSERT_EQ(0, lock_ctx.wait());
std::string oid = librbd::ObjectMap::object_map_name(ictx->id, CEPH_NOSNAP);
bufferlist bl;
bl.append("corrupt");
ASSERT_EQ(0, ictx->data_ctx.write_full(oid, bl));
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- RWLock::WLocker snap_locker(ictx->snap_lock);
- ictx->object_map.refresh(CEPH_NOSNAP);
- }
+ ASSERT_EQ(0, when_open_object_map(ictx));
ASSERT_TRUE(ictx->test_flags(RBD_FLAG_OBJECT_MAP_INVALID));
ASSERT_EQ(0, open_image(m_image_name, &ictx));
corrupt_bl.append("corrupt");
ASSERT_EQ(0, ictx->data_ctx.write_full(oid, corrupt_bl));
- {
- RWLock::RLocker owner_locker(ictx->owner_lock);
- RWLock::WLocker snap_locker(ictx->snap_lock);
- ictx->object_map.refresh(CEPH_NOSNAP);
- }
+ ASSERT_EQ(0, when_open_object_map(ictx));
ASSERT_TRUE(ictx->test_flags(RBD_FLAG_OBJECT_MAP_INVALID));
ASSERT_EQ(0, ictx->data_ctx.write_full(oid, valid_bl));
#include "test/librbd/test_support.h"
#include "include/stringify.h"
#include "librbd/AioImageRequestWQ.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/ImageState.h"
#include "librbd/ImageWatcher.h"
#include "cls/lock/cls_lock_client.h"
#include "cls/lock/cls_lock_types.h"
unlock_image();
for (std::set<librbd::ImageCtx *>::iterator iter = m_ictxs.begin();
iter != m_ictxs.end(); ++iter) {
- librbd::close_image(*iter);
+ (*iter)->state->close();
}
m_ioctx.close();
librbd::ImageCtx **ictx) {
*ictx = new librbd::ImageCtx(image_name.c_str(), "", NULL, m_ioctx, false);
m_ictxs.insert(*ictx);
- return librbd::open_image(*ictx);
+
+ return (*ictx)->state->open();
}
void TestFixture::close_image(librbd::ImageCtx *ictx) {
m_ictxs.erase(ictx);
- librbd::close_image(ictx);
+
+ ictx->state->close();
}
int TestFixture::lock_image(librbd::ImageCtx &ictx, ClsLockType lock_type,
}
RWLock::RLocker owner_locker(ictx.owner_lock);
- return ictx.image_watcher->is_lock_owner() ? 0 : -EINVAL;
+ assert(ictx.exclusive_lock != nullptr);
+ return ictx.exclusive_lock->is_lock_owner() ? 0 : -EINVAL;
}
#include "librbd/AioCompletion.h"
#include "librbd/AioImageRequest.h"
#include "librbd/AioImageRequestWQ.h"
+#include "librbd/ExclusiveLock.h"
#include "librbd/ImageWatcher.h"
#include "librbd/internal.h"
#include "librbd/ObjectMap.h"
ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
ASSERT_FALSE(is_owner);
+ C_SaferCond ctx;
{
RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
+ ictx->exclusive_lock->try_lock(&ctx);
}
-
+ ASSERT_EQ(0, ctx.wait());
ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
ASSERT_TRUE(is_owner);
}
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ C_SaferCond ctx;
{
RWLock::WLocker l(ictx->owner_lock);
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
+ ictx->exclusive_lock->try_lock(&ctx);
+ }
+
+ ASSERT_EQ(0, ctx.wait());
+ {
+ RWLock::RLocker owner_locker(ictx->owner_lock);
+ ASSERT_TRUE(ictx->exclusive_lock->is_lock_owner());
}
uint64_t size;
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
- {
- RWLock::WLocker l(ictx->owner_lock);
- if (ictx->image_watcher->is_lock_supported()) {
- ASSERT_EQ(0, ictx->image_watcher->try_lock());
- ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
+ if (ictx->exclusive_lock != nullptr) {
+ C_SaferCond ctx;
+ {
+ RWLock::WLocker l(ictx->owner_lock);
+ ictx->exclusive_lock->try_lock(&ctx);
}
+
+ RWLock::RLocker owner_locker(ictx->owner_lock);
+ ASSERT_EQ(0, ctx.wait());
+ ASSERT_TRUE(ictx->exclusive_lock->is_lock_owner());
}
uint64_t size;
bufferlist read_bl;
read_bl.push_back(read_ptr);
- std::list<std::string> snaps = boost::assign::list_of(
- "snap1")("snap2")("");
+ std::list<std::string> snaps = {"snap1", "snap2", ""};
for (std::list<std::string>::iterator it = snaps.begin();
it != snaps.end(); ++it) {
const char *snap_name = it->empty() ? NULL : it->c_str();
it != snaps.begin() && snap_name != NULL) {
state = OBJECT_EXISTS_CLEAN;
}
+
+ librbd::ObjectMap object_map(*ictx2, ictx2->snap_id);
+ C_SaferCond ctx;
+ object_map.open(&ctx);
+ ASSERT_EQ(0, ctx.wait());
+
RWLock::WLocker object_map_locker(ictx2->object_map_lock);
- ASSERT_EQ(state, ictx2->object_map[0]);
+ ASSERT_EQ(state, object_map[0]);
}
}
}
librbd::Image image1;
ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
- uint64_t flags;
- ASSERT_EQ(0, image1.get_flags(&flags));
- ASSERT_TRUE((flags & RBD_FLAG_OBJECT_MAP_INVALID) != 0);
-
bool lock_owner;
bl.clear();
ASSERT_EQ(0, image1.write(0, 0, bl));
ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
ASSERT_TRUE(lock_owner);
+ uint64_t flags;
+ ASSERT_EQ(0, image1.get_flags(&flags));
+ ASSERT_TRUE((flags & RBD_FLAG_OBJECT_MAP_INVALID) != 0);
+
librbd::Image image2;
ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
librbd::Image image1;
ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+ bool lock_owner;
+ bl.clear();
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
uint64_t flags;
ASSERT_EQ(0, image1.get_flags(&flags));
ASSERT_TRUE((flags & RBD_FLAG_OBJECT_MAP_INVALID) != 0);
using ::testing::_;
using ::testing::Invoke;
using ::testing::InSequence;
+using ::testing::Return;
class TestMockExclusiveLock : public TestMockFixture {
public:
typedef exclusive_lock::AcquireRequest<MockImageCtx> MockAcquireRequest;
typedef exclusive_lock::ReleaseRequest<MockImageCtx> MockReleaseRequest;
+ void expect_get_watch_handle(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, get_watch_handle())
+ .WillRepeatedly(Return(1234567890));
+ }
+
void expect_block_writes(MockImageCtx &mock_image_ctx) {
EXPECT_CALL(*mock_image_ctx.aio_work_queue, block_writes(_))
.WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
void expect_acquire_lock(MockImageCtx &mock_image_ctx,
MockAcquireRequest &acquire_request, int r) {
+ expect_get_watch_handle(mock_image_ctx);
EXPECT_CALL(acquire_request, send())
.WillOnce(FinishRequest(&acquire_request, r, &mock_image_ctx));
if (r == 0) {
+ expect_notify_acquired_lock(mock_image_ctx);
expect_unblock_writes(mock_image_ctx);
}
}
if (!shutting_down && r < 0) {
expect_unblock_writes(mock_image_ctx);
}
+ if (r == 0) {
+ expect_notify_released_lock(mock_image_ctx);
+ expect_writes_empty(mock_image_ctx);
+ }
}
void expect_notify_request_lock(MockImageCtx &mock_image_ctx,
&MockExclusiveLock::handle_lock_released));
}
+ void expect_notify_acquired_lock(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, notify_acquired_lock())
+ .Times(1);
+ }
+
+ void expect_notify_released_lock(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.image_watcher, notify_released_lock())
+ .Times(1);
+ }
+
+ void expect_writes_empty(MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.aio_work_queue, writes_empty())
+ .WillRepeatedly(Return(true));
+ }
+
int when_init(MockImageCtx &mock_image_ctx,
MockExclusiveLock &exclusive_lock) {
C_SaferCond ctx;
RWLock::WLocker owner_locker(mock_image_ctx.owner_lock);
exclusive_lock.init(&ctx);
}
- exclusive_lock.set_watch_handle(123);
return ctx.wait();
}
MockExclusiveLock &exclusive_lock) {
C_SaferCond ctx;
{
- RWLock::WLocker owner_locker(mock_image_ctx.owner_lock);
+ RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
exclusive_lock.request_lock(&ctx);
}
return ctx.wait();
ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
MockReleaseRequest shutdown_release;
+ expect_op_work_queue(mock_image_ctx);
expect_release_lock(mock_image_ctx, shutdown_release, 0, true);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock));
MockReleaseRequest shutdown_release;
+ expect_op_work_queue(mock_image_ctx);
expect_release_lock(mock_image_ctx, shutdown_release, 0, true);
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_op_work_queue(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_op_work_queue(mock_image_ctx);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}
ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock));
MockReleaseRequest shutdown_release;
+ expect_op_work_queue(mock_image_ctx);
expect_release_lock(mock_image_ctx, shutdown_release, 0, true);
ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock));
ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
expect_unblock_writes(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}
ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
MockReleaseRequest shutdown_release;
+ expect_op_work_queue(mock_image_ctx);
expect_release_lock(mock_image_ctx, shutdown_release, 0, true);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}
ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
MockReleaseRequest shutdown_release;
+ expect_op_work_queue(mock_image_ctx);
expect_release_lock(mock_image_ctx, shutdown_release, 0, true);
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_op_work_queue(mock_image_ctx);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}
ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
MockReleaseRequest shutdown_release;
+ expect_op_work_queue(mock_image_ctx);
expect_release_lock(mock_image_ctx, shutdown_release, 0, true);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
MockAcquireRequest try_lock_acquire;
C_SaferCond wait_for_send_ctx1;
+ expect_get_watch_handle(mock_image_ctx);
EXPECT_CALL(try_lock_acquire, send())
.WillOnce(Notify(&wait_for_send_ctx1));
expect_block_writes(mock_image_ctx);
EXPECT_CALL(release, send())
.WillOnce(Notify(&wait_for_send_ctx2));
+ expect_notify_released_lock(mock_image_ctx);
+ expect_writes_empty(mock_image_ctx);
C_SaferCond try_request_ctx1;
{
C_SaferCond request_lock_ctx1;
C_SaferCond request_lock_ctx2;
{
- RWLock::WLocker owner_locker(mock_image_ctx.owner_lock);
+ RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
exclusive_lock.request_lock(&request_lock_ctx1);
exclusive_lock.request_lock(&request_lock_ctx2);
}
C_SaferCond request_lock_ctx3;
{
- RWLock::WLocker owner_locker(mock_image_ctx.owner_lock);
+ RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
exclusive_lock.request_lock(&request_lock_ctx3);
}
ASSERT_EQ(0, release_lock_ctx1.wait());
expect_unblock_writes(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
}