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