]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/operation/test_mock_ResizeRequest.cc
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / test / librbd / operation / test_mock_ResizeRequest.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 "test/librbd/mock/io/MockObjectDispatch.h"
8 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
9 #include "common/bit_vector.hpp"
10 #include "librbd/internal.h"
11 #include "librbd/ObjectMap.h"
12 #include "librbd/io/ImageDispatchSpec.h"
13 #include "librbd/operation/ResizeRequest.h"
14 #include "librbd/operation/TrimRequest.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17
18 namespace librbd {
19
20 namespace util {
21
22 inline ImageCtx* get_image_ctx(MockImageCtx* image_ctx) {
23 return image_ctx->image_ctx;
24 }
25
26 } // namespace util
27
28 namespace io {
29
30 template <>
31 struct ImageDispatchSpec<MockImageCtx> {
32 static ImageDispatchSpec* s_instance;
33 AioCompletion *aio_comp = nullptr;
34
35 static ImageDispatchSpec* create_flush_request(
36 MockImageCtx &image_ctx, AioCompletion *aio_comp,
37 FlushSource flush_source, const ZTracer::Trace &parent_trace) {
38 ceph_assert(s_instance != nullptr);
39 s_instance->aio_comp = aio_comp;
40 return s_instance;
41 }
42
43 MOCK_CONST_METHOD0(send, void());
44
45 ImageDispatchSpec() {
46 s_instance = this;
47 }
48 };
49
50 ImageDispatchSpec<MockImageCtx>* ImageDispatchSpec<MockImageCtx>::s_instance = nullptr;
51
52 } // namespace io
53
54 namespace operation {
55
56 template <>
57 class TrimRequest<MockImageCtx> {
58 public:
59 static TrimRequest *s_instance;
60 static TrimRequest *create(MockImageCtx &image_ctx, Context *on_finish,
61 uint64_t original_size, uint64_t new_size,
62 ProgressContext &prog_ctx) {
63 ceph_assert(s_instance != nullptr);
64 s_instance->on_finish = on_finish;
65 return s_instance;
66 }
67
68 Context *on_finish = nullptr;
69
70 TrimRequest() {
71 s_instance = this;
72 }
73
74 MOCK_METHOD0(send, void());
75 };
76
77 TrimRequest<MockImageCtx> *TrimRequest<MockImageCtx>::s_instance = nullptr;
78
79 } // namespace operation
80 } // namespace librbd
81
82 // template definitions
83 #include "librbd/operation/ResizeRequest.cc"
84
85 namespace librbd {
86 namespace operation {
87
88 using ::testing::_;
89 using ::testing::DoAll;
90 using ::testing::Invoke;
91 using ::testing::InSequence;
92 using ::testing::Return;
93 using ::testing::StrEq;
94 using ::testing::WithArg;
95
96 class TestMockOperationResizeRequest : public TestMockFixture {
97 public:
98 typedef ResizeRequest<MockImageCtx> MockResizeRequest;
99 typedef TrimRequest<MockImageCtx> MockTrimRequest;
100 typedef io::ImageDispatchSpec<MockImageCtx> MockIoImageDispatchSpec;
101
102 void expect_block_writes(MockImageCtx &mock_image_ctx, int r) {
103 EXPECT_CALL(*mock_image_ctx.io_work_queue, block_writes(_))
104 .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
105 }
106
107 void expect_unblock_writes(MockImageCtx &mock_image_ctx) {
108 EXPECT_CALL(*mock_image_ctx.io_work_queue, unblock_writes())
109 .Times(1);
110 }
111
112 void expect_is_lock_owner(MockImageCtx &mock_image_ctx) {
113 if (mock_image_ctx.exclusive_lock != nullptr) {
114 EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
115 .WillOnce(Return(true));
116 }
117 }
118
119 void expect_grow_object_map(MockImageCtx &mock_image_ctx) {
120 if (mock_image_ctx.object_map != nullptr) {
121 expect_is_lock_owner(mock_image_ctx);
122 EXPECT_CALL(*mock_image_ctx.object_map, aio_resize(_, _, _))
123 .WillOnce(WithArg<2>(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)));
124 }
125 }
126
127 void expect_shrink_object_map(MockImageCtx &mock_image_ctx) {
128 if (mock_image_ctx.object_map != nullptr) {
129 expect_is_lock_owner(mock_image_ctx);
130 EXPECT_CALL(*mock_image_ctx.object_map, aio_resize(_, _, _))
131 .WillOnce(WithArg<2>(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)));
132 }
133 }
134
135 void expect_update_header(MockImageCtx &mock_image_ctx, int r) {
136 if (mock_image_ctx.old_format) {
137 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
138 write(mock_image_ctx.header_oid, _, _, _, _))
139 .WillOnce(Return(r));
140 } else {
141 expect_is_lock_owner(mock_image_ctx);
142 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
143 exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("set_size"), _, _, _))
144 .WillOnce(Return(r));
145 }
146 }
147
148 void expect_trim(MockImageCtx &mock_image_ctx,
149 MockTrimRequest &mock_trim_request, int r) {
150 EXPECT_CALL(mock_trim_request, send())
151 .WillOnce(FinishRequest(&mock_trim_request, r, &mock_image_ctx));
152 }
153
154 void expect_flush_cache(MockImageCtx &mock_image_ctx,
155 MockIoImageDispatchSpec& mock_io_image_dispatch_spec,
156 int r) {
157 EXPECT_CALL(mock_io_image_dispatch_spec, send())
158 .WillOnce(Invoke([&mock_image_ctx, &mock_io_image_dispatch_spec, r]() {
159 auto aio_comp = mock_io_image_dispatch_spec.s_instance->aio_comp;
160 auto ctx = new FunctionContext([aio_comp](int r) {
161 aio_comp->get();
162 aio_comp->fail(r);
163 });
164 mock_image_ctx.image_ctx->op_work_queue->queue(ctx, r);
165 }));
166 }
167
168 void expect_invalidate_cache(MockImageCtx &mock_image_ctx,
169 int r) {
170 EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, invalidate_cache(_))
171 .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
172 expect_op_work_queue(mock_image_ctx);
173 }
174
175 void expect_resize_object_map(MockImageCtx &mock_image_ctx,
176 uint64_t new_size) {
177 EXPECT_CALL(*mock_image_ctx.object_map, aio_resize(new_size, _, _))
178 .WillOnce(WithArg<2>(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)));
179 }
180
181 int when_resize(MockImageCtx &mock_image_ctx, uint64_t new_size,
182 bool allow_shrink, uint64_t journal_op_tid,
183 bool disable_journal) {
184 C_SaferCond cond_ctx;
185 librbd::NoOpProgressContext prog_ctx;
186 MockResizeRequest *req = new MockResizeRequest(
187 mock_image_ctx, &cond_ctx, new_size, allow_shrink, prog_ctx,
188 journal_op_tid, disable_journal);
189 {
190 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
191 req->send();
192 }
193 return cond_ctx.wait();
194 }
195 };
196
197 TEST_F(TestMockOperationResizeRequest, NoOpSuccess) {
198 librbd::ImageCtx *ictx;
199 ASSERT_EQ(0, open_image(m_image_name, &ictx));
200
201 MockImageCtx mock_image_ctx(*ictx);
202 MockExclusiveLock mock_exclusive_lock;
203 MockJournal mock_journal;
204 MockObjectMap mock_object_map;
205 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
206 mock_object_map);
207
208 InSequence seq;
209 expect_block_writes(mock_image_ctx, 0);
210 expect_append_op_event(mock_image_ctx, true, 0);
211 expect_unblock_writes(mock_image_ctx);
212 expect_commit_op_event(mock_image_ctx, 0);
213 ASSERT_EQ(0, when_resize(mock_image_ctx, ictx->size, true, 0, false));
214 }
215
216 TEST_F(TestMockOperationResizeRequest, GrowSuccess) {
217 librbd::ImageCtx *ictx;
218 ASSERT_EQ(0, open_image(m_image_name, &ictx));
219
220 MockImageCtx mock_image_ctx(*ictx);
221 MockExclusiveLock mock_exclusive_lock;
222 MockJournal mock_journal;
223 MockObjectMap mock_object_map;
224 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
225 mock_object_map);
226
227 InSequence seq;
228 expect_block_writes(mock_image_ctx, 0);
229 expect_append_op_event(mock_image_ctx, true, 0);
230 expect_grow_object_map(mock_image_ctx);
231 expect_update_header(mock_image_ctx, 0);
232 expect_unblock_writes(mock_image_ctx);
233 expect_commit_op_event(mock_image_ctx, 0);
234 ASSERT_EQ(0, when_resize(mock_image_ctx, ictx->size * 2, true, 0, false));
235 }
236
237 TEST_F(TestMockOperationResizeRequest, ShrinkSuccess) {
238 librbd::ImageCtx *ictx;
239 ASSERT_EQ(0, open_image(m_image_name, &ictx));
240
241 MockImageCtx mock_image_ctx(*ictx);
242 MockExclusiveLock mock_exclusive_lock;
243 MockJournal mock_journal;
244 MockObjectMap mock_object_map;
245 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
246 mock_object_map);
247
248 InSequence seq;
249 expect_block_writes(mock_image_ctx, 0);
250 expect_append_op_event(mock_image_ctx, true, 0);
251 expect_unblock_writes(mock_image_ctx);
252
253 MockTrimRequest mock_trim_request;
254 auto mock_io_image_dispatch_spec = new MockIoImageDispatchSpec();
255 expect_flush_cache(mock_image_ctx, *mock_io_image_dispatch_spec, 0);
256 expect_invalidate_cache(mock_image_ctx, 0);
257 expect_trim(mock_image_ctx, mock_trim_request, 0);
258 expect_block_writes(mock_image_ctx, 0);
259 expect_update_header(mock_image_ctx, 0);
260 expect_shrink_object_map(mock_image_ctx);
261 expect_unblock_writes(mock_image_ctx);
262 expect_commit_op_event(mock_image_ctx, 0);
263 ASSERT_EQ(0, when_resize(mock_image_ctx, ictx->size / 2, true, 0, false));
264 }
265
266 TEST_F(TestMockOperationResizeRequest, ShrinkError) {
267 librbd::ImageCtx *ictx;
268 ASSERT_EQ(0, open_image(m_image_name, &ictx));
269
270 MockImageCtx mock_image_ctx(*ictx);
271 MockExclusiveLock mock_exclusive_lock;
272 MockJournal mock_journal;
273 MockObjectMap mock_object_map;
274 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
275 mock_object_map);
276
277 InSequence seq;
278 expect_block_writes(mock_image_ctx, -EINVAL);
279 expect_unblock_writes(mock_image_ctx);
280 ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, false, 0, false));
281 }
282
283 TEST_F(TestMockOperationResizeRequest, PreBlockWritesError) {
284 librbd::ImageCtx *ictx;
285 ASSERT_EQ(0, open_image(m_image_name, &ictx));
286
287 MockImageCtx mock_image_ctx(*ictx);
288 MockExclusiveLock mock_exclusive_lock;
289 MockJournal mock_journal;
290 MockObjectMap mock_object_map;
291 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
292 mock_object_map);
293
294 InSequence seq;
295 expect_block_writes(mock_image_ctx, -EINVAL);
296 expect_unblock_writes(mock_image_ctx);
297 ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size, true, 0, false));
298 }
299
300 TEST_F(TestMockOperationResizeRequest, TrimError) {
301 librbd::ImageCtx *ictx;
302 ASSERT_EQ(0, open_image(m_image_name, &ictx));
303
304 MockImageCtx mock_image_ctx(*ictx);
305 MockExclusiveLock mock_exclusive_lock;
306 MockJournal mock_journal;
307 MockObjectMap mock_object_map;
308 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
309 mock_object_map);
310
311 InSequence seq;
312 expect_block_writes(mock_image_ctx, 0);
313 expect_append_op_event(mock_image_ctx, true, 0);
314 expect_unblock_writes(mock_image_ctx);
315
316 MockTrimRequest mock_trim_request;
317 auto mock_io_image_dispatch_spec = new MockIoImageDispatchSpec();
318 expect_flush_cache(mock_image_ctx, *mock_io_image_dispatch_spec, 0);
319 expect_invalidate_cache(mock_image_ctx, -EBUSY);
320 expect_trim(mock_image_ctx, mock_trim_request, -EINVAL);
321 expect_commit_op_event(mock_image_ctx, -EINVAL);
322 ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, true, 0, false));
323 }
324
325 TEST_F(TestMockOperationResizeRequest, FlushCacheError) {
326 librbd::ImageCtx *ictx;
327 ASSERT_EQ(0, open_image(m_image_name, &ictx));
328 REQUIRE(ictx->cache);
329
330 MockImageCtx mock_image_ctx(*ictx);
331 MockExclusiveLock mock_exclusive_lock;
332 MockJournal mock_journal;
333 MockObjectMap mock_object_map;
334 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
335 mock_object_map);
336
337 InSequence seq;
338 expect_block_writes(mock_image_ctx, 0);
339 expect_append_op_event(mock_image_ctx, true, 0);
340 expect_unblock_writes(mock_image_ctx);
341
342 MockTrimRequest mock_trim_request;
343 auto mock_io_image_dispatch_spec = new MockIoImageDispatchSpec();
344 expect_flush_cache(mock_image_ctx, *mock_io_image_dispatch_spec, -EINVAL);
345 expect_commit_op_event(mock_image_ctx, -EINVAL);
346 ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, true, 0, false));
347 }
348
349 TEST_F(TestMockOperationResizeRequest, InvalidateCacheError) {
350 librbd::ImageCtx *ictx;
351 ASSERT_EQ(0, open_image(m_image_name, &ictx));
352 REQUIRE(ictx->cache);
353
354 MockImageCtx mock_image_ctx(*ictx);
355 MockExclusiveLock mock_exclusive_lock;
356 MockJournal mock_journal;
357 MockObjectMap mock_object_map;
358 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
359 mock_object_map);
360
361 InSequence seq;
362 expect_block_writes(mock_image_ctx, 0);
363 expect_append_op_event(mock_image_ctx, true, 0);
364 expect_unblock_writes(mock_image_ctx);
365
366 MockTrimRequest mock_trim_request;
367 auto mock_io_image_dispatch_spec = new MockIoImageDispatchSpec();
368 expect_flush_cache(mock_image_ctx, *mock_io_image_dispatch_spec, 0);
369 expect_invalidate_cache(mock_image_ctx, -EINVAL);
370 expect_commit_op_event(mock_image_ctx, -EINVAL);
371 ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, true, 0, false));
372 }
373
374 TEST_F(TestMockOperationResizeRequest, PostBlockWritesError) {
375 librbd::ImageCtx *ictx;
376 ASSERT_EQ(0, open_image(m_image_name, &ictx));
377
378 MockImageCtx mock_image_ctx(*ictx);
379 MockExclusiveLock mock_exclusive_lock;
380 MockJournal mock_journal;
381 MockObjectMap mock_object_map;
382 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
383 mock_object_map);
384
385 InSequence seq;
386 expect_block_writes(mock_image_ctx, 0);
387 expect_append_op_event(mock_image_ctx, true, 0);
388 expect_unblock_writes(mock_image_ctx);
389
390 MockTrimRequest mock_trim_request;
391 auto mock_io_image_dispatch_spec = new MockIoImageDispatchSpec();
392 expect_flush_cache(mock_image_ctx, *mock_io_image_dispatch_spec, 0);
393 expect_invalidate_cache(mock_image_ctx, 0);
394 expect_trim(mock_image_ctx, mock_trim_request, 0);
395 expect_block_writes(mock_image_ctx, -EINVAL);
396 expect_unblock_writes(mock_image_ctx);
397 expect_commit_op_event(mock_image_ctx, -EINVAL);
398 ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, true, 0,
399 false));
400 }
401
402 TEST_F(TestMockOperationResizeRequest, UpdateHeaderError) {
403 librbd::ImageCtx *ictx;
404 ASSERT_EQ(0, open_image(m_image_name, &ictx));
405
406 MockImageCtx mock_image_ctx(*ictx);
407 MockExclusiveLock mock_exclusive_lock;
408 MockJournal mock_journal;
409 MockObjectMap mock_object_map;
410 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
411 mock_object_map);
412
413 InSequence seq;
414 expect_block_writes(mock_image_ctx, 0);
415 expect_append_op_event(mock_image_ctx, true, 0);
416 expect_grow_object_map(mock_image_ctx);
417 expect_update_header(mock_image_ctx, -EINVAL);
418 expect_unblock_writes(mock_image_ctx);
419 expect_commit_op_event(mock_image_ctx, -EINVAL);
420 ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size * 2, true, 0, false));
421 }
422
423 TEST_F(TestMockOperationResizeRequest, JournalAppendError) {
424 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
425
426 librbd::ImageCtx *ictx;
427 ASSERT_EQ(0, open_image(m_image_name, &ictx));
428
429 MockImageCtx mock_image_ctx(*ictx);
430 MockExclusiveLock mock_exclusive_lock;
431 MockJournal mock_journal;
432 MockObjectMap mock_object_map;
433 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
434 mock_object_map);
435
436 InSequence seq;
437 expect_block_writes(mock_image_ctx, 0);
438 expect_append_op_event(mock_image_ctx, true, -EINVAL);
439 expect_unblock_writes(mock_image_ctx);
440 ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size, true, 0, false));
441 }
442
443 TEST_F(TestMockOperationResizeRequest, JournalDisabled) {
444 librbd::ImageCtx *ictx;
445 ASSERT_EQ(0, open_image(m_image_name, &ictx));
446
447 MockImageCtx mock_image_ctx(*ictx);
448 MockExclusiveLock mock_exclusive_lock;
449 MockJournal mock_journal;
450 MockObjectMap mock_object_map;
451 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
452 mock_object_map);
453
454 InSequence seq;
455 expect_block_writes(mock_image_ctx, 0);
456 expect_unblock_writes(mock_image_ctx);
457 ASSERT_EQ(0, when_resize(mock_image_ctx, ictx->size, true, 0, true));
458 }
459
460 } // namespace operation
461 } // namespace librbd