]>
Commit | Line | Data |
---|---|---|
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 "test/librbd/mock/MockJournal.h" | |
8 | #include "test/librbd/mock/cache/MockImageCache.h" | |
9 | #include "librbd/io/ImageRequest.h" | |
10 | #include "librbd/io/ObjectRequest.h" | |
11 | ||
12 | namespace librbd { | |
13 | namespace { | |
14 | ||
15 | struct MockTestImageCtx : public MockImageCtx { | |
16 | MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { | |
17 | } | |
18 | }; | |
19 | ||
20 | } // anonymous namespace | |
21 | ||
22 | namespace util { | |
23 | ||
24 | inline ImageCtx *get_image_ctx(MockTestImageCtx *image_ctx) { | |
25 | return image_ctx->image_ctx; | |
26 | } | |
27 | ||
28 | } // namespace util | |
29 | ||
30 | namespace io { | |
31 | ||
32 | template <> | |
33 | struct ObjectRequest<librbd::MockTestImageCtx> : public ObjectRequestHandle { | |
34 | static ObjectRequest* s_instance; | |
35 | Context *on_finish = nullptr; | |
36 | ||
37 | static ObjectRequest* create_remove(librbd::MockTestImageCtx *ictx, | |
38 | const std::string &oid, | |
39 | uint64_t object_no, | |
40 | const ::SnapContext &snapc, | |
41 | const ZTracer::Trace &parent_trace, | |
42 | Context *completion) { | |
43 | assert(s_instance != nullptr); | |
44 | s_instance->on_finish = completion; | |
45 | return s_instance; | |
46 | } | |
47 | ||
48 | static ObjectRequest* create_truncate(librbd::MockTestImageCtx *ictx, | |
49 | const std::string &oid, | |
50 | uint64_t object_no, | |
51 | uint64_t object_off, | |
52 | const ::SnapContext &snapc, | |
53 | const ZTracer::Trace &parent_trace, | |
54 | Context *completion) { | |
55 | assert(s_instance != nullptr); | |
56 | s_instance->on_finish = completion; | |
57 | return s_instance; | |
58 | } | |
59 | ||
60 | static ObjectRequest* create_write(librbd::MockTestImageCtx *ictx, | |
61 | const std::string &oid, | |
62 | uint64_t object_no, | |
63 | uint64_t object_off, | |
64 | const ceph::bufferlist &data, | |
65 | const ::SnapContext &snapc, int op_flags, | |
66 | const ZTracer::Trace &parent_trace, | |
67 | Context *completion) { | |
68 | assert(s_instance != nullptr); | |
69 | s_instance->on_finish = completion; | |
70 | return s_instance; | |
71 | } | |
72 | ||
73 | static ObjectRequest* create_zero(librbd::MockTestImageCtx *ictx, | |
74 | const std::string &oid, | |
75 | uint64_t object_no, uint64_t object_off, | |
76 | uint64_t object_len, | |
77 | const ::SnapContext &snapc, | |
78 | const ZTracer::Trace &parent_trace, | |
79 | Context *completion) { | |
80 | assert(s_instance != nullptr); | |
81 | s_instance->on_finish = completion; | |
82 | return s_instance; | |
83 | } | |
84 | ||
85 | static ObjectRequest* create_writesame(librbd::MockTestImageCtx *ictx, | |
86 | const std::string &oid, | |
87 | uint64_t object_no, | |
88 | uint64_t object_off, | |
89 | uint64_t object_len, | |
90 | const ceph::bufferlist &data, | |
91 | const ::SnapContext &snapc, | |
92 | int op_flags, | |
93 | const ZTracer::Trace &parent_trace, | |
94 | Context *completion) { | |
95 | assert(s_instance != nullptr); | |
96 | s_instance->on_finish = completion; | |
97 | return s_instance; | |
98 | } | |
99 | ||
100 | static ObjectRequest* create_compare_and_write(librbd::MockTestImageCtx *ictx, | |
101 | const std::string &oid, | |
102 | uint64_t object_no, | |
103 | uint64_t object_off, | |
104 | const ceph::bufferlist &cmp_data, | |
105 | const ceph::bufferlist &write_data, | |
106 | const ::SnapContext &snapc, | |
107 | uint64_t *mismatch_offset, | |
108 | int op_flags, | |
109 | const ZTracer::Trace &parent_trace, | |
110 | Context *completion) { | |
111 | assert(s_instance != nullptr); | |
112 | s_instance->on_finish = completion; | |
113 | return s_instance; | |
114 | } | |
115 | ||
116 | ObjectRequest() { | |
117 | assert(s_instance == nullptr); | |
118 | s_instance = this; | |
119 | } | |
120 | ~ObjectRequest() override { | |
121 | s_instance = nullptr; | |
122 | } | |
123 | ||
124 | MOCK_METHOD1(complete, void(int)); | |
125 | MOCK_METHOD0(send, void()); | |
126 | }; | |
127 | ||
128 | template <> | |
129 | struct ObjectReadRequest<librbd::MockTestImageCtx> : public ObjectRequest<librbd::MockTestImageCtx> { | |
130 | typedef std::vector<std::pair<uint64_t, uint64_t> > Extents; | |
131 | typedef std::map<uint64_t, uint64_t> ExtentMap; | |
132 | ||
133 | static ObjectReadRequest* s_instance; | |
134 | ||
135 | static ObjectReadRequest* create(librbd::MockTestImageCtx *ictx, | |
136 | const std::string &oid, | |
137 | uint64_t objectno, uint64_t offset, | |
138 | uint64_t len, Extents &buffer_extents, | |
139 | librados::snap_t snap_id, bool sparse, | |
140 | int op_flags, | |
141 | const ZTracer::Trace &parent_trace, | |
142 | Context *completion) { | |
143 | assert(s_instance != nullptr); | |
144 | s_instance->on_finish = completion; | |
145 | return s_instance; | |
146 | } | |
147 | ||
148 | ObjectReadRequest() { | |
149 | assert(s_instance == nullptr); | |
150 | s_instance = this; | |
151 | } | |
152 | ~ObjectReadRequest() override { | |
153 | s_instance = nullptr; | |
154 | } | |
155 | ||
156 | MOCK_CONST_METHOD0(get_offset, uint64_t()); | |
157 | MOCK_CONST_METHOD0(get_length, uint64_t()); | |
158 | MOCK_METHOD0(data, ceph::bufferlist &()); | |
159 | MOCK_CONST_METHOD0(get_buffer_extents, const Extents &()); | |
160 | MOCK_METHOD0(get_extent_map, ExtentMap &()); | |
161 | ||
162 | }; | |
163 | ||
164 | ObjectRequest<librbd::MockTestImageCtx>* ObjectRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
165 | ObjectReadRequest<librbd::MockTestImageCtx>* ObjectReadRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
166 | ||
167 | } // namespace io | |
168 | } // namespace librbd | |
169 | ||
170 | #include "librbd/io/ImageRequest.cc" | |
171 | ||
172 | namespace librbd { | |
173 | namespace io { | |
174 | ||
175 | using ::testing::_; | |
176 | using ::testing::InSequence; | |
177 | using ::testing::Invoke; | |
178 | using ::testing::Return; | |
179 | using ::testing::WithArg; | |
180 | ||
181 | struct TestMockIoImageRequest : public TestMockFixture { | |
182 | typedef ImageRequest<librbd::MockTestImageCtx> MockImageRequest; | |
183 | typedef ImageWriteRequest<librbd::MockTestImageCtx> MockImageWriteRequest; | |
184 | typedef ImageDiscardRequest<librbd::MockTestImageCtx> MockImageDiscardRequest; | |
185 | typedef ImageFlushRequest<librbd::MockTestImageCtx> MockImageFlushRequest; | |
186 | typedef ImageWriteSameRequest<librbd::MockTestImageCtx> MockImageWriteSameRequest; | |
187 | typedef ImageCompareAndWriteRequest<librbd::MockTestImageCtx> MockImageCompareAndWriteRequest; | |
188 | typedef ObjectRequest<librbd::MockTestImageCtx> MockObjectRequest; | |
189 | typedef ObjectReadRequest<librbd::MockTestImageCtx> MockObjectReadRequest; | |
190 | ||
191 | void expect_is_journal_appending(MockJournal &mock_journal, bool appending) { | |
192 | EXPECT_CALL(mock_journal, is_journal_appending()) | |
193 | .WillOnce(Return(appending)); | |
194 | } | |
195 | ||
196 | void expect_write_to_cache(MockImageCtx &mock_image_ctx, | |
197 | const object_t &object, | |
198 | uint64_t offset, uint64_t length, | |
199 | uint64_t journal_tid, int r) { | |
200 | EXPECT_CALL(mock_image_ctx, write_to_cache(object, _, length, offset, _, _, | |
201 | journal_tid, _)) | |
202 | .WillOnce(WithArg<4>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))); | |
203 | } | |
204 | ||
205 | void expect_object_request_send(MockImageCtx &mock_image_ctx, | |
206 | MockObjectRequest &mock_object_request, | |
207 | int r) { | |
208 | EXPECT_CALL(mock_object_request, send()) | |
209 | .WillOnce(Invoke([&mock_image_ctx, &mock_object_request, r]() { | |
210 | mock_image_ctx.image_ctx->op_work_queue->queue( | |
211 | mock_object_request.on_finish, r); | |
212 | })); | |
213 | } | |
214 | ||
215 | void expect_user_flushed(MockImageCtx &mock_image_ctx) { | |
216 | EXPECT_CALL(mock_image_ctx, user_flushed()); | |
217 | } | |
218 | ||
219 | void expect_flush(MockImageCtx &mock_image_ctx, int r) { | |
220 | EXPECT_CALL(mock_image_ctx, flush(_)) | |
221 | .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); | |
222 | } | |
223 | }; | |
224 | ||
225 | TEST_F(TestMockIoImageRequest, AioWriteJournalAppendDisabled) { | |
226 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
227 | ||
228 | librbd::ImageCtx *ictx; | |
229 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
230 | ||
231 | MockObjectRequest mock_aio_object_request; | |
232 | MockTestImageCtx mock_image_ctx(*ictx); | |
233 | MockJournal mock_journal; | |
234 | mock_image_ctx.journal = &mock_journal; | |
235 | ||
236 | InSequence seq; | |
237 | expect_is_journal_appending(mock_journal, false); | |
238 | if (mock_image_ctx.image_ctx->cache) { | |
239 | expect_write_to_cache(mock_image_ctx, ictx->get_object_name(0), | |
240 | 0, 1, 0, 0); | |
241 | } else { | |
242 | expect_object_request_send(mock_image_ctx, mock_aio_object_request, 0); | |
243 | } | |
244 | ||
245 | C_SaferCond aio_comp_ctx; | |
246 | AioCompletion *aio_comp = AioCompletion::create_and_start( | |
247 | &aio_comp_ctx, ictx, AIO_TYPE_WRITE); | |
248 | ||
249 | bufferlist bl; | |
250 | bl.append("1"); | |
251 | MockImageWriteRequest mock_aio_image_write(mock_image_ctx, aio_comp, | |
252 | {{0, 1}}, std::move(bl), 0, {}); | |
253 | { | |
254 | RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); | |
255 | mock_aio_image_write.send(); | |
256 | } | |
257 | ASSERT_EQ(0, aio_comp_ctx.wait()); | |
258 | } | |
259 | ||
260 | TEST_F(TestMockIoImageRequest, AioDiscardJournalAppendDisabled) { | |
261 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
262 | ||
263 | librbd::ImageCtx *ictx; | |
264 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
265 | ||
266 | MockObjectRequest mock_aio_object_request; | |
267 | MockTestImageCtx mock_image_ctx(*ictx); | |
268 | MockJournal mock_journal; | |
269 | mock_image_ctx.journal = &mock_journal; | |
270 | ||
271 | InSequence seq; | |
272 | expect_is_journal_appending(mock_journal, false); | |
273 | if (!ictx->skip_partial_discard) { | |
274 | expect_object_request_send(mock_image_ctx, mock_aio_object_request, 0); | |
275 | } | |
276 | ||
277 | C_SaferCond aio_comp_ctx; | |
278 | AioCompletion *aio_comp = AioCompletion::create_and_start( | |
279 | &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); | |
280 | MockImageDiscardRequest mock_aio_image_discard(mock_image_ctx, aio_comp, | |
281 | 0, 1, | |
282 | ictx->skip_partial_discard, | |
283 | {}); | |
284 | { | |
285 | RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); | |
286 | mock_aio_image_discard.send(); | |
287 | } | |
288 | ASSERT_EQ(0, aio_comp_ctx.wait()); | |
289 | } | |
290 | ||
291 | TEST_F(TestMockIoImageRequest, AioFlushJournalAppendDisabled) { | |
292 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
293 | ||
294 | librbd::ImageCtx *ictx; | |
295 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
296 | ||
297 | MockTestImageCtx mock_image_ctx(*ictx); | |
298 | MockJournal mock_journal; | |
299 | mock_image_ctx.journal = &mock_journal; | |
300 | ||
301 | InSequence seq; | |
302 | expect_user_flushed(mock_image_ctx); | |
303 | expect_is_journal_appending(mock_journal, false); | |
304 | expect_flush(mock_image_ctx, 0); | |
305 | ||
306 | C_SaferCond aio_comp_ctx; | |
307 | AioCompletion *aio_comp = AioCompletion::create_and_start( | |
308 | &aio_comp_ctx, ictx, AIO_TYPE_FLUSH); | |
309 | MockImageFlushRequest mock_aio_image_flush(mock_image_ctx, aio_comp, {}); | |
310 | { | |
311 | RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); | |
312 | mock_aio_image_flush.send(); | |
313 | } | |
314 | ASSERT_EQ(0, aio_comp_ctx.wait()); | |
315 | } | |
316 | ||
317 | TEST_F(TestMockIoImageRequest, AioWriteSameJournalAppendDisabled) { | |
318 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
319 | ||
320 | librbd::ImageCtx *ictx; | |
321 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
322 | ||
323 | MockObjectRequest mock_aio_object_request; | |
324 | MockTestImageCtx mock_image_ctx(*ictx); | |
325 | MockJournal mock_journal; | |
326 | mock_image_ctx.journal = &mock_journal; | |
327 | ||
328 | InSequence seq; | |
329 | expect_is_journal_appending(mock_journal, false); | |
330 | if (mock_image_ctx.image_ctx->cache) { | |
331 | expect_write_to_cache(mock_image_ctx, ictx->get_object_name(0), | |
332 | 0, 1, 0, 0); | |
333 | } else { | |
334 | expect_object_request_send(mock_image_ctx, mock_aio_object_request, 0); | |
335 | } | |
336 | ||
337 | ||
338 | C_SaferCond aio_comp_ctx; | |
339 | AioCompletion *aio_comp = AioCompletion::create_and_start( | |
340 | &aio_comp_ctx, ictx, AIO_TYPE_WRITESAME); | |
341 | ||
342 | bufferlist bl; | |
343 | bl.append("1"); | |
344 | MockImageWriteSameRequest mock_aio_image_writesame(mock_image_ctx, aio_comp, | |
345 | 0, 1, std::move(bl), 0, | |
346 | {}); | |
347 | { | |
348 | RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); | |
349 | mock_aio_image_writesame.send(); | |
350 | } | |
351 | ASSERT_EQ(0, aio_comp_ctx.wait()); | |
352 | } | |
353 | ||
354 | TEST_F(TestMockIoImageRequest, AioCompareAndWriteJournalAppendDisabled) { | |
355 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
356 | ||
357 | librbd::ImageCtx *ictx; | |
358 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
359 | ||
360 | MockObjectRequest mock_aio_object_request; | |
361 | MockTestImageCtx mock_image_ctx(*ictx); | |
362 | MockJournal mock_journal; | |
363 | mock_image_ctx.journal = &mock_journal; | |
364 | ||
365 | InSequence seq; | |
366 | expect_is_journal_appending(mock_journal, false); | |
367 | expect_object_request_send(mock_image_ctx, mock_aio_object_request, 0); | |
368 | ||
369 | C_SaferCond aio_comp_ctx; | |
370 | AioCompletion *aio_comp = AioCompletion::create_and_start( | |
371 | &aio_comp_ctx, ictx, AIO_TYPE_COMPARE_AND_WRITE); | |
372 | ||
373 | bufferlist cmp_bl; | |
374 | cmp_bl.append("1"); | |
375 | bufferlist write_bl; | |
376 | write_bl.append("1"); | |
377 | uint64_t mismatch_offset; | |
378 | MockImageCompareAndWriteRequest mock_aio_image_write(mock_image_ctx, aio_comp, | |
379 | {{0, 1}}, std::move(cmp_bl), | |
380 | std::move(write_bl), | |
381 | &mismatch_offset, | |
382 | 0, {}); | |
383 | { | |
384 | RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); | |
385 | mock_aio_image_write.send(); | |
386 | } | |
387 | ASSERT_EQ(0, aio_comp_ctx.wait()); | |
388 | } | |
389 | ||
390 | } // namespace io | |
391 | } // namespace librbd |