1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "test/librbd/test_mock_fixture.h"
5 #include "test/librbd/test_support.h"
6 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
7 #include "common/bit_vector.hpp"
8 #include "librbd/ImageState.h"
9 #include "librbd/internal.h"
10 #include "librbd/ObjectMap.h"
11 #include "librbd/object_map/SnapshotRemoveRequest.h"
12 #include "gmock/gmock.h"
13 #include "gtest/gtest.h"
16 namespace object_map
{
19 using ::testing::DoDefault
;
20 using ::testing::Return
;
21 using ::testing::StrEq
;
23 class TestMockObjectMapSnapshotRemoveRequest
: public TestMockFixture
{
25 void expect_load_map(librbd::ImageCtx
*ictx
, uint64_t snap_id
, int r
) {
26 std::string
snap_oid(ObjectMap
<>::object_map_name(ictx
->id
, snap_id
));
28 EXPECT_CALL(get_mock_io_ctx(ictx
->md_ctx
),
29 exec(snap_oid
, _
, StrEq("rbd"), StrEq("object_map_load"), _
,
33 EXPECT_CALL(get_mock_io_ctx(ictx
->md_ctx
),
34 exec(snap_oid
, _
, StrEq("rbd"), StrEq("object_map_load"), _
,
36 .WillOnce(DoDefault());
40 void expect_remove_snapshot(librbd::ImageCtx
*ictx
, int r
) {
41 std::string
oid(ObjectMap
<>::object_map_name(ictx
->id
, CEPH_NOSNAP
));
43 EXPECT_CALL(get_mock_io_ctx(ictx
->md_ctx
),
44 exec(oid
, _
, StrEq("lock"), StrEq("assert_locked"), _
, _
, _
,
48 EXPECT_CALL(get_mock_io_ctx(ictx
->md_ctx
),
49 exec(oid
, _
, StrEq("lock"), StrEq("assert_locked"), _
, _
, _
,
51 .WillOnce(DoDefault());
52 EXPECT_CALL(get_mock_io_ctx(ictx
->md_ctx
),
53 exec(oid
, _
, StrEq("rbd"), StrEq("object_map_snap_remove"), _
,
55 .WillOnce(DoDefault());
59 void expect_remove_map(librbd::ImageCtx
*ictx
, uint64_t snap_id
, int r
) {
60 std::string
snap_oid(ObjectMap
<>::object_map_name(ictx
->id
, snap_id
));
62 EXPECT_CALL(get_mock_io_ctx(ictx
->md_ctx
), remove(snap_oid
, _
))
65 EXPECT_CALL(get_mock_io_ctx(ictx
->md_ctx
), remove(snap_oid
, _
))
66 .WillOnce(DoDefault());
70 void expect_invalidate(librbd::ImageCtx
*ictx
) {
71 EXPECT_CALL(get_mock_io_ctx(ictx
->md_ctx
),
72 exec(ictx
->header_oid
, _
, StrEq("rbd"), StrEq("set_flags"), _
,
74 .WillOnce(DoDefault());
78 TEST_F(TestMockObjectMapSnapshotRemoveRequest
, Success
) {
79 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
81 librbd::ImageCtx
*ictx
;
82 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
83 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
84 ASSERT_EQ(0, ictx
->state
->refresh_if_required());
86 uint64_t snap_id
= ictx
->snap_info
.rbegin()->first
;
87 if (ictx
->test_features(RBD_FEATURE_FAST_DIFF
)) {
88 expect_load_map(ictx
, snap_id
, 0);
89 expect_remove_snapshot(ictx
, 0);
91 expect_remove_map(ictx
, snap_id
, 0);
93 ceph::shared_mutex object_map_lock
= ceph::make_shared_mutex("lock");
94 ceph::BitVector
<2> object_map
;
96 AsyncRequest
<> *request
= new SnapshotRemoveRequest(
97 *ictx
, &object_map_lock
, &object_map
, snap_id
, &cond_ctx
);
99 std::shared_lock owner_locker
{ictx
->owner_lock
};
100 std::unique_lock image_locker
{ictx
->image_lock
};
103 ASSERT_EQ(0, cond_ctx
.wait());
105 expect_unlock_exclusive_lock(*ictx
);
108 TEST_F(TestMockObjectMapSnapshotRemoveRequest
, LoadMapMissing
) {
109 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
111 librbd::ImageCtx
*ictx
;
112 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
113 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
114 ASSERT_EQ(0, ictx
->state
->refresh_if_required());
116 uint64_t snap_id
= ictx
->snap_info
.rbegin()->first
;
117 auto snap_it
= ictx
->snap_info
.find(snap_id
);
118 ASSERT_NE(ictx
->snap_info
.end(), snap_it
);
119 snap_it
->second
.flags
|= RBD_FLAG_OBJECT_MAP_INVALID
;
121 expect_load_map(ictx
, snap_id
, -ENOENT
);
123 ceph::shared_mutex object_map_lock
= ceph::make_shared_mutex("lock");
124 ceph::BitVector
<2> object_map
;
125 C_SaferCond cond_ctx
;
126 AsyncRequest
<> *request
= new SnapshotRemoveRequest(
127 *ictx
, &object_map_lock
, &object_map
, snap_id
, &cond_ctx
);
129 std::shared_lock owner_locker
{ictx
->owner_lock
};
130 std::unique_lock image_locker
{ictx
->image_lock
};
133 ASSERT_EQ(0, cond_ctx
.wait());
136 // shouldn't invalidate the HEAD revision when we fail to load
137 // the already deleted snapshot
138 std::shared_lock image_locker
{ictx
->image_lock
};
140 ASSERT_EQ(0, ictx
->get_flags(CEPH_NOSNAP
, &flags
));
141 ASSERT_EQ(0U, flags
& RBD_FLAG_OBJECT_MAP_INVALID
);
144 expect_unlock_exclusive_lock(*ictx
);
147 TEST_F(TestMockObjectMapSnapshotRemoveRequest
, LoadMapError
) {
148 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
150 librbd::ImageCtx
*ictx
;
151 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
152 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
153 ASSERT_EQ(0, ictx
->state
->refresh_if_required());
155 uint64_t snap_id
= ictx
->snap_info
.rbegin()->first
;
156 expect_load_map(ictx
, snap_id
, -EINVAL
);
157 expect_invalidate(ictx
);
158 expect_remove_map(ictx
, snap_id
, 0);
160 ceph::shared_mutex object_map_lock
= ceph::make_shared_mutex("lock");
161 ceph::BitVector
<2> object_map
;
162 C_SaferCond cond_ctx
;
163 AsyncRequest
<> *request
= new SnapshotRemoveRequest(
164 *ictx
, &object_map_lock
, &object_map
, snap_id
, &cond_ctx
);
166 std::shared_lock owner_locker
{ictx
->owner_lock
};
167 std::unique_lock image_locker
{ictx
->image_lock
};
170 ASSERT_EQ(0, cond_ctx
.wait());
172 expect_unlock_exclusive_lock(*ictx
);
175 TEST_F(TestMockObjectMapSnapshotRemoveRequest
, RemoveSnapshotMissing
) {
176 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
178 librbd::ImageCtx
*ictx
;
179 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
180 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
181 ASSERT_EQ(0, ictx
->state
->refresh_if_required());
183 uint64_t snap_id
= ictx
->snap_info
.rbegin()->first
;
184 expect_load_map(ictx
, snap_id
, 0);
185 expect_remove_snapshot(ictx
, -ENOENT
);
186 expect_remove_map(ictx
, snap_id
, 0);
188 ceph::shared_mutex object_map_lock
= ceph::make_shared_mutex("lock");
189 ceph::BitVector
<2> object_map
;
190 C_SaferCond cond_ctx
;
191 AsyncRequest
<> *request
= new SnapshotRemoveRequest(
192 *ictx
, &object_map_lock
, &object_map
, snap_id
, &cond_ctx
);
194 std::shared_lock owner_locker
{ictx
->owner_lock
};
195 std::unique_lock image_locker
{ictx
->image_lock
};
198 ASSERT_EQ(0, cond_ctx
.wait());
200 expect_unlock_exclusive_lock(*ictx
);
203 TEST_F(TestMockObjectMapSnapshotRemoveRequest
, RemoveSnapshotError
) {
204 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
206 librbd::ImageCtx
*ictx
;
207 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
208 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
209 ASSERT_EQ(0, ictx
->state
->refresh_if_required());
211 uint64_t snap_id
= ictx
->snap_info
.rbegin()->first
;
212 expect_load_map(ictx
, snap_id
, 0);
213 expect_remove_snapshot(ictx
, -EINVAL
);
214 expect_invalidate(ictx
);
215 expect_remove_map(ictx
, snap_id
, 0);
217 ceph::shared_mutex object_map_lock
= ceph::make_shared_mutex("lock");
218 ceph::BitVector
<2> object_map
;
219 C_SaferCond cond_ctx
;
220 AsyncRequest
<> *request
= new SnapshotRemoveRequest(
221 *ictx
, &object_map_lock
, &object_map
, snap_id
, &cond_ctx
);
223 std::shared_lock owner_locker
{ictx
->owner_lock
};
224 std::unique_lock image_locker
{ictx
->image_lock
};
227 ASSERT_EQ(0, cond_ctx
.wait());
229 expect_unlock_exclusive_lock(*ictx
);
232 TEST_F(TestMockObjectMapSnapshotRemoveRequest
, RemoveMapMissing
) {
233 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
235 librbd::ImageCtx
*ictx
;
236 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
237 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
238 ASSERT_EQ(0, ictx
->state
->refresh_if_required());
240 uint64_t snap_id
= ictx
->snap_info
.rbegin()->first
;
241 if (ictx
->test_features(RBD_FEATURE_FAST_DIFF
)) {
242 expect_load_map(ictx
, snap_id
, 0);
243 expect_remove_snapshot(ictx
, 0);
245 expect_remove_map(ictx
, snap_id
, -ENOENT
);
247 ceph::shared_mutex object_map_lock
= ceph::make_shared_mutex("lock");
248 ceph::BitVector
<2> object_map
;
249 C_SaferCond cond_ctx
;
250 AsyncRequest
<> *request
= new SnapshotRemoveRequest(
251 *ictx
, &object_map_lock
, &object_map
, snap_id
, &cond_ctx
);
253 std::shared_lock owner_locker
{ictx
->owner_lock
};
254 std::unique_lock image_locker
{ictx
->image_lock
};
257 ASSERT_EQ(0, cond_ctx
.wait());
259 expect_unlock_exclusive_lock(*ictx
);
262 TEST_F(TestMockObjectMapSnapshotRemoveRequest
, RemoveMapError
) {
263 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
265 librbd::ImageCtx
*ictx
;
266 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
267 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
268 ASSERT_EQ(0, ictx
->state
->refresh_if_required());
270 uint64_t snap_id
= ictx
->snap_info
.rbegin()->first
;
271 if (ictx
->test_features(RBD_FEATURE_FAST_DIFF
)) {
272 expect_load_map(ictx
, snap_id
, 0);
273 expect_remove_snapshot(ictx
, 0);
275 expect_remove_map(ictx
, snap_id
, -EINVAL
);
277 ceph::shared_mutex object_map_lock
= ceph::make_shared_mutex("lock");
278 ceph::BitVector
<2> object_map
;
279 C_SaferCond cond_ctx
;
280 AsyncRequest
<> *request
= new SnapshotRemoveRequest(
281 *ictx
, &object_map_lock
, &object_map
, snap_id
, &cond_ctx
);
283 std::shared_lock owner_locker
{ictx
->owner_lock
};
284 std::unique_lock image_locker
{ictx
->image_lock
};
287 ASSERT_EQ(-EINVAL
, cond_ctx
.wait());
289 expect_unlock_exclusive_lock(*ictx
);
292 TEST_F(TestMockObjectMapSnapshotRemoveRequest
, ScrubCleanObjects
) {
293 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
295 librbd::ImageCtx
*ictx
;
296 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
297 librbd::NoOpProgressContext prog_ctx
;
298 uint64_t size
= 4294967296; // 4GB = 1024 * 4MB
299 ASSERT_EQ(0, resize(ictx
, size
));
301 // update image objectmap for snap inherit
302 ceph::shared_mutex object_map_lock
= ceph::make_shared_mutex("lock");
303 ceph::BitVector
<2> object_map
;
304 object_map
.resize(1024);
305 for (uint64_t i
= 512; i
< object_map
.size(); ++i
) {
306 object_map
[i
] = i
% 2 == 0 ? OBJECT_EXISTS
: OBJECT_NONEXISTENT
;
309 C_SaferCond cond_ctx1
;
311 librbd::ObjectMap
<> *om
= new librbd::ObjectMap
<>(*ictx
, ictx
->snap_id
);
312 std::shared_lock owner_locker
{ictx
->owner_lock
};
313 std::unique_lock image_locker
{ictx
->image_lock
};
314 om
->set_object_map(object_map
);
315 om
->aio_save(&cond_ctx1
);
318 ASSERT_EQ(0, cond_ctx1
.wait());
319 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
320 ASSERT_EQ(0, ictx
->state
->refresh_if_required());
322 // simutate the image objectmap state after creating snap
323 for (uint64_t i
= 512; i
< object_map
.size(); ++i
) {
324 object_map
[i
] = i
% 2 == 0 ? OBJECT_EXISTS_CLEAN
: OBJECT_NONEXISTENT
;
327 C_SaferCond cond_ctx2
;
328 uint64_t snap_id
= ictx
->snap_info
.rbegin()->first
;
329 AsyncRequest
<> *request
= new SnapshotRemoveRequest(
330 *ictx
, &object_map_lock
, &object_map
, snap_id
, &cond_ctx2
);
332 std::shared_lock owner_locker
{ictx
->owner_lock
};
333 std::unique_lock image_locker
{ictx
->image_lock
};
336 ASSERT_EQ(0, cond_ctx2
.wait());
338 for (uint64_t i
= 512; i
< object_map
.size(); ++i
) {
339 ASSERT_EQ(i
% 2 == 0 ? OBJECT_EXISTS
: OBJECT_NONEXISTENT
,
344 } // namespace object_map
345 } // namespace librbd