]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/io/ObjectRequest.cc
3966dd2e725b5f01a8ca0fbdf16b6e4169eccfa0
[ceph.git] / ceph / src / librbd / io / ObjectRequest.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 "librbd/io/ObjectRequest.h"
5 #include "common/ceph_context.h"
6 #include "common/dout.h"
7 #include "common/errno.h"
8 #include "common/ceph_mutex.h"
9 #include "common/WorkQueue.h"
10 #include "include/Context.h"
11 #include "include/err.h"
12 #include "osd/osd_types.h"
13
14 #include "librbd/ExclusiveLock.h"
15 #include "librbd/ImageCtx.h"
16 #include "librbd/ObjectMap.h"
17 #include "librbd/Utils.h"
18 #include "librbd/io/AioCompletion.h"
19 #include "librbd/io/CopyupRequest.h"
20 #include "librbd/io/ImageRequest.h"
21 #include "librbd/io/ReadResult.h"
22
23 #include <boost/bind.hpp>
24 #include <boost/optional.hpp>
25
26 #define dout_subsys ceph_subsys_rbd
27 #undef dout_prefix
28 #define dout_prefix *_dout << "librbd::io::ObjectRequest: " << this \
29 << " " << __func__ << ": " \
30 << data_object_name(this->m_ictx, \
31 this->m_object_no) << " "
32
33 namespace librbd {
34 namespace io {
35
36 using librbd::util::data_object_name;
37
38 namespace {
39
40 template <typename I>
41 inline bool is_copy_on_read(I *ictx, librados::snap_t snap_id) {
42 std::shared_lock image_locker{ictx->image_lock};
43 return (ictx->clone_copy_on_read &&
44 !ictx->read_only && snap_id == CEPH_NOSNAP &&
45 (ictx->exclusive_lock == nullptr ||
46 ictx->exclusive_lock->is_lock_owner()));
47 }
48
49 } // anonymous namespace
50
51 template <typename I>
52 ObjectRequest<I>*
53 ObjectRequest<I>::create_write(
54 I *ictx, uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data,
55 const ::SnapContext &snapc, int op_flags,
56 const ZTracer::Trace &parent_trace, Context *completion) {
57 return new ObjectWriteRequest<I>(ictx, object_no, object_off,
58 std::move(data), snapc, op_flags,
59 parent_trace, completion);
60 }
61
62 template <typename I>
63 ObjectRequest<I>*
64 ObjectRequest<I>::create_discard(
65 I *ictx, uint64_t object_no, uint64_t object_off, uint64_t object_len,
66 const ::SnapContext &snapc, int discard_flags,
67 const ZTracer::Trace &parent_trace, Context *completion) {
68 return new ObjectDiscardRequest<I>(ictx, object_no, object_off,
69 object_len, snapc, discard_flags,
70 parent_trace, completion);
71 }
72
73 template <typename I>
74 ObjectRequest<I>*
75 ObjectRequest<I>::create_write_same(
76 I *ictx, uint64_t object_no, uint64_t object_off, uint64_t object_len,
77 ceph::bufferlist&& data, const ::SnapContext &snapc, int op_flags,
78 const ZTracer::Trace &parent_trace, Context *completion) {
79 return new ObjectWriteSameRequest<I>(ictx, object_no, object_off,
80 object_len, std::move(data), snapc,
81 op_flags, parent_trace, completion);
82 }
83
84 template <typename I>
85 ObjectRequest<I>*
86 ObjectRequest<I>::create_compare_and_write(
87 I *ictx, uint64_t object_no, uint64_t object_off,
88 ceph::bufferlist&& cmp_data, ceph::bufferlist&& write_data,
89 const ::SnapContext &snapc, uint64_t *mismatch_offset, int op_flags,
90 const ZTracer::Trace &parent_trace, Context *completion) {
91 return new ObjectCompareAndWriteRequest<I>(ictx, object_no, object_off,
92 std::move(cmp_data),
93 std::move(write_data), snapc,
94 mismatch_offset, op_flags,
95 parent_trace, completion);
96 }
97
98 template <typename I>
99 ObjectRequest<I>::ObjectRequest(
100 I *ictx, uint64_t objectno, uint64_t off, uint64_t len,
101 librados::snap_t snap_id, const char *trace_name,
102 const ZTracer::Trace &trace, Context *completion)
103 : m_ictx(ictx), m_object_no(objectno), m_object_off(off),
104 m_object_len(len), m_snap_id(snap_id), m_completion(completion),
105 m_trace(util::create_trace(*ictx, "", trace)) {
106 ceph_assert(m_ictx->data_ctx.is_valid());
107 if (m_trace.valid()) {
108 m_trace.copy_name(trace_name + std::string(" ") +
109 data_object_name(ictx, objectno));
110 m_trace.event("start");
111 }
112 }
113
114 template <typename I>
115 void ObjectRequest<I>::add_write_hint(I& image_ctx,
116 librados::ObjectWriteOperation *wr) {
117 if (image_ctx.enable_alloc_hint) {
118 wr->set_alloc_hint2(image_ctx.get_object_size(),
119 image_ctx.get_object_size(),
120 image_ctx.alloc_hint_flags);
121 } else if (image_ctx.alloc_hint_flags != 0U) {
122 wr->set_alloc_hint2(0, 0, image_ctx.alloc_hint_flags);
123 }
124 }
125
126 template <typename I>
127 bool ObjectRequest<I>::compute_parent_extents(Extents *parent_extents,
128 bool read_request) {
129 ceph_assert(ceph_mutex_is_locked(m_ictx->image_lock));
130
131 m_has_parent = false;
132 parent_extents->clear();
133
134 uint64_t parent_overlap;
135 int r = m_ictx->get_parent_overlap(m_snap_id, &parent_overlap);
136 if (r < 0) {
137 // NOTE: it's possible for a snapshot to be deleted while we are
138 // still reading from it
139 lderr(m_ictx->cct) << "failed to retrieve parent overlap: "
140 << cpp_strerror(r) << dendl;
141 return false;
142 }
143
144 if (!read_request && !m_ictx->migration_info.empty()) {
145 parent_overlap = m_ictx->migration_info.overlap;
146 }
147
148 if (parent_overlap == 0) {
149 return false;
150 }
151
152 Striper::extent_to_file(m_ictx->cct, &m_ictx->layout, m_object_no, 0,
153 m_ictx->layout.object_size, *parent_extents);
154 uint64_t object_overlap = m_ictx->prune_parent_extents(*parent_extents,
155 parent_overlap);
156 if (object_overlap > 0) {
157 ldout(m_ictx->cct, 20) << "overlap " << parent_overlap << " "
158 << "extents " << *parent_extents << dendl;
159 m_has_parent = !parent_extents->empty();
160 return true;
161 }
162 return false;
163 }
164
165 template <typename I>
166 void ObjectRequest<I>::async_finish(int r) {
167 ldout(m_ictx->cct, 20) << "r=" << r << dendl;
168 m_ictx->op_work_queue->queue(util::create_context_callback<
169 ObjectRequest<I>, &ObjectRequest<I>::finish>(this), r);
170 }
171
172 template <typename I>
173 void ObjectRequest<I>::finish(int r) {
174 ldout(m_ictx->cct, 20) << "r=" << r << dendl;
175 m_completion->complete(r);
176 delete this;
177 }
178
179 /** read **/
180
181 template <typename I>
182 ObjectReadRequest<I>::ObjectReadRequest(
183 I *ictx, uint64_t objectno, uint64_t offset, uint64_t len,
184 librados::snap_t snap_id, int op_flags, const ZTracer::Trace &parent_trace,
185 bufferlist* read_data, ExtentMap* extent_map, Context *completion)
186 : ObjectRequest<I>(ictx, objectno, offset, len, snap_id, "read",
187 parent_trace, completion),
188 m_op_flags(op_flags), m_read_data(read_data), m_extent_map(extent_map) {
189 }
190
191 template <typename I>
192 void ObjectReadRequest<I>::send() {
193 I *image_ctx = this->m_ictx;
194 ldout(image_ctx->cct, 20) << dendl;
195
196 read_object();
197 }
198
199 template <typename I>
200 void ObjectReadRequest<I>::read_object() {
201 I *image_ctx = this->m_ictx;
202 {
203 std::shared_lock image_locker{image_ctx->image_lock};
204 if (image_ctx->object_map != nullptr &&
205 !image_ctx->object_map->object_may_exist(this->m_object_no)) {
206 image_ctx->op_work_queue->queue(new LambdaContext([this](int r) {
207 read_parent();
208 }), 0);
209 return;
210 }
211 }
212
213 ldout(image_ctx->cct, 20) << dendl;
214
215 librados::ObjectReadOperation op;
216 if (this->m_object_len >= image_ctx->sparse_read_threshold_bytes) {
217 op.sparse_read(this->m_object_off, this->m_object_len, m_extent_map,
218 m_read_data, nullptr);
219 } else {
220 op.read(this->m_object_off, this->m_object_len, m_read_data, nullptr);
221 }
222 op.set_op_flags2(m_op_flags);
223
224 librados::AioCompletion *rados_completion = util::create_rados_callback<
225 ObjectReadRequest<I>, &ObjectReadRequest<I>::handle_read_object>(this);
226 int flags = image_ctx->get_read_flags(this->m_snap_id);
227 int r = image_ctx->data_ctx.aio_operate(
228 data_object_name(this->m_ictx, this->m_object_no), rados_completion, &op,
229 flags, nullptr,
230 (this->m_trace.valid() ? this->m_trace.get_info() : nullptr));
231 ceph_assert(r == 0);
232
233 rados_completion->release();
234 }
235
236 template <typename I>
237 void ObjectReadRequest<I>::handle_read_object(int r) {
238 I *image_ctx = this->m_ictx;
239 ldout(image_ctx->cct, 20) << "r=" << r << dendl;
240
241 if (r == -ENOENT) {
242 read_parent();
243 return;
244 } else if (r < 0) {
245 lderr(image_ctx->cct) << "failed to read from object: "
246 << cpp_strerror(r) << dendl;
247 this->finish(r);
248 return;
249 }
250
251 this->finish(0);
252 }
253
254 template <typename I>
255 void ObjectReadRequest<I>::read_parent() {
256 I *image_ctx = this->m_ictx;
257
258 std::shared_lock image_locker{image_ctx->image_lock};
259
260 // calculate reverse mapping onto the image
261 Extents parent_extents;
262 Striper::extent_to_file(image_ctx->cct, &image_ctx->layout,
263 this->m_object_no, this->m_object_off,
264 this->m_object_len, parent_extents);
265
266 uint64_t parent_overlap = 0;
267 uint64_t object_overlap = 0;
268 int r = image_ctx->get_parent_overlap(this->m_snap_id, &parent_overlap);
269 if (r == 0) {
270 object_overlap = image_ctx->prune_parent_extents(parent_extents,
271 parent_overlap);
272 }
273
274 if (object_overlap == 0) {
275 image_locker.unlock();
276
277 this->finish(-ENOENT);
278 return;
279 }
280
281 ldout(image_ctx->cct, 20) << dendl;
282
283 auto parent_completion = AioCompletion::create_and_start<
284 ObjectReadRequest<I>, &ObjectReadRequest<I>::handle_read_parent>(
285 this, util::get_image_ctx(image_ctx->parent), AIO_TYPE_READ);
286 ImageRequest<I>::aio_read(image_ctx->parent, parent_completion,
287 std::move(parent_extents), ReadResult{m_read_data},
288 0, this->m_trace);
289 }
290
291 template <typename I>
292 void ObjectReadRequest<I>::handle_read_parent(int r) {
293 I *image_ctx = this->m_ictx;
294 ldout(image_ctx->cct, 20) << "r=" << r << dendl;
295
296 if (r == -ENOENT) {
297 this->finish(r);
298 return;
299 } else if (r < 0) {
300 lderr(image_ctx->cct) << "failed to read parent extents: "
301 << cpp_strerror(r) << dendl;
302 this->finish(r);
303 return;
304 }
305
306 copyup();
307 }
308
309 template <typename I>
310 void ObjectReadRequest<I>::copyup() {
311 I *image_ctx = this->m_ictx;
312 if (!is_copy_on_read(image_ctx, this->m_snap_id)) {
313 this->finish(0);
314 return;
315 }
316
317 image_ctx->owner_lock.lock_shared();
318 image_ctx->image_lock.lock_shared();
319 Extents parent_extents;
320 if (!this->compute_parent_extents(&parent_extents, true) ||
321 (image_ctx->exclusive_lock != nullptr &&
322 !image_ctx->exclusive_lock->is_lock_owner())) {
323 image_ctx->image_lock.unlock_shared();
324 image_ctx->owner_lock.unlock_shared();
325 this->finish(0);
326 return;
327 }
328
329 ldout(image_ctx->cct, 20) << dendl;
330
331 image_ctx->copyup_list_lock.lock();
332 auto it = image_ctx->copyup_list.find(this->m_object_no);
333 if (it == image_ctx->copyup_list.end()) {
334 // create and kick off a CopyupRequest
335 auto new_req = CopyupRequest<I>::create(
336 image_ctx, this->m_object_no, std::move(parent_extents), this->m_trace);
337
338 image_ctx->copyup_list[this->m_object_no] = new_req;
339 image_ctx->copyup_list_lock.unlock();
340 image_ctx->image_lock.unlock_shared();
341 new_req->send();
342 } else {
343 image_ctx->copyup_list_lock.unlock();
344 image_ctx->image_lock.unlock_shared();
345 }
346
347 image_ctx->owner_lock.unlock_shared();
348 this->finish(0);
349 }
350
351 /** write **/
352
353 template <typename I>
354 AbstractObjectWriteRequest<I>::AbstractObjectWriteRequest(
355 I *ictx, uint64_t object_no, uint64_t object_off, uint64_t len,
356 const ::SnapContext &snapc, const char *trace_name,
357 const ZTracer::Trace &parent_trace, Context *completion)
358 : ObjectRequest<I>(ictx, object_no, object_off, len, CEPH_NOSNAP, trace_name,
359 parent_trace, completion),
360 m_snap_seq(snapc.seq.val)
361 {
362 m_snaps.insert(m_snaps.end(), snapc.snaps.begin(), snapc.snaps.end());
363
364 if (this->m_object_off == 0 &&
365 this->m_object_len == ictx->get_object_size()) {
366 m_full_object = true;
367 }
368
369 compute_parent_info();
370
371 ictx->image_lock.lock_shared();
372 if (!ictx->migration_info.empty()) {
373 m_guarding_migration_write = true;
374 }
375 ictx->image_lock.unlock_shared();
376 }
377
378 template <typename I>
379 void AbstractObjectWriteRequest<I>::compute_parent_info() {
380 I *image_ctx = this->m_ictx;
381 std::shared_lock image_locker{image_ctx->image_lock};
382
383 this->compute_parent_extents(&m_parent_extents, false);
384
385 if (!this->has_parent() ||
386 (m_full_object && m_snaps.empty() && !is_post_copyup_write_required())) {
387 m_copyup_enabled = false;
388 }
389 }
390
391 template <typename I>
392 void AbstractObjectWriteRequest<I>::add_write_hint(
393 librados::ObjectWriteOperation *wr) {
394 I *image_ctx = this->m_ictx;
395 std::shared_lock image_locker{image_ctx->image_lock};
396 if (image_ctx->object_map == nullptr || !this->m_object_may_exist) {
397 ObjectRequest<I>::add_write_hint(*image_ctx, wr);
398 }
399 }
400
401 template <typename I>
402 void AbstractObjectWriteRequest<I>::send() {
403 I *image_ctx = this->m_ictx;
404 ldout(image_ctx->cct, 20) << this->get_op_type() << " "
405 << this->m_object_off << "~" << this->m_object_len
406 << dendl;
407 {
408 std::shared_lock image_lock{image_ctx->image_lock};
409 if (image_ctx->object_map == nullptr) {
410 m_object_may_exist = true;
411 } else {
412 // should have been flushed prior to releasing lock
413 ceph_assert(image_ctx->exclusive_lock->is_lock_owner());
414 m_object_may_exist = image_ctx->object_map->object_may_exist(
415 this->m_object_no);
416 }
417 }
418
419 if (!m_object_may_exist && is_no_op_for_nonexistent_object()) {
420 ldout(image_ctx->cct, 20) << "skipping no-op on nonexistent object"
421 << dendl;
422 this->async_finish(0);
423 return;
424 }
425
426 pre_write_object_map_update();
427 }
428
429 template <typename I>
430 void AbstractObjectWriteRequest<I>::pre_write_object_map_update() {
431 I *image_ctx = this->m_ictx;
432
433 image_ctx->image_lock.lock_shared();
434 if (image_ctx->object_map == nullptr || !is_object_map_update_enabled()) {
435 image_ctx->image_lock.unlock_shared();
436 write_object();
437 return;
438 }
439
440 if (!m_object_may_exist && m_copyup_enabled) {
441 // optimization: copyup required
442 image_ctx->image_lock.unlock_shared();
443 copyup();
444 return;
445 }
446
447 uint8_t new_state = this->get_pre_write_object_map_state();
448 ldout(image_ctx->cct, 20) << this->m_object_off << "~" << this->m_object_len
449 << dendl;
450
451 if (image_ctx->object_map->template aio_update<
452 AbstractObjectWriteRequest<I>,
453 &AbstractObjectWriteRequest<I>::handle_pre_write_object_map_update>(
454 CEPH_NOSNAP, this->m_object_no, new_state, {}, this->m_trace, false,
455 this)) {
456 image_ctx->image_lock.unlock_shared();
457 return;
458 }
459
460 image_ctx->image_lock.unlock_shared();
461 write_object();
462 }
463
464 template <typename I>
465 void AbstractObjectWriteRequest<I>::handle_pre_write_object_map_update(int r) {
466 I *image_ctx = this->m_ictx;
467 ldout(image_ctx->cct, 20) << "r=" << r << dendl;
468 if (r < 0) {
469 lderr(image_ctx->cct) << "failed to update object map: "
470 << cpp_strerror(r) << dendl;
471 this->finish(r);
472 return;
473 }
474
475 write_object();
476 }
477
478 template <typename I>
479 void AbstractObjectWriteRequest<I>::write_object() {
480 I *image_ctx = this->m_ictx;
481 ldout(image_ctx->cct, 20) << dendl;
482
483 librados::ObjectWriteOperation write;
484 if (m_copyup_enabled) {
485 ldout(image_ctx->cct, 20) << "guarding write" << dendl;
486 if (m_guarding_migration_write) {
487 cls_client::assert_snapc_seq(
488 &write, m_snap_seq, cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ);
489 } else {
490 write.assert_exists();
491 }
492 }
493
494 add_write_hint(&write);
495 add_write_ops(&write);
496 ceph_assert(write.size() != 0);
497
498 librados::AioCompletion *rados_completion = util::create_rados_callback<
499 AbstractObjectWriteRequest<I>,
500 &AbstractObjectWriteRequest<I>::handle_write_object>(this);
501 int r = image_ctx->data_ctx.aio_operate(
502 data_object_name(this->m_ictx, this->m_object_no), rados_completion,
503 &write, m_snap_seq, m_snaps,
504 (this->m_trace.valid() ? this->m_trace.get_info() : nullptr));
505 ceph_assert(r == 0);
506 rados_completion->release();
507 }
508
509 template <typename I>
510 void AbstractObjectWriteRequest<I>::handle_write_object(int r) {
511 I *image_ctx = this->m_ictx;
512 ldout(image_ctx->cct, 20) << "r=" << r << dendl;
513
514 r = filter_write_result(r);
515 if (r == -ENOENT) {
516 if (m_copyup_enabled) {
517 copyup();
518 return;
519 }
520 } else if (r == -ERANGE && m_guarding_migration_write) {
521 image_ctx->image_lock.lock_shared();
522 m_guarding_migration_write = !image_ctx->migration_info.empty();
523 image_ctx->image_lock.unlock_shared();
524
525 if (m_guarding_migration_write) {
526 copyup();
527 } else {
528 ldout(image_ctx->cct, 10) << "migration parent gone, restart io" << dendl;
529 compute_parent_info();
530 write_object();
531 }
532 return;
533 } else if (r == -EILSEQ) {
534 ldout(image_ctx->cct, 10) << "failed to write object" << dendl;
535 this->finish(r);
536 return;
537 } else if (r < 0) {
538 lderr(image_ctx->cct) << "failed to write object: " << cpp_strerror(r)
539 << dendl;
540 this->finish(r);
541 return;
542 }
543
544 post_write_object_map_update();
545 }
546
547 template <typename I>
548 void AbstractObjectWriteRequest<I>::copyup() {
549 I *image_ctx = this->m_ictx;
550 ldout(image_ctx->cct, 20) << dendl;
551
552 ceph_assert(!m_copyup_in_progress);
553 m_copyup_in_progress = true;
554
555 image_ctx->copyup_list_lock.lock();
556 auto it = image_ctx->copyup_list.find(this->m_object_no);
557 if (it == image_ctx->copyup_list.end()) {
558 auto new_req = CopyupRequest<I>::create(
559 image_ctx, this->m_object_no, std::move(this->m_parent_extents),
560 this->m_trace);
561 this->m_parent_extents.clear();
562
563 // make sure to wait on this CopyupRequest
564 new_req->append_request(this);
565 image_ctx->copyup_list[this->m_object_no] = new_req;
566
567 image_ctx->copyup_list_lock.unlock();
568 new_req->send();
569 } else {
570 it->second->append_request(this);
571 image_ctx->copyup_list_lock.unlock();
572 }
573 }
574
575 template <typename I>
576 void AbstractObjectWriteRequest<I>::handle_copyup(int r) {
577 I *image_ctx = this->m_ictx;
578 ldout(image_ctx->cct, 20) << "r=" << r << dendl;
579
580 ceph_assert(m_copyup_in_progress);
581 m_copyup_in_progress = false;
582
583 if (r < 0 && r != -ERESTART) {
584 lderr(image_ctx->cct) << "failed to copyup object: " << cpp_strerror(r)
585 << dendl;
586 this->finish(r);
587 return;
588 }
589
590 if (r == -ERESTART || is_post_copyup_write_required()) {
591 write_object();
592 return;
593 }
594
595 post_write_object_map_update();
596 }
597
598 template <typename I>
599 void AbstractObjectWriteRequest<I>::post_write_object_map_update() {
600 I *image_ctx = this->m_ictx;
601
602 image_ctx->image_lock.lock_shared();
603 if (image_ctx->object_map == nullptr || !is_object_map_update_enabled() ||
604 !is_non_existent_post_write_object_map_state()) {
605 image_ctx->image_lock.unlock_shared();
606 this->finish(0);
607 return;
608 }
609
610 ldout(image_ctx->cct, 20) << dendl;
611
612 // should have been flushed prior to releasing lock
613 ceph_assert(image_ctx->exclusive_lock->is_lock_owner());
614 if (image_ctx->object_map->template aio_update<
615 AbstractObjectWriteRequest<I>,
616 &AbstractObjectWriteRequest<I>::handle_post_write_object_map_update>(
617 CEPH_NOSNAP, this->m_object_no, OBJECT_NONEXISTENT, OBJECT_PENDING,
618 this->m_trace, false, this)) {
619 image_ctx->image_lock.unlock_shared();
620 return;
621 }
622
623 image_ctx->image_lock.unlock_shared();
624 this->finish(0);
625 }
626
627 template <typename I>
628 void AbstractObjectWriteRequest<I>::handle_post_write_object_map_update(int r) {
629 I *image_ctx = this->m_ictx;
630 ldout(image_ctx->cct, 20) << "r=" << r << dendl;
631 if (r < 0) {
632 lderr(image_ctx->cct) << "failed to update object map: "
633 << cpp_strerror(r) << dendl;
634 this->finish(r);
635 return;
636 }
637
638 this->finish(0);
639 }
640
641 template <typename I>
642 void ObjectWriteRequest<I>::add_write_ops(librados::ObjectWriteOperation *wr) {
643 if (this->m_full_object) {
644 wr->write_full(m_write_data);
645 } else {
646 wr->write(this->m_object_off, m_write_data);
647 }
648 wr->set_op_flags2(m_op_flags);
649 }
650
651 template <typename I>
652 void ObjectWriteSameRequest<I>::add_write_ops(
653 librados::ObjectWriteOperation *wr) {
654 wr->writesame(this->m_object_off, this->m_object_len, m_write_data);
655 wr->set_op_flags2(m_op_flags);
656 }
657
658 template <typename I>
659 void ObjectCompareAndWriteRequest<I>::add_write_ops(
660 librados::ObjectWriteOperation *wr) {
661 wr->cmpext(this->m_object_off, m_cmp_bl, nullptr);
662
663 if (this->m_full_object) {
664 wr->write_full(m_write_bl);
665 } else {
666 wr->write(this->m_object_off, m_write_bl);
667 }
668 wr->set_op_flags2(m_op_flags);
669 }
670
671 template <typename I>
672 int ObjectCompareAndWriteRequest<I>::filter_write_result(int r) const {
673 if (r <= -MAX_ERRNO) {
674 I *image_ctx = this->m_ictx;
675 Extents image_extents;
676
677 // object extent compare mismatch
678 uint64_t offset = -MAX_ERRNO - r;
679 Striper::extent_to_file(image_ctx->cct, &image_ctx->layout,
680 this->m_object_no, offset, this->m_object_len,
681 image_extents);
682 ceph_assert(image_extents.size() == 1);
683
684 if (m_mismatch_offset) {
685 *m_mismatch_offset = image_extents[0].first;
686 }
687 r = -EILSEQ;
688 }
689 return r;
690 }
691
692 } // namespace io
693 } // namespace librbd
694
695 template class librbd::io::ObjectRequest<librbd::ImageCtx>;
696 template class librbd::io::ObjectReadRequest<librbd::ImageCtx>;
697 template class librbd::io::AbstractObjectWriteRequest<librbd::ImageCtx>;
698 template class librbd::io::ObjectWriteRequest<librbd::ImageCtx>;
699 template class librbd::io::ObjectDiscardRequest<librbd::ImageCtx>;
700 template class librbd::io::ObjectWriteSameRequest<librbd::ImageCtx>;
701 template class librbd::io::ObjectCompareAndWriteRequest<librbd::ImageCtx>;