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/FlattenRequest.h"
5 #include "librbd/AsyncObjectThrottle.h"
6 #include "librbd/ExclusiveLock.h"
7 #include "librbd/ImageCtx.h"
8 #include "librbd/io/ObjectRequest.h"
9 #include "common/dout.h"
10 #include "common/errno.h"
11 #include <boost/lambda/bind.hpp>
12 #include <boost/lambda/construct.hpp>
14 #define dout_subsys ceph_subsys_rbd
16 #define dout_prefix *_dout << "librbd::FlattenRequest: "
22 class C_FlattenObject
: public C_AsyncObjectThrottle
<I
> {
24 C_FlattenObject(AsyncObjectThrottle
<I
> &throttle
, I
*image_ctx
,
25 uint64_t object_size
, ::SnapContext snapc
, uint64_t object_no
)
26 : C_AsyncObjectThrottle
<I
>(throttle
, *image_ctx
), m_object_size(object_size
),
27 m_snapc(snapc
), m_object_no(object_no
)
32 I
&image_ctx
= this->m_image_ctx
;
33 assert(image_ctx
.owner_lock
.is_locked());
34 CephContext
*cct
= image_ctx
.cct
;
36 if (image_ctx
.exclusive_lock
!= nullptr &&
37 !image_ctx
.exclusive_lock
->is_lock_owner()) {
38 ldout(cct
, 1) << "lost exclusive lock during flatten" << dendl
;
43 string oid
= image_ctx
.get_object_name(m_object_no
);
44 auto req
= new io::ObjectWriteRequest(&image_ctx
, oid
, m_object_no
, 0,
45 bl
, m_snapc
, this, 0);
46 if (!req
->has_parent()) {
47 // stop early if the parent went away - it just means
48 // another flatten finished first or the image was resized
58 uint64_t m_object_size
;
59 ::SnapContext m_snapc
;
64 bool FlattenRequest
<I
>::should_complete(int r
) {
65 I
&image_ctx
= this->m_image_ctx
;
66 CephContext
*cct
= image_ctx
.cct
;
67 ldout(cct
, 5) << this << " should_complete: " << " r=" << r
<< dendl
;
69 ldout(cct
, 5) << "flatten operation interrupted" << dendl
;
71 } else if (r
< 0 && !(r
== -ENOENT
&& m_ignore_enoent
) ) {
72 lderr(cct
) << "flatten encountered an error: " << cpp_strerror(r
) << dendl
;
76 RWLock::RLocker
owner_locker(image_ctx
.owner_lock
);
78 case STATE_FLATTEN_OBJECTS
:
79 ldout(cct
, 5) << "FLATTEN_OBJECTS" << dendl
;
80 return send_update_header();
82 case STATE_UPDATE_HEADER
:
83 ldout(cct
, 5) << "UPDATE_HEADER" << dendl
;
84 return send_update_children();
86 case STATE_UPDATE_CHILDREN
:
87 ldout(cct
, 5) << "UPDATE_CHILDREN" << dendl
;
91 lderr(cct
) << "invalid state: " << m_state
<< dendl
;
99 void FlattenRequest
<I
>::send_op() {
100 I
&image_ctx
= this->m_image_ctx
;
101 assert(image_ctx
.owner_lock
.is_locked());
102 CephContext
*cct
= image_ctx
.cct
;
103 ldout(cct
, 5) << this << " send" << dendl
;
105 m_state
= STATE_FLATTEN_OBJECTS
;
106 typename AsyncObjectThrottle
<I
>::ContextFactory
context_factory(
107 boost::lambda::bind(boost::lambda::new_ptr
<C_FlattenObject
<I
> >(),
108 boost::lambda::_1
, &image_ctx
, m_object_size
, m_snapc
,
110 AsyncObjectThrottle
<I
> *throttle
= new AsyncObjectThrottle
<I
>(
111 this, image_ctx
, context_factory
, this->create_callback_context(), &m_prog_ctx
,
112 0, m_overlap_objects
);
113 throttle
->start_ops(image_ctx
.concurrent_management_ops
);
116 template <typename I
>
117 bool FlattenRequest
<I
>::send_update_header() {
118 I
&image_ctx
= this->m_image_ctx
;
119 assert(image_ctx
.owner_lock
.is_locked());
120 CephContext
*cct
= image_ctx
.cct
;
122 ldout(cct
, 5) << this << " send_update_header" << dendl
;
123 m_state
= STATE_UPDATE_HEADER
;
125 // should have been canceled prior to releasing lock
126 assert(image_ctx
.exclusive_lock
== nullptr ||
127 image_ctx
.exclusive_lock
->is_lock_owner());
130 RWLock::RLocker
parent_locker(image_ctx
.parent_lock
);
131 // stop early if the parent went away - it just means
132 // another flatten finished first, so this one is useless.
133 if (!image_ctx
.parent
) {
134 ldout(cct
, 5) << "image already flattened" << dendl
;
137 m_parent_spec
= image_ctx
.parent_md
.spec
;
139 m_ignore_enoent
= true;
141 // remove parent from this (base) image
142 librados::ObjectWriteOperation op
;
143 cls_client::remove_parent(&op
);
145 librados::AioCompletion
*rados_completion
= this->create_callback_completion();
146 int r
= image_ctx
.md_ctx
.aio_operate(image_ctx
.header_oid
,
147 rados_completion
, &op
);
149 rados_completion
->release();
153 template <typename I
>
154 bool FlattenRequest
<I
>::send_update_children() {
155 I
&image_ctx
= this->m_image_ctx
;
156 assert(image_ctx
.owner_lock
.is_locked());
157 CephContext
*cct
= image_ctx
.cct
;
159 // should have been canceled prior to releasing lock
160 assert(image_ctx
.exclusive_lock
== nullptr ||
161 image_ctx
.exclusive_lock
->is_lock_owner());
163 // if there are no snaps, remove from the children object as well
164 // (if snapshots remain, they have their own parent info, and the child
165 // will be removed when the last snap goes away)
166 RWLock::RLocker
snap_locker(image_ctx
.snap_lock
);
167 if ((image_ctx
.features
& RBD_FEATURE_DEEP_FLATTEN
) == 0 &&
168 !image_ctx
.snaps
.empty()) {
172 ldout(cct
, 2) << "removing child from children list..." << dendl
;
173 m_state
= STATE_UPDATE_CHILDREN
;
175 librados::ObjectWriteOperation op
;
176 cls_client::remove_child(&op
, m_parent_spec
, image_ctx
.id
);
178 librados::AioCompletion
*rados_completion
= this->create_callback_completion();
179 int r
= image_ctx
.md_ctx
.aio_operate(RBD_CHILDREN
, rados_completion
,
182 rados_completion
->release();
186 } // namespace operation
187 } // namespace librbd
189 template class librbd::operation::FlattenRequest
<librbd::ImageCtx
>;