]>
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/ResizeRequest.h" | |
5 | #include "librbd/ExclusiveLock.h" | |
6 | #include "librbd/ImageCtx.h" | |
7 | #include "librbd/internal.h" | |
8 | #include "librbd/ObjectMap.h" | |
9 | #include "librbd/Utils.h" | |
11fdf7f2 TL |
10 | #include "librbd/io/AioCompletion.h" |
11 | #include "librbd/io/ImageDispatchSpec.h" | |
7c673cae | 12 | #include "librbd/io/ImageRequestWQ.h" |
11fdf7f2 | 13 | #include "librbd/io/ObjectDispatcher.h" |
7c673cae FG |
14 | #include "librbd/operation/TrimRequest.h" |
15 | #include "common/dout.h" | |
16 | #include "common/errno.h" | |
17 | ||
18 | #define dout_subsys ceph_subsys_rbd | |
19 | #undef dout_prefix | |
494da23a TL |
20 | #define dout_prefix *_dout << "librbd::operation::ResizeRequest: " << this \ |
21 | << " " << __func__ << ": " | |
7c673cae FG |
22 | |
23 | namespace librbd { | |
24 | namespace operation { | |
25 | ||
26 | using util::create_async_context_callback; | |
27 | using util::create_context_callback; | |
28 | using util::create_rados_callback; | |
29 | ||
30 | template <typename I> | |
31 | ResizeRequest<I>::ResizeRequest(I &image_ctx, Context *on_finish, | |
32 | uint64_t new_size, bool allow_shrink, ProgressContext &prog_ctx, | |
33 | uint64_t journal_op_tid, bool disable_journal) | |
34 | : Request<I>(image_ctx, on_finish, journal_op_tid), | |
35 | m_original_size(0), m_new_size(new_size), m_allow_shrink(allow_shrink), | |
36 | m_prog_ctx(prog_ctx), m_new_parent_overlap(0), m_disable_journal(disable_journal), | |
37 | m_xlist_item(this) | |
38 | { | |
39 | } | |
40 | ||
41 | template <typename I> | |
42 | ResizeRequest<I>::~ResizeRequest() { | |
43 | I &image_ctx = this->m_image_ctx; | |
44 | ResizeRequest *next_req = NULL; | |
45 | { | |
9f95a23c | 46 | std::unique_lock image_locker{image_ctx.image_lock}; |
11fdf7f2 | 47 | ceph_assert(m_xlist_item.remove_myself()); |
7c673cae FG |
48 | if (!image_ctx.resize_reqs.empty()) { |
49 | next_req = image_ctx.resize_reqs.front(); | |
50 | } | |
51 | } | |
52 | ||
53 | if (next_req != NULL) { | |
9f95a23c | 54 | std::shared_lock owner_locker{image_ctx.owner_lock}; |
7c673cae FG |
55 | next_req->send(); |
56 | } | |
57 | } | |
58 | ||
59 | template <typename I> | |
60 | void ResizeRequest<I>::send() { | |
61 | I &image_ctx = this->m_image_ctx; | |
9f95a23c | 62 | ceph_assert(ceph_mutex_is_locked(image_ctx.owner_lock)); |
7c673cae FG |
63 | |
64 | { | |
9f95a23c | 65 | std::unique_lock image_locker{image_ctx.image_lock}; |
7c673cae FG |
66 | if (!m_xlist_item.is_on_list()) { |
67 | image_ctx.resize_reqs.push_back(&m_xlist_item); | |
68 | if (image_ctx.resize_reqs.front() != this) { | |
69 | return; | |
70 | } | |
71 | } | |
72 | ||
11fdf7f2 | 73 | ceph_assert(image_ctx.resize_reqs.front() == this); |
7c673cae FG |
74 | m_original_size = image_ctx.size; |
75 | compute_parent_overlap(); | |
76 | } | |
77 | ||
78 | Request<I>::send(); | |
79 | } | |
80 | ||
81 | template <typename I> | |
82 | void ResizeRequest<I>::send_op() { | |
9f95a23c TL |
83 | [[maybe_unused]] I &image_ctx = this->m_image_ctx; |
84 | ceph_assert(ceph_mutex_is_locked(image_ctx.owner_lock)); | |
7c673cae FG |
85 | |
86 | if (this->is_canceled()) { | |
87 | this->async_complete(-ERESTART); | |
88 | } else { | |
89 | send_pre_block_writes(); | |
90 | } | |
91 | } | |
92 | ||
93 | template <typename I> | |
94 | void ResizeRequest<I>::send_pre_block_writes() { | |
95 | I &image_ctx = this->m_image_ctx; | |
96 | CephContext *cct = image_ctx.cct; | |
494da23a | 97 | ldout(cct, 5) << dendl; |
7c673cae FG |
98 | |
99 | image_ctx.io_work_queue->block_writes(create_context_callback< | |
100 | ResizeRequest<I>, &ResizeRequest<I>::handle_pre_block_writes>(this)); | |
101 | } | |
102 | ||
103 | template <typename I> | |
104 | Context *ResizeRequest<I>::handle_pre_block_writes(int *result) { | |
105 | I &image_ctx = this->m_image_ctx; | |
106 | CephContext *cct = image_ctx.cct; | |
494da23a | 107 | ldout(cct, 5) << "r=" << *result << dendl; |
7c673cae FG |
108 | |
109 | if (*result < 0) { | |
110 | lderr(cct) << "failed to block writes: " << cpp_strerror(*result) << dendl; | |
111 | image_ctx.io_work_queue->unblock_writes(); | |
112 | return this->create_context_finisher(*result); | |
113 | } | |
114 | ||
115 | return send_append_op_event(); | |
116 | } | |
117 | ||
118 | template <typename I> | |
119 | Context *ResizeRequest<I>::send_append_op_event() { | |
120 | I &image_ctx = this->m_image_ctx; | |
121 | CephContext *cct = image_ctx.cct; | |
122 | ||
123 | if (m_new_size < m_original_size && !m_allow_shrink) { | |
494da23a | 124 | ldout(cct, 1) << "shrinking the image is not permitted" << dendl; |
f64942e4 | 125 | image_ctx.io_work_queue->unblock_writes(); |
7c673cae FG |
126 | this->async_complete(-EINVAL); |
127 | return nullptr; | |
128 | } | |
129 | ||
130 | if (m_disable_journal || !this->template append_op_event< | |
131 | ResizeRequest<I>, &ResizeRequest<I>::handle_append_op_event>(this)) { | |
132 | return send_grow_object_map(); | |
133 | } | |
134 | ||
494da23a | 135 | ldout(cct, 5) << dendl; |
7c673cae FG |
136 | return nullptr; |
137 | } | |
138 | ||
139 | template <typename I> | |
140 | Context *ResizeRequest<I>::handle_append_op_event(int *result) { | |
141 | I &image_ctx = this->m_image_ctx; | |
142 | CephContext *cct = image_ctx.cct; | |
494da23a | 143 | ldout(cct, 5) << "r=" << *result << dendl; |
7c673cae FG |
144 | |
145 | if (*result < 0) { | |
146 | lderr(cct) << "failed to commit journal entry: " << cpp_strerror(*result) | |
147 | << dendl; | |
148 | image_ctx.io_work_queue->unblock_writes(); | |
149 | return this->create_context_finisher(*result); | |
150 | } | |
151 | ||
152 | return send_grow_object_map(); | |
153 | } | |
154 | ||
155 | template <typename I> | |
156 | void ResizeRequest<I>::send_trim_image() { | |
157 | I &image_ctx = this->m_image_ctx; | |
158 | CephContext *cct = image_ctx.cct; | |
494da23a | 159 | ldout(cct, 5) << dendl; |
7c673cae | 160 | |
9f95a23c | 161 | std::shared_lock owner_locker{image_ctx.owner_lock}; |
7c673cae FG |
162 | TrimRequest<I> *req = TrimRequest<I>::create( |
163 | image_ctx, create_context_callback< | |
164 | ResizeRequest<I>, &ResizeRequest<I>::handle_trim_image>(this), | |
165 | m_original_size, m_new_size, m_prog_ctx); | |
166 | req->send(); | |
167 | } | |
168 | ||
169 | template <typename I> | |
170 | Context *ResizeRequest<I>::handle_trim_image(int *result) { | |
171 | I &image_ctx = this->m_image_ctx; | |
172 | CephContext *cct = image_ctx.cct; | |
494da23a | 173 | ldout(cct, 5) << "r=" << *result << dendl; |
7c673cae FG |
174 | |
175 | if (*result == -ERESTART) { | |
176 | ldout(cct, 5) << "resize operation interrupted" << dendl; | |
177 | return this->create_context_finisher(*result); | |
178 | } else if (*result < 0) { | |
179 | lderr(cct) << "failed to trim image: " << cpp_strerror(*result) << dendl; | |
180 | return this->create_context_finisher(*result); | |
181 | } | |
182 | ||
183 | send_post_block_writes(); | |
184 | return nullptr; | |
185 | } | |
186 | ||
187 | template <typename I> | |
188 | void ResizeRequest<I>::send_flush_cache() { | |
189 | I &image_ctx = this->m_image_ctx; | |
7c673cae FG |
190 | |
191 | CephContext *cct = image_ctx.cct; | |
494da23a | 192 | ldout(cct, 5) << dendl; |
7c673cae | 193 | |
9f95a23c | 194 | std::shared_lock owner_locker{image_ctx.owner_lock}; |
11fdf7f2 TL |
195 | auto ctx = create_context_callback< |
196 | ResizeRequest<I>, &ResizeRequest<I>::handle_flush_cache>(this); | |
494da23a | 197 | auto aio_comp = io::AioCompletion::create_and_start( |
11fdf7f2 TL |
198 | ctx, util::get_image_ctx(&image_ctx), io::AIO_TYPE_FLUSH); |
199 | auto req = io::ImageDispatchSpec<I>::create_flush_request( | |
200 | image_ctx, aio_comp, io::FLUSH_SOURCE_INTERNAL, {}); | |
201 | req->send(); | |
202 | delete req; | |
7c673cae FG |
203 | } |
204 | ||
205 | template <typename I> | |
206 | Context *ResizeRequest<I>::handle_flush_cache(int *result) { | |
207 | I &image_ctx = this->m_image_ctx; | |
208 | CephContext *cct = image_ctx.cct; | |
494da23a | 209 | ldout(cct, 5) << "r=" << *result << dendl; |
7c673cae FG |
210 | |
211 | if (*result < 0) { | |
212 | lderr(cct) << "failed to flush cache: " << cpp_strerror(*result) << dendl; | |
213 | return this->create_context_finisher(*result); | |
214 | } | |
215 | ||
216 | send_invalidate_cache(); | |
217 | return nullptr; | |
218 | } | |
219 | ||
220 | template <typename I> | |
221 | void ResizeRequest<I>::send_invalidate_cache() { | |
222 | I &image_ctx = this->m_image_ctx; | |
223 | CephContext *cct = image_ctx.cct; | |
494da23a | 224 | ldout(cct, 5) << dendl; |
7c673cae FG |
225 | |
226 | // need to invalidate since we're deleting objects, and | |
227 | // ObjectCacher doesn't track non-existent objects | |
9f95a23c | 228 | std::shared_lock owner_locker{image_ctx.owner_lock}; |
11fdf7f2 TL |
229 | image_ctx.io_object_dispatcher->invalidate_cache(create_context_callback< |
230 | ResizeRequest<I>, &ResizeRequest<I>::handle_invalidate_cache>(this)); | |
7c673cae FG |
231 | } |
232 | ||
233 | template <typename I> | |
234 | Context *ResizeRequest<I>::handle_invalidate_cache(int *result) { | |
235 | I &image_ctx = this->m_image_ctx; | |
236 | CephContext *cct = image_ctx.cct; | |
494da23a | 237 | ldout(cct, 5) << "r=" << *result << dendl; |
7c673cae FG |
238 | |
239 | // ignore busy error -- writeback was successfully flushed so we might be | |
240 | // wasting some cache space for trimmed objects, but they will get purged | |
241 | // eventually. Most likely cause of the issue was a in-flight cache read | |
242 | if (*result < 0 && *result != -EBUSY) { | |
243 | lderr(cct) << "failed to invalidate cache: " << cpp_strerror(*result) | |
244 | << dendl; | |
245 | return this->create_context_finisher(*result); | |
246 | } | |
247 | ||
248 | send_trim_image(); | |
249 | return nullptr; | |
250 | } | |
251 | ||
252 | template <typename I> | |
253 | Context *ResizeRequest<I>::send_grow_object_map() { | |
254 | I &image_ctx = this->m_image_ctx; | |
255 | ||
256 | { | |
9f95a23c | 257 | std::unique_lock image_locker{image_ctx.image_lock}; |
7c673cae FG |
258 | m_shrink_size_visible = true; |
259 | } | |
7c673cae FG |
260 | |
261 | if (m_original_size == m_new_size) { | |
494da23a | 262 | image_ctx.io_work_queue->unblock_writes(); |
7c673cae FG |
263 | return this->create_context_finisher(0); |
264 | } else if (m_new_size < m_original_size) { | |
494da23a | 265 | image_ctx.io_work_queue->unblock_writes(); |
7c673cae FG |
266 | send_flush_cache(); |
267 | return nullptr; | |
268 | } | |
269 | ||
9f95a23c TL |
270 | image_ctx.owner_lock.lock_shared(); |
271 | image_ctx.image_lock.lock_shared(); | |
7c673cae | 272 | if (image_ctx.object_map == nullptr) { |
9f95a23c TL |
273 | image_ctx.image_lock.unlock_shared(); |
274 | image_ctx.owner_lock.unlock_shared(); | |
7c673cae | 275 | |
494da23a TL |
276 | // IO is still blocked |
277 | send_update_header(); | |
7c673cae FG |
278 | return nullptr; |
279 | } | |
280 | ||
281 | CephContext *cct = image_ctx.cct; | |
494da23a | 282 | ldout(cct, 5) << dendl; |
7c673cae FG |
283 | |
284 | // should have been canceled prior to releasing lock | |
11fdf7f2 TL |
285 | ceph_assert(image_ctx.exclusive_lock == nullptr || |
286 | image_ctx.exclusive_lock->is_lock_owner()); | |
7c673cae FG |
287 | |
288 | image_ctx.object_map->aio_resize( | |
289 | m_new_size, OBJECT_NONEXISTENT, create_context_callback< | |
290 | ResizeRequest<I>, &ResizeRequest<I>::handle_grow_object_map>(this)); | |
9f95a23c TL |
291 | image_ctx.image_lock.unlock_shared(); |
292 | image_ctx.owner_lock.unlock_shared(); | |
7c673cae FG |
293 | return nullptr; |
294 | } | |
295 | ||
296 | template <typename I> | |
297 | Context *ResizeRequest<I>::handle_grow_object_map(int *result) { | |
298 | I &image_ctx = this->m_image_ctx; | |
299 | CephContext *cct = image_ctx.cct; | |
494da23a | 300 | ldout(cct, 5) << "r=" << *result << dendl; |
7c673cae | 301 | |
11fdf7f2 | 302 | if (*result < 0) { |
494da23a | 303 | lderr(cct) << "failed to resize object map: " |
11fdf7f2 | 304 | << cpp_strerror(*result) << dendl; |
494da23a | 305 | image_ctx.io_work_queue->unblock_writes(); |
11fdf7f2 TL |
306 | return this->create_context_finisher(*result); |
307 | } | |
308 | ||
494da23a TL |
309 | // IO is still blocked |
310 | send_update_header(); | |
7c673cae FG |
311 | return nullptr; |
312 | } | |
313 | ||
314 | template <typename I> | |
315 | Context *ResizeRequest<I>::send_shrink_object_map() { | |
316 | I &image_ctx = this->m_image_ctx; | |
317 | ||
9f95a23c TL |
318 | image_ctx.owner_lock.lock_shared(); |
319 | image_ctx.image_lock.lock_shared(); | |
7c673cae | 320 | if (image_ctx.object_map == nullptr || m_new_size > m_original_size) { |
9f95a23c TL |
321 | image_ctx.image_lock.unlock_shared(); |
322 | image_ctx.owner_lock.unlock_shared(); | |
7c673cae FG |
323 | |
324 | update_size_and_overlap(); | |
325 | return this->create_context_finisher(0); | |
326 | } | |
327 | ||
328 | CephContext *cct = image_ctx.cct; | |
494da23a | 329 | ldout(cct, 5) << "original_size=" << m_original_size << ", " |
7c673cae FG |
330 | << "new_size=" << m_new_size << dendl; |
331 | ||
332 | // should have been canceled prior to releasing lock | |
11fdf7f2 TL |
333 | ceph_assert(image_ctx.exclusive_lock == nullptr || |
334 | image_ctx.exclusive_lock->is_lock_owner()); | |
7c673cae FG |
335 | |
336 | image_ctx.object_map->aio_resize( | |
337 | m_new_size, OBJECT_NONEXISTENT, create_context_callback< | |
338 | ResizeRequest<I>, &ResizeRequest<I>::handle_shrink_object_map>(this)); | |
9f95a23c TL |
339 | image_ctx.image_lock.unlock_shared(); |
340 | image_ctx.owner_lock.unlock_shared(); | |
7c673cae FG |
341 | return nullptr; |
342 | } | |
343 | ||
344 | template <typename I> | |
345 | Context *ResizeRequest<I>::handle_shrink_object_map(int *result) { | |
346 | I &image_ctx = this->m_image_ctx; | |
347 | CephContext *cct = image_ctx.cct; | |
494da23a | 348 | ldout(cct, 5) << "r=" << *result << dendl; |
7c673cae | 349 | |
11fdf7f2 | 350 | if (*result < 0) { |
494da23a | 351 | lderr(cct) << "failed to resize object map: " |
11fdf7f2 TL |
352 | << cpp_strerror(*result) << dendl; |
353 | image_ctx.io_work_queue->unblock_writes(); | |
354 | return this->create_context_finisher(*result); | |
355 | } | |
356 | ||
7c673cae | 357 | update_size_and_overlap(); |
7c673cae FG |
358 | return this->create_context_finisher(0); |
359 | } | |
360 | ||
361 | template <typename I> | |
362 | void ResizeRequest<I>::send_post_block_writes() { | |
363 | I &image_ctx = this->m_image_ctx; | |
364 | CephContext *cct = image_ctx.cct; | |
494da23a | 365 | ldout(cct, 5) << dendl; |
7c673cae | 366 | |
9f95a23c | 367 | std::shared_lock owner_locker{image_ctx.owner_lock}; |
7c673cae FG |
368 | image_ctx.io_work_queue->block_writes(create_context_callback< |
369 | ResizeRequest<I>, &ResizeRequest<I>::handle_post_block_writes>(this)); | |
370 | } | |
371 | ||
372 | template <typename I> | |
373 | Context *ResizeRequest<I>::handle_post_block_writes(int *result) { | |
374 | I &image_ctx = this->m_image_ctx; | |
375 | CephContext *cct = image_ctx.cct; | |
494da23a | 376 | ldout(cct, 5) << "r=" << *result << dendl; |
7c673cae FG |
377 | |
378 | if (*result < 0) { | |
379 | image_ctx.io_work_queue->unblock_writes(); | |
380 | lderr(cct) << "failed to block writes prior to header update: " | |
381 | << cpp_strerror(*result) << dendl; | |
382 | return this->create_context_finisher(*result); | |
383 | } | |
384 | ||
385 | send_update_header(); | |
386 | return nullptr; | |
387 | } | |
388 | ||
389 | template <typename I> | |
390 | void ResizeRequest<I>::send_update_header() { | |
391 | I &image_ctx = this->m_image_ctx; | |
392 | CephContext *cct = image_ctx.cct; | |
494da23a | 393 | ldout(cct, 5) << "original_size=" << m_original_size << ", " |
7c673cae FG |
394 | << "new_size=" << m_new_size << dendl;; |
395 | ||
396 | // should have been canceled prior to releasing lock | |
9f95a23c | 397 | std::shared_lock owner_locker{image_ctx.owner_lock}; |
11fdf7f2 TL |
398 | ceph_assert(image_ctx.exclusive_lock == nullptr || |
399 | image_ctx.exclusive_lock->is_lock_owner()); | |
7c673cae FG |
400 | |
401 | librados::ObjectWriteOperation op; | |
402 | if (image_ctx.old_format) { | |
403 | // rewrite only the size field of the header | |
eafe8130 | 404 | ceph_le64 new_size = init_le64(m_new_size); |
7c673cae | 405 | bufferlist bl; |
eafe8130 | 406 | bl.append(reinterpret_cast<const char*>(&new_size), sizeof(new_size)); |
7c673cae FG |
407 | op.write(offsetof(rbd_obj_header_ondisk, image_size), bl); |
408 | } else { | |
409 | cls_client::set_size(&op, m_new_size); | |
410 | } | |
411 | ||
412 | librados::AioCompletion *rados_completion = create_rados_callback< | |
413 | ResizeRequest<I>, &ResizeRequest<I>::handle_update_header>(this); | |
414 | int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, | |
415 | rados_completion, &op); | |
11fdf7f2 | 416 | ceph_assert(r == 0); |
7c673cae FG |
417 | rados_completion->release(); |
418 | } | |
419 | ||
420 | template <typename I> | |
421 | Context *ResizeRequest<I>::handle_update_header(int *result) { | |
422 | I &image_ctx = this->m_image_ctx; | |
423 | CephContext *cct = image_ctx.cct; | |
494da23a | 424 | ldout(cct, 5) << "r=" << *result << dendl; |
7c673cae FG |
425 | |
426 | if (*result < 0) { | |
427 | lderr(cct) << "failed to update image header: " << cpp_strerror(*result) | |
428 | << dendl; | |
429 | image_ctx.io_work_queue->unblock_writes(); | |
430 | return this->create_context_finisher(*result); | |
431 | } | |
432 | ||
433 | return send_shrink_object_map(); | |
434 | } | |
435 | ||
436 | template <typename I> | |
437 | void ResizeRequest<I>::compute_parent_overlap() { | |
438 | I &image_ctx = this->m_image_ctx; | |
9f95a23c TL |
439 | ceph_assert(ceph_mutex_is_locked(image_ctx.image_lock)); |
440 | ||
7c673cae FG |
441 | if (image_ctx.parent == NULL) { |
442 | m_new_parent_overlap = 0; | |
443 | } else { | |
11fdf7f2 | 444 | m_new_parent_overlap = std::min(m_new_size, image_ctx.parent_md.overlap); |
7c673cae FG |
445 | } |
446 | } | |
447 | ||
448 | template <typename I> | |
449 | void ResizeRequest<I>::update_size_and_overlap() { | |
450 | I &image_ctx = this->m_image_ctx; | |
451 | { | |
9f95a23c | 452 | std::unique_lock image_locker{image_ctx.image_lock}; |
7c673cae FG |
453 | image_ctx.size = m_new_size; |
454 | ||
7c673cae FG |
455 | if (image_ctx.parent != NULL && m_new_size < m_original_size) { |
456 | image_ctx.parent_md.overlap = m_new_parent_overlap; | |
457 | } | |
458 | } | |
459 | ||
494da23a | 460 | // blocked by PRE_BLOCK_WRITES (grow) or POST_BLOCK_WRITES (shrink) state |
7c673cae FG |
461 | image_ctx.io_work_queue->unblock_writes(); |
462 | } | |
463 | ||
464 | } // namespace operation | |
465 | } // namespace librbd | |
466 | ||
467 | template class librbd::operation::ResizeRequest<librbd::ImageCtx>; |