]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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/operation/TrimRequest.h" | |
5 | #include "librbd/AsyncObjectThrottle.h" | |
6 | #include "librbd/ExclusiveLock.h" | |
7 | #include "librbd/ImageCtx.h" | |
8 | #include "librbd/internal.h" | |
9 | #include "librbd/ObjectMap.h" | |
10 | #include "librbd/Utils.h" | |
11 | #include "librbd/io/ObjectRequest.h" | |
12 | #include "common/ContextCompletion.h" | |
13 | #include "common/dout.h" | |
14 | #include "common/errno.h" | |
15 | #include "osdc/Striper.h" | |
16 | ||
17 | #include <boost/bind.hpp> | |
18 | #include <boost/lambda/bind.hpp> | |
19 | #include <boost/lambda/construct.hpp> | |
20 | #include <boost/scope_exit.hpp> | |
21 | ||
22 | #define dout_subsys ceph_subsys_rbd | |
23 | #undef dout_prefix | |
24 | #define dout_prefix *_dout << "librbd::TrimRequest: " | |
25 | ||
26 | namespace librbd { | |
27 | namespace operation { | |
28 | ||
29 | template <typename I> | |
30 | class C_CopyupObject : public C_AsyncObjectThrottle<I> { | |
31 | public: | |
32 | C_CopyupObject(AsyncObjectThrottle<I> &throttle, I *image_ctx, | |
33 | ::SnapContext snapc, uint64_t object_no) | |
34 | : C_AsyncObjectThrottle<I>(throttle, *image_ctx), m_snapc(snapc), | |
35 | m_object_no(object_no) | |
36 | { | |
37 | } | |
38 | ||
39 | int send() override { | |
40 | I &image_ctx = this->m_image_ctx; | |
41 | assert(image_ctx.owner_lock.is_locked()); | |
42 | assert(image_ctx.exclusive_lock == nullptr || | |
43 | image_ctx.exclusive_lock->is_lock_owner()); | |
44 | ||
45 | string oid = image_ctx.get_object_name(m_object_no); | |
46 | ldout(image_ctx.cct, 10) << "removing (with copyup) " << oid << dendl; | |
47 | ||
b32b8144 FG |
48 | auto req = io::ObjectRequest<I>::create_discard( |
49 | &image_ctx, oid, m_object_no, 0, image_ctx.layout.object_size, m_snapc, | |
50 | false, false, {}, this); | |
7c673cae FG |
51 | req->send(); |
52 | return 0; | |
53 | } | |
54 | private: | |
55 | ::SnapContext m_snapc; | |
56 | uint64_t m_object_no; | |
57 | }; | |
58 | ||
59 | template <typename I> | |
60 | class C_RemoveObject : public C_AsyncObjectThrottle<I> { | |
61 | public: | |
3efd9988 | 62 | C_RemoveObject(AsyncObjectThrottle<I> &throttle, I *image_ctx, |
7c673cae FG |
63 | uint64_t object_no) |
64 | : C_AsyncObjectThrottle<I>(throttle, *image_ctx), m_object_no(object_no) | |
65 | { | |
66 | } | |
67 | ||
68 | int send() override { | |
69 | I &image_ctx = this->m_image_ctx; | |
70 | assert(image_ctx.owner_lock.is_locked()); | |
71 | assert(image_ctx.exclusive_lock == nullptr || | |
72 | image_ctx.exclusive_lock->is_lock_owner()); | |
73 | ||
74 | { | |
75 | RWLock::RLocker snap_locker(image_ctx.snap_lock); | |
76 | if (image_ctx.object_map != nullptr && | |
77 | !image_ctx.object_map->object_may_exist(m_object_no)) { | |
78 | return 1; | |
79 | } | |
80 | } | |
81 | ||
82 | string oid = image_ctx.get_object_name(m_object_no); | |
83 | ldout(image_ctx.cct, 10) << "removing " << oid << dendl; | |
84 | ||
85 | librados::AioCompletion *rados_completion = | |
86 | util::create_rados_callback(this); | |
87 | int r = image_ctx.data_ctx.aio_remove(oid, rados_completion); | |
88 | assert(r == 0); | |
89 | rados_completion->release(); | |
90 | return 0; | |
91 | } | |
92 | ||
93 | private: | |
94 | uint64_t m_object_no; | |
95 | }; | |
96 | ||
97 | template <typename I> | |
98 | TrimRequest<I>::TrimRequest(I &image_ctx, Context *on_finish, | |
99 | uint64_t original_size, uint64_t new_size, | |
100 | ProgressContext &prog_ctx) | |
101 | : AsyncRequest<I>(image_ctx, on_finish), m_new_size(new_size), | |
102 | m_prog_ctx(prog_ctx) | |
103 | { | |
104 | uint64_t period = image_ctx.get_stripe_period(); | |
105 | uint64_t new_num_periods = ((m_new_size + period - 1) / period); | |
106 | m_delete_off = MIN(new_num_periods * period, original_size); | |
107 | // first object we can delete free and clear | |
108 | m_delete_start = new_num_periods * image_ctx.get_stripe_count(); | |
3efd9988 | 109 | m_delete_start_min = m_delete_start; |
7c673cae FG |
110 | m_num_objects = Striper::get_num_objects(image_ctx.layout, original_size); |
111 | ||
112 | CephContext *cct = image_ctx.cct; | |
113 | ldout(cct, 10) << this << " trim image " << original_size << " -> " | |
114 | << m_new_size << " periods " << new_num_periods | |
115 | << " discard to offset " << m_delete_off | |
116 | << " delete objects " << m_delete_start | |
117 | << " to " << m_num_objects << dendl; | |
118 | } | |
119 | ||
120 | template <typename I> | |
121 | bool TrimRequest<I>::should_complete(int r) | |
122 | { | |
123 | I &image_ctx = this->m_image_ctx; | |
124 | CephContext *cct = image_ctx.cct; | |
125 | ldout(cct, 5) << this << " should_complete: r=" << r << dendl; | |
126 | if (r == -ERESTART) { | |
127 | ldout(cct, 5) << "trim operation interrupted" << dendl; | |
128 | return true; | |
129 | } else if (r < 0) { | |
130 | lderr(cct) << "trim encountered an error: " << cpp_strerror(r) << dendl; | |
131 | return true; | |
132 | } | |
133 | ||
134 | RWLock::RLocker owner_lock(image_ctx.owner_lock); | |
135 | switch (m_state) { | |
3efd9988 FG |
136 | case STATE_PRE_TRIM: |
137 | ldout(cct, 5) << " PRE_TRIM" << dendl; | |
7c673cae FG |
138 | send_copyup_objects(); |
139 | break; | |
140 | ||
141 | case STATE_COPYUP_OBJECTS: | |
142 | ldout(cct, 5) << " COPYUP_OBJECTS" << dendl; | |
7c673cae FG |
143 | send_remove_objects(); |
144 | break; | |
145 | ||
146 | case STATE_REMOVE_OBJECTS: | |
147 | ldout(cct, 5) << " REMOVE_OBJECTS" << dendl; | |
3efd9988 | 148 | send_post_trim(); |
7c673cae FG |
149 | break; |
150 | ||
3efd9988 FG |
151 | case STATE_POST_TRIM: |
152 | ldout(cct, 5) << " POST_TRIM" << dendl; | |
7c673cae FG |
153 | send_clean_boundary(); |
154 | break; | |
155 | ||
156 | case STATE_CLEAN_BOUNDARY: | |
157 | ldout(cct, 5) << "CLEAN_BOUNDARY" << dendl; | |
158 | send_finish(0); | |
159 | break; | |
160 | ||
161 | case STATE_FINISHED: | |
162 | ldout(cct, 5) << "FINISHED" << dendl; | |
163 | return true; | |
164 | ||
165 | default: | |
166 | lderr(cct) << "invalid state: " << m_state << dendl; | |
167 | assert(false); | |
168 | break; | |
169 | } | |
170 | return false; | |
171 | } | |
172 | ||
173 | template <typename I> | |
174 | void TrimRequest<I>::send() { | |
3efd9988 | 175 | send_pre_trim(); |
7c673cae FG |
176 | } |
177 | ||
178 | template<typename I> | |
3efd9988 | 179 | void TrimRequest<I>::send_pre_trim() { |
7c673cae FG |
180 | I &image_ctx = this->m_image_ctx; |
181 | assert(image_ctx.owner_lock.is_locked()); | |
182 | ||
3efd9988 FG |
183 | if (m_delete_start >= m_num_objects) { |
184 | send_clean_boundary(); | |
185 | return; | |
186 | } | |
7c673cae | 187 | |
7c673cae FG |
188 | { |
189 | RWLock::RLocker snap_locker(image_ctx.snap_lock); | |
3efd9988 FG |
190 | if (image_ctx.object_map != nullptr) { |
191 | ldout(image_ctx.cct, 5) << this << " send_pre_trim: " | |
192 | << " delete_start_min=" << m_delete_start_min | |
193 | << " num_objects=" << m_num_objects << dendl; | |
194 | m_state = STATE_PRE_TRIM; | |
7c673cae | 195 | |
3efd9988 | 196 | assert(image_ctx.exclusive_lock->is_lock_owner()); |
7c673cae | 197 | |
3efd9988 FG |
198 | RWLock::WLocker object_map_locker(image_ctx.object_map_lock); |
199 | if (image_ctx.object_map->template aio_update<AsyncRequest<I> >( | |
200 | CEPH_NOSNAP, m_delete_start_min, m_num_objects, OBJECT_PENDING, | |
201 | OBJECT_EXISTS, {}, this)) { | |
202 | return; | |
203 | } | |
204 | } | |
205 | } | |
7c673cae | 206 | |
3efd9988 | 207 | send_copyup_objects(); |
7c673cae FG |
208 | } |
209 | ||
210 | template<typename I> | |
3efd9988 | 211 | void TrimRequest<I>::send_copyup_objects() { |
7c673cae FG |
212 | I &image_ctx = this->m_image_ctx; |
213 | assert(image_ctx.owner_lock.is_locked()); | |
214 | ||
3efd9988 | 215 | ::SnapContext snapc; |
7c673cae FG |
216 | bool has_snapshots; |
217 | uint64_t parent_overlap; | |
218 | { | |
219 | RWLock::RLocker snap_locker(image_ctx.snap_lock); | |
220 | RWLock::RLocker parent_locker(image_ctx.parent_lock); | |
221 | ||
3efd9988 | 222 | snapc = image_ctx.snapc; |
7c673cae FG |
223 | has_snapshots = !image_ctx.snaps.empty(); |
224 | int r = image_ctx.get_parent_overlap(CEPH_NOSNAP, &parent_overlap); | |
225 | assert(r == 0); | |
226 | } | |
227 | ||
228 | // copyup is only required for portion of image that overlaps parent | |
3efd9988 FG |
229 | uint64_t copyup_end = Striper::get_num_objects(image_ctx.layout, |
230 | parent_overlap); | |
7c673cae FG |
231 | |
232 | // TODO: protect against concurrent shrink and snap create? | |
233 | // skip to remove if no copyup is required. | |
3efd9988 FG |
234 | if (copyup_end <= m_delete_start || !has_snapshots) { |
235 | send_remove_objects(); | |
7c673cae FG |
236 | return; |
237 | } | |
238 | ||
3efd9988 FG |
239 | uint64_t copyup_start = m_delete_start; |
240 | m_delete_start = copyup_end; | |
7c673cae | 241 | |
3efd9988 FG |
242 | ldout(image_ctx.cct, 5) << this << " send_copyup_objects: " |
243 | << " start object=" << copyup_start << ", " | |
244 | << " end object=" << copyup_end << dendl; | |
245 | m_state = STATE_COPYUP_OBJECTS; | |
7c673cae | 246 | |
3efd9988 FG |
247 | Context *ctx = this->create_callback_context(); |
248 | typename AsyncObjectThrottle<I>::ContextFactory context_factory( | |
249 | boost::lambda::bind(boost::lambda::new_ptr<C_CopyupObject<I> >(), | |
250 | boost::lambda::_1, &image_ctx, snapc, boost::lambda::_2)); | |
251 | AsyncObjectThrottle<I> *throttle = new AsyncObjectThrottle<I>( | |
252 | this, image_ctx, context_factory, ctx, &m_prog_ctx, copyup_start, | |
253 | copyup_end); | |
254 | throttle->start_ops(image_ctx.concurrent_management_ops); | |
7c673cae FG |
255 | } |
256 | ||
257 | template <typename I> | |
3efd9988 | 258 | void TrimRequest<I>::send_remove_objects() { |
7c673cae FG |
259 | I &image_ctx = this->m_image_ctx; |
260 | assert(image_ctx.owner_lock.is_locked()); | |
7c673cae | 261 | |
3efd9988 FG |
262 | ldout(image_ctx.cct, 5) << this << " send_remove_objects: " |
263 | << " delete_start=" << m_delete_start | |
264 | << " num_objects=" << m_num_objects << dendl; | |
265 | m_state = STATE_REMOVE_OBJECTS; | |
7c673cae | 266 | |
3efd9988 FG |
267 | Context *ctx = this->create_callback_context(); |
268 | typename AsyncObjectThrottle<I>::ContextFactory context_factory( | |
269 | boost::lambda::bind(boost::lambda::new_ptr<C_RemoveObject<I> >(), | |
270 | boost::lambda::_1, &image_ctx, boost::lambda::_2)); | |
271 | AsyncObjectThrottle<I> *throttle = new AsyncObjectThrottle<I>( | |
272 | this, image_ctx, context_factory, ctx, &m_prog_ctx, m_delete_start, | |
273 | m_num_objects); | |
274 | throttle->start_ops(image_ctx.concurrent_management_ops); | |
7c673cae FG |
275 | } |
276 | ||
277 | template<typename I> | |
3efd9988 | 278 | void TrimRequest<I>::send_post_trim() { |
7c673cae FG |
279 | I &image_ctx = this->m_image_ctx; |
280 | assert(image_ctx.owner_lock.is_locked()); | |
281 | ||
282 | { | |
283 | RWLock::RLocker snap_locker(image_ctx.snap_lock); | |
284 | if (image_ctx.object_map != nullptr) { | |
3efd9988 FG |
285 | ldout(image_ctx.cct, 5) << this << " send_post_trim:" |
286 | << " delete_start_min=" << m_delete_start_min | |
287 | << " num_objects=" << m_num_objects << dendl; | |
288 | m_state = STATE_POST_TRIM; | |
7c673cae FG |
289 | |
290 | assert(image_ctx.exclusive_lock->is_lock_owner()); | |
291 | ||
7c673cae FG |
292 | RWLock::WLocker object_map_locker(image_ctx.object_map_lock); |
293 | if (image_ctx.object_map->template aio_update<AsyncRequest<I> >( | |
3efd9988 | 294 | CEPH_NOSNAP, m_delete_start_min, m_num_objects, OBJECT_NONEXISTENT, |
31f18b77 | 295 | OBJECT_PENDING, {}, this)) { |
7c673cae FG |
296 | return; |
297 | } | |
298 | } | |
299 | } | |
300 | ||
7c673cae FG |
301 | send_clean_boundary(); |
302 | } | |
303 | ||
304 | template <typename I> | |
305 | void TrimRequest<I>::send_clean_boundary() { | |
306 | I &image_ctx = this->m_image_ctx; | |
307 | assert(image_ctx.owner_lock.is_locked()); | |
308 | CephContext *cct = image_ctx.cct; | |
309 | if (m_delete_off <= m_new_size) { | |
310 | send_finish(0); | |
311 | return; | |
312 | } | |
313 | ||
314 | // should have been canceled prior to releasing lock | |
315 | assert(image_ctx.exclusive_lock == nullptr || | |
316 | image_ctx.exclusive_lock->is_lock_owner()); | |
317 | uint64_t delete_len = m_delete_off - m_new_size; | |
318 | ldout(image_ctx.cct, 5) << this << " send_clean_boundary: " | |
319 | << " delete_off=" << m_delete_off | |
320 | << " length=" << delete_len << dendl; | |
321 | m_state = STATE_CLEAN_BOUNDARY; | |
322 | ||
323 | ::SnapContext snapc; | |
324 | { | |
325 | RWLock::RLocker snap_locker(image_ctx.snap_lock); | |
326 | snapc = image_ctx.snapc; | |
327 | } | |
328 | ||
329 | // discard the weird boundary | |
330 | std::vector<ObjectExtent> extents; | |
331 | Striper::file_to_extents(cct, image_ctx.format_string, | |
332 | &image_ctx.layout, m_new_size, delete_len, 0, | |
333 | extents); | |
334 | ||
335 | ContextCompletion *completion = | |
336 | new ContextCompletion(this->create_async_callback_context(), true); | |
337 | for (vector<ObjectExtent>::iterator p = extents.begin(); | |
338 | p != extents.end(); ++p) { | |
339 | ldout(cct, 20) << " ex " << *p << dendl; | |
340 | Context *req_comp = new C_ContextCompletion(*completion); | |
341 | ||
7c673cae | 342 | if (p->offset == 0) { |
b32b8144 FG |
343 | // treat as a full object delete on the boundary |
344 | p->length = image_ctx.layout.object_size; | |
7c673cae | 345 | } |
b32b8144 FG |
346 | auto req = io::ObjectRequest<I>::create_discard(&image_ctx, p->oid.name, |
347 | p->objectno, p->offset, | |
348 | p->length, snapc, false, | |
349 | true, {}, req_comp); | |
7c673cae FG |
350 | req->send(); |
351 | } | |
352 | completion->finish_adding_requests(); | |
353 | } | |
354 | ||
355 | template <typename I> | |
356 | void TrimRequest<I>::send_finish(int r) { | |
357 | m_state = STATE_FINISHED; | |
358 | this->async_complete(r); | |
359 | } | |
360 | ||
361 | } // namespace operation | |
362 | } // namespace librbd | |
363 | ||
364 | template class librbd::operation::TrimRequest<librbd::ImageCtx>; |