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/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"
10 #include "librbd/io/AioCompletion.h"
11 #include "librbd/io/ImageDispatchSpec.h"
12 #include "librbd/io/ImageRequestWQ.h"
13 #include "librbd/io/ObjectDispatcher.h"
14 #include "librbd/operation/TrimRequest.h"
15 #include "common/dout.h"
16 #include "common/errno.h"
18 #define dout_subsys ceph_subsys_rbd
20 #define dout_prefix *_dout << "librbd::operation::ResizeRequest: " << this \
21 << " " << __func__ << ": "
26 using util::create_async_context_callback
;
27 using util::create_context_callback
;
28 using util::create_rados_callback
;
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
),
42 ResizeRequest
<I
>::~ResizeRequest() {
43 I
&image_ctx
= this->m_image_ctx
;
44 ResizeRequest
*next_req
= NULL
;
46 RWLock::WLocker
snap_locker(image_ctx
.snap_lock
);
47 ceph_assert(m_xlist_item
.remove_myself());
48 if (!image_ctx
.resize_reqs
.empty()) {
49 next_req
= image_ctx
.resize_reqs
.front();
53 if (next_req
!= NULL
) {
54 RWLock::RLocker
owner_locker(image_ctx
.owner_lock
);
60 void ResizeRequest
<I
>::send() {
61 I
&image_ctx
= this->m_image_ctx
;
62 ceph_assert(image_ctx
.owner_lock
.is_locked());
65 RWLock::WLocker
snap_locker(image_ctx
.snap_lock
);
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) {
73 ceph_assert(image_ctx
.resize_reqs
.front() == this);
74 m_original_size
= image_ctx
.size
;
75 compute_parent_overlap();
82 void ResizeRequest
<I
>::send_op() {
83 I
&image_ctx
= this->m_image_ctx
;
84 ceph_assert(image_ctx
.owner_lock
.is_locked());
86 if (this->is_canceled()) {
87 this->async_complete(-ERESTART
);
89 send_pre_block_writes();
94 void ResizeRequest
<I
>::send_pre_block_writes() {
95 I
&image_ctx
= this->m_image_ctx
;
96 CephContext
*cct
= image_ctx
.cct
;
97 ldout(cct
, 5) << dendl
;
99 image_ctx
.io_work_queue
->block_writes(create_context_callback
<
100 ResizeRequest
<I
>, &ResizeRequest
<I
>::handle_pre_block_writes
>(this));
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
;
107 ldout(cct
, 5) << "r=" << *result
<< dendl
;
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
);
115 return send_append_op_event();
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
;
123 if (m_new_size
< m_original_size
&& !m_allow_shrink
) {
124 ldout(cct
, 1) << "shrinking the image is not permitted" << dendl
;
125 image_ctx
.io_work_queue
->unblock_writes();
126 this->async_complete(-EINVAL
);
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();
135 ldout(cct
, 5) << dendl
;
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
;
143 ldout(cct
, 5) << "r=" << *result
<< dendl
;
146 lderr(cct
) << "failed to commit journal entry: " << cpp_strerror(*result
)
148 image_ctx
.io_work_queue
->unblock_writes();
149 return this->create_context_finisher(*result
);
152 return send_grow_object_map();
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
;
159 ldout(cct
, 5) << dendl
;
161 RWLock::RLocker
owner_locker(image_ctx
.owner_lock
);
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
);
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
;
173 ldout(cct
, 5) << "r=" << *result
<< dendl
;
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
);
183 send_post_block_writes();
187 template <typename I
>
188 void ResizeRequest
<I
>::send_flush_cache() {
189 I
&image_ctx
= this->m_image_ctx
;
191 CephContext
*cct
= image_ctx
.cct
;
192 ldout(cct
, 5) << dendl
;
194 RWLock::RLocker
owner_locker(image_ctx
.owner_lock
);
195 auto ctx
= create_context_callback
<
196 ResizeRequest
<I
>, &ResizeRequest
<I
>::handle_flush_cache
>(this);
197 auto aio_comp
= io::AioCompletion::create_and_start(
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
, {});
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
;
209 ldout(cct
, 5) << "r=" << *result
<< dendl
;
212 lderr(cct
) << "failed to flush cache: " << cpp_strerror(*result
) << dendl
;
213 return this->create_context_finisher(*result
);
216 send_invalidate_cache();
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
;
224 ldout(cct
, 5) << dendl
;
226 // need to invalidate since we're deleting objects, and
227 // ObjectCacher doesn't track non-existent objects
228 RWLock::RLocker
owner_locker(image_ctx
.owner_lock
);
229 image_ctx
.io_object_dispatcher
->invalidate_cache(create_context_callback
<
230 ResizeRequest
<I
>, &ResizeRequest
<I
>::handle_invalidate_cache
>(this));
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
;
237 ldout(cct
, 5) << "r=" << *result
<< dendl
;
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
)
245 return this->create_context_finisher(*result
);
252 template <typename I
>
253 Context
*ResizeRequest
<I
>::send_grow_object_map() {
254 I
&image_ctx
= this->m_image_ctx
;
257 RWLock::WLocker
snap_locker(image_ctx
.snap_lock
);
258 m_shrink_size_visible
= true;
261 if (m_original_size
== m_new_size
) {
262 image_ctx
.io_work_queue
->unblock_writes();
263 return this->create_context_finisher(0);
264 } else if (m_new_size
< m_original_size
) {
265 image_ctx
.io_work_queue
->unblock_writes();
270 image_ctx
.owner_lock
.get_read();
271 image_ctx
.snap_lock
.get_read();
272 if (image_ctx
.object_map
== nullptr) {
273 image_ctx
.snap_lock
.put_read();
274 image_ctx
.owner_lock
.put_read();
276 // IO is still blocked
277 send_update_header();
281 CephContext
*cct
= image_ctx
.cct
;
282 ldout(cct
, 5) << dendl
;
284 // should have been canceled prior to releasing lock
285 ceph_assert(image_ctx
.exclusive_lock
== nullptr ||
286 image_ctx
.exclusive_lock
->is_lock_owner());
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));
291 image_ctx
.snap_lock
.put_read();
292 image_ctx
.owner_lock
.put_read();
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
;
300 ldout(cct
, 5) << "r=" << *result
<< dendl
;
303 lderr(cct
) << "failed to resize object map: "
304 << cpp_strerror(*result
) << dendl
;
305 image_ctx
.io_work_queue
->unblock_writes();
306 return this->create_context_finisher(*result
);
309 // IO is still blocked
310 send_update_header();
314 template <typename I
>
315 Context
*ResizeRequest
<I
>::send_shrink_object_map() {
316 I
&image_ctx
= this->m_image_ctx
;
318 image_ctx
.owner_lock
.get_read();
319 image_ctx
.snap_lock
.get_read();
320 if (image_ctx
.object_map
== nullptr || m_new_size
> m_original_size
) {
321 image_ctx
.snap_lock
.put_read();
322 image_ctx
.owner_lock
.put_read();
324 update_size_and_overlap();
325 return this->create_context_finisher(0);
328 CephContext
*cct
= image_ctx
.cct
;
329 ldout(cct
, 5) << "original_size=" << m_original_size
<< ", "
330 << "new_size=" << m_new_size
<< dendl
;
332 // should have been canceled prior to releasing lock
333 ceph_assert(image_ctx
.exclusive_lock
== nullptr ||
334 image_ctx
.exclusive_lock
->is_lock_owner());
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));
339 image_ctx
.snap_lock
.put_read();
340 image_ctx
.owner_lock
.put_read();
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
;
348 ldout(cct
, 5) << "r=" << *result
<< dendl
;
351 lderr(cct
) << "failed to resize object map: "
352 << cpp_strerror(*result
) << dendl
;
353 image_ctx
.io_work_queue
->unblock_writes();
354 return this->create_context_finisher(*result
);
357 update_size_and_overlap();
358 return this->create_context_finisher(0);
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
;
365 ldout(cct
, 5) << dendl
;
367 RWLock::RLocker
owner_locker(image_ctx
.owner_lock
);
368 image_ctx
.io_work_queue
->block_writes(create_context_callback
<
369 ResizeRequest
<I
>, &ResizeRequest
<I
>::handle_post_block_writes
>(this));
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
;
376 ldout(cct
, 5) << "r=" << *result
<< dendl
;
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
);
385 send_update_header();
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
;
393 ldout(cct
, 5) << "original_size=" << m_original_size
<< ", "
394 << "new_size=" << m_new_size
<< dendl
;;
396 // should have been canceled prior to releasing lock
397 RWLock::RLocker
owner_locker(image_ctx
.owner_lock
);
398 ceph_assert(image_ctx
.exclusive_lock
== nullptr ||
399 image_ctx
.exclusive_lock
->is_lock_owner());
401 librados::ObjectWriteOperation op
;
402 if (image_ctx
.old_format
) {
403 // rewrite only the size field of the header
404 // NOTE: format 1 image headers are not stored in fixed endian format
406 bl
.append(reinterpret_cast<const char*>(&m_new_size
), sizeof(m_new_size
));
407 op
.write(offsetof(rbd_obj_header_ondisk
, image_size
), bl
);
409 cls_client::set_size(&op
, m_new_size
);
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
);
417 rados_completion
->release();
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
;
424 ldout(cct
, 5) << "r=" << *result
<< dendl
;
427 lderr(cct
) << "failed to update image header: " << cpp_strerror(*result
)
429 image_ctx
.io_work_queue
->unblock_writes();
430 return this->create_context_finisher(*result
);
433 return send_shrink_object_map();
436 template <typename I
>
437 void ResizeRequest
<I
>::compute_parent_overlap() {
438 I
&image_ctx
= this->m_image_ctx
;
439 RWLock::RLocker
l2(image_ctx
.parent_lock
);
440 if (image_ctx
.parent
== NULL
) {
441 m_new_parent_overlap
= 0;
443 m_new_parent_overlap
= std::min(m_new_size
, image_ctx
.parent_md
.overlap
);
447 template <typename I
>
448 void ResizeRequest
<I
>::update_size_and_overlap() {
449 I
&image_ctx
= this->m_image_ctx
;
451 RWLock::WLocker
snap_locker(image_ctx
.snap_lock
);
452 image_ctx
.size
= m_new_size
;
454 RWLock::WLocker
parent_locker(image_ctx
.parent_lock
);
455 if (image_ctx
.parent
!= NULL
&& m_new_size
< m_original_size
) {
456 image_ctx
.parent_md
.overlap
= m_new_parent_overlap
;
460 // blocked by PRE_BLOCK_WRITES (grow) or POST_BLOCK_WRITES (shrink) state
461 image_ctx
.io_work_queue
->unblock_writes();
464 } // namespace operation
465 } // namespace librbd
467 template class librbd::operation::ResizeRequest
<librbd::ImageCtx
>;