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