]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/operation/test_mock_TrimRequest.cc
update sources to v12.2.3
[ceph.git] / ceph / src / test / librbd / operation / test_mock_TrimRequest.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/librados_test_stub/MockTestMemIoCtxImpl.h"
8 #include "common/bit_vector.hpp"
9 #include "librbd/AsyncRequest.h"
10 #include "librbd/internal.h"
11 #include "librbd/ObjectMap.h"
12 #include "librbd/Utils.h"
13 #include "librbd/io/ObjectRequest.h"
14 #include "librbd/operation/TrimRequest.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17
18 namespace librbd {
19 namespace {
20
21 struct MockTestImageCtx : public MockImageCtx {
22 MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
23 }
24 };
25
26 } // anonymous namespace
27
28 template<>
29 struct AsyncRequest<librbd::MockTestImageCtx> {
30 librbd::MockTestImageCtx& m_image_ctx;
31 Context *on_finish;
32
33 AsyncRequest(librbd::MockTestImageCtx& image_ctx, Context* on_finish)
34 : m_image_ctx(image_ctx), on_finish(on_finish) {
35 }
36 virtual ~AsyncRequest() {
37 }
38
39 Context* create_callback_context() {
40 return util::create_context_callback(this);
41 }
42
43 Context* create_async_callback_context() {
44 return util::create_context_callback<AsyncRequest,
45 &AsyncRequest::async_complete>(this);
46 }
47
48 void complete(int r) {
49 if (should_complete(r)) {
50 async_complete(r);
51 }
52 }
53
54 void async_complete(int r) {
55 on_finish->complete(r);
56 }
57
58 bool is_canceled() const {
59 return false;
60 }
61
62 virtual void send() = 0;
63 virtual bool should_complete(int r) = 0;
64 };
65
66 namespace io {
67
68 template <>
69 struct ObjectRequest<librbd::MockTestImageCtx> : public ObjectRequestHandle {
70 static ObjectRequest* s_instance;
71 Context *on_finish = nullptr;
72
73 static ObjectRequest* create_discard(librbd::MockTestImageCtx *ictx,
74 const std::string &oid,
75 uint64_t object_no,
76 uint64_t object_off,
77 uint64_t object_len,
78 const ::SnapContext &snapc,
79 bool disable_remove_on_clone,
80 bool update_object_map,
81 const ZTracer::Trace &parent_trace,
82 Context *completion) {
83 assert(s_instance != nullptr);
84 EXPECT_FALSE(disable_remove_on_clone);
85 s_instance->on_finish = completion;
86 s_instance->construct(object_off, object_len, update_object_map);
87 return s_instance;
88 }
89
90 ObjectRequest() {
91 s_instance = this;
92 }
93
94 MOCK_METHOD3(construct, void(uint64_t, uint64_t, bool));
95 MOCK_METHOD0(send, void());
96 MOCK_METHOD1(fail, void(int));
97 };
98
99 ObjectRequest<librbd::MockTestImageCtx>* ObjectRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
100
101 } // namespace io
102 } // namespace librbd
103
104 // template definitions
105 #include "librbd/AsyncObjectThrottle.cc"
106 #include "librbd/operation/TrimRequest.cc"
107
108 namespace librbd {
109 namespace operation {
110
111 using ::testing::_;
112 using ::testing::DoAll;
113 using ::testing::InSequence;
114 using ::testing::Invoke;
115 using ::testing::Return;
116 using ::testing::StrEq;
117 using ::testing::WithArg;
118
119 class TestMockOperationTrimRequest : public TestMockFixture {
120 public:
121 typedef TrimRequest<MockTestImageCtx> MockTrimRequest;
122 typedef librbd::io::ObjectRequest<MockTestImageCtx> MockObjectRequest;
123
124 int create_snapshot(const char *snap_name) {
125 librbd::ImageCtx *ictx;
126 int r = open_image(m_image_name, &ictx);
127 if (r < 0) {
128 return r;
129 }
130
131 r = snap_create(*ictx, snap_name);
132 if (r < 0) {
133 return r;
134 }
135
136 r = snap_protect(*ictx, snap_name);
137 if (r < 0) {
138 return r;
139 }
140 close_image(ictx);
141 return 0;
142 }
143
144 void expect_is_lock_owner(MockTestImageCtx &mock_image_ctx) {
145 if (mock_image_ctx.exclusive_lock != nullptr) {
146 EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
147 .WillRepeatedly(Return(true));
148 }
149 }
150
151 void expect_object_map_update(MockTestImageCtx &mock_image_ctx,
152 uint64_t start_object, uint64_t end_object,
153 uint8_t state, uint8_t current_state,
154 bool updated, int ret_val) {
155 if (mock_image_ctx.object_map != nullptr) {
156 EXPECT_CALL(*mock_image_ctx.object_map,
157 aio_update(CEPH_NOSNAP, start_object, end_object, state,
158 boost::optional<uint8_t>(current_state), _, _))
159 .WillOnce(WithArg<6>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
160 if (updated) {
161 mock_image_ctx.op_work_queue->queue(ctx, ret_val);
162 }
163 return updated;
164 })));
165 }
166 }
167
168 void expect_get_parent_overlap(MockTestImageCtx &mock_image_ctx,
169 uint64_t overlap) {
170 EXPECT_CALL(mock_image_ctx, get_parent_overlap(CEPH_NOSNAP, _))
171 .WillOnce(WithArg<1>(Invoke([overlap](uint64_t *o) {
172 *o = overlap;
173 return 0;
174 })));
175 }
176
177 void expect_object_may_exist(MockTestImageCtx &mock_image_ctx,
178 uint64_t object_no, bool exists) {
179 if (mock_image_ctx.object_map != nullptr) {
180 EXPECT_CALL(*mock_image_ctx.object_map, object_may_exist(object_no))
181 .WillOnce(Return(exists));
182 }
183 }
184
185 void expect_get_object_name(MockTestImageCtx &mock_image_ctx,
186 uint64_t object_no, const std::string& oid) {
187 EXPECT_CALL(mock_image_ctx, get_object_name(object_no))
188 .WillOnce(Return(oid));
189 }
190
191 void expect_aio_remove(MockTestImageCtx &mock_image_ctx,
192 const std::string& oid, int ret_val) {
193 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx), remove(oid, _))
194 .WillOnce(Return(ret_val));
195 }
196
197 void expect_object_discard(MockImageCtx &mock_image_ctx,
198 MockObjectRequest &mock_object_request,
199 uint64_t offset, uint64_t length,
200 bool update_object_map, int ret_val) {
201 EXPECT_CALL(mock_object_request, construct(offset, length,
202 update_object_map));
203 EXPECT_CALL(mock_object_request, send())
204 .WillOnce(Invoke([&mock_image_ctx, &mock_object_request, ret_val]() {
205 mock_image_ctx.op_work_queue->queue(mock_object_request.on_finish, ret_val);
206 }));
207 }
208 };
209
210 TEST_F(TestMockOperationTrimRequest, SuccessRemove) {
211 librbd::ImageCtx *ictx;
212 ASSERT_EQ(0, open_image(m_image_name, &ictx));
213
214 MockTestImageCtx mock_image_ctx(*ictx);
215 MockExclusiveLock mock_exclusive_lock;
216 MockJournal mock_journal;
217 MockObjectMap mock_object_map;
218 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
219 mock_object_map);
220 expect_op_work_queue(mock_image_ctx);
221 expect_is_lock_owner(mock_image_ctx);
222
223 InSequence seq;
224 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
225 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
226
227 // pre
228 expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_PENDING, OBJECT_EXISTS,
229 true, 0);
230
231 // copy-up
232 expect_get_parent_overlap(mock_image_ctx, 0);
233
234 // remove
235 expect_object_may_exist(mock_image_ctx, 0, true);
236 expect_get_object_name(mock_image_ctx, 0, "object0");
237 expect_aio_remove(mock_image_ctx, "object0", 0);
238
239 // post
240 expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_NONEXISTENT,
241 OBJECT_PENDING, true, 0);
242
243 C_SaferCond cond_ctx;
244 librbd::NoOpProgressContext progress_ctx;
245 MockTrimRequest *req = new MockTrimRequest(
246 mock_image_ctx, &cond_ctx, m_image_size, 0, progress_ctx);
247 {
248 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
249 req->send();
250 }
251 ASSERT_EQ(0, cond_ctx.wait());
252 }
253
254 TEST_F(TestMockOperationTrimRequest, SuccessCopyUp) {
255 REQUIRE_FEATURE(RBD_FEATURE_LAYERING)
256 ASSERT_EQ(0, create_snapshot("snap1"));
257
258 int order = 22;
259 uint64_t features;
260 ASSERT_TRUE(::get_features(&features));
261 std::string clone_name = get_temp_image_name();
262 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
263 clone_name.c_str(), features, &order, 0, 0));
264
265 librbd::ImageCtx *ictx;
266 ASSERT_EQ(0, open_image(clone_name, &ictx));
267 ASSERT_EQ(0, snap_create(*ictx, "snap"));
268
269 MockTestImageCtx mock_image_ctx(*ictx);
270 MockExclusiveLock mock_exclusive_lock;
271 MockJournal mock_journal;
272 MockObjectMap mock_object_map;
273 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
274 mock_object_map);
275 expect_op_work_queue(mock_image_ctx);
276 expect_is_lock_owner(mock_image_ctx);
277
278 InSequence seq;
279 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
280 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
281
282 // pre
283 expect_object_map_update(mock_image_ctx, 0, 2, OBJECT_PENDING, OBJECT_EXISTS,
284 true, 0);
285
286 // copy-up
287 expect_get_parent_overlap(mock_image_ctx, ictx->get_object_size());
288 expect_get_object_name(mock_image_ctx, 0, "object0");
289
290 MockObjectRequest mock_object_request;
291 expect_object_discard(mock_image_ctx, mock_object_request, 0,
292 ictx->get_object_size(), false, 0);
293
294 // remove
295 expect_object_may_exist(mock_image_ctx, 1, true);
296 expect_get_object_name(mock_image_ctx, 1, "object1");
297 expect_aio_remove(mock_image_ctx, "object1", 0);
298
299 // post
300 expect_object_map_update(mock_image_ctx, 0, 2, OBJECT_NONEXISTENT,
301 OBJECT_PENDING, true, 0);
302
303 C_SaferCond cond_ctx;
304 librbd::NoOpProgressContext progress_ctx;
305 MockTrimRequest *req = new MockTrimRequest(
306 mock_image_ctx, &cond_ctx, 2 * ictx->get_object_size(), 0, progress_ctx);
307 {
308 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
309 req->send();
310 }
311 ASSERT_EQ(0, cond_ctx.wait());
312 }
313
314 TEST_F(TestMockOperationTrimRequest, SuccessBoundary) {
315 librbd::ImageCtx *ictx;
316 ASSERT_EQ(0, open_image(m_image_name, &ictx));
317
318 MockTestImageCtx mock_image_ctx(*ictx);
319 MockExclusiveLock mock_exclusive_lock;
320 MockJournal mock_journal;
321 MockObjectMap mock_object_map;
322 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
323 mock_object_map);
324 expect_op_work_queue(mock_image_ctx);
325 expect_is_lock_owner(mock_image_ctx);
326
327 InSequence seq;
328 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
329 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
330
331 // boundary
332 MockObjectRequest mock_object_request;
333 expect_object_discard(mock_image_ctx, mock_object_request, 1,
334 ictx->get_object_size() - 1, true, 0);
335
336 C_SaferCond cond_ctx;
337 librbd::NoOpProgressContext progress_ctx;
338 MockTrimRequest *req = new MockTrimRequest(
339 mock_image_ctx, &cond_ctx, ictx->get_object_size(), 1, progress_ctx);
340 {
341 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
342 req->send();
343 }
344 ASSERT_EQ(0, cond_ctx.wait());
345 }
346
347 TEST_F(TestMockOperationTrimRequest, SuccessNoOp) {
348 librbd::ImageCtx *ictx;
349 ASSERT_EQ(0, open_image(m_image_name, &ictx));
350
351 MockTestImageCtx mock_image_ctx(*ictx);
352 MockExclusiveLock mock_exclusive_lock;
353 MockJournal mock_journal;
354 MockObjectMap mock_object_map;
355 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
356 mock_object_map);
357 }
358
359 TEST_F(TestMockOperationTrimRequest, RemoveError) {
360 librbd::ImageCtx *ictx;
361 ASSERT_EQ(0, open_image(m_image_name, &ictx));
362
363 MockTestImageCtx mock_image_ctx(*ictx);
364 MockExclusiveLock mock_exclusive_lock;
365 MockJournal mock_journal;
366 MockObjectMap mock_object_map;
367 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
368 mock_object_map);
369 expect_op_work_queue(mock_image_ctx);
370 expect_is_lock_owner(mock_image_ctx);
371
372 InSequence seq;
373 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
374 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
375
376 // pre
377 expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_PENDING, OBJECT_EXISTS,
378 false, 0);
379
380 // copy-up
381 expect_get_parent_overlap(mock_image_ctx, 0);
382
383 // remove
384 expect_object_may_exist(mock_image_ctx, 0, true);
385 expect_get_object_name(mock_image_ctx, 0, "object0");
386 expect_aio_remove(mock_image_ctx, "object0", -EPERM);
387
388 C_SaferCond cond_ctx;
389 librbd::NoOpProgressContext progress_ctx;
390 MockTrimRequest *req = new MockTrimRequest(
391 mock_image_ctx, &cond_ctx, m_image_size, 0, progress_ctx);
392 {
393 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
394 req->send();
395 }
396 ASSERT_EQ(-EPERM, cond_ctx.wait());
397 }
398
399 TEST_F(TestMockOperationTrimRequest, CopyUpError) {
400 REQUIRE_FEATURE(RBD_FEATURE_LAYERING)
401 ASSERT_EQ(0, create_snapshot("snap1"));
402
403 int order = 22;
404 uint64_t features;
405 ASSERT_TRUE(::get_features(&features));
406 std::string clone_name = get_temp_image_name();
407 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
408 clone_name.c_str(), features, &order, 0, 0));
409
410 librbd::ImageCtx *ictx;
411 ASSERT_EQ(0, open_image(clone_name, &ictx));
412 ASSERT_EQ(0, snap_create(*ictx, "snap"));
413
414 MockTestImageCtx mock_image_ctx(*ictx);
415 MockExclusiveLock mock_exclusive_lock;
416 MockJournal mock_journal;
417 MockObjectMap mock_object_map;
418 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
419 mock_object_map);
420 expect_op_work_queue(mock_image_ctx);
421 expect_is_lock_owner(mock_image_ctx);
422
423 InSequence seq;
424 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
425 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
426
427 // pre
428 expect_object_map_update(mock_image_ctx, 0, 2, OBJECT_PENDING, OBJECT_EXISTS,
429 false, 0);
430
431 // copy-up
432 expect_get_parent_overlap(mock_image_ctx, ictx->get_object_size());
433 expect_get_object_name(mock_image_ctx, 0, "object0");
434
435 MockObjectRequest mock_object_request;
436 expect_object_discard(mock_image_ctx, mock_object_request, 0,
437 ictx->get_object_size(), false, -EINVAL);
438
439 C_SaferCond cond_ctx;
440 librbd::NoOpProgressContext progress_ctx;
441 MockTrimRequest *req = new MockTrimRequest(
442 mock_image_ctx, &cond_ctx, 2 * ictx->get_object_size(), 0, progress_ctx);
443 {
444 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
445 req->send();
446 }
447 ASSERT_EQ(-EINVAL, cond_ctx.wait());
448 }
449
450 TEST_F(TestMockOperationTrimRequest, BoundaryError) {
451 librbd::ImageCtx *ictx;
452 ASSERT_EQ(0, open_image(m_image_name, &ictx));
453
454 MockTestImageCtx mock_image_ctx(*ictx);
455 MockExclusiveLock mock_exclusive_lock;
456 MockJournal mock_journal;
457 MockObjectMap mock_object_map;
458 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
459 mock_object_map);
460 expect_op_work_queue(mock_image_ctx);
461 expect_is_lock_owner(mock_image_ctx);
462
463 InSequence seq;
464 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
465 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
466
467 // boundary
468 MockObjectRequest mock_object_request;
469 expect_object_discard(mock_image_ctx, mock_object_request, 1,
470 ictx->get_object_size() - 1, true, -EINVAL);
471
472 C_SaferCond cond_ctx;
473 librbd::NoOpProgressContext progress_ctx;
474 MockTrimRequest *req = new MockTrimRequest(
475 mock_image_ctx, &cond_ctx, ictx->get_object_size(), 1, progress_ctx);
476 {
477 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
478 req->send();
479 }
480 ASSERT_EQ(-EINVAL, cond_ctx.wait());
481 }
482
483 } // namespace operation
484 } // namespace librbd