cache/ObjectCacherObjectDispatch.cc
cache/ObjectCacherWriteback.cc
cache/PassthroughImageCache.cc
+ cache/rwl/InitRequest.cc
+ cache/rwl/ShutdownRequest.cc
cache/WriteAroundObjectDispatch.cc
deep_copy/ImageCopyRequest.cc
deep_copy/MetadataCopyRequest.cc
set(librbd_internal_srcs
${librbd_internal_srcs}
cache/rwl/ImageCacheState.cc
- cache/rwl/InitRequest.cc
cache/rwl/LogEntry.cc
cache/rwl/LogMap.cc
cache/rwl/LogOperation.cc
cache/rwl/ReadRequest.cc
cache/rwl/Request.cc
- cache/rwl/ShutdownRequest.cc
cache/rwl/SyncPoint.cc
cache/rwl/Types.cc
cache/ReplicatedWriteLog.cc)
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
+#include "librbd/cache/Utils.h"
#include "librbd/ExclusiveLock.h"
#include "librbd/ImageCtx.h"
#include "librbd/ImageWatcher.h"
on_finish->complete(r);
});
+ bool rwl_enabled = cache::util::is_rwl_enabled(m_image_ctx);
if (m_image_ctx.clone_copy_on_read ||
- (features & RBD_FEATURE_JOURNALING) != 0) {
+ (features & RBD_FEATURE_JOURNALING) != 0 ||
+ rwl_enabled) {
m_image_dispatch->set_require_lock(io::DIRECTION_BOTH, on_finish);
} else {
m_image_dispatch->set_require_lock(io::DIRECTION_WRITE, on_finish);
template <typename> class PluginRegistry;
namespace asio { struct ContextWQ; }
- namespace cache {
- template <typename> class ImageCache;
- namespace rwl {
- template <typename> class ImageCacheState;
- } // namespace rwl
- } // namespace cache
+ namespace cache { template <typename> class ImageCache; }
namespace exclusive_lock { struct Policy; }
namespace io {
class AioCompletion;
file_layout_t layout;
cache::ImageCache<ImageCtx> *image_cache = nullptr;
- cache::rwl::ImageCacheState<ImageCtx> *cache_state = nullptr;
Readahead readahead;
std::atomic<uint64_t> total_bytes_read = {0};
return -ENOSYS;
}
features &= ~RBD_FEATURES_INTERNAL;
+ features &= ~RBD_FEATURE_DIRTY_CACHE;
features |= RBD_FEATURE_MIGRATING;
opts.set(RBD_IMAGE_OPTION_FEATURES, features);
template <typename T>
bool is_rwl_enabled(T& image_ctx) {
+#if defined(WITH_RBD_RWL)
return image_ctx.config.template get_val<bool>("rbd_rwl_enabled");
+#else
+ return false;
+#endif
}
} // namespace util
// vim: ts=8 sw=2 smarttab
#include "librbd/cache/Types.h"
+#include "librbd/cache/Utils.h"
#include "librbd/cache/rwl/ImageCacheState.h"
#include "librbd/ImageCtx.h"
#include "librbd/Operations.h"
IMAGE_CACHE_STATE, &cache_state_str);
}
- bool rwl_enabled = image_ctx->config.template get_val<bool>("rbd_rwl_enabled");
+ bool rwl_enabled = cache::util::is_rwl_enabled(*image_ctx);
bool cache_desired = rwl_enabled;
cache_desired &= !image_ctx->read_only;
cache_desired &= !image_ctx->test_features(RBD_FEATURE_MIGRATING);
void dump(ceph::Formatter *f) const;
- static void get_image_cache_state(ImageCtxT* image_ctx, Context *on_finish);
+ static ImageCacheState<ImageCtxT>* get_image_cache_state(
+ ImageCtxT* image_ctx, int &r);
bool is_valid();
};
#include "common/dout.h"
#include "common/errno.h"
#include "include/stringify.h"
+#include "librbd/cache/rwl/InitRequest.h"
+#include "librbd/cache/rwl/ShutdownRequest.h"
#include "librbd/ExclusiveLock.h"
#include "librbd/ImageCtx.h"
#include "librbd/ImageState.h"
}
if (!journal_enabled) {
apply();
- finish();
+ send_open_image_cache();
return;
}
return;
}
+ send_open_image_cache();
+}
+
+template <typename I>
+void PostAcquireRequest<I>::send_open_image_cache() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << dendl;
+
+ using klass = PostAcquireRequest<I>;
+ Context *ctx = create_async_context_callback(
+ m_image_ctx, create_context_callback<
+ klass, &klass::handle_open_image_cache>(this));
+ cache::rwl::InitRequest<I> *req = cache::rwl::InitRequest<I>::create(
+ m_image_ctx, ctx);
+ req->send();
+}
+
+template <typename I>
+void PostAcquireRequest<I>::handle_open_image_cache(int r) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << "r=" << r << dendl;
+
+ save_result(r);
+ if (r < 0) {
+ lderr(cct) << "failed to open image cache: " << cpp_strerror(r)
+ << dendl;
+ send_close_image_cache();
+ return;
+ }
+
finish();
}
+template <typename I>
+void PostAcquireRequest<I>::send_close_image_cache() {
+ if (m_image_ctx.image_cache == nullptr) {
+ send_close_journal();
+ }
+
+ using klass = PostAcquireRequest<I>;
+ Context *ctx = create_context_callback<klass, &klass::handle_close_image_cache>(
+ this);
+ cache::rwl::ShutdownRequest<I> *req = cache::rwl::ShutdownRequest<I>::create(
+ m_image_ctx, ctx);
+ req->send();
+}
+
+template <typename I>
+void PostAcquireRequest<I>::handle_close_image_cache(int r) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << "r=" << r << dendl;
+
+ save_result(r);
+ if (r < 0) {
+ lderr(cct) << "failed to close image_cache: " << cpp_strerror(r) << dendl;
+ }
+
+ send_close_journal();
+}
+
template <typename I>
void PostAcquireRequest<I>::send_close_journal() {
+ if (m_journal == nullptr) {
+ send_close_object_map();
+ return;
+ }
+
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << dendl;
* ALLOCATE_JOURNAL_TAG *
* | * *
* | * *
- * | v v
+ * v * *
+ * OPEN_IMAGE_CACHE * *
+ * | * * *
+ * | * * *
+ * | v v v
* | CLOSE_JOURNAL
* | |
* | v
void send_close_object_map();
void handle_close_object_map(int r);
+ void send_open_image_cache();
+ void handle_open_image_cache(int r);
+
+ void send_close_image_cache();
+ void handle_close_image_cache(int r);
+
void apply();
void revert();
#include "common/AsyncOpTracker.h"
#include "common/dout.h"
#include "common/errno.h"
+#include "librbd/cache/rwl/ShutdownRequest.h"
#include "librbd/ExclusiveLock.h"
#include "librbd/ImageState.h"
#include "librbd/ImageWatcher.h"
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << dendl;
+ send_shut_down_image_cache();
+}
+
+template <typename I>
+void PreReleaseRequest<I>::send_shut_down_image_cache() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << dendl;
+
+ /* Shut down existing image cache whether the feature bit is on or not */
+ if (!m_image_ctx.image_cache) {
+ send_invalidate_cache();
+ return;
+ }
+ std::shared_lock owner_lock{m_image_ctx.owner_lock};
+ Context *ctx = create_async_context_callback(m_image_ctx, create_context_callback<
+ PreReleaseRequest<I>,
+ &PreReleaseRequest<I>::handle_shut_down_image_cache>(this));
+ cache::rwl::ShutdownRequest<I> *req = cache::rwl::ShutdownRequest<I>::create(
+ m_image_ctx, ctx);
+ req->send();
+}
+
+template <typename I>
+void PreReleaseRequest<I>::handle_shut_down_image_cache(int r) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to shut down image cache: " << cpp_strerror(r)
+ << dendl;
+ m_image_dispatch->unset_require_lock(io::DIRECTION_BOTH);
+ save_result(r);
+ finish();
+ return;
+ }
+
send_invalidate_cache();
}
* WAIT_FOR_OPS
* |
* v
+ * SHUT_DOWN_IMAGE_CACHE
+ * |
+ * v
* INVALIDATE_CACHE
* |
* v
void send_wait_for_ops();
void handle_wait_for_ops(int r);
+ void send_shut_down_image_cache();
+ void handle_shut_down_image_cache(int r);
+
void send_invalidate_cache();
void handle_invalidate_cache(int r);
AioCompletion *aio_comp = this->m_aio_comp;
aio_comp->set_request_count(1);
C_AioRequest *req_comp = new C_AioRequest(aio_comp);
- image_ctx.image_cache->aio_flush(librbd::io::FLUSH_SOURCE_USER, req_comp);
+ image_ctx.image_cache->aio_flush(m_flush_source, req_comp);
}
template <typename I>
#include "test/librbd/test_mock_fixture.h"
#include "test/librbd/test_support.h"
+#include "test/librbd/mock/cache/MockImageCache.h"
#include "test/librbd/mock/MockImageCtx.h"
#include "test/librbd/mock/MockImageState.h"
#include "test/librbd/mock/MockJournal.h"
#include "test/librbd/mock/MockObjectMap.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/cache/rwl/InitRequest.h"
+#include "librbd/cache/rwl/ShutdownRequest.h"
#include "librbd/exclusive_lock/PostAcquireRequest.h"
#include "librbd/image/RefreshRequest.h"
+
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <arpa/inet.h>
RefreshRequest<librbd::MockTestImageCtx> *RefreshRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
} // namespace image
+
+namespace cache {
+namespace rwl {
+
+template<>
+struct InitRequest<librbd::MockTestImageCtx> {
+ static InitRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static InitRequest *create(librbd::MockTestImageCtx &image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ InitRequest() {
+ s_instance = this;
+ }
+ MOCK_METHOD0(send, void());
+};
+
+InitRequest<librbd::MockTestImageCtx> *InitRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+template<>
+struct ShutdownRequest<librbd::MockTestImageCtx> {
+ static ShutdownRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static ShutdownRequest *create(librbd::MockTestImageCtx &image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ ShutdownRequest() {
+ s_instance = this;
+ }
+ MOCK_METHOD0(send, void());
+};
+
+ShutdownRequest<librbd::MockTestImageCtx> *ShutdownRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace rwl
+} // namespace cache
} // namespace librbd
// template definitions
public:
typedef PostAcquireRequest<MockTestImageCtx> MockPostAcquireRequest;
typedef librbd::image::RefreshRequest<MockTestImageCtx> MockRefreshRequest;
+ typedef librbd::cache::rwl::InitRequest<MockTestImageCtx> MockInitRequest;
+ typedef librbd::cache::rwl::ShutdownRequest<MockTestImageCtx> MockShutdownRequest;
void expect_test_features(MockTestImageCtx &mock_image_ctx, uint64_t features,
bool enabled) {
EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
}
+ void expect_init_image_cache(MockTestImageCtx &mock_image_ctx,
+ MockInitRequest &mock_init_request, int r) {
+ EXPECT_CALL(mock_init_request, send())
+ .WillOnce(FinishRequest(&mock_init_request, r, &mock_image_ctx));
+ }
+
+ void expect_close_image_cache(MockTestImageCtx &mock_image_ctx,
+ MockShutdownRequest &mock_shutdown_request, int r) {
+ EXPECT_CALL(mock_shutdown_request, send())
+ .WillOnce(FinishRequest(&mock_shutdown_request, r, &mock_image_ctx));
+ }
+
};
TEST_F(TestMockExclusiveLockPostAcquireRequest, Success) {
expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0);
+ MockInitRequest mock_init_request;
+ expect_init_image_cache(mock_image_ctx, mock_init_request, 0);
C_SaferCond acquire_ctx;
C_SaferCond ctx;
MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
mock_image_ctx.image_lock, false);
expect_handle_prepare_lock_complete(mock_image_ctx);
+ MockInitRequest mock_init_request;
+ expect_init_image_cache(mock_image_ctx, mock_init_request, 0);
+
C_SaferCond acquire_ctx;
C_SaferCond ctx;
MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
mock_image_ctx.image_lock, false);
expect_handle_prepare_lock_complete(mock_image_ctx);
+ MockInitRequest mock_init_request;
+ expect_init_image_cache(mock_image_ctx, mock_init_request, 0);
+
C_SaferCond acquire_ctx;
C_SaferCond ctx;
MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0);
+ MockInitRequest mock_init_request;
+ expect_init_image_cache(mock_image_ctx, mock_init_request, 0);
+
C_SaferCond acquire_ctx;
C_SaferCond ctx;
MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
mock_image_ctx.image_lock, false);
expect_handle_prepare_lock_complete(mock_image_ctx);
+ MockInitRequest mock_init_request;
+ expect_init_image_cache(mock_image_ctx, mock_init_request, 0);
+
C_SaferCond acquire_ctx;
C_SaferCond ctx;
MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
ASSERT_EQ(-EPERM, ctx.wait());
}
+TEST_F(TestMockExclusiveLockPostAcquireRequest, InitImageCacheError) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ expect_is_refresh_required(mock_image_ctx, false);
+
+ MockObjectMap mock_object_map;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_OBJECT_MAP, true);
+ expect_create_object_map(mock_image_ctx, &mock_object_map);
+ expect_open_object_map(mock_image_ctx, mock_object_map, 0);
+
+ MockJournal mock_journal;
+ MockJournalPolicy mock_journal_policy;
+ expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING,
+ mock_image_ctx.image_lock, true);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_journal_disabled(mock_journal_policy, false);
+ expect_create_journal(mock_image_ctx, &mock_journal);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+ expect_open_journal(mock_image_ctx, mock_journal, 0);
+ expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
+ expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0);
+
+ MockInitRequest mock_init_request;
+ expect_init_image_cache(mock_image_ctx, mock_init_request, -ENOENT);
+
+ cache::MockImageCache mock_image_cache;
+ mock_image_ctx.image_cache = &mock_image_cache;
+ MockShutdownRequest mock_shutdown_request;
+ expect_close_image_cache(mock_image_ctx, mock_shutdown_request, 0);
+
+ expect_close_journal(mock_image_ctx, mock_journal);
+ expect_close_object_map(mock_image_ctx, mock_object_map);
+
+ C_SaferCond acquire_ctx;
+ C_SaferCond ctx;
+ MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
+ &acquire_ctx,
+ &ctx);
+ req->send();
+ ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
TEST_F(TestMockExclusiveLockPostAcquireRequest, OpenObjectMapError) {
REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
expect_get_journal_policy(mock_image_ctx, mock_journal_policy);
expect_allocate_journal_tag(mock_image_ctx, mock_journal_policy, 0);
+ MockInitRequest mock_init_request;
+ expect_init_image_cache(mock_image_ctx, mock_init_request, 0);
+
C_SaferCond acquire_ctx;
C_SaferCond ctx;
MockPostAcquireRequest *req = MockPostAcquireRequest::create(mock_image_ctx,
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
+#include "librbd/cache/rwl/ShutdownRequest.h"
#include "test/librbd/test_mock_fixture.h"
#include "test/librbd/test_support.h"
+#include "test/librbd/mock/cache/MockImageCache.h"
#include "test/librbd/mock/MockImageCtx.h"
#include "test/librbd/mock/MockJournal.h"
#include "test/librbd/mock/MockObjectMap.h"
#include "common/AsyncOpTracker.h"
#include "librbd/exclusive_lock/ImageDispatch.h"
#include "librbd/exclusive_lock/PreReleaseRequest.h"
+
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <list>
};
} // namespace exclusive_lock
+
+namespace cache {
+namespace rwl {
+template<>
+struct ShutdownRequest<librbd::MockTestImageCtx> {
+ static ShutdownRequest *s_instance;
+ Context *on_finish = nullptr;
+
+ static ShutdownRequest *create(librbd::MockTestImageCtx &image_ctx,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ ShutdownRequest() {
+ s_instance = this;
+ }
+ MOCK_METHOD0(send, void());
+};
+
+ShutdownRequest<librbd::MockTestImageCtx> *ShutdownRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace rwl
+} // namespace cache
} // namespace librbd
// template definitions
public:
typedef ImageDispatch<MockTestImageCtx> MockImageDispatch;
typedef PreReleaseRequest<MockTestImageCtx> MockPreReleaseRequest;
+ typedef librbd::cache::rwl::ShutdownRequest<MockTestImageCtx> MockShutdownRequest;
void expect_complete_context(MockContext &mock_context, int r) {
EXPECT_CALL(mock_context, complete(r));
.WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
}
+ void expect_close_image_cache(MockTestImageCtx &mock_image_ctx,
+ MockShutdownRequest &mock_shutdown_req, int r) {
+ EXPECT_CALL(mock_shutdown_req, send())
+ .WillOnce(FinishRequest(&mock_shutdown_req, r, &mock_image_ctx));
+ }
+
void expect_invalidate_cache(MockTestImageCtx &mock_image_ctx,
int r) {
EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, invalidate_cache(_))
expect_cancel_op_requests(mock_image_ctx, 0);
MockImageDispatch mock_image_dispatch;
expect_set_require_lock(mock_image_ctx, mock_image_dispatch, 0);
+
+ cache::MockImageCache mock_image_cache;
+ mock_image_ctx.image_cache = &mock_image_cache;
+ MockShutdownRequest mock_shutdown_request;
+ expect_close_image_cache(mock_image_ctx, mock_shutdown_request, 0);
+
expect_invalidate_cache(mock_image_ctx, 0);
expect_flush_notifies(mock_image_ctx);
InSequence seq;
expect_prepare_lock(mock_image_ctx);
expect_cancel_op_requests(mock_image_ctx, 0);
+
+ cache::MockImageCache mock_image_cache;
+ mock_image_ctx.image_cache = &mock_image_cache;
+ MockShutdownRequest mock_shutdown_request;
+ expect_close_image_cache(mock_image_ctx, mock_shutdown_request, 0);
+
expect_invalidate_cache(mock_image_ctx, 0);
expect_flush_notifies(mock_image_ctx);
InSequence seq;
expect_cancel_op_requests(mock_image_ctx, 0);
+
+ cache::MockImageCache mock_image_cache;
+ mock_image_ctx.image_cache = &mock_image_cache;
+ MockShutdownRequest mock_shutdown_request;
+ expect_close_image_cache(mock_image_ctx, mock_shutdown_request, 0);
+
expect_invalidate_cache(mock_image_ctx, 0);
expect_flush_notifies(mock_image_ctx);
expect_cancel_op_requests(mock_image_ctx, 0);
MockImageDispatch mock_image_dispatch;
expect_set_require_lock(mock_image_ctx, mock_image_dispatch, -EBLACKLISTED);
+
+ cache::MockImageCache mock_image_cache;
+ mock_image_ctx.image_cache = &mock_image_cache;
+ MockShutdownRequest mock_shutdown_request;
+ expect_close_image_cache(mock_image_ctx, mock_shutdown_request, 0);
+
expect_invalidate_cache(mock_image_ctx, -EBLACKLISTED);
expect_flush_notifies(mock_image_ctx);
no_progress));
ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+ expect_unlock_exclusive_lock(*ictx);
InSequence seq;
expect_update(ictx, CEPH_NOSNAP, 0, 262144, OBJECT_NONEXISTENT, OBJECT_EXISTS,
0);
OBJECT_EXISTS, 0);
expect_update(ictx, CEPH_NOSNAP, 524288, 712312, OBJECT_NONEXISTENT,
OBJECT_EXISTS, 0);
- expect_unlock_exclusive_lock(*ictx);
ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock");
ceph::BitVector<2> object_map;
}
void flush() {
- ASSERT_EQ(0, api::Io<>::flush(*m_ref_ictx));
- ASSERT_EQ(0, api::Io<>::flush(*m_ictx));
+ ASSERT_EQ(0, TestFixture::flush_writeback_cache(m_ref_ictx));
+ ASSERT_EQ(0, TestFixture::flush_writeback_cache(m_ictx));
}
void snap_create(const std::string &snap_name) {
TEST_F(TestMigration, Empty)
{
uint64_t features = m_ictx->features ^ RBD_FEATURE_LAYERING;
+ features &= ~RBD_FEATURE_DIRTY_CACHE;
ASSERT_EQ(0, m_opts.set(RBD_IMAGE_OPTION_FEATURES, features));
migrate(m_ioctx, m_image_name);
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
+#include "common/Cond.h"
#include "test/librbd/test_fixture.h"
#include "test/librbd/test_support.h"
#include "include/stringify.h"
#include "librbd/ExclusiveLock.h"
#include "librbd/ImageState.h"
#include "librbd/ImageWatcher.h"
+#include "librbd/io/ImageDispatchSpec.h"
#include "librbd/Operations.h"
#include "librbd/api/Io.h"
#include "cls/lock/cls_lock_client.h"
ceph_assert(ictx.exclusive_lock != nullptr);
return ictx.exclusive_lock->is_lock_owner() ? 0 : -EINVAL;
}
+
+int TestFixture::flush_writeback_cache(librbd::ImageCtx *image_ctx) {
+ if (image_ctx->test_features(RBD_FEATURE_DIRTY_CACHE)) {
+ // cache exists. Close to flush data
+ C_SaferCond ctx;
+ auto aio_comp = librbd::io::AioCompletion::create_and_start(
+ &ctx, image_ctx, librbd::io::AIO_TYPE_FLUSH);
+ auto req = librbd::io::ImageDispatchSpec<>::create_flush(
+ *image_ctx, librbd::io::IMAGE_DISPATCH_LAYER_INTERNAL_START, aio_comp,
+ librbd::io::FLUSH_SOURCE_INTERNAL, {});
+ req->send();
+ return ctx.wait();
+ } else {
+ return librbd::api::Io<>::flush(*image_ctx);
+ }
+}
const std::string &cookie);
int unlock_image();
+ int flush_writeback_cache(librbd::ImageCtx *image_ctx);
+
int acquire_exclusive_lock(librbd::ImageCtx &ictx);
static std::string _pool_name;
map<string, bufferlist> pairs;
r = librbd::metadata_list(ictx, "", 0, &pairs);
ASSERT_EQ(0, r);
- ASSERT_EQ(5u, pairs.size());
+
+ uint8_t original_pairs_num = 0;
+ if (ictx->test_features(RBD_FEATURE_DIRTY_CACHE)) {
+ original_pairs_num = 1;
+ }
+ ASSERT_EQ(original_pairs_num + 5, pairs.size());
r = ictx->operations->metadata_remove("abcd");
ASSERT_EQ(0, r);
r = ictx->operations->metadata_remove("xyz");
pairs.clear();
r = librbd::metadata_list(ictx, "", 0, &pairs);
ASSERT_EQ(0, r);
- ASSERT_EQ(3u, pairs.size());
+ ASSERT_EQ(original_pairs_num + 3, pairs.size());
string val;
r = librbd::metadata_get(ictx, it->first, &val);
ASSERT_EQ(0, r);
ASSERT_EQ(256, api::Io<>::write(*ictx2, 256, bl.length(), bufferlist{bl},
0));
+ ASSERT_EQ(0, flush_writeback_cache(ictx2));
librados::IoCtx snap_ctx;
snap_ctx.dup(ictx2->data_ctx);
snap_ctx.snap_set_read(CEPH_SNAPDIR);
bufferlist bl;
bl.append(std::string(256, '1'));
- ASSERT_EQ(256, api::Io<>::write(*ictx2, 256, bl.length(), bufferlist{bl},
- 0));
+ ASSERT_EQ(256, api::Io<>::write(*ictx2, 256, bl.length(), bufferlist{bl}, 0));
+
+ ASSERT_EQ(0, flush_writeback_cache(ictx2));
librados::IoCtx snap_ctx;
snap_ctx.dup(ictx2->data_ctx);
bl.append(std::string(1 << ictx->order, '1'));
ASSERT_EQ((ssize_t)bl.length(),
api::Io<>::write(*ictx, 0, bl.length(), bufferlist{bl}, 0));
- ASSERT_EQ(0, api::Io<>::flush(*ictx));
+ ASSERT_EQ(0, flush_writeback_cache(ictx));
ASSERT_EQ(0, create_snapshot("snap1", true));
ASSERT_EQ(TEST_IO_SIZE, image.write(itr->second, TEST_IO_SIZE, bl));
}
- ASSERT_EQ(0, image.flush());
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
bufferlist readbl;
printf("verify written data by reading\n");
api::Io<>::write(*ictx, (1 << ictx->order) * 10 + 4096 * 10,
bl2.length(), bufferlist{bl2}, 0));
- ASSERT_EQ(0, api::Io<>::flush(*ictx));
+ ASSERT_EQ(0, flush_writeback_cache(ictx));
ASSERT_EQ(0, ictx->operations->sparsify(4096, no_op));
ASSERT_EQ((ssize_t)bl.length(),
api::Io<>::write(*ictx, (1 << ictx->order) * 10, bl.length(),
bufferlist{bl}, 0));
- ASSERT_EQ(0, api::Io<>::flush(*ictx));
+ ASSERT_EQ(0, flush_writeback_cache(ictx));
ASSERT_EQ(0, ictx->operations->sparsify(4096, no_op));
rados_shutdown(_cluster);
_rados.wait_for_latest_osdmap();
_pool_names.insert(_pool_names.end(), _unique_pool_names.begin(),
- _unique_pool_names.end());
+ _unique_pool_names.end());
for (size_t i = 1; i < _pool_names.size(); ++i) {
ASSERT_EQ(0, _rados.pool_delete(_pool_names[i].c_str()));
}
return value == "true";
}
+ bool is_skip_partial_discard_enabled(rbd_image_t image) {
+ if (is_skip_partial_discard_enabled()) {
+ rbd_flush(image);
+ uint64_t features;
+ EXPECT_EQ(0, rbd_get_features(image, &features));
+ return !(features & RBD_FEATURE_DIRTY_CACHE);
+ }
+ return false;
+ }
+
+ bool is_skip_partial_discard_enabled(librbd::Image& image) {
+ if (is_skip_partial_discard_enabled()) {
+ image.flush();
+ uint64_t features;
+ EXPECT_EQ(0, image.features(&features));
+ return !(features & RBD_FEATURE_DIRTY_CACHE);
+ }
+ return false;
+ }
+
void validate_object_map(rbd_image_t image, bool *passed) {
uint64_t flags;
ASSERT_EQ(0, rbd_get_flags(image, &flags));
ASSERT_EQ(0, rbd_copy(image, ioctx, name2.c_str()));
ASSERT_EQ(2, test_ls(ioctx, 2, name.c_str(), name2.c_str()));
ASSERT_EQ(0, rbd_open(ioctx, name2.c_str(), &image2, NULL));
- ASSERT_EQ(0, rbd_metadata_list(image2, "", 70, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image2, "key", 70, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, sum_key_len);
ASSERT_EQ(vals_len, sum_value_len);
keys_len = sizeof(keys);
vals_len = sizeof(vals);
ASSERT_EQ(0, rbd_open(ioctx, name3.c_str(), &image3, NULL));
- ASSERT_EQ(0, rbd_metadata_list(image3, "", 70, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image3, "key", 70, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, sum_key_len);
ASSERT_EQ(vals_len, sum_value_len);
BOOST_SCOPE_EXIT_ALL( (&image2) ) {
ASSERT_EQ(0, rbd_close(image2));
};
- ASSERT_EQ(0, rbd_metadata_list(image2, "", 70, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image2, "key", 70, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, sum_key_len);
ASSERT_EQ(vals_len, sum_value_len);
BOOST_SCOPE_EXIT_ALL( (&image3) ) {
ASSERT_EQ(0, rbd_close(image3));
};
- ASSERT_EQ(0, rbd_metadata_list(image3, "", 70, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image3, "key", 70, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, sum_key_len);
ASSERT_EQ(vals_len, sum_value_len);
BOOST_SCOPE_EXIT_ALL( (&image5) ) {
ASSERT_EQ(0, rbd_close(image5));
};
- ASSERT_EQ(0, rbd_metadata_list(image5, "", 70, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image5, "key", 70, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, sum_key_len);
ASSERT_EQ(vals_len, sum_value_len);
BOOST_SCOPE_EXIT_ALL( (&image6) ) {
ASSERT_EQ(0, rbd_close(image6));
};
- ASSERT_EQ(0, rbd_metadata_list(image6, "", 70, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image6, "key", 70, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, sum_key_len);
ASSERT_EQ(vals_len, sum_value_len);
rados_ioctx_t ioctx;
rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
- bool skip_discard = is_skip_partial_discard_enabled();
-
rbd_image_t image;
int order = 0;
std::string name = get_temp_image_name();
ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_read_from_replica_policy", "balance"));
ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
-
+
+ bool skip_discard = is_skip_partial_discard_enabled(image);
+
char test_data[TEST_IO_SIZE + 1];
char zero_data[TEST_IO_SIZE + 1];
char mismatch_data[TEST_IO_SIZE + 1];
rados_ioctx_t ioctx;
rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
- bool skip_discard = is_skip_partial_discard_enabled();
-
rbd_image_t image;
int order = 0;
std::string name = get_temp_image_name();
ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ bool skip_discard = is_skip_partial_discard_enabled(image);
+
char test_data[TEST_IO_SIZE + 1];
char zero_data[TEST_IO_SIZE + 1];
char mismatch_data[TEST_IO_SIZE + 1];
std::string data_pool_name = create_pool(true);
- bool skip_discard = is_skip_partial_discard_enabled();
-
rbd_image_t image;
std::string name = get_temp_image_name();
uint64_t size = 2 << 20;
ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
ASSERT_NE(-1, rbd_get_data_pool_id(image));
+ bool skip_discard = is_skip_partial_discard_enabled(image);
+
char test_data[TEST_IO_SIZE + 1];
char zero_data[TEST_IO_SIZE + 1];
int i;
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
- bool skip_discard = is_skip_partial_discard_enabled();
-
{
librbd::RBD rbd;
librbd::Image image;
ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
char test_data[TEST_IO_SIZE + 1];
char zero_data[TEST_IO_SIZE + 1];
int i;
printf("sizes and overlaps are good between parent and child\n");
// check key/value pairs in child image
- ASSERT_EQ(0, rbd_metadata_list(child, "", 70, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(child, "key", 70, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(sum_key_len, keys_len);
ASSERT_EQ(sum_value_len, vals_len);
printf("made and opened clone \"child\"\n");
// check key/value pairs in child image
- ASSERT_EQ(0, rbd_metadata_list(child, "", 70, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(child, "key", 70, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(sum_key_len, keys_len);
ASSERT_EQ(sum_value_len, vals_len);
librados::IoCtx ioctx;
ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
- bool skip_discard = this->is_skip_partial_discard_enabled();
-
{
librbd::RBD rbd;
librbd::Image image;
ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
uint64_t object_size = 0;
if (this->whole_object) {
object_size = 1 << order;
TYPED_TEST(DiffIterateTest, DiffIterateStress)
{
+ REQUIRE(!is_rbd_rwl_enabled((CephContext *)this->_rados.cct()));
librados::IoCtx ioctx;
ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
- bool skip_discard = this->is_skip_partial_discard_enabled();
-
librbd::RBD rbd;
librbd::Image image;
int order = 0;
ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
uint64_t object_size = 0;
if (this->whole_object) {
object_size = 1 << order;
librados::IoCtx ioctx;
ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
- bool skip_discard = this->is_skip_partial_discard_enabled();
-
librbd::RBD rbd;
librbd::Image image;
std::string name = this->get_temp_image_name();
ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
uint64_t object_size = 0;
if (this->whole_object) {
object_size = 1 << order;
librados::IoCtx ioctx;
ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
- bool skip_discard = this->is_skip_partial_discard_enabled();
-
{
librbd::RBD rbd;
librbd::Image image;
ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
interval_set<uint64_t> exists;
interval_set<uint64_t> one;
scribble(image, 10, 102400, skip_discard, &exists, &one);
librados::IoCtx ioctx;
ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx));
- bool skip_discard = this->is_skip_partial_discard_enabled();
-
librbd::RBD rbd;
librbd::Image image;
std::string name = this->get_temp_image_name();
ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
uint64_t object_size = 0;
if (this->whole_object) {
object_size = 1 << order;
memset_rand(keys, keys_len);
memset_rand(vals, vals_len);
- ASSERT_EQ(0, rbd_metadata_list(image, "", 0, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image, "key", 0, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(0U, keys_len);
ASSERT_EQ(0U, vals_len);
ASSERT_EQ(-ERANGE, rbd_metadata_get(image1, "key1", value, &value_len));
ASSERT_EQ(value_len, strlen("value1") + 1);
- ASSERT_EQ(-ERANGE, rbd_metadata_list(image1, "", 0, keys, &keys_len, vals,
+ ASSERT_EQ(-ERANGE, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
&vals_len));
keys_len = sizeof(keys);
vals_len = sizeof(vals);
memset_rand(keys, keys_len);
memset_rand(vals, vals_len);
- ASSERT_EQ(0, rbd_metadata_list(image1, "", 0, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1);
ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1);
ASSERT_EQ(-ENOENT, rbd_metadata_remove(image1, "key3"));
value_len = sizeof(value);
ASSERT_EQ(-ENOENT, rbd_metadata_get(image1, "key3", value, &value_len));
- ASSERT_EQ(0, rbd_metadata_list(image1, "", 0, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, strlen("key2") + 1);
ASSERT_EQ(vals_len, strlen("value2") + 1);
vals_len = sizeof(vals);
memset_rand(keys, keys_len);
memset_rand(vals, vals_len);
- ASSERT_EQ(0, rbd_metadata_list(image1, "", 0, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len,
strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") + 1);
vals_len = sizeof(vals);
memset_rand(keys, keys_len);
memset_rand(vals, vals_len);
- ASSERT_EQ(0, rbd_metadata_list(image1, "", 0, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len,
strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") + 1);
vals_len = sizeof(vals);
memset_rand(keys, keys_len);
memset_rand(vals, vals_len);
- ASSERT_EQ(0, rbd_metadata_list(image2, "", 0, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image2, "key", 0, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") +
1 + strlen("key4") + 1);
ASSERT_STREQ(vals + strlen("value1") + 1 + strlen("value2") + 1 +
strlen("value3") + 1, "value4");
- ASSERT_EQ(0, rbd_metadata_list(image1, "", 0, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image1, "key", 0, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len,
strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") + 1);
vals_len = strlen("value1") + 1;
memset_rand(keys, keys_len);
memset_rand(vals, vals_len);
- ASSERT_EQ(0, rbd_metadata_list(image2, "", 1, keys, &keys_len, vals,
+ ASSERT_EQ(0, rbd_metadata_list(image2, "key", 1, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, strlen("key1") + 1);
ASSERT_EQ(vals_len, strlen("value1") + 1);
ASSERT_STREQ(keys, "key1");
ASSERT_STREQ(vals, "value1");
- ASSERT_EQ(-ERANGE, rbd_metadata_list(image2, "", 2, keys, &keys_len, vals,
+ ASSERT_EQ(-ERANGE, rbd_metadata_list(image2, "key", 2, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1);
ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1);
- ASSERT_EQ(-ERANGE, rbd_metadata_list(image2, "", 0, keys, &keys_len, vals,
+ ASSERT_EQ(-ERANGE, rbd_metadata_list(image2, "key", 0, keys, &keys_len, vals,
&vals_len));
ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") +
1 + strlen("key4") + 1);
librbd::Image image1;
ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
map<string, bufferlist> pairs;
- ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
ASSERT_TRUE(pairs.empty());
ASSERT_EQ(0, image1.metadata_set("key1", "value1"));
ASSERT_EQ(0, image1.metadata_set("key2", "value2"));
ASSERT_EQ(0, image1.metadata_get("key1", &value));
ASSERT_EQ(0, strcmp("value1", value.c_str()));
- ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
ASSERT_EQ(2U, pairs.size());
ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
ASSERT_EQ(0, image1.metadata_remove("key1"));
ASSERT_EQ(-ENOENT, image1.metadata_remove("key3"));
ASSERT_TRUE(image1.metadata_get("key3", &value) < 0);
- ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
ASSERT_EQ(1U, pairs.size());
ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
pairs.clear();
ASSERT_EQ(0, image1.metadata_set("key1", "value1"));
ASSERT_EQ(0, image1.metadata_set("key3", "value3"));
- ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
ASSERT_EQ(3U, pairs.size());
ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
ASSERT_EQ(0, strncmp("value3", pairs["key3"].c_str(), 6));
ASSERT_EQ(0, image1.snap_set(NULL));
- ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
ASSERT_EQ(3U, pairs.size());
ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
ASSERT_EQ(0, rbd.open(ioctx, image2, cname.c_str(), NULL));
ASSERT_EQ(0, image2.metadata_set("key4", "value4"));
pairs.clear();
- ASSERT_EQ(0, image2.metadata_list("", 0, &pairs));
+ ASSERT_EQ(0, image2.metadata_list("key", 0, &pairs));
ASSERT_EQ(4U, pairs.size());
pairs.clear();
- ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
+ ASSERT_EQ(0, image1.metadata_list("key", 0, &pairs));
ASSERT_EQ(3U, pairs.size());
ASSERT_EQ(-ENOENT, image1.metadata_get("key4", &value));
}
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
- bool skip_discard = is_skip_partial_discard_enabled();
-
librbd::RBD rbd;
std::string name = get_temp_image_name();
uint64_t size = 1 << 20;
librbd::Image image;
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ bool skip_discard = this->is_skip_partial_discard_enabled(image);
+
bufferlist bl;
ASSERT_EQ(0, image.write(0, bl.length(), bl));
TEST_F(TestLibRBD, BreakLock)
{
REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+ REQUIRE(!is_rbd_rwl_enabled((CephContext *)_rados.cct()));
static char buf[10];
TEST_F(TestLibRBD, DiscardAfterWrite)
{
- REQUIRE(!is_skip_partial_discard_enabled());
-
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
librbd::Image image;
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+ if (this->is_skip_partial_discard_enabled(image)) {
+ return;
+ }
+
// enable writeback cache
ASSERT_EQ(0, image.flush());
ASSERT_EQ(features & ~RBD_FEATURES_IMPLICIT_ENABLE, image_state.features);
ASSERT_EQ(1U, image_state.snapshots.size());
ASSERT_EQ("snap", image_state.snapshots.begin()->second.name);
- ASSERT_TRUE(image_state.metadata.empty());
+ uint8_t original_pairs_num = image_state.metadata.size();
{
C_SaferCond cond;
ASSERT_EQ(image_name, image_state.name);
ASSERT_EQ(features & ~RBD_FEATURES_IMPLICIT_ENABLE, image_state.features);
- ASSERT_EQ(10U, image_state.metadata.size());
+ ASSERT_EQ(original_pairs_num + 10, image_state.metadata.size());
for (int i = 0; i < 10; i++) {
auto &bl = image_state.metadata[stringify(i)];
ASSERT_EQ(0, strncmp(std::string(1024, 'A' + i).c_str(), bl.c_str(),
void expect_set_require_lock(MockExclusiveLockImageCtx &mock_image_ctx,
MockImageDispatch &mock_image_dispatch) {
if (mock_image_ctx.clone_copy_on_read ||
- (mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0) {
+ (mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0 ||
+ is_rbd_rwl_enabled(mock_image_ctx.cct)) {
expect_set_require_lock(mock_image_dispatch, io::DIRECTION_BOTH);
} else {
expect_set_require_lock(mock_image_dispatch, io::DIRECTION_WRITE);
EXPECT_CALL(get_mock_io_ctx(ictx.md_ctx),
exec(_, _, StrEq("lock"), StrEq("unlock"), _, _, _, _))
.WillRepeatedly(DoDefault());
+ if (ictx.test_features(RBD_FEATURE_DIRTY_CACHE)) {
+ EXPECT_CALL(get_mock_io_ctx(ictx.md_ctx),
+ exec(ictx.header_oid, _, StrEq("rbd"), StrEq("set_features"), _, _, _, _))
+ .WillOnce(DoDefault());
+ EXPECT_CALL(get_mock_io_ctx(ictx.md_ctx),
+ exec(ictx.header_oid, _, StrEq("rbd"), StrEq("metadata_set"), _, _, _, _))
+ .WillOnce(DoDefault());
+ EXPECT_CALL(get_mock_io_ctx(ictx.md_ctx),
+ exec(ictx.header_oid, _, StrEq("rbd"), StrEq("metadata_remove"), _, _, _, _))
+ .WillOnce(DoDefault());
+ }
}
void TestMockFixture::expect_op_work_queue(librbd::MockImageCtx &mock_image_ctx) {
#include "test/librbd/test_support.h"
#include "include/rbd_types.h"
#include "gtest/gtest.h"
+#include "common/ceph_context.h"
#include <sstream>
bool get_features(uint64_t *features) {
return fsid == "00000000-1111-2222-3333-444444444444";
}
+bool is_rbd_rwl_enabled(ceph::common::CephContext *cct) {
+#if defined(WITH_RBD_RWL)
+ return cct->_conf.get_val<bool>("rbd_rwl_enabled");
+#else
+ return false;
+#endif
+}
bool is_librados_test_stub(librados::Rados &rados);
+bool is_rbd_rwl_enabled(ceph::common::CephContext *ctx);
+
#define REQUIRE(x) { \
if (!(x)) { \
std::cout << "SKIPPING" << std::endl; \