1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 #include "test/librbd/test_fixture.h"
4 #include "test/librbd/test_support.h"
5 #include "librbd/ExclusiveLock.h"
6 #include "librbd/ImageCtx.h"
7 #include "librbd/ImageState.h"
8 #include "librbd/ImageWatcher.h"
9 #include "librbd/internal.h"
10 #include "librbd/ObjectMap.h"
11 #include "common/Cond.h"
12 #include "common/Throttle.h"
13 #include "cls/rbd/cls_rbd_client.h"
14 #include "cls/rbd/cls_rbd_types.h"
16 #include <boost/accumulators/accumulators.hpp>
17 #include <boost/accumulators/statistics/stats.hpp>
18 #include <boost/accumulators/statistics/rolling_sum.hpp>
20 void register_test_object_map() {
23 class TestObjectMap
: public TestFixture
{
26 int when_open_object_map(librbd::ImageCtx
*ictx
) {
28 librbd::ObjectMap
<> *object_map
= new librbd::ObjectMap
<>(*ictx
, ictx
->snap_id
);
29 object_map
->open(&ctx
);
37 TEST_F(TestObjectMap
, RefreshInvalidatesWhenCorrupt
) {
38 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
40 librbd::ImageCtx
*ictx
;
41 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
43 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
45 ASSERT_FALSE(flags_set
);
49 std::unique_lock owner_locker
{ictx
->owner_lock
};
50 ictx
->exclusive_lock
->try_acquire_lock(&lock_ctx
);
52 ASSERT_EQ(0, lock_ctx
.wait());
54 std::string oid
= librbd::ObjectMap
<>::object_map_name(ictx
->id
, CEPH_NOSNAP
);
57 ASSERT_EQ(0, ictx
->md_ctx
.write_full(oid
, bl
));
59 ASSERT_EQ(0, when_open_object_map(ictx
));
60 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
62 ASSERT_TRUE(flags_set
);
65 TEST_F(TestObjectMap
, RefreshInvalidatesWhenTooSmall
) {
66 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
68 librbd::ImageCtx
*ictx
;
69 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
71 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
73 ASSERT_FALSE(flags_set
);
77 std::unique_lock owner_locker
{ictx
->owner_lock
};
78 ictx
->exclusive_lock
->try_acquire_lock(&lock_ctx
);
80 ASSERT_EQ(0, lock_ctx
.wait());
82 librados::ObjectWriteOperation op
;
83 librbd::cls_client::object_map_resize(&op
, 0, OBJECT_NONEXISTENT
);
85 std::string oid
= librbd::ObjectMap
<>::object_map_name(ictx
->id
, CEPH_NOSNAP
);
86 ASSERT_EQ(0, ictx
->md_ctx
.operate(oid
, &op
));
88 ASSERT_EQ(0, when_open_object_map(ictx
));
89 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
91 ASSERT_TRUE(flags_set
);
94 TEST_F(TestObjectMap
, InvalidateFlagOnDisk
) {
95 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
97 librbd::ImageCtx
*ictx
;
98 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
100 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
102 ASSERT_FALSE(flags_set
);
104 C_SaferCond lock_ctx
;
106 std::unique_lock owner_locker
{ictx
->owner_lock
};
107 ictx
->exclusive_lock
->try_acquire_lock(&lock_ctx
);
109 ASSERT_EQ(0, lock_ctx
.wait());
111 std::string oid
= librbd::ObjectMap
<>::object_map_name(ictx
->id
, CEPH_NOSNAP
);
113 bl
.append("corrupt");
114 ASSERT_EQ(0, ictx
->md_ctx
.write_full(oid
, bl
));
116 ASSERT_EQ(0, when_open_object_map(ictx
));
117 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
119 ASSERT_TRUE(flags_set
);
121 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
122 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
124 ASSERT_TRUE(flags_set
);
127 TEST_F(TestObjectMap
, AcquireLockInvalidatesWhenTooSmall
) {
128 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
130 librbd::ImageCtx
*ictx
;
131 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
133 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
135 ASSERT_FALSE(flags_set
);
137 librados::ObjectWriteOperation op
;
138 librbd::cls_client::object_map_resize(&op
, 0, OBJECT_NONEXISTENT
);
140 std::string oid
= librbd::ObjectMap
<>::object_map_name(ictx
->id
, CEPH_NOSNAP
);
141 ASSERT_EQ(0, ictx
->md_ctx
.operate(oid
, &op
));
143 C_SaferCond lock_ctx
;
145 std::unique_lock owner_locker
{ictx
->owner_lock
};
146 ictx
->exclusive_lock
->try_acquire_lock(&lock_ctx
);
148 ASSERT_EQ(0, lock_ctx
.wait());
150 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
152 ASSERT_TRUE(flags_set
);
154 // Test the flag is stored on disk
155 ASSERT_EQ(0, ictx
->state
->refresh());
156 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
158 ASSERT_TRUE(flags_set
);
161 namespace chrono
= std::chrono
;
163 TEST_F(TestObjectMap
, DISABLED_StressTest
) {
164 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
166 uint64_t object_count
= cls::rbd::MAX_OBJECT_MAP_OBJECT_COUNT
;
167 librbd::ImageCtx
*ictx
;
168 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
169 ASSERT_EQ(0, resize(ictx
, ictx
->layout
.object_size
* object_count
));
172 ASSERT_EQ(0, ictx
->test_flags(CEPH_NOSNAP
, RBD_FLAG_OBJECT_MAP_INVALID
,
174 ASSERT_FALSE(flags_set
);
176 srand(time(NULL
) % (unsigned long) -1);
178 coarse_mono_time start
= coarse_mono_clock::now();
179 chrono::duration
<double> last
= chrono::duration
<double>::zero();
181 const int WINDOW_SIZE
= 5;
182 typedef boost::accumulators::accumulator_set
<
183 double, boost::accumulators::stats
<
184 boost::accumulators::tag::rolling_sum
> > RollingSum
;
187 boost::accumulators::tag::rolling_window::window_size
= WINDOW_SIZE
);
189 boost::accumulators::tag::rolling_window::window_size
= WINDOW_SIZE
);
191 uint32_t io_threads
= 16;
192 uint64_t cur_ios
= 0;
193 SimpleThrottle
throttle(io_threads
, false);
194 for (uint64_t ios
= 0; ios
< 100000;) {
195 if (throttle
.pending_error()) {
200 uint64_t object_no
= (rand() % object_count
);
201 auto ctx
= new LambdaContext([&throttle
, object_no
](int r
) {
202 ASSERT_EQ(0, r
) << "object_no=" << object_no
;
206 std::shared_lock owner_locker
{ictx
->owner_lock
};
207 std::shared_lock image_locker
{ictx
->image_lock
};
208 ASSERT_TRUE(ictx
->object_map
!= nullptr);
210 if (!ictx
->object_map
->aio_update
<
211 Context
, &Context::complete
>(CEPH_NOSNAP
, object_no
,
212 OBJECT_EXISTS
, {}, {}, true,
220 coarse_mono_time now
= coarse_mono_clock::now();
221 chrono::duration
<double> elapsed
= now
- start
;
222 if (last
== chrono::duration
<double>::zero()) {
224 } else if ((int)elapsed
.count() != (int)last
.count()) {
225 time_acc((elapsed
- last
).count());
226 ios_acc(static_cast<double>(cur_ios
));
229 double time_sum
= boost::accumulators::rolling_sum(time_acc
);
230 std::cerr
<< std::setw(5) << (int)elapsed
.count() << "\t"
231 << std::setw(8) << (int)ios
<< "\t"
232 << std::fixed
<< std::setw(8) << std::setprecision(2)
233 << boost::accumulators::rolling_sum(ios_acc
) / time_sum
239 ASSERT_EQ(0, throttle
.wait_for_ret());