1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "librbd/io/ImageRequest.h"
5 #include "librbd/ImageCtx.h"
6 #include "librbd/internal.h"
7 #include "librbd/Journal.h"
8 #include "librbd/Utils.h"
9 #include "librbd/cache/ImageCache.h"
10 #include "librbd/io/AioCompletion.h"
11 #include "librbd/io/ObjectRequest.h"
12 #include "librbd/journal/Types.h"
13 #include "include/rados/librados.hpp"
14 #include "common/WorkQueue.h"
15 #include "osdc/Striper.h"
17 #define dout_subsys ceph_subsys_rbd
19 #define dout_prefix *_dout << "librbd::io::ImageRequest: " << this \
20 << " " << __func__ << ": "
25 using util::get_image_ctx
;
29 template <typename ImageCtxT
= ImageCtx
>
30 struct C_DiscardJournalCommit
: public Context
{
31 typedef std::vector
<ObjectExtent
> ObjectExtents
;
34 AioCompletion
*aio_comp
;
35 ObjectExtents object_extents
;
37 C_DiscardJournalCommit(ImageCtxT
&_image_ctx
, AioCompletion
*_aio_comp
,
38 const ObjectExtents
&_object_extents
, uint64_t tid
)
39 : image_ctx(_image_ctx
), aio_comp(_aio_comp
),
40 object_extents(_object_extents
) {
41 CephContext
*cct
= image_ctx
.cct
;
42 ldout(cct
, 20) << "delaying cache discard until journal tid " << tid
<< " "
45 aio_comp
->add_request();
48 void finish(int r
) override
{
49 CephContext
*cct
= image_ctx
.cct
;
50 ldout(cct
, 20) << "C_DiscardJournalCommit: "
51 << "journal committed: discarding from cache" << dendl
;
53 Mutex::Locker
cache_locker(image_ctx
.cache_lock
);
54 image_ctx
.object_cacher
->discard_set(image_ctx
.object_set
, object_extents
);
55 aio_comp
->complete_request(r
);
59 template <typename ImageCtxT
= ImageCtx
>
60 struct C_FlushJournalCommit
: public Context
{
62 AioCompletion
*aio_comp
;
64 C_FlushJournalCommit(ImageCtxT
&_image_ctx
, AioCompletion
*_aio_comp
,
66 : image_ctx(_image_ctx
), aio_comp(_aio_comp
) {
67 CephContext
*cct
= image_ctx
.cct
;
68 ldout(cct
, 20) << "delaying flush until journal tid " << tid
<< " "
71 aio_comp
->add_request();
74 void finish(int r
) override
{
75 CephContext
*cct
= image_ctx
.cct
;
76 ldout(cct
, 20) << "C_FlushJournalCommit: journal committed" << dendl
;
77 aio_comp
->complete_request(r
);
81 template <typename ImageCtxT
>
82 class C_ObjectCacheRead
: public Context
{
84 explicit C_ObjectCacheRead(ImageCtxT
&ictx
, ObjectReadRequest
<ImageCtxT
> *req
)
85 : m_image_ctx(ictx
), m_req(req
), m_enqueued(false) {}
87 void complete(int r
) override
{
89 // cache_lock creates a lock ordering issue -- so re-execute this context
90 // outside the cache_lock
92 m_image_ctx
.op_work_queue
->queue(this, r
);
99 void finish(int r
) override
{
104 ImageCtxT
&m_image_ctx
;
105 ObjectReadRequest
<ImageCtxT
> *m_req
;
109 } // anonymous namespace
111 template <typename I
>
112 void ImageRequest
<I
>::aio_read(I
*ictx
, AioCompletion
*c
,
113 Extents
&&image_extents
,
114 ReadResult
&&read_result
, int op_flags
,
115 const ZTracer::Trace
&parent_trace
) {
116 ImageReadRequest
<I
> req(*ictx
, c
, std::move(image_extents
),
117 std::move(read_result
), op_flags
, parent_trace
);
121 template <typename I
>
122 void ImageRequest
<I
>::aio_write(I
*ictx
, AioCompletion
*c
,
123 Extents
&&image_extents
, bufferlist
&&bl
,
125 const ZTracer::Trace
&parent_trace
) {
126 ImageWriteRequest
<I
> req(*ictx
, c
, std::move(image_extents
), std::move(bl
),
127 op_flags
, parent_trace
);
131 template <typename I
>
132 void ImageRequest
<I
>::aio_discard(I
*ictx
, AioCompletion
*c
,
133 uint64_t off
, uint64_t len
,
134 bool skip_partial_discard
,
135 const ZTracer::Trace
&parent_trace
) {
136 ImageDiscardRequest
<I
> req(*ictx
, c
, off
, len
, skip_partial_discard
,
141 template <typename I
>
142 void ImageRequest
<I
>::aio_flush(I
*ictx
, AioCompletion
*c
,
143 const ZTracer::Trace
&parent_trace
) {
144 ImageFlushRequest
<I
> req(*ictx
, c
, parent_trace
);
148 template <typename I
>
149 void ImageRequest
<I
>::aio_writesame(I
*ictx
, AioCompletion
*c
,
150 uint64_t off
, uint64_t len
,
151 bufferlist
&&bl
, int op_flags
,
152 const ZTracer::Trace
&parent_trace
) {
153 ImageWriteSameRequest
<I
> req(*ictx
, c
, off
, len
, std::move(bl
), op_flags
,
158 template <typename I
>
159 void ImageRequest
<I
>::send() {
160 I
&image_ctx
= this->m_image_ctx
;
161 assert(m_aio_comp
->is_initialized(get_aio_type()));
162 assert(m_aio_comp
->is_started() ^ (get_aio_type() == AIO_TYPE_FLUSH
));
164 CephContext
*cct
= image_ctx
.cct
;
165 AioCompletion
*aio_comp
= this->m_aio_comp
;
166 ldout(cct
, 20) << get_request_type() << ": ictx=" << &image_ctx
<< ", "
167 << "completion=" << aio_comp
<< dendl
;
170 int r
= clip_request();
176 if (m_bypass_image_cache
|| m_image_ctx
.image_cache
== nullptr) {
179 send_image_cache_request();
183 template <typename I
>
184 int ImageRequest
<I
>::clip_request() {
185 RWLock::RLocker
snap_locker(m_image_ctx
.snap_lock
);
186 for (auto &image_extent
: m_image_extents
) {
187 auto clip_len
= image_extent
.second
;
188 int r
= clip_io(get_image_ctx(&m_image_ctx
), image_extent
.first
, &clip_len
);
193 image_extent
.second
= clip_len
;
198 template <typename I
>
199 void ImageRequest
<I
>::start_op() {
200 m_aio_comp
->start_op();
203 template <typename I
>
204 void ImageRequest
<I
>::fail(int r
) {
205 AioCompletion
*aio_comp
= this->m_aio_comp
;
210 template <typename I
>
211 ImageReadRequest
<I
>::ImageReadRequest(I
&image_ctx
, AioCompletion
*aio_comp
,
212 Extents
&&image_extents
,
213 ReadResult
&&read_result
, int op_flags
,
214 const ZTracer::Trace
&parent_trace
)
215 : ImageRequest
<I
>(image_ctx
, aio_comp
, std::move(image_extents
), "read",
217 m_op_flags(op_flags
) {
218 aio_comp
->read_result
= std::move(read_result
);
221 template <typename I
>
222 int ImageReadRequest
<I
>::clip_request() {
223 int r
= ImageRequest
<I
>::clip_request();
228 uint64_t buffer_length
= 0;
229 auto &image_extents
= this->m_image_extents
;
230 for (auto &image_extent
: image_extents
) {
231 buffer_length
+= image_extent
.second
;
233 this->m_aio_comp
->read_result
.set_clip_length(buffer_length
);
237 template <typename I
>
238 void ImageReadRequest
<I
>::send_request() {
239 I
&image_ctx
= this->m_image_ctx
;
240 CephContext
*cct
= image_ctx
.cct
;
242 auto &image_extents
= this->m_image_extents
;
243 if (image_ctx
.object_cacher
&& image_ctx
.readahead_max_bytes
> 0 &&
244 !(m_op_flags
& LIBRADOS_OP_FLAG_FADVISE_RANDOM
)) {
245 readahead(get_image_ctx(&image_ctx
), image_extents
);
248 AioCompletion
*aio_comp
= this->m_aio_comp
;
249 librados::snap_t snap_id
;
250 map
<object_t
,vector
<ObjectExtent
> > object_extents
;
251 uint64_t buffer_ofs
= 0;
253 // prevent image size from changing between computing clip and recording
254 // pending async operation
255 RWLock::RLocker
snap_locker(image_ctx
.snap_lock
);
256 snap_id
= image_ctx
.snap_id
;
258 // map image extents to object extents
259 for (auto &extent
: image_extents
) {
260 if (extent
.second
== 0) {
264 Striper::file_to_extents(cct
, image_ctx
.format_string
, &image_ctx
.layout
,
265 extent
.first
, extent
.second
, 0, object_extents
,
267 buffer_ofs
+= extent
.second
;
271 // pre-calculate the expected number of read requests
272 uint32_t request_count
= 0;
273 for (auto &object_extent
: object_extents
) {
274 request_count
+= object_extent
.second
.size();
276 aio_comp
->set_request_count(request_count
);
278 // issue the requests
279 for (auto &object_extent
: object_extents
) {
280 for (auto &extent
: object_extent
.second
) {
281 ldout(cct
, 20) << "oid " << extent
.oid
<< " " << extent
.offset
<< "~"
282 << extent
.length
<< " from " << extent
.buffer_extents
285 auto req_comp
= new io::ReadResult::C_SparseReadRequest
<I
>(
287 ObjectReadRequest
<I
> *req
= ObjectReadRequest
<I
>::create(
288 &image_ctx
, extent
.oid
.name
, extent
.objectno
, extent
.offset
,
289 extent
.length
, extent
.buffer_extents
, snap_id
, true, m_op_flags
,
290 this->m_trace
, req_comp
);
291 req_comp
->request
= req
;
293 if (image_ctx
.object_cacher
) {
294 C_ObjectCacheRead
<I
> *cache_comp
= new C_ObjectCacheRead
<I
>(image_ctx
,
296 image_ctx
.aio_read_from_cache(
297 extent
.oid
, extent
.objectno
, &req
->data(), extent
.length
,
298 extent
.offset
, cache_comp
, m_op_flags
,
299 (this->m_trace
.valid() ? &this->m_trace
: nullptr));
308 image_ctx
.perfcounter
->inc(l_librbd_rd
);
309 image_ctx
.perfcounter
->inc(l_librbd_rd_bytes
, buffer_ofs
);
312 template <typename I
>
313 void ImageReadRequest
<I
>::send_image_cache_request() {
314 I
&image_ctx
= this->m_image_ctx
;
315 assert(image_ctx
.image_cache
!= nullptr);
317 AioCompletion
*aio_comp
= this->m_aio_comp
;
318 aio_comp
->set_request_count(1);
320 auto *req_comp
= new io::ReadResult::C_ImageReadRequest(
321 aio_comp
, this->m_image_extents
);
322 image_ctx
.image_cache
->aio_read(std::move(this->m_image_extents
),
323 &req_comp
->bl
, m_op_flags
,
327 template <typename I
>
328 void AbstractImageWriteRequest
<I
>::send_request() {
329 I
&image_ctx
= this->m_image_ctx
;
330 CephContext
*cct
= image_ctx
.cct
;
332 RWLock::RLocker
md_locker(image_ctx
.md_lock
);
334 bool journaling
= false;
336 AioCompletion
*aio_comp
= this->m_aio_comp
;
337 uint64_t clip_len
= 0;
338 ObjectExtents object_extents
;
341 // prevent image size from changing between computing clip and recording
342 // pending async operation
343 RWLock::RLocker
snap_locker(image_ctx
.snap_lock
);
344 if (image_ctx
.snap_id
!= CEPH_NOSNAP
|| image_ctx
.read_only
) {
345 aio_comp
->fail(-EROFS
);
349 for (auto &extent
: this->m_image_extents
) {
350 if (extent
.second
== 0) {
354 // map to object extents
355 Striper::file_to_extents(cct
, image_ctx
.format_string
, &image_ctx
.layout
,
356 extent
.first
, extent
.second
, 0, object_extents
);
357 clip_len
+= extent
.second
;
360 snapc
= image_ctx
.snapc
;
361 journaling
= (image_ctx
.journal
!= nullptr &&
362 image_ctx
.journal
->is_journal_appending());
365 prune_object_extents(object_extents
);
367 if (!object_extents
.empty()) {
368 uint64_t journal_tid
= 0;
369 aio_comp
->set_request_count(
370 object_extents
.size() + get_object_cache_request_count(journaling
));
372 ObjectRequests requests
;
373 send_object_requests(object_extents
, snapc
,
374 (journaling
? &requests
: nullptr));
377 // in-flight ops are flushed prior to closing the journal
378 assert(image_ctx
.journal
!= NULL
);
379 journal_tid
= append_journal_event(requests
, m_synchronous
);
382 if (image_ctx
.object_cacher
!= NULL
) {
383 send_object_cache_requests(object_extents
, journal_tid
);
386 // no IO to perform -- fire completion
390 update_stats(clip_len
);
394 template <typename I
>
395 void AbstractImageWriteRequest
<I
>::send_object_requests(
396 const ObjectExtents
&object_extents
, const ::SnapContext
&snapc
,
397 ObjectRequests
*object_requests
) {
398 I
&image_ctx
= this->m_image_ctx
;
399 CephContext
*cct
= image_ctx
.cct
;
401 AioCompletion
*aio_comp
= this->m_aio_comp
;
402 for (ObjectExtents::const_iterator p
= object_extents
.begin();
403 p
!= object_extents
.end(); ++p
) {
404 ldout(cct
, 20) << "oid " << p
->oid
<< " " << p
->offset
<< "~" << p
->length
405 << " from " << p
->buffer_extents
<< dendl
;
406 C_AioRequest
*req_comp
= new C_AioRequest(aio_comp
);
407 ObjectRequestHandle
*request
= create_object_request(*p
, snapc
,
410 // if journaling, stash the request for later; otherwise send
411 if (request
!= NULL
) {
412 if (object_requests
!= NULL
) {
413 object_requests
->push_back(request
);
421 template <typename I
>
422 void ImageWriteRequest
<I
>::assemble_extent(const ObjectExtent
&object_extent
,
424 for (auto q
= object_extent
.buffer_extents
.begin();
425 q
!= object_extent
.buffer_extents
.end(); ++q
) {
427 sub_bl
.substr_of(m_bl
, q
->first
, q
->second
);
428 bl
->claim_append(sub_bl
);
432 template <typename I
>
433 uint64_t ImageWriteRequest
<I
>::append_journal_event(
434 const ObjectRequests
&requests
, bool synchronous
) {
435 I
&image_ctx
= this->m_image_ctx
;
438 uint64_t buffer_offset
= 0;
439 assert(!this->m_image_extents
.empty());
440 for (auto &extent
: this->m_image_extents
) {
442 sub_bl
.substr_of(m_bl
, buffer_offset
, extent
.second
);
443 buffer_offset
+= extent
.second
;
445 tid
= image_ctx
.journal
->append_write_event(extent
.first
, extent
.second
,
446 sub_bl
, requests
, synchronous
);
449 if (image_ctx
.object_cacher
== NULL
) {
450 AioCompletion
*aio_comp
= this->m_aio_comp
;
451 aio_comp
->associate_journal_event(tid
);
456 template <typename I
>
457 void ImageWriteRequest
<I
>::send_image_cache_request() {
458 I
&image_ctx
= this->m_image_ctx
;
459 assert(image_ctx
.image_cache
!= nullptr);
461 AioCompletion
*aio_comp
= this->m_aio_comp
;
462 aio_comp
->set_request_count(1);
463 C_AioRequest
*req_comp
= new C_AioRequest(aio_comp
);
464 image_ctx
.image_cache
->aio_write(std::move(this->m_image_extents
),
465 std::move(m_bl
), m_op_flags
, req_comp
);
468 template <typename I
>
469 void ImageWriteRequest
<I
>::send_object_cache_requests(
470 const ObjectExtents
&object_extents
, uint64_t journal_tid
) {
471 I
&image_ctx
= this->m_image_ctx
;
472 for (auto p
= object_extents
.begin(); p
!= object_extents
.end(); ++p
) {
473 const ObjectExtent
&object_extent
= *p
;
476 assemble_extent(object_extent
, &bl
);
478 AioCompletion
*aio_comp
= this->m_aio_comp
;
479 C_AioRequest
*req_comp
= new C_AioRequest(aio_comp
);
480 image_ctx
.write_to_cache(
481 object_extent
.oid
, bl
, object_extent
.length
, object_extent
.offset
,
482 req_comp
, m_op_flags
, journal_tid
,
483 (this->m_trace
.valid() ? &this->m_trace
: nullptr));
487 template <typename I
>
488 void ImageWriteRequest
<I
>::send_object_requests(
489 const ObjectExtents
&object_extents
, const ::SnapContext
&snapc
,
490 ObjectRequests
*object_requests
) {
491 I
&image_ctx
= this->m_image_ctx
;
493 // cache handles creating object requests during writeback
494 if (image_ctx
.object_cacher
== NULL
) {
495 AbstractImageWriteRequest
<I
>::send_object_requests(object_extents
, snapc
,
500 template <typename I
>
501 ObjectRequestHandle
*ImageWriteRequest
<I
>::create_object_request(
502 const ObjectExtent
&object_extent
, const ::SnapContext
&snapc
,
503 Context
*on_finish
) {
504 I
&image_ctx
= this->m_image_ctx
;
505 assert(image_ctx
.object_cacher
== NULL
);
508 assemble_extent(object_extent
, &bl
);
509 ObjectRequest
<I
> *req
= ObjectRequest
<I
>::create_write(
510 &image_ctx
, object_extent
.oid
.name
, object_extent
.objectno
,
511 object_extent
.offset
, bl
, snapc
, m_op_flags
, this->m_trace
, on_finish
);
515 template <typename I
>
516 void ImageWriteRequest
<I
>::update_stats(size_t length
) {
517 I
&image_ctx
= this->m_image_ctx
;
518 image_ctx
.perfcounter
->inc(l_librbd_wr
);
519 image_ctx
.perfcounter
->inc(l_librbd_wr_bytes
, length
);
522 template <typename I
>
523 uint64_t ImageDiscardRequest
<I
>::append_journal_event(
524 const ObjectRequests
&requests
, bool synchronous
) {
525 I
&image_ctx
= this->m_image_ctx
;
528 assert(!this->m_image_extents
.empty());
529 for (auto &extent
: this->m_image_extents
) {
530 journal::EventEntry
event_entry(journal::AioDiscardEvent(extent
.first
,
532 this->m_skip_partial_discard
));
533 tid
= image_ctx
.journal
->append_io_event(std::move(event_entry
),
534 requests
, extent
.first
,
535 extent
.second
, synchronous
);
538 AioCompletion
*aio_comp
= this->m_aio_comp
;
539 aio_comp
->associate_journal_event(tid
);
543 template <typename I
>
544 void ImageDiscardRequest
<I
>::prune_object_extents(ObjectExtents
&object_extents
) {
545 I
&image_ctx
= this->m_image_ctx
;
546 CephContext
*cct
= image_ctx
.cct
;
547 if (!this->m_skip_partial_discard
) {
551 for (auto p
= object_extents
.begin(); p
!= object_extents
.end(); ) {
552 if (p
->offset
+ p
->length
< image_ctx
.layout
.object_size
) {
553 ldout(cct
, 20) << "oid " << p
->oid
<< " " << p
->offset
<< "~"
554 << p
->length
<< " from " << p
->buffer_extents
555 << ": skip partial discard" << dendl
;
556 p
= object_extents
.erase(p
);
563 template <typename I
>
564 uint32_t ImageDiscardRequest
<I
>::get_object_cache_request_count(bool journaling
) const {
565 // extra completion request is required for tracking journal commit
566 I
&image_ctx
= this->m_image_ctx
;
567 return (image_ctx
.object_cacher
!= nullptr && journaling
? 1 : 0);
570 template <typename I
>
571 void ImageDiscardRequest
<I
>::send_image_cache_request() {
572 I
&image_ctx
= this->m_image_ctx
;
573 assert(image_ctx
.image_cache
!= nullptr);
575 AioCompletion
*aio_comp
= this->m_aio_comp
;
576 aio_comp
->set_request_count(this->m_image_extents
.size());
577 for (auto &extent
: this->m_image_extents
) {
578 C_AioRequest
*req_comp
= new C_AioRequest(aio_comp
);
579 image_ctx
.image_cache
->aio_discard(extent
.first
, extent
.second
,
580 this->m_skip_partial_discard
, req_comp
);
584 template <typename I
>
585 void ImageDiscardRequest
<I
>::send_object_cache_requests(
586 const ObjectExtents
&object_extents
, uint64_t journal_tid
) {
587 I
&image_ctx
= this->m_image_ctx
;
588 if (journal_tid
== 0) {
589 Mutex::Locker
cache_locker(image_ctx
.cache_lock
);
590 image_ctx
.object_cacher
->discard_set(image_ctx
.object_set
,
593 // cannot discard from cache until journal has committed
594 assert(image_ctx
.journal
!= NULL
);
595 AioCompletion
*aio_comp
= this->m_aio_comp
;
596 image_ctx
.journal
->wait_event(
597 journal_tid
, new C_DiscardJournalCommit
<I
>(image_ctx
, aio_comp
,
598 object_extents
, journal_tid
));
602 template <typename I
>
603 ObjectRequestHandle
*ImageDiscardRequest
<I
>::create_object_request(
604 const ObjectExtent
&object_extent
, const ::SnapContext
&snapc
,
605 Context
*on_finish
) {
606 I
&image_ctx
= this->m_image_ctx
;
608 ObjectRequest
<I
> *req
;
609 if (object_extent
.length
== image_ctx
.layout
.object_size
) {
610 req
= ObjectRequest
<I
>::create_remove(
611 &image_ctx
, object_extent
.oid
.name
, object_extent
.objectno
, snapc
,
612 this->m_trace
, on_finish
);
613 } else if (object_extent
.offset
+ object_extent
.length
==
614 image_ctx
.layout
.object_size
) {
615 req
= ObjectRequest
<I
>::create_truncate(
616 &image_ctx
, object_extent
.oid
.name
, object_extent
.objectno
,
617 object_extent
.offset
, snapc
, this->m_trace
, on_finish
);
619 req
= ObjectRequest
<I
>::create_zero(
620 &image_ctx
, object_extent
.oid
.name
, object_extent
.objectno
,
621 object_extent
.offset
, object_extent
.length
, snapc
,
622 this->m_trace
, on_finish
);
627 template <typename I
>
628 void ImageDiscardRequest
<I
>::update_stats(size_t length
) {
629 I
&image_ctx
= this->m_image_ctx
;
630 image_ctx
.perfcounter
->inc(l_librbd_discard
);
631 image_ctx
.perfcounter
->inc(l_librbd_discard_bytes
, length
);
634 template <typename I
>
635 void ImageFlushRequest
<I
>::send_request() {
636 I
&image_ctx
= this->m_image_ctx
;
637 image_ctx
.user_flushed();
639 bool journaling
= false;
641 RWLock::RLocker
snap_locker(image_ctx
.snap_lock
);
642 journaling
= (image_ctx
.journal
!= nullptr &&
643 image_ctx
.journal
->is_journal_appending());
646 AioCompletion
*aio_comp
= this->m_aio_comp
;
648 // in-flight ops are flushed prior to closing the journal
649 uint64_t journal_tid
= image_ctx
.journal
->append_io_event(
650 journal::EventEntry(journal::AioFlushEvent()),
651 ObjectRequests(), 0, 0, false);
653 aio_comp
->set_request_count(1);
654 aio_comp
->associate_journal_event(journal_tid
);
656 FunctionContext
*flush_ctx
= new FunctionContext(
657 [aio_comp
, &image_ctx
, journal_tid
] (int r
) {
658 auto ctx
= new C_FlushJournalCommit
<I
>(image_ctx
, aio_comp
,
660 image_ctx
.journal
->flush_event(journal_tid
, ctx
);
662 // track flush op for block writes
663 aio_comp
->start_op(true);
667 image_ctx
.flush_async_operations(flush_ctx
);
669 // flush rbd cache only when journaling is not enabled
670 aio_comp
->set_request_count(1);
671 C_AioRequest
*req_comp
= new C_AioRequest(aio_comp
);
672 image_ctx
.flush(req_comp
);
674 aio_comp
->start_op(true);
678 image_ctx
.perfcounter
->inc(l_librbd_aio_flush
);
681 template <typename I
>
682 void ImageFlushRequest
<I
>::send_image_cache_request() {
683 I
&image_ctx
= this->m_image_ctx
;
684 assert(image_ctx
.image_cache
!= nullptr);
686 AioCompletion
*aio_comp
= this->m_aio_comp
;
687 aio_comp
->set_request_count(1);
688 C_AioRequest
*req_comp
= new C_AioRequest(aio_comp
);
689 image_ctx
.image_cache
->aio_flush(req_comp
);
692 template <typename I
>
693 bool ImageWriteSameRequest
<I
>::assemble_writesame_extent(const ObjectExtent
&object_extent
,
694 bufferlist
*bl
, bool force_write
) {
695 size_t m_data_len
= m_data_bl
.length();
698 bool may_writesame
= true;
700 for (auto q
= object_extent
.buffer_extents
.begin();
701 q
!= object_extent
.buffer_extents
.end(); ++q
) {
702 if (!(q
->first
% m_data_len
== 0 && q
->second
% m_data_len
== 0)) {
703 may_writesame
= false;
709 bl
->append(m_data_bl
);
714 for (auto q
= object_extent
.buffer_extents
.begin();
715 q
!= object_extent
.buffer_extents
.end(); ++q
) {
717 uint64_t sub_off
= q
->first
% m_data_len
;
718 uint64_t sub_len
= m_data_len
- sub_off
;
719 uint64_t extent_left
= q
->second
;
720 while (extent_left
>= sub_len
) {
721 sub_bl
.substr_of(m_data_bl
, sub_off
, sub_len
);
722 bl
->claim_append(sub_bl
);
723 extent_left
-= sub_len
;
726 sub_len
= m_data_len
;
730 sub_bl
.substr_of(m_data_bl
, sub_off
, extent_left
);
731 bl
->claim_append(sub_bl
);
737 template <typename I
>
738 uint64_t ImageWriteSameRequest
<I
>::append_journal_event(
739 const ObjectRequests
&requests
, bool synchronous
) {
740 I
&image_ctx
= this->m_image_ctx
;
743 assert(!this->m_image_extents
.empty());
744 for (auto &extent
: this->m_image_extents
) {
745 journal::EventEntry
event_entry(journal::AioWriteSameEvent(extent
.first
,
748 tid
= image_ctx
.journal
->append_io_event(std::move(event_entry
),
749 requests
, extent
.first
,
750 extent
.second
, synchronous
);
753 if (image_ctx
.object_cacher
== NULL
) {
754 AioCompletion
*aio_comp
= this->m_aio_comp
;
755 aio_comp
->associate_journal_event(tid
);
760 template <typename I
>
761 void ImageWriteSameRequest
<I
>::send_image_cache_request() {
762 I
&image_ctx
= this->m_image_ctx
;
763 assert(image_ctx
.image_cache
!= nullptr);
765 AioCompletion
*aio_comp
= this->m_aio_comp
;
766 aio_comp
->set_request_count(this->m_image_extents
.size());
767 for (auto &extent
: this->m_image_extents
) {
768 C_AioRequest
*req_comp
= new C_AioRequest(aio_comp
);
769 image_ctx
.image_cache
->aio_writesame(extent
.first
, extent
.second
,
770 std::move(m_data_bl
), m_op_flags
,
775 template <typename I
>
776 void ImageWriteSameRequest
<I
>::send_object_cache_requests(
777 const ObjectExtents
&object_extents
, uint64_t journal_tid
) {
778 I
&image_ctx
= this->m_image_ctx
;
779 for (auto p
= object_extents
.begin(); p
!= object_extents
.end(); ++p
) {
780 const ObjectExtent
&object_extent
= *p
;
783 assemble_writesame_extent(object_extent
, &bl
, true);
785 AioCompletion
*aio_comp
= this->m_aio_comp
;
786 C_AioRequest
*req_comp
= new C_AioRequest(aio_comp
);
787 image_ctx
.write_to_cache(
788 object_extent
.oid
, bl
, object_extent
.length
, object_extent
.offset
,
789 req_comp
, m_op_flags
, journal_tid
,
790 (this->m_trace
.valid() ? &this->m_trace
: nullptr));
794 template <typename I
>
795 void ImageWriteSameRequest
<I
>::send_object_requests(
796 const ObjectExtents
&object_extents
, const ::SnapContext
&snapc
,
797 ObjectRequests
*object_requests
) {
798 I
&image_ctx
= this->m_image_ctx
;
800 // cache handles creating object requests during writeback
801 if (image_ctx
.object_cacher
== NULL
) {
802 AbstractImageWriteRequest
<I
>::send_object_requests(object_extents
, snapc
,
807 template <typename I
>
808 ObjectRequestHandle
*ImageWriteSameRequest
<I
>::create_object_request(
809 const ObjectExtent
&object_extent
, const ::SnapContext
&snapc
,
810 Context
*on_finish
) {
811 I
&image_ctx
= this->m_image_ctx
;
812 assert(image_ctx
.object_cacher
== NULL
);
815 ObjectRequest
<I
> *req
;
817 if (assemble_writesame_extent(object_extent
, &bl
, false)) {
818 req
= ObjectRequest
<I
>::create_writesame(
819 &image_ctx
, object_extent
.oid
.name
, object_extent
.objectno
,
820 object_extent
.offset
, object_extent
.length
,
821 bl
, snapc
, m_op_flags
, this->m_trace
, on_finish
);
824 req
= ObjectRequest
<I
>::create_write(
825 &image_ctx
, object_extent
.oid
.name
, object_extent
.objectno
,
826 object_extent
.offset
, bl
, snapc
, m_op_flags
, this->m_trace
, on_finish
);
830 template <typename I
>
831 void ImageWriteSameRequest
<I
>::update_stats(size_t length
) {
832 I
&image_ctx
= this->m_image_ctx
;
833 image_ctx
.perfcounter
->inc(l_librbd_ws
);
834 image_ctx
.perfcounter
->inc(l_librbd_ws_bytes
, length
);
838 } // namespace librbd
840 template class librbd::io::ImageRequest
<librbd::ImageCtx
>;
841 template class librbd::io::ImageReadRequest
<librbd::ImageCtx
>;
842 template class librbd::io::AbstractImageWriteRequest
<librbd::ImageCtx
>;
843 template class librbd::io::ImageWriteRequest
<librbd::ImageCtx
>;
844 template class librbd::io::ImageDiscardRequest
<librbd::ImageCtx
>;
845 template class librbd::io::ImageFlushRequest
<librbd::ImageCtx
>;
846 template class librbd::io::ImageWriteSameRequest
<librbd::ImageCtx
>;