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