]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
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/Operations.h" | |
11fdf7f2 | 12 | #include "librbd/api/Image.h" |
7c673cae FG |
13 | #include "librbd/object_map/UpdateRequest.h" |
14 | #include "gmock/gmock.h" | |
15 | #include "gtest/gtest.h" | |
16 | ||
17 | namespace librbd { | |
18 | namespace object_map { | |
19 | ||
20 | using ::testing::_; | |
21 | using ::testing::DoDefault; | |
3efd9988 | 22 | using ::testing::InSequence; |
7c673cae FG |
23 | using ::testing::Return; |
24 | using ::testing::StrEq; | |
25 | ||
26 | class TestMockObjectMapUpdateRequest : public TestMockFixture { | |
27 | public: | |
3efd9988 FG |
28 | void expect_update(librbd::ImageCtx *ictx, uint64_t snap_id, |
29 | uint64_t start_object_no, uint64_t end_object_no, | |
30 | uint8_t new_state, | |
31 | const boost::optional<uint8_t>& current_state, int r) { | |
32 | bufferlist bl; | |
11fdf7f2 TL |
33 | encode(start_object_no, bl); |
34 | encode(end_object_no, bl); | |
35 | encode(new_state, bl); | |
36 | encode(current_state, bl); | |
3efd9988 | 37 | |
7c673cae FG |
38 | std::string oid(ObjectMap<>::object_map_name(ictx->id, snap_id)); |
39 | if (snap_id == CEPH_NOSNAP) { | |
40 | EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx), | |
f67539c2 TL |
41 | exec(oid, _, StrEq("lock"), StrEq("assert_locked"), _, _, _, |
42 | _)) | |
7c673cae FG |
43 | .WillOnce(DoDefault()); |
44 | } | |
45 | ||
46 | if (r < 0) { | |
47 | EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx), | |
3efd9988 | 48 | exec(oid, _, StrEq("rbd"), StrEq("object_map_update"), |
f67539c2 | 49 | ContentsEqual(bl), _, _, _)) |
7c673cae FG |
50 | .WillOnce(Return(r)); |
51 | } else { | |
52 | EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx), | |
3efd9988 | 53 | exec(oid, _, StrEq("rbd"), StrEq("object_map_update"), |
f67539c2 | 54 | ContentsEqual(bl), _, _, _)) |
7c673cae FG |
55 | .WillOnce(DoDefault()); |
56 | } | |
57 | } | |
58 | ||
59 | void expect_invalidate(librbd::ImageCtx *ictx) { | |
60 | EXPECT_CALL(get_mock_io_ctx(ictx->md_ctx), | |
f67539c2 TL |
61 | exec(ictx->header_oid, _, StrEq("rbd"), StrEq("set_flags"), _, |
62 | _, _, _)) | |
7c673cae FG |
63 | .WillOnce(DoDefault()); |
64 | } | |
65 | }; | |
66 | ||
67 | TEST_F(TestMockObjectMapUpdateRequest, UpdateInMemory) { | |
68 | REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); | |
69 | ||
70 | librbd::ImageCtx *ictx; | |
71 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
72 | ||
73 | librbd::NoOpProgressContext no_progress; | |
74 | ASSERT_EQ(0, ictx->operations->resize(4 << ictx->order, true, no_progress)); | |
75 | ASSERT_EQ(0, acquire_exclusive_lock(*ictx)); | |
76 | ||
9f95a23c | 77 | ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock"); |
7c673cae FG |
78 | ceph::BitVector<2> object_map; |
79 | object_map.resize(4); | |
80 | for (uint64_t i = 0; i < object_map.size(); ++i) { | |
81 | object_map[i] = i % 4; | |
82 | } | |
83 | ||
84 | C_SaferCond cond_ctx; | |
85 | AsyncRequest<> *req = new UpdateRequest<>( | |
9f95a23c TL |
86 | *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, 0, object_map.size(), |
87 | OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, false, &cond_ctx); | |
7c673cae | 88 | { |
9f95a23c TL |
89 | std::shared_lock image_locker{ictx->image_lock}; |
90 | std::unique_lock object_map_locker{object_map_lock}; | |
7c673cae FG |
91 | req->send(); |
92 | } | |
93 | ASSERT_EQ(0, cond_ctx.wait()); | |
94 | ||
95 | for (uint64_t i = 0; i < object_map.size(); ++i) { | |
96 | if (i % 4 == OBJECT_EXISTS || i % 4 == OBJECT_EXISTS_CLEAN) { | |
97 | ASSERT_EQ(OBJECT_NONEXISTENT, object_map[i]); | |
98 | } else { | |
99 | ASSERT_EQ(i % 4, object_map[i]); | |
100 | } | |
101 | } | |
102 | } | |
103 | ||
104 | TEST_F(TestMockObjectMapUpdateRequest, UpdateHeadOnDisk) { | |
105 | REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); | |
106 | ||
107 | librbd::ImageCtx *ictx; | |
108 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
109 | ASSERT_EQ(0, acquire_exclusive_lock(*ictx)); | |
110 | ||
3efd9988 | 111 | expect_update(ictx, CEPH_NOSNAP, 0, 1, OBJECT_NONEXISTENT, OBJECT_EXISTS, 0); |
7c673cae | 112 | |
9f95a23c TL |
113 | ceph::shared_mutex object_map_lock = |
114 | ceph::make_shared_mutex("lock"); | |
7c673cae FG |
115 | ceph::BitVector<2> object_map; |
116 | object_map.resize(1); | |
117 | ||
118 | C_SaferCond cond_ctx; | |
119 | AsyncRequest<> *req = new UpdateRequest<>( | |
9f95a23c TL |
120 | *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, 0, object_map.size(), |
121 | OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, false, &cond_ctx); | |
7c673cae | 122 | { |
9f95a23c TL |
123 | std::shared_lock image_locker{ictx->image_lock}; |
124 | std::unique_lock object_map_locker{object_map_lock}; | |
7c673cae FG |
125 | req->send(); |
126 | } | |
127 | ASSERT_EQ(0, cond_ctx.wait()); | |
128 | ||
129 | expect_unlock_exclusive_lock(*ictx); | |
130 | } | |
131 | ||
132 | TEST_F(TestMockObjectMapUpdateRequest, UpdateSnapOnDisk) { | |
133 | REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); | |
134 | ||
135 | librbd::ImageCtx *ictx; | |
136 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
137 | ASSERT_EQ(0, snap_create(*ictx, "snap1")); | |
11fdf7f2 TL |
138 | ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx, |
139 | cls::rbd::UserSnapshotNamespace(), | |
140 | "snap1")); | |
7c673cae FG |
141 | |
142 | uint64_t snap_id = ictx->snap_id; | |
3efd9988 | 143 | expect_update(ictx, snap_id, 0, 1, OBJECT_NONEXISTENT, OBJECT_EXISTS, 0); |
7c673cae | 144 | |
9f95a23c TL |
145 | ceph::shared_mutex object_map_lock = |
146 | ceph::make_shared_mutex("lock"); | |
7c673cae FG |
147 | ceph::BitVector<2> object_map; |
148 | object_map.resize(1); | |
149 | ||
150 | C_SaferCond cond_ctx; | |
151 | AsyncRequest<> *req = new UpdateRequest<>( | |
9f95a23c TL |
152 | *ictx, &object_map_lock, &object_map, snap_id, 0, object_map.size(), |
153 | OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, false, &cond_ctx); | |
7c673cae | 154 | { |
9f95a23c TL |
155 | std::shared_lock image_locker{ictx->image_lock}; |
156 | std::unique_lock object_map_locker{object_map_lock}; | |
7c673cae FG |
157 | req->send(); |
158 | } | |
159 | ASSERT_EQ(0, cond_ctx.wait()); | |
160 | ||
161 | expect_unlock_exclusive_lock(*ictx); | |
162 | } | |
163 | ||
164 | TEST_F(TestMockObjectMapUpdateRequest, UpdateOnDiskError) { | |
165 | REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); | |
166 | ||
167 | librbd::ImageCtx *ictx; | |
168 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
169 | ASSERT_EQ(0, acquire_exclusive_lock(*ictx)); | |
170 | ||
3efd9988 FG |
171 | expect_update(ictx, CEPH_NOSNAP, 0, 1, OBJECT_NONEXISTENT, OBJECT_EXISTS, |
172 | -EINVAL); | |
7c673cae FG |
173 | expect_invalidate(ictx); |
174 | ||
9f95a23c | 175 | ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock"); |
7c673cae FG |
176 | ceph::BitVector<2> object_map; |
177 | object_map.resize(1); | |
178 | ||
179 | C_SaferCond cond_ctx; | |
180 | AsyncRequest<> *req = new UpdateRequest<>( | |
9f95a23c TL |
181 | *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, 0, object_map.size(), |
182 | OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, false, &cond_ctx); | |
7c673cae | 183 | { |
9f95a23c TL |
184 | std::shared_lock image_locker{ictx->image_lock}; |
185 | std::unique_lock object_map_locker{object_map_lock}; | |
7c673cae FG |
186 | req->send(); |
187 | } | |
188 | ASSERT_EQ(0, cond_ctx.wait()); | |
189 | ||
190 | expect_unlock_exclusive_lock(*ictx); | |
191 | } | |
192 | ||
193 | TEST_F(TestMockObjectMapUpdateRequest, RebuildSnapOnDisk) { | |
194 | REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); | |
195 | ||
196 | librbd::ImageCtx *ictx; | |
197 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
198 | ASSERT_EQ(0, snap_create(*ictx, "snap1")); | |
199 | ASSERT_EQ(0, ictx->state->refresh_if_required()); | |
200 | ASSERT_EQ(CEPH_NOSNAP, ictx->snap_id); | |
201 | ||
202 | uint64_t snap_id = ictx->snap_info.rbegin()->first; | |
3efd9988 FG |
203 | expect_update(ictx, snap_id, 0, 1, OBJECT_EXISTS_CLEAN, |
204 | boost::optional<uint8_t>(), 0); | |
7c673cae FG |
205 | expect_unlock_exclusive_lock(*ictx); |
206 | ||
9f95a23c | 207 | ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock"); |
7c673cae FG |
208 | ceph::BitVector<2> object_map; |
209 | object_map.resize(1); | |
210 | ||
211 | C_SaferCond cond_ctx; | |
212 | AsyncRequest<> *req = new UpdateRequest<>( | |
9f95a23c TL |
213 | *ictx, &object_map_lock, &object_map, snap_id, 0, object_map.size(), |
214 | OBJECT_EXISTS_CLEAN, boost::optional<uint8_t>(), {}, false, &cond_ctx); | |
7c673cae | 215 | { |
9f95a23c TL |
216 | std::shared_lock image_locker{ictx->image_lock}; |
217 | std::unique_lock object_map_locker{object_map_lock}; | |
7c673cae FG |
218 | req->send(); |
219 | } | |
220 | ASSERT_EQ(0, cond_ctx.wait()); | |
221 | ||
222 | // do not update the in-memory map if rebuilding a snapshot | |
223 | ASSERT_NE(OBJECT_EXISTS_CLEAN, object_map[0]); | |
224 | } | |
225 | ||
3efd9988 FG |
226 | TEST_F(TestMockObjectMapUpdateRequest, BatchUpdate) { |
227 | REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); | |
228 | ||
229 | librbd::ImageCtx *ictx; | |
230 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
231 | ||
232 | librbd::NoOpProgressContext no_progress; | |
233 | ASSERT_EQ(0, ictx->operations->resize(712312 * ictx->get_object_size(), false, | |
234 | no_progress)); | |
235 | ASSERT_EQ(0, acquire_exclusive_lock(*ictx)); | |
236 | ||
f67539c2 | 237 | expect_unlock_exclusive_lock(*ictx); |
3efd9988 FG |
238 | InSequence seq; |
239 | expect_update(ictx, CEPH_NOSNAP, 0, 262144, OBJECT_NONEXISTENT, OBJECT_EXISTS, | |
240 | 0); | |
241 | expect_update(ictx, CEPH_NOSNAP, 262144, 524288, OBJECT_NONEXISTENT, | |
242 | OBJECT_EXISTS, 0); | |
243 | expect_update(ictx, CEPH_NOSNAP, 524288, 712312, OBJECT_NONEXISTENT, | |
244 | OBJECT_EXISTS, 0); | |
3efd9988 | 245 | |
9f95a23c | 246 | ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock"); |
3efd9988 FG |
247 | ceph::BitVector<2> object_map; |
248 | object_map.resize(712312); | |
249 | ||
250 | C_SaferCond cond_ctx; | |
251 | AsyncRequest<> *req = new UpdateRequest<>( | |
9f95a23c TL |
252 | *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, 0, object_map.size(), |
253 | OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, false, &cond_ctx); | |
3efd9988 | 254 | { |
9f95a23c TL |
255 | std::shared_lock image_locker{ictx->image_lock}; |
256 | std::unique_lock object_map_locker{object_map_lock}; | |
3efd9988 FG |
257 | req->send(); |
258 | } | |
259 | ASSERT_EQ(0, cond_ctx.wait()); | |
260 | } | |
261 | ||
91327a77 AA |
262 | TEST_F(TestMockObjectMapUpdateRequest, IgnoreMissingObjectMap) { |
263 | REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); | |
264 | ||
265 | librbd::ImageCtx *ictx; | |
266 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
267 | ASSERT_EQ(0, acquire_exclusive_lock(*ictx)); | |
268 | ||
269 | expect_update(ictx, CEPH_NOSNAP, 0, 1, OBJECT_NONEXISTENT, OBJECT_EXISTS, | |
270 | -ENOENT); | |
271 | ||
9f95a23c | 272 | ceph::shared_mutex object_map_lock = ceph::make_shared_mutex("lock"); |
91327a77 AA |
273 | ceph::BitVector<2> object_map; |
274 | object_map.resize(1); | |
275 | ||
276 | C_SaferCond cond_ctx; | |
277 | AsyncRequest<> *req = new UpdateRequest<>( | |
9f95a23c TL |
278 | *ictx, &object_map_lock, &object_map, CEPH_NOSNAP, 0, object_map.size(), |
279 | OBJECT_NONEXISTENT, OBJECT_EXISTS, {}, true, &cond_ctx); | |
91327a77 | 280 | { |
9f95a23c TL |
281 | std::shared_lock image_locker{ictx->image_lock}; |
282 | std::unique_lock object_map_locker{object_map_lock}; | |
91327a77 AA |
283 | req->send(); |
284 | } | |
285 | ASSERT_EQ(0, cond_ctx.wait()); | |
286 | ||
287 | expect_unlock_exclusive_lock(*ictx); | |
288 | } | |
289 | ||
7c673cae FG |
290 | } // namespace object_map |
291 | } // namespace librbd |