]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/test_mock_ObjectMap.cc
import ceph quincy 17.2.6
[ceph.git] / ceph / src / test / librbd / test_mock_ObjectMap.cc
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/librbd/mock/MockImageCtx.h"
7 #include "librbd/ObjectMap.h"
8 #include "librbd/object_map/RefreshRequest.h"
9 #include "librbd/object_map/UnlockRequest.h"
10 #include "librbd/object_map/UpdateRequest.h"
11 #include <boost/scope_exit.hpp>
12
13 namespace librbd {
14
15 namespace {
16
17 struct MockTestImageCtx : public MockImageCtx {
18 MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
19 }
20 };
21
22 } // anonymous namespace
23
24 namespace object_map {
25
26 template <>
27 struct RefreshRequest<MockTestImageCtx> {
28 Context *on_finish = nullptr;
29 ceph::BitVector<2u> *object_map = nullptr;
30 static RefreshRequest *s_instance;
31 static RefreshRequest *create(MockTestImageCtx &image_ctx, ceph::shared_mutex*,
32 ceph::BitVector<2u> *object_map,
33 uint64_t snap_id, Context *on_finish) {
34 ceph_assert(s_instance != nullptr);
35 s_instance->on_finish = on_finish;
36 s_instance->object_map = object_map;
37 return s_instance;
38 }
39
40 MOCK_METHOD0(send, void());
41
42 RefreshRequest() {
43 s_instance = this;
44 }
45 };
46
47 template <>
48 struct UnlockRequest<MockTestImageCtx> {
49 Context *on_finish = nullptr;
50 static UnlockRequest *s_instance;
51 static UnlockRequest *create(MockTestImageCtx &image_ctx,
52 Context *on_finish) {
53 ceph_assert(s_instance != nullptr);
54 s_instance->on_finish = on_finish;
55 return s_instance;
56 }
57
58 MOCK_METHOD0(send, void());
59 UnlockRequest() {
60 s_instance = this;
61 }
62 };
63
64 template <>
65 struct UpdateRequest<MockTestImageCtx> {
66 Context *on_finish = nullptr;
67 static UpdateRequest *s_instance;
68 static UpdateRequest *create(MockTestImageCtx &image_ctx, ceph::shared_mutex*,
69 ceph::BitVector<2u> *object_map,
70 uint64_t snap_id,
71 uint64_t start_object_no, uint64_t end_object_no,
72 uint8_t new_state,
73 const boost::optional<uint8_t> &current_state,
74 const ZTracer::Trace &parent_trace,
75 bool ignore_enoent, Context *on_finish) {
76 ceph_assert(s_instance != nullptr);
77 s_instance->on_finish = on_finish;
78 s_instance->construct(snap_id, start_object_no, end_object_no, new_state,
79 current_state, ignore_enoent);
80 return s_instance;
81 }
82
83 MOCK_METHOD6(construct, void(uint64_t snap_id, uint64_t start_object_no,
84 uint64_t end_object_no, uint8_t new_state,
85 const boost::optional<uint8_t> &current_state,
86 bool ignore_enoent));
87 MOCK_METHOD0(send, void());
88 UpdateRequest() {
89 s_instance = this;
90 }
91 };
92
93 RefreshRequest<MockTestImageCtx> *RefreshRequest<MockTestImageCtx>::s_instance = nullptr;
94 UnlockRequest<MockTestImageCtx> *UnlockRequest<MockTestImageCtx>::s_instance = nullptr;
95 UpdateRequest<MockTestImageCtx> *UpdateRequest<MockTestImageCtx>::s_instance = nullptr;
96
97 } // namespace object_map
98 } // namespace librbd
99
100 #include "librbd/ObjectMap.cc"
101
102 namespace librbd {
103
104 using testing::_;
105 using testing::InSequence;
106 using testing::Invoke;
107
108 class TestMockObjectMap : public TestMockFixture {
109 public:
110 typedef ObjectMap<MockTestImageCtx> MockObjectMap;
111 typedef object_map::RefreshRequest<MockTestImageCtx> MockRefreshRequest;
112 typedef object_map::UnlockRequest<MockTestImageCtx> MockUnlockRequest;
113 typedef object_map::UpdateRequest<MockTestImageCtx> MockUpdateRequest;
114
115 void expect_refresh(MockTestImageCtx &mock_image_ctx,
116 MockRefreshRequest &mock_refresh_request,
117 const ceph::BitVector<2u> &object_map, int r) {
118 EXPECT_CALL(mock_refresh_request, send())
119 .WillOnce(Invoke([&mock_image_ctx, &mock_refresh_request, &object_map, r]() {
120 *mock_refresh_request.object_map = object_map;
121 mock_image_ctx.image_ctx->op_work_queue->queue(mock_refresh_request.on_finish, r);
122 }));
123 }
124
125 void expect_unlock(MockTestImageCtx &mock_image_ctx,
126 MockUnlockRequest &mock_unlock_request, int r) {
127 EXPECT_CALL(mock_unlock_request, send())
128 .WillOnce(Invoke([&mock_image_ctx, &mock_unlock_request, r]() {
129 mock_image_ctx.image_ctx->op_work_queue->queue(mock_unlock_request.on_finish, r);
130 }));
131 }
132
133 void expect_update(MockTestImageCtx &mock_image_ctx,
134 MockUpdateRequest &mock_update_request,
135 uint64_t snap_id, uint64_t start_object_no,
136 uint64_t end_object_no, uint8_t new_state,
137 const boost::optional<uint8_t> &current_state,
138 bool ignore_enoent, Context **on_finish) {
139 EXPECT_CALL(mock_update_request, construct(snap_id, start_object_no,
140 end_object_no, new_state,
141 current_state, ignore_enoent))
142 .Times(1);
143 EXPECT_CALL(mock_update_request, send())
144 .WillOnce(Invoke([&mock_update_request, on_finish]() {
145 *on_finish = mock_update_request.on_finish;
146 }));
147 }
148
149 };
150
151 TEST_F(TestMockObjectMap, NonDetainedUpdate) {
152 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
153
154 librbd::ImageCtx *ictx;
155 ASSERT_EQ(0, open_image(m_image_name, &ictx));
156
157 MockTestImageCtx mock_image_ctx(*ictx);
158
159 InSequence seq;
160 ceph::BitVector<2u> object_map;
161 object_map.resize(4);
162 MockRefreshRequest mock_refresh_request;
163 expect_refresh(mock_image_ctx, mock_refresh_request, object_map, 0);
164
165 MockUpdateRequest mock_update_request;
166 Context *finish_update_1;
167 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
168 0, 1, 1, {}, false, &finish_update_1);
169 Context *finish_update_2;
170 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
171 1, 2, 1, {}, false, &finish_update_2);
172
173 MockUnlockRequest mock_unlock_request;
174 expect_unlock(mock_image_ctx, mock_unlock_request, 0);
175
176 MockObjectMap *mock_object_map = new MockObjectMap(mock_image_ctx, CEPH_NOSNAP);
177 BOOST_SCOPE_EXIT(&mock_object_map) {
178 mock_object_map->put();
179 } BOOST_SCOPE_EXIT_END
180
181 C_SaferCond open_ctx;
182 mock_object_map->open(&open_ctx);
183 ASSERT_EQ(0, open_ctx.wait());
184
185 C_SaferCond update_ctx1;
186 C_SaferCond update_ctx2;
187 {
188 std::shared_lock image_locker{mock_image_ctx.image_lock};
189 mock_object_map->aio_update(CEPH_NOSNAP, 0, 1, {}, {}, false, &update_ctx1);
190 mock_object_map->aio_update(CEPH_NOSNAP, 1, 1, {}, {}, false, &update_ctx2);
191 }
192
193 finish_update_2->complete(0);
194 ASSERT_EQ(0, update_ctx2.wait());
195
196 finish_update_1->complete(0);
197 ASSERT_EQ(0, update_ctx1.wait());
198
199 C_SaferCond close_ctx;
200 mock_object_map->close(&close_ctx);
201 ASSERT_EQ(0, close_ctx.wait());
202 }
203
204 TEST_F(TestMockObjectMap, DetainedUpdate) {
205 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
206
207 librbd::ImageCtx *ictx;
208 ASSERT_EQ(0, open_image(m_image_name, &ictx));
209
210 MockTestImageCtx mock_image_ctx(*ictx);
211
212 InSequence seq;
213 ceph::BitVector<2u> object_map;
214 object_map.resize(4);
215 MockRefreshRequest mock_refresh_request;
216 expect_refresh(mock_image_ctx, mock_refresh_request, object_map, 0);
217
218 MockUpdateRequest mock_update_request;
219 Context *finish_update_1;
220 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
221 1, 4, 1, {}, false, &finish_update_1);
222 Context *finish_update_2 = nullptr;
223 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
224 1, 3, 1, {}, false, &finish_update_2);
225 Context *finish_update_3 = nullptr;
226 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
227 2, 3, 1, {}, false, &finish_update_3);
228 Context *finish_update_4 = nullptr;
229 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
230 0, 2, 1, {}, false, &finish_update_4);
231
232 MockUnlockRequest mock_unlock_request;
233 expect_unlock(mock_image_ctx, mock_unlock_request, 0);
234
235 MockObjectMap *mock_object_map = new MockObjectMap(mock_image_ctx, CEPH_NOSNAP);
236 BOOST_SCOPE_EXIT(&mock_object_map) {
237 mock_object_map->put();
238 } BOOST_SCOPE_EXIT_END
239
240 C_SaferCond open_ctx;
241 mock_object_map->open(&open_ctx);
242 ASSERT_EQ(0, open_ctx.wait());
243
244 C_SaferCond update_ctx1;
245 C_SaferCond update_ctx2;
246 C_SaferCond update_ctx3;
247 C_SaferCond update_ctx4;
248 {
249 std::shared_lock image_locker{mock_image_ctx.image_lock};
250 mock_object_map->aio_update(CEPH_NOSNAP, 1, 4, 1, {}, {}, false,
251 &update_ctx1);
252 mock_object_map->aio_update(CEPH_NOSNAP, 1, 3, 1, {}, {}, false,
253 &update_ctx2);
254 mock_object_map->aio_update(CEPH_NOSNAP, 2, 3, 1, {}, {}, false,
255 &update_ctx3);
256 mock_object_map->aio_update(CEPH_NOSNAP, 0, 2, 1, {}, {}, false,
257 &update_ctx4);
258 }
259
260 // updates 2, 3, and 4 are blocked on update 1
261 ASSERT_EQ(nullptr, finish_update_2);
262 finish_update_1->complete(0);
263 ASSERT_EQ(0, update_ctx1.wait());
264
265 // updates 3 and 4 are blocked on update 2
266 ASSERT_NE(nullptr, finish_update_2);
267 ASSERT_EQ(nullptr, finish_update_3);
268 ASSERT_EQ(nullptr, finish_update_4);
269 finish_update_2->complete(0);
270 ASSERT_EQ(0, update_ctx2.wait());
271
272 ASSERT_NE(nullptr, finish_update_3);
273 ASSERT_NE(nullptr, finish_update_4);
274 finish_update_3->complete(0);
275 finish_update_4->complete(0);
276 ASSERT_EQ(0, update_ctx3.wait());
277 ASSERT_EQ(0, update_ctx4.wait());
278
279 C_SaferCond close_ctx;
280 mock_object_map->close(&close_ctx);
281 ASSERT_EQ(0, close_ctx.wait());
282 }
283
284 } // namespace librbd
285