1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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/ObjectDispatchSpec.h"
11 #include "librbd/io/Utils.h"
16 struct MockTestImageCtx
;
18 struct MockTestJournal
: public MockJournal
{
19 MOCK_METHOD4(append_write_event
, uint64_t(uint64_t, size_t,
20 const bufferlist
&, bool));
21 MOCK_METHOD5(append_io_event_mock
, uint64_t(const journal::EventEntry
&,
22 uint64_t, size_t, bool, int));
23 uint64_t append_io_event(journal::EventEntry
&&event_entry
,
24 uint64_t offset
, size_t length
,
25 bool flush_entry
, int filter_ret_val
) {
26 // googlemock doesn't support move semantics
27 return append_io_event_mock(event_entry
, offset
, length
, flush_entry
,
31 MOCK_METHOD2(commit_io_event
, void(uint64_t, int));
34 struct MockTestImageCtx
: public MockImageCtx
{
35 MockTestImageCtx(ImageCtx
&image_ctx
) : MockImageCtx(image_ctx
) {
38 MockTestJournal
* journal
;
41 } // anonymous namespace
45 inline ImageCtx
*get_image_ctx(MockTestImageCtx
*image_ctx
) {
46 return image_ctx
->image_ctx
;
52 #include "librbd/io/ImageRequest.cc"
61 MockTestImageCtx
*image_ctx
, uint64_t offset
, uint64_t length
,
62 uint64_t buffer_offset
,
63 striper::LightweightObjectExtents
*object_extents
) {
64 Striper::file_to_extents(image_ctx
->cct
, &image_ctx
->layout
, offset
, length
,
65 0, buffer_offset
, object_extents
);
68 template <> void extent_to_file(
69 MockTestImageCtx
* image_ctx
, uint64_t object_no
, uint64_t offset
,
71 std::vector
<std::pair
<uint64_t, uint64_t> >& extents
) {
72 Striper::extent_to_file(image_ctx
->cct
, &image_ctx
->layout
, object_no
,
73 offset
, length
, extents
);
79 using ::testing::InSequence
;
80 using ::testing::Invoke
;
81 using ::testing::Return
;
82 using ::testing::WithArg
;
83 using ::testing::WithoutArgs
;
84 using ::testing::Exactly
;
86 struct TestMockIoImageRequest
: public TestMockFixture
{
87 typedef ImageRequest
<librbd::MockTestImageCtx
> MockImageRequest
;
88 typedef ImageReadRequest
<librbd::MockTestImageCtx
> MockImageReadRequest
;
89 typedef ImageWriteRequest
<librbd::MockTestImageCtx
> MockImageWriteRequest
;
90 typedef ImageDiscardRequest
<librbd::MockTestImageCtx
> MockImageDiscardRequest
;
91 typedef ImageFlushRequest
<librbd::MockTestImageCtx
> MockImageFlushRequest
;
92 typedef ImageWriteSameRequest
<librbd::MockTestImageCtx
> MockImageWriteSameRequest
;
93 typedef ImageCompareAndWriteRequest
<librbd::MockTestImageCtx
> MockImageCompareAndWriteRequest
;
94 typedef ImageListSnapsRequest
<librbd::MockTestImageCtx
> MockImageListSnapsRequest
;
96 void expect_is_journal_appending(MockTestJournal
&mock_journal
, bool appending
) {
97 EXPECT_CALL(mock_journal
, is_journal_appending())
98 .WillOnce(Return(appending
));
101 void expect_get_modify_timestamp(MockTestImageCtx
&mock_image_ctx
,
104 mock_image_ctx
.mtime_update_interval
= 5;
105 EXPECT_CALL(mock_image_ctx
, get_modify_timestamp())
106 .WillOnce(Return(ceph_clock_now() - utime_t(10,0)));
108 mock_image_ctx
.mtime_update_interval
= 600;
109 EXPECT_CALL(mock_image_ctx
, get_modify_timestamp())
110 .WillOnce(Return(ceph_clock_now()));
114 void expect_object_discard_request(MockTestImageCtx
&mock_image_ctx
,
115 uint64_t object_no
, uint64_t offset
,
116 uint32_t length
, int r
) {
117 EXPECT_CALL(*mock_image_ctx
.io_object_dispatcher
, send(_
))
118 .WillOnce(Invoke([&mock_image_ctx
, object_no
, offset
, length
, r
]
119 (ObjectDispatchSpec
* spec
) {
120 auto* discard_spec
= boost::get
<ObjectDispatchSpec::DiscardRequest
>(&spec
->request
);
121 ASSERT_TRUE(discard_spec
!= nullptr);
122 ASSERT_EQ(object_no
, discard_spec
->object_no
);
123 ASSERT_EQ(offset
, discard_spec
->object_off
);
124 ASSERT_EQ(length
, discard_spec
->object_len
);
126 spec
->dispatch_result
= io::DISPATCH_RESULT_COMPLETE
;
127 mock_image_ctx
.image_ctx
->op_work_queue
->queue(&spec
->dispatcher_ctx
, r
);
131 void expect_object_request_send(MockTestImageCtx
&mock_image_ctx
,
133 EXPECT_CALL(*mock_image_ctx
.io_object_dispatcher
, send(_
))
134 .WillOnce(Invoke([&mock_image_ctx
, r
](ObjectDispatchSpec
* spec
) {
135 spec
->dispatch_result
= io::DISPATCH_RESULT_COMPLETE
;
136 mock_image_ctx
.image_ctx
->op_work_queue
->queue(&spec
->dispatcher_ctx
, r
);
140 void expect_object_list_snaps_request(MockTestImageCtx
&mock_image_ctx
,
142 const SnapshotDelta
& snap_delta
,
144 EXPECT_CALL(*mock_image_ctx
.io_object_dispatcher
, send(_
))
146 Invoke([&mock_image_ctx
, object_no
, snap_delta
, r
]
147 (ObjectDispatchSpec
* spec
) {
148 auto request
= boost::get
<
149 librbd::io::ObjectDispatchSpec::ListSnapsRequest
>(
151 ASSERT_TRUE(request
!= nullptr);
152 ASSERT_EQ(object_no
, request
->object_no
);
154 *request
->snapshot_delta
= snap_delta
;
155 spec
->dispatch_result
= io::DISPATCH_RESULT_COMPLETE
;
156 mock_image_ctx
.image_ctx
->op_work_queue
->queue(&spec
->dispatcher_ctx
, r
);
161 TEST_F(TestMockIoImageRequest
, AioWriteModifyTimestamp
) {
164 librbd::ImageCtx
*ictx
;
165 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
167 MockTestImageCtx
mock_image_ctx(*ictx
);
168 MockTestJournal mock_journal
;
169 mock_image_ctx
.journal
= &mock_journal
;
171 mock_image_ctx
.mtime_update_interval
= 5;
173 utime_t dummy
= ceph_clock_now();
174 dummy
-= utime_t(10,0);
176 EXPECT_CALL(mock_image_ctx
, get_modify_timestamp())
178 .WillOnce(Return(dummy
))
179 .WillOnce(Return(dummy
))
180 .WillOnce(Return(dummy
+ utime_t(10,0)));
182 EXPECT_CALL(mock_image_ctx
, set_modify_timestamp(_
))
186 expect_is_journal_appending(mock_journal
, false);
187 expect_object_request_send(mock_image_ctx
, 0);
189 C_SaferCond aio_comp_ctx_1
, aio_comp_ctx_2
;
190 AioCompletion
*aio_comp_1
= AioCompletion::create_and_start(
191 &aio_comp_ctx_1
, ictx
, AIO_TYPE_WRITE
);
193 AioCompletion
*aio_comp_2
= AioCompletion::create_and_start(
194 &aio_comp_ctx_2
, ictx
, AIO_TYPE_WRITE
);
198 MockImageWriteRequest
mock_aio_image_write_1(
199 mock_image_ctx
, aio_comp_1
, {{0, 1}}, std::move(bl
),
200 mock_image_ctx
.get_data_io_context(), 0, {});
202 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
203 mock_aio_image_write_1
.send();
205 ASSERT_EQ(0, aio_comp_ctx_1
.wait());
207 expect_is_journal_appending(mock_journal
, false);
208 expect_object_request_send(mock_image_ctx
, 0);
211 MockImageWriteRequest
mock_aio_image_write_2(
212 mock_image_ctx
, aio_comp_2
, {{0, 1}}, std::move(bl
),
213 mock_image_ctx
.get_data_io_context(), 0, {});
215 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
216 mock_aio_image_write_2
.send();
218 ASSERT_EQ(0, aio_comp_ctx_2
.wait());
221 TEST_F(TestMockIoImageRequest
, AioReadAccessTimestamp
) {
224 librbd::ImageCtx
*ictx
;
225 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
227 MockTestImageCtx
mock_image_ctx(*ictx
);
228 MockTestJournal mock_journal
;
229 mock_image_ctx
.journal
= &mock_journal
;
231 mock_image_ctx
.atime_update_interval
= 5;
233 utime_t dummy
= ceph_clock_now();
234 dummy
-= utime_t(10,0);
236 EXPECT_CALL(mock_image_ctx
, get_access_timestamp())
238 .WillOnce(Return(dummy
))
239 .WillOnce(Return(dummy
))
240 .WillOnce(Return(dummy
+ utime_t(10,0)));
242 EXPECT_CALL(mock_image_ctx
, set_access_timestamp(_
))
246 expect_object_request_send(mock_image_ctx
, 0);
248 C_SaferCond aio_comp_ctx_1
, aio_comp_ctx_2
;
249 AioCompletion
*aio_comp_1
= AioCompletion::create_and_start(
250 &aio_comp_ctx_1
, ictx
, AIO_TYPE_READ
);
254 MockImageReadRequest
mock_aio_image_read_1(
255 mock_image_ctx
, aio_comp_1
, {{0, 1}}, std::move(rr
),
256 mock_image_ctx
.get_data_io_context(), 0, 0, {});
258 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
259 mock_aio_image_read_1
.send();
261 ASSERT_EQ(1, aio_comp_ctx_1
.wait());
263 AioCompletion
*aio_comp_2
= AioCompletion::create_and_start(
264 &aio_comp_ctx_2
, ictx
, AIO_TYPE_READ
);
265 expect_object_request_send(mock_image_ctx
, 0);
267 MockImageReadRequest
mock_aio_image_read_2(
268 mock_image_ctx
, aio_comp_2
, {{0, 1}}, std::move(rr
),
269 mock_image_ctx
.get_data_io_context(), 0, 0, {});
271 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
272 mock_aio_image_read_2
.send();
274 ASSERT_EQ(1, aio_comp_ctx_2
.wait());
277 TEST_F(TestMockIoImageRequest
, PartialDiscard
) {
278 librbd::ImageCtx
*ictx
;
279 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
280 ictx
->discard_granularity_bytes
= 0;
282 MockTestImageCtx
mock_image_ctx(*ictx
);
283 mock_image_ctx
.journal
= nullptr;
286 expect_get_modify_timestamp(mock_image_ctx
, false);
287 expect_object_discard_request(mock_image_ctx
, 0, 16, 63, 0);
288 expect_object_discard_request(mock_image_ctx
, 0, 84, 100, 0);
290 C_SaferCond aio_comp_ctx
;
291 AioCompletion
*aio_comp
= AioCompletion::create_and_start(
292 &aio_comp_ctx
, ictx
, AIO_TYPE_DISCARD
);
293 MockImageDiscardRequest
mock_aio_image_discard(
294 mock_image_ctx
, aio_comp
, {{16, 63}, {84, 100}},
295 ictx
->discard_granularity_bytes
, mock_image_ctx
.get_data_io_context(), {});
297 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
298 mock_aio_image_discard
.send();
300 ASSERT_EQ(0, aio_comp_ctx
.wait());
303 TEST_F(TestMockIoImageRequest
, TailDiscard
) {
304 librbd::ImageCtx
*ictx
;
305 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
306 ASSERT_EQ(0, resize(ictx
, ictx
->layout
.object_size
));
307 ictx
->discard_granularity_bytes
= 2 * ictx
->layout
.object_size
;
309 MockTestImageCtx
mock_image_ctx(*ictx
);
310 mock_image_ctx
.journal
= nullptr;
313 expect_get_modify_timestamp(mock_image_ctx
, false);
314 expect_object_discard_request(
315 mock_image_ctx
, 0, ictx
->layout
.object_size
- 1024, 1024, 0);
317 C_SaferCond aio_comp_ctx
;
318 AioCompletion
*aio_comp
= AioCompletion::create_and_start(
319 &aio_comp_ctx
, ictx
, AIO_TYPE_DISCARD
);
320 MockImageDiscardRequest
mock_aio_image_discard(
321 mock_image_ctx
, aio_comp
,
322 {{ictx
->layout
.object_size
- 1024, 1024}},
323 ictx
->discard_granularity_bytes
, mock_image_ctx
.get_data_io_context(), {});
325 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
326 mock_aio_image_discard
.send();
328 ASSERT_EQ(0, aio_comp_ctx
.wait());
331 TEST_F(TestMockIoImageRequest
, DiscardGranularity
) {
332 librbd::ImageCtx
*ictx
;
333 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
334 ASSERT_EQ(0, resize(ictx
, ictx
->layout
.object_size
));
335 ictx
->discard_granularity_bytes
= 32;
337 MockTestImageCtx
mock_image_ctx(*ictx
);
338 mock_image_ctx
.journal
= nullptr;
341 expect_get_modify_timestamp(mock_image_ctx
, false);
342 expect_object_discard_request(mock_image_ctx
, 0, 32, 32, 0);
343 expect_object_discard_request(mock_image_ctx
, 0, 96, 64, 0);
344 expect_object_discard_request(
345 mock_image_ctx
, 0, ictx
->layout
.object_size
- 32, 32, 0);
347 C_SaferCond aio_comp_ctx
;
348 AioCompletion
*aio_comp
= AioCompletion::create_and_start(
349 &aio_comp_ctx
, ictx
, AIO_TYPE_DISCARD
);
350 MockImageDiscardRequest
mock_aio_image_discard(
351 mock_image_ctx
, aio_comp
,
352 {{16, 63}, {96, 31}, {84, 100}, {ictx
->layout
.object_size
- 33, 33}},
353 ictx
->discard_granularity_bytes
, mock_image_ctx
.get_data_io_context(), {});
355 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
356 mock_aio_image_discard
.send();
358 ASSERT_EQ(0, aio_comp_ctx
.wait());
361 TEST_F(TestMockIoImageRequest
, AioWriteJournalAppendDisabled
) {
362 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
364 librbd::ImageCtx
*ictx
;
365 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
367 MockTestImageCtx
mock_image_ctx(*ictx
);
368 MockTestJournal mock_journal
;
369 mock_image_ctx
.journal
= &mock_journal
;
372 expect_get_modify_timestamp(mock_image_ctx
, false);
373 expect_is_journal_appending(mock_journal
, false);
374 expect_object_request_send(mock_image_ctx
, 0);
376 C_SaferCond aio_comp_ctx
;
377 AioCompletion
*aio_comp
= AioCompletion::create_and_start(
378 &aio_comp_ctx
, ictx
, AIO_TYPE_WRITE
);
382 MockImageWriteRequest
mock_aio_image_write(
383 mock_image_ctx
, aio_comp
, {{0, 1}}, std::move(bl
),
384 mock_image_ctx
.get_data_io_context(), 0, {});
386 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
387 mock_aio_image_write
.send();
389 ASSERT_EQ(0, aio_comp_ctx
.wait());
392 TEST_F(TestMockIoImageRequest
, AioDiscardJournalAppendDisabled
) {
393 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
395 librbd::ImageCtx
*ictx
;
396 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
397 ictx
->discard_granularity_bytes
= 0;
399 MockTestImageCtx
mock_image_ctx(*ictx
);
400 MockTestJournal mock_journal
;
401 mock_image_ctx
.journal
= &mock_journal
;
404 expect_get_modify_timestamp(mock_image_ctx
, false);
405 expect_is_journal_appending(mock_journal
, false);
406 expect_object_request_send(mock_image_ctx
, 0);
408 C_SaferCond aio_comp_ctx
;
409 AioCompletion
*aio_comp
= AioCompletion::create_and_start(
410 &aio_comp_ctx
, ictx
, AIO_TYPE_DISCARD
);
411 MockImageDiscardRequest
mock_aio_image_discard(
412 mock_image_ctx
, aio_comp
, {{0, 1}}, ictx
->discard_granularity_bytes
,
413 mock_image_ctx
.get_data_io_context(), {});
415 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
416 mock_aio_image_discard
.send();
418 ASSERT_EQ(0, aio_comp_ctx
.wait());
421 TEST_F(TestMockIoImageRequest
, AioFlushJournalAppendDisabled
) {
422 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
424 librbd::ImageCtx
*ictx
;
425 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
427 MockTestImageCtx
mock_image_ctx(*ictx
);
428 MockTestJournal mock_journal
;
429 mock_image_ctx
.journal
= &mock_journal
;
431 expect_op_work_queue(mock_image_ctx
);
434 expect_is_journal_appending(mock_journal
, false);
435 expect_object_request_send(mock_image_ctx
, 0);
437 C_SaferCond aio_comp_ctx
;
438 AioCompletion
*aio_comp
= AioCompletion::create_and_start(
439 &aio_comp_ctx
, ictx
, AIO_TYPE_FLUSH
);
440 MockImageFlushRequest
mock_aio_image_flush(mock_image_ctx
, aio_comp
,
441 FLUSH_SOURCE_USER
, {});
443 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
444 mock_aio_image_flush
.send();
446 ASSERT_EQ(0, aio_comp_ctx
.wait());
449 TEST_F(TestMockIoImageRequest
, AioWriteSameJournalAppendDisabled
) {
450 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
452 librbd::ImageCtx
*ictx
;
453 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
455 MockTestImageCtx
mock_image_ctx(*ictx
);
456 MockTestJournal mock_journal
;
457 mock_image_ctx
.journal
= &mock_journal
;
460 expect_get_modify_timestamp(mock_image_ctx
, false);
461 expect_is_journal_appending(mock_journal
, false);
462 expect_object_request_send(mock_image_ctx
, 0);
464 C_SaferCond aio_comp_ctx
;
465 AioCompletion
*aio_comp
= AioCompletion::create_and_start(
466 &aio_comp_ctx
, ictx
, AIO_TYPE_WRITESAME
);
470 MockImageWriteSameRequest
mock_aio_image_writesame(
471 mock_image_ctx
, aio_comp
, {{0, 1}}, std::move(bl
),
472 mock_image_ctx
.get_data_io_context(), 0, {});
474 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
475 mock_aio_image_writesame
.send();
477 ASSERT_EQ(0, aio_comp_ctx
.wait());
480 TEST_F(TestMockIoImageRequest
, AioCompareAndWriteJournalAppendDisabled
) {
481 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
483 librbd::ImageCtx
*ictx
;
484 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
486 MockTestImageCtx
mock_image_ctx(*ictx
);
487 MockTestJournal mock_journal
;
488 mock_image_ctx
.journal
= &mock_journal
;
491 expect_get_modify_timestamp(mock_image_ctx
, false);
492 expect_is_journal_appending(mock_journal
, false);
493 expect_object_request_send(mock_image_ctx
, 0);
495 C_SaferCond aio_comp_ctx
;
496 AioCompletion
*aio_comp
= AioCompletion::create_and_start(
497 &aio_comp_ctx
, ictx
, AIO_TYPE_COMPARE_AND_WRITE
);
502 write_bl
.append("1");
503 uint64_t mismatch_offset
;
504 MockImageCompareAndWriteRequest
mock_aio_image_write(
505 mock_image_ctx
, aio_comp
, {{0, 1}}, std::move(cmp_bl
), std::move(write_bl
),
506 &mismatch_offset
, mock_image_ctx
.get_data_io_context(), 0, {});
508 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
509 mock_aio_image_write
.send();
511 ASSERT_EQ(0, aio_comp_ctx
.wait());
514 TEST_F(TestMockIoImageRequest
, ListSnaps
) {
515 librbd::ImageCtx
*ictx
;
516 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
518 MockTestImageCtx
mock_image_ctx(*ictx
);
519 mock_image_ctx
.layout
.object_size
= 16384;
520 mock_image_ctx
.layout
.stripe_unit
= 4096;
521 mock_image_ctx
.layout
.stripe_count
= 2;
525 SnapshotDelta object_snapshot_delta
;
526 object_snapshot_delta
[{5,6}].insert(
527 0, 1024, {SPARSE_EXTENT_STATE_DATA
, 1024});
528 object_snapshot_delta
[{5,5}].insert(
529 4096, 4096, {SPARSE_EXTENT_STATE_ZEROED
, 4096});
530 expect_object_list_snaps_request(mock_image_ctx
, 0, object_snapshot_delta
, 0);
531 object_snapshot_delta
= {};
532 object_snapshot_delta
[{5,6}].insert(
533 1024, 3072, {SPARSE_EXTENT_STATE_DATA
, 3072});
534 object_snapshot_delta
[{5,5}].insert(
535 2048, 2048, {SPARSE_EXTENT_STATE_ZEROED
, 2048});
536 expect_object_list_snaps_request(mock_image_ctx
, 1, object_snapshot_delta
, 0);
538 SnapshotDelta snapshot_delta
;
539 C_SaferCond aio_comp_ctx
;
540 AioCompletion
*aio_comp
= AioCompletion::create_and_start(
541 &aio_comp_ctx
, ictx
, AIO_TYPE_GENERIC
);
542 MockImageListSnapsRequest
mock_image_list_snaps_request(
543 mock_image_ctx
, aio_comp
, {{0, 16384}, {16384, 16384}}, {0, CEPH_NOSNAP
},
544 0, &snapshot_delta
, {});
546 std::shared_lock owner_locker
{mock_image_ctx
.owner_lock
};
547 mock_image_list_snaps_request
.send();
549 ASSERT_EQ(0, aio_comp_ctx
.wait());
551 SnapshotDelta expected_snapshot_delta
;
552 expected_snapshot_delta
[{5,6}].insert(
553 0, 1024, {SPARSE_EXTENT_STATE_DATA
, 1024});
554 expected_snapshot_delta
[{5,6}].insert(
555 5120, 3072, {SPARSE_EXTENT_STATE_DATA
, 3072});
556 expected_snapshot_delta
[{5,5}].insert(
557 6144, 6144, {SPARSE_EXTENT_STATE_ZEROED
, 6144});
558 ASSERT_EQ(expected_snapshot_delta
, snapshot_delta
);
562 } // namespace librbd