1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "librbd/operation/SparsifyRequest.h"
5 #include "cls/rbd/cls_rbd_client.h"
6 #include "common/dout.h"
7 #include "common/errno.h"
8 #include "include/err.h"
9 #include "librbd/AsyncObjectThrottle.h"
10 #include "librbd/ExclusiveLock.h"
11 #include "librbd/ImageCtx.h"
12 #include "librbd/Types.h"
13 #include "librbd/io/ObjectRequest.h"
14 #include "osdc/Striper.h"
15 #include <boost/lambda/bind.hpp>
16 #include <boost/lambda/construct.hpp>
18 #define dout_subsys ceph_subsys_rbd
25 bool may_be_trimmed(const std::map
<uint64_t,uint64_t> &extent_map
,
26 const bufferlist
&bl
, size_t sparse_size
,
27 uint64_t *new_end_ptr
) {
28 if (extent_map
.empty()) {
33 uint64_t end
= extent_map
.rbegin()->first
+ extent_map
.rbegin()->second
;
34 uint64_t new_end
= end
;
35 uint64_t bl_off
= bl
.length();
37 for (auto it
= extent_map
.rbegin(); it
!= extent_map
.rend(); it
++) {
39 auto len
= it
->second
;
41 new_end
= p2roundup
<uint64_t>(off
+ len
, sparse_size
);
43 uint64_t extent_left
= len
;
44 uint64_t sub_len
= len
% sparse_size
;
46 sub_len
= sparse_size
;
48 while (extent_left
> 0) {
49 ceph_assert(bl_off
>= sub_len
);
52 sub_bl
.substr_of(bl
, bl_off
, sub_len
);
53 if (!sub_bl
.is_zero()) {
56 new_end
-= sparse_size
;
57 extent_left
-= sub_len
;
58 sub_len
= sparse_size
;
60 if (extent_left
> 0) {
66 *new_end_ptr
= new_end
;
73 } // anonymous namespace
75 using util::create_context_callback
;
76 using util::create_rados_callback
;
79 #define dout_prefix *_dout << "librbd::operation::SparsifyObject: " << this \
80 << " " << m_oid << " " << __func__ << ": "
83 class C_SparsifyObject
: public C_AsyncObjectThrottle
<I
> {
92 * SPARSIFY * * * * * * * * * * * * > READ < * * * * * * * * * * (concurrent
94 * | (object map disabled) | (can trim) * detected)
95 * |------------------------\ V *
96 * | | PRE UPDATE OBJECT MAP *
97 * | (object map enabled) | | (if needed) *
99 * PRE UPDATE OBJECT MAP | TRIM * * * * * * * * * * *
102 * CHECK EXISTS | POST UPDATE OBJECT MAP
105 * POST UPDATE OBJECT MAP | |
108 * <finish> <------------------/<-------/
114 C_SparsifyObject(AsyncObjectThrottle
<I
> &throttle
, I
*image_ctx
,
115 uint64_t object_no
, size_t sparse_size
)
116 : C_AsyncObjectThrottle
<I
>(throttle
, *image_ctx
), m_cct(image_ctx
->cct
),
117 m_object_no(object_no
), m_sparse_size(sparse_size
),
118 m_oid(image_ctx
->get_object_name(object_no
)) {
121 int send() override
{
122 I
&image_ctx
= this->m_image_ctx
;
123 ceph_assert(image_ctx
.owner_lock
.is_locked());
125 ldout(m_cct
, 20) << dendl
;
127 if (!image_ctx
.data_ctx
.is_valid()) {
128 lderr(m_cct
) << "missing data pool" << dendl
;
132 if (image_ctx
.exclusive_lock
!= nullptr &&
133 !image_ctx
.exclusive_lock
->is_lock_owner()) {
134 ldout(m_cct
, 1) << "lost exclusive lock during sparsify" << dendl
;
139 RWLock::RLocker
snap_locker(image_ctx
.snap_lock
);
140 if (image_ctx
.object_map
!= nullptr &&
141 !image_ctx
.object_map
->object_may_exist(m_object_no
)) {
142 // can skip because the object does not exist
146 RWLock::RLocker
parent_locker(image_ctx
.parent_lock
);
147 uint64_t overlap_objects
= 0;
149 int r
= image_ctx
.get_parent_overlap(CEPH_NOSNAP
, &overlap
);
150 if (r
== 0 && overlap
> 0) {
151 overlap_objects
= Striper::get_num_objects(image_ctx
.layout
, overlap
);
153 m_remove_empty
= (m_object_no
>= overlap_objects
);
160 void send_sparsify() {
161 I
&image_ctx
= this->m_image_ctx
;
162 ldout(m_cct
, 20) << dendl
;
164 librados::ObjectWriteOperation op
;
165 cls_client::sparsify(&op
, m_sparse_size
, m_remove_empty
);
166 auto comp
= create_rados_callback
<
167 C_SparsifyObject
, &C_SparsifyObject::handle_sparsify
>(this);
168 int r
= image_ctx
.data_ctx
.aio_operate(m_oid
, comp
, &op
);
173 void handle_sparsify(int r
) {
174 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
176 if (r
== -EOPNOTSUPP
) {
177 m_trying_trim
= true;
188 lderr(m_cct
) << "failed to sparsify: " << cpp_strerror(r
) << dendl
;
193 send_pre_update_object_map();
196 void send_pre_update_object_map() {
197 I
&image_ctx
= this->m_image_ctx
;
200 if (!m_remove_empty
|| m_new_end
!= 0 ||
201 !image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
)) {
205 } else if (!m_remove_empty
||
206 !image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
)) {
211 ldout(m_cct
, 20) << dendl
;
213 image_ctx
.owner_lock
.get_read();
214 image_ctx
.snap_lock
.get_read();
215 if (image_ctx
.object_map
== nullptr) {
216 // possible that exclusive lock was lost in background
217 lderr(m_cct
) << "object map is not initialized" << dendl
;
219 image_ctx
.snap_lock
.put_read();
220 image_ctx
.owner_lock
.put_read();
226 m_finish_op_ctx
= image_ctx
.exclusive_lock
->start_op(&r
);
227 if (m_finish_op_ctx
== nullptr) {
228 lderr(m_cct
) << "lost exclusive lock" << dendl
;
229 image_ctx
.snap_lock
.put_read();
230 image_ctx
.owner_lock
.put_read();
235 auto ctx
= create_context_callback
<
237 &C_SparsifyObject
<I
>::handle_pre_update_object_map
>(this);
239 image_ctx
.object_map_lock
.get_write();
240 bool sent
= image_ctx
.object_map
->template aio_update
<
241 Context
, &Context::complete
>(CEPH_NOSNAP
, m_object_no
, OBJECT_PENDING
,
242 OBJECT_EXISTS
, {}, false, ctx
);
244 // NOTE: state machine might complete before we reach here
245 image_ctx
.object_map_lock
.put_write();
246 image_ctx
.snap_lock
.put_read();
247 image_ctx
.owner_lock
.put_read();
253 void handle_pre_update_object_map(int r
) {
254 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
257 lderr(m_cct
) << "failed to update object map: " << cpp_strerror(r
)
270 void send_check_exists() {
271 I
&image_ctx
= this->m_image_ctx
;
273 ldout(m_cct
, 20) << dendl
;
275 librados::ObjectReadOperation op
;
276 op
.stat(NULL
, NULL
, NULL
);
278 auto comp
= create_rados_callback
<
279 C_SparsifyObject
, &C_SparsifyObject::handle_check_exists
>(this);
280 int r
= image_ctx
.data_ctx
.aio_operate(m_oid
, comp
, &op
, &m_bl
);
285 void handle_check_exists(int r
) {
286 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
288 if (r
< 0 && r
!= -ENOENT
) {
289 lderr(m_cct
) << "stat failed: " << cpp_strerror(r
) << dendl
;
294 send_post_update_object_map(r
== 0);
297 void send_post_update_object_map(bool exists
) {
298 I
&image_ctx
= this->m_image_ctx
;
300 ldout(m_cct
, 20) << dendl
;
302 auto ctx
= create_context_callback
<
304 &C_SparsifyObject
<I
>::handle_post_update_object_map
>(this);
307 RWLock::RLocker
owner_locker(image_ctx
.owner_lock
);
308 RWLock::RLocker
snap_locker(image_ctx
.snap_lock
);
310 assert(image_ctx
.exclusive_lock
->is_lock_owner());
311 assert(image_ctx
.object_map
!= nullptr);
313 RWLock::WLocker
object_map_locker(image_ctx
.object_map_lock
);
315 sent
= image_ctx
.object_map
->template aio_update
<
316 Context
, &Context::complete
>(CEPH_NOSNAP
, m_object_no
,
317 exists
? OBJECT_EXISTS
: OBJECT_NONEXISTENT
,
318 OBJECT_PENDING
, {}, false, ctx
);
325 void handle_post_update_object_map(int r
) {
326 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
329 lderr(m_cct
) << "failed to update object map: " << cpp_strerror(r
)
339 I
&image_ctx
= this->m_image_ctx
;
341 ldout(m_cct
, 20) << dendl
;
343 librados::ObjectReadOperation op
;
345 op
.sparse_read(0, image_ctx
.layout
.object_size
, &m_extent_map
, &m_bl
,
347 auto comp
= create_rados_callback
<
348 C_SparsifyObject
, &C_SparsifyObject::handle_read
>(this);
349 int r
= image_ctx
.data_ctx
.aio_operate(m_oid
, comp
, &op
, &m_bl
);
354 void handle_read(int r
) {
355 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
361 lderr(m_cct
) << "failed to read object: " << cpp_strerror(r
) << dendl
;
367 if (!may_be_trimmed(m_extent_map
, m_bl
, m_sparse_size
, &m_new_end
)) {
372 send_pre_update_object_map();
376 I
&image_ctx
= this->m_image_ctx
;
378 ldout(m_cct
, 20) << dendl
;
380 ceph_assert(m_new_end
< image_ctx
.layout
.object_size
);
382 librados::ObjectWriteOperation op
;
384 m_bl
.append_zero(image_ctx
.layout
.object_size
- m_new_end
);
385 op
.cmpext(m_new_end
, m_bl
, nullptr);
386 if (m_new_end
== 0 && m_remove_empty
) {
389 op
.truncate(m_new_end
);
392 auto comp
= create_rados_callback
<
393 C_SparsifyObject
, &C_SparsifyObject::handle_trim
>(this);
394 int r
= image_ctx
.data_ctx
.aio_operate(m_oid
, comp
, &op
);
399 void handle_trim(int r
) {
400 I
&image_ctx
= this->m_image_ctx
;
402 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
404 if (r
<= -MAX_ERRNO
) {
405 m_finish_op_ctx
->complete(0);
406 m_finish_op_ctx
= nullptr;
411 if (r
< 0 && r
!= -ENOENT
) {
412 lderr(m_cct
) << "failed to trim: " << cpp_strerror(r
) << dendl
;
417 if (!m_remove_empty
|| m_new_end
!= 0 ||
418 !image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
)) {
423 send_post_update_object_map(false);
426 void finish_op(int r
) {
427 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
429 if (m_finish_op_ctx
!= nullptr) {
430 m_finish_op_ctx
->complete(0);
437 uint64_t m_object_no
;
438 size_t m_sparse_size
;
441 bool m_remove_empty
= false;
442 bool m_trying_trim
= false;
444 std::map
<uint64_t,uint64_t> m_extent_map
;
445 uint64_t m_new_end
= 0;
446 Context
*m_finish_op_ctx
= nullptr;
450 #define dout_prefix *_dout << "librbd::operation::SparsifyRequest: " << this \
451 << " " << __func__ << ": "
453 template <typename I
>
454 bool SparsifyRequest
<I
>::should_complete(int r
) {
455 I
&image_ctx
= this->m_image_ctx
;
456 CephContext
*cct
= image_ctx
.cct
;
457 ldout(cct
, 5) << "r=" << r
<< dendl
;
459 lderr(cct
) << "encountered error: " << cpp_strerror(r
) << dendl
;
464 template <typename I
>
465 void SparsifyRequest
<I
>::send_op() {
469 template <typename I
>
470 void SparsifyRequest
<I
>::sparsify_objects() {
471 I
&image_ctx
= this->m_image_ctx
;
472 ceph_assert(image_ctx
.owner_lock
.is_locked());
474 CephContext
*cct
= image_ctx
.cct
;
475 ldout(cct
, 5) << dendl
;
477 assert(image_ctx
.owner_lock
.is_locked());
479 uint64_t objects
= 0;
481 RWLock::RLocker
snap_locker(image_ctx
.snap_lock
);
482 objects
= image_ctx
.get_object_count(CEPH_NOSNAP
);
485 auto ctx
= create_context_callback
<
487 &SparsifyRequest
<I
>::handle_sparsify_objects
>(this);
488 typename AsyncObjectThrottle
<I
>::ContextFactory
context_factory(
489 boost::lambda::bind(boost::lambda::new_ptr
<C_SparsifyObject
<I
> >(),
490 boost::lambda::_1
, &image_ctx
, boost::lambda::_2
, m_sparse_size
));
491 AsyncObjectThrottle
<I
> *throttle
= new AsyncObjectThrottle
<I
>(
492 this, image_ctx
, context_factory
, ctx
, &m_prog_ctx
, 0, objects
);
494 image_ctx
.config
.template get_val
<uint64_t>("rbd_concurrent_management_ops"));
497 template <typename I
>
498 void SparsifyRequest
<I
>::handle_sparsify_objects(int r
) {
499 I
&image_ctx
= this->m_image_ctx
;
500 CephContext
*cct
= image_ctx
.cct
;
501 ldout(cct
, 5) << "r=" << r
<< dendl
;
503 if (r
== -ERESTART
) {
504 ldout(cct
, 5) << "sparsify operation interrupted" << dendl
;
508 lderr(cct
) << "sparsify encountered an error: " << cpp_strerror(r
) << dendl
;
516 } // namespace operation
517 } // namespace librbd
519 template class librbd::operation::SparsifyRequest
<librbd::ImageCtx
>;