summaryrefslogtreecommitdiffstats
path: root/src/test/librbd/test_ObjectMap.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/librbd/test_ObjectMap.cc')
-rw-r--r--src/test/librbd/test_ObjectMap.cc236
1 files changed, 236 insertions, 0 deletions
diff --git a/src/test/librbd/test_ObjectMap.cc b/src/test/librbd/test_ObjectMap.cc
new file mode 100644
index 00000000..d939ffb9
--- /dev/null
+++ b/src/test/librbd/test_ObjectMap.cc
@@ -0,0 +1,236 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// 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/ImageState.h"
+#include "librbd/ImageWatcher.h"
+#include "librbd/internal.h"
+#include "librbd/ObjectMap.h"
+#include "common/Cond.h"
+#include "common/Throttle.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include <list>
+#include <boost/accumulators/accumulators.hpp>
+#include <boost/accumulators/statistics/stats.hpp>
+#include <boost/accumulators/statistics/rolling_sum.hpp>
+
+void register_test_object_map() {
+}
+
+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) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ C_SaferCond lock_ctx;
+ {
+ RWLock::WLocker owner_locker(ictx->owner_lock);
+ ictx->exclusive_lock->try_acquire_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->md_ctx.write_full(oid, bl));
+
+ ASSERT_EQ(0, when_open_object_map(ictx));
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+}
+
+TEST_F(TestObjectMap, RefreshInvalidatesWhenTooSmall) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ C_SaferCond lock_ctx;
+ {
+ RWLock::WLocker owner_locker(ictx->owner_lock);
+ ictx->exclusive_lock->try_acquire_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->md_ctx.operate(oid, &op));
+
+ ASSERT_EQ(0, when_open_object_map(ictx));
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+}
+
+TEST_F(TestObjectMap, InvalidateFlagOnDisk) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ C_SaferCond lock_ctx;
+ {
+ RWLock::WLocker owner_locker(ictx->owner_lock);
+ ictx->exclusive_lock->try_acquire_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->md_ctx.write_full(oid, bl));
+
+ ASSERT_EQ(0, when_open_object_map(ictx));
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+}
+
+TEST_F(TestObjectMap, AcquireLockInvalidatesWhenTooSmall) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ 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->md_ctx.operate(oid, &op));
+
+ C_SaferCond lock_ctx;
+ {
+ RWLock::WLocker owner_locker(ictx->owner_lock);
+ ictx->exclusive_lock->try_acquire_lock(&lock_ctx);
+ }
+ ASSERT_EQ(0, lock_ctx.wait());
+
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+
+ // Test the flag is stored on disk
+ ASSERT_EQ(0, ictx->state->refresh());
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_TRUE(flags_set);
+}
+
+TEST_F(TestObjectMap, DISABLED_StressTest) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ uint64_t object_count = cls::rbd::MAX_OBJECT_MAP_OBJECT_COUNT;
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, resize(ictx, ictx->layout.object_size * object_count));
+
+ bool flags_set;
+ ASSERT_EQ(0, ictx->test_flags(CEPH_NOSNAP, RBD_FLAG_OBJECT_MAP_INVALID,
+ &flags_set));
+ ASSERT_FALSE(flags_set);
+
+ srand(time(NULL) % (unsigned long) -1);
+
+ coarse_mono_time start = coarse_mono_clock::now();
+ chrono::duration<double> last = chrono::duration<double>::zero();
+
+ const int WINDOW_SIZE = 5;
+ typedef boost::accumulators::accumulator_set<
+ double, boost::accumulators::stats<
+ boost::accumulators::tag::rolling_sum> > RollingSum;
+
+ RollingSum time_acc(
+ boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
+ RollingSum ios_acc(
+ boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE);
+
+ uint32_t io_threads = 16;
+ uint64_t cur_ios = 0;
+ SimpleThrottle throttle(io_threads, false);
+ for (uint64_t ios = 0; ios < 100000;) {
+ if (throttle.pending_error()) {
+ break;
+ }
+
+ throttle.start_op();
+ uint64_t object_no = (rand() % object_count);
+ auto ctx = new FunctionContext([&throttle, object_no](int r) {
+ ASSERT_EQ(0, r) << "object_no=" << object_no;
+ throttle.end_op(r);
+ });
+
+ RWLock::RLocker owner_locker(ictx->owner_lock);
+ RWLock::RLocker snap_locker(ictx->snap_lock);
+ RWLock::WLocker object_map_locker(ictx->object_map_lock);
+ ASSERT_TRUE(ictx->object_map != nullptr);
+
+ if (!ictx->object_map->aio_update<
+ Context, &Context::complete>(CEPH_NOSNAP, object_no,
+ OBJECT_EXISTS, {}, {}, true,
+ ctx)) {
+ ctx->complete(0);
+ } else {
+ ++cur_ios;
+ ++ios;
+ }
+
+ coarse_mono_time now = coarse_mono_clock::now();
+ chrono::duration<double> elapsed = now - start;
+ if (last == chrono::duration<double>::zero()) {
+ last = elapsed;
+ } else if ((int)elapsed.count() != (int)last.count()) {
+ time_acc((elapsed - last).count());
+ ios_acc(static_cast<double>(cur_ios));
+ cur_ios = 0;
+
+ double time_sum = boost::accumulators::rolling_sum(time_acc);
+ std::cerr << std::setw(5) << (int)elapsed.count() << "\t"
+ << std::setw(8) << (int)ios << "\t"
+ << std::fixed << std::setw(8) << std::setprecision(2)
+ << boost::accumulators::rolling_sum(ios_acc) / time_sum
+ << std::endl;
+ last = elapsed;
+ }
+ }
+
+ ASSERT_EQ(0, throttle.wait_for_ret());
+}