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