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/DisableFeaturesRequest.h"
5 #include "common/dout.h"
6 #include "common/errno.h"
7 #include "cls/rbd/cls_rbd_client.h"
8 #include "librbd/ExclusiveLock.h"
9 #include "librbd/ImageCtx.h"
10 #include "librbd/ImageState.h"
11 #include "librbd/Journal.h"
12 #include "librbd/Utils.h"
13 #include "librbd/image/SetFlagsRequest.h"
14 #include "librbd/io/ImageRequestWQ.h"
15 #include "librbd/journal/RemoveRequest.h"
16 #include "librbd/mirror/DisableRequest.h"
17 #include "librbd/object_map/RemoveRequest.h"
19 #define dout_subsys ceph_subsys_rbd
21 #define dout_prefix *_dout << "librbd::DisableFeaturesRequest: "
26 using util::create_async_context_callback
;
27 using util::create_context_callback
;
28 using util::create_rados_callback
;
31 DisableFeaturesRequest
<I
>::DisableFeaturesRequest(I
&image_ctx
,
33 uint64_t journal_op_tid
,
36 : Request
<I
>(image_ctx
, on_finish
, journal_op_tid
), m_features(features
),
41 void DisableFeaturesRequest
<I
>::send_op() {
42 I
&image_ctx
= this->m_image_ctx
;
43 CephContext
*cct
= image_ctx
.cct
;
44 ceph_assert(ceph_mutex_is_locked(image_ctx
.owner_lock
));
46 ldout(cct
, 20) << this << " " << __func__
<< ": features=" << m_features
53 bool DisableFeaturesRequest
<I
>::should_complete(int r
) {
54 I
&image_ctx
= this->m_image_ctx
;
55 CephContext
*cct
= image_ctx
.cct
;
56 ldout(cct
, 20) << this << " " << __func__
<< " r=" << r
<< dendl
;
59 lderr(cct
) << "encountered error: " << cpp_strerror(r
) << dendl
;
65 void DisableFeaturesRequest
<I
>::send_prepare_lock() {
66 I
&image_ctx
= this->m_image_ctx
;
67 CephContext
*cct
= image_ctx
.cct
;
68 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
70 image_ctx
.state
->prepare_lock(create_async_context_callback(
71 image_ctx
, create_context_callback
<
72 DisableFeaturesRequest
<I
>,
73 &DisableFeaturesRequest
<I
>::handle_prepare_lock
>(this)));
77 Context
*DisableFeaturesRequest
<I
>::handle_prepare_lock(int *result
) {
78 I
&image_ctx
= this->m_image_ctx
;
79 CephContext
*cct
= image_ctx
.cct
;
80 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
83 lderr(cct
) << "failed to lock image: " << cpp_strerror(*result
) << dendl
;
84 return this->create_context_finisher(*result
);
92 void DisableFeaturesRequest
<I
>::send_block_writes() {
93 I
&image_ctx
= this->m_image_ctx
;
94 CephContext
*cct
= image_ctx
.cct
;
95 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
97 std::unique_lock locker
{image_ctx
.owner_lock
};
98 image_ctx
.io_work_queue
->block_writes(create_context_callback
<
99 DisableFeaturesRequest
<I
>,
100 &DisableFeaturesRequest
<I
>::handle_block_writes
>(this));
103 template <typename I
>
104 Context
*DisableFeaturesRequest
<I
>::handle_block_writes(int *result
) {
105 I
&image_ctx
= this->m_image_ctx
;
106 CephContext
*cct
= image_ctx
.cct
;
107 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
110 lderr(cct
) << "failed to block writes: " << cpp_strerror(*result
) << dendl
;
111 return handle_finish(*result
);
113 m_writes_blocked
= true;
116 std::unique_lock locker
{image_ctx
.owner_lock
};
117 // avoid accepting new requests from peers while we manipulate
118 // the image features
119 if (image_ctx
.exclusive_lock
!= nullptr &&
120 (image_ctx
.journal
== nullptr ||
121 !image_ctx
.journal
->is_journal_replaying())) {
122 image_ctx
.exclusive_lock
->block_requests(0);
123 m_requests_blocked
= true;
127 return send_acquire_exclusive_lock(result
);
130 template <typename I
>
131 Context
*DisableFeaturesRequest
<I
>::send_acquire_exclusive_lock(int *result
) {
132 I
&image_ctx
= this->m_image_ctx
;
133 CephContext
*cct
= image_ctx
.cct
;
134 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
137 std::unique_lock locker
{image_ctx
.owner_lock
};
138 // if disabling features w/ exclusive lock supported, we need to
139 // acquire the lock to temporarily block IO against the image
140 if (image_ctx
.exclusive_lock
!= nullptr &&
141 !image_ctx
.exclusive_lock
->is_lock_owner()) {
142 m_acquired_lock
= true;
144 Context
*ctx
= create_context_callback
<
145 DisableFeaturesRequest
<I
>,
146 &DisableFeaturesRequest
<I
>::handle_acquire_exclusive_lock
>(
147 this, image_ctx
.exclusive_lock
);
148 image_ctx
.exclusive_lock
->acquire_lock(ctx
);
153 return handle_acquire_exclusive_lock(result
);
156 template <typename I
>
157 Context
*DisableFeaturesRequest
<I
>::handle_acquire_exclusive_lock(int *result
) {
158 I
&image_ctx
= this->m_image_ctx
;
159 CephContext
*cct
= image_ctx
.cct
;
160 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
162 image_ctx
.owner_lock
.lock_shared();
164 lderr(cct
) << "failed to lock image: " << cpp_strerror(*result
) << dendl
;
165 image_ctx
.owner_lock
.unlock_shared();
166 return handle_finish(*result
);
167 } else if (image_ctx
.exclusive_lock
!= nullptr &&
168 !image_ctx
.exclusive_lock
->is_lock_owner()) {
169 lderr(cct
) << "failed to acquire exclusive lock" << dendl
;
170 *result
= image_ctx
.exclusive_lock
->get_unlocked_op_error();
171 image_ctx
.owner_lock
.unlock_shared();
172 return handle_finish(*result
);
176 m_features
&= image_ctx
.features
;
178 // interlock object-map and fast-diff together
179 if (((m_features
& RBD_FEATURE_OBJECT_MAP
) != 0) ||
180 ((m_features
& RBD_FEATURE_FAST_DIFF
) != 0)) {
181 m_features
|= (RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
);
184 m_new_features
= image_ctx
.features
& ~m_features
;
185 m_features_mask
= m_features
;
187 if ((m_features
& RBD_FEATURE_EXCLUSIVE_LOCK
) != 0) {
188 if ((m_new_features
& RBD_FEATURE_OBJECT_MAP
) != 0 ||
189 (m_new_features
& RBD_FEATURE_JOURNALING
) != 0) {
190 lderr(cct
) << "cannot disable exclusive-lock. object-map "
191 "or journaling must be disabled before "
192 "disabling exclusive-lock." << dendl
;
196 m_features_mask
|= (RBD_FEATURE_OBJECT_MAP
|
197 RBD_FEATURE_FAST_DIFF
|
198 RBD_FEATURE_JOURNALING
);
200 if ((m_features
& RBD_FEATURE_FAST_DIFF
) != 0) {
201 m_disable_flags
|= RBD_FLAG_FAST_DIFF_INVALID
;
203 if ((m_features
& RBD_FEATURE_OBJECT_MAP
) != 0) {
204 m_disable_flags
|= RBD_FLAG_OBJECT_MAP_INVALID
;
207 image_ctx
.owner_lock
.unlock_shared();
210 return handle_finish(*result
);
213 send_get_mirror_mode();
217 template <typename I
>
218 void DisableFeaturesRequest
<I
>::send_get_mirror_mode() {
219 I
&image_ctx
= this->m_image_ctx
;
220 CephContext
*cct
= image_ctx
.cct
;
222 if ((m_features
& RBD_FEATURE_JOURNALING
) == 0) {
223 send_append_op_event();
227 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
229 librados::ObjectReadOperation op
;
230 cls_client::mirror_mode_get_start(&op
);
232 using klass
= DisableFeaturesRequest
<I
>;
233 librados::AioCompletion
*comp
=
234 create_rados_callback
<klass
, &klass::handle_get_mirror_mode
>(this);
236 int r
= image_ctx
.md_ctx
.aio_operate(RBD_MIRRORING
, comp
, &op
, &m_out_bl
);
241 template <typename I
>
242 Context
*DisableFeaturesRequest
<I
>::handle_get_mirror_mode(int *result
) {
243 I
&image_ctx
= this->m_image_ctx
;
244 CephContext
*cct
= image_ctx
.cct
;
245 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
248 auto it
= m_out_bl
.cbegin();
249 *result
= cls_client::mirror_mode_get_finish(&it
, &m_mirror_mode
);
252 if (*result
< 0 && *result
!= -ENOENT
) {
253 lderr(cct
) << "failed to retrieve pool mirror mode: "
254 << cpp_strerror(*result
) << dendl
;
255 return handle_finish(*result
);
258 ldout(cct
, 20) << this << " " << __func__
<< ": m_mirror_mode="
259 << m_mirror_mode
<< dendl
;
261 send_get_mirror_image();
265 template <typename I
>
266 void DisableFeaturesRequest
<I
>::send_get_mirror_image() {
267 I
&image_ctx
= this->m_image_ctx
;
268 CephContext
*cct
= image_ctx
.cct
;
270 if (m_mirror_mode
!= cls::rbd::MIRROR_MODE_IMAGE
) {
271 send_disable_mirror_image();
275 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
277 librados::ObjectReadOperation op
;
278 cls_client::mirror_image_get_start(&op
, image_ctx
.id
);
280 using klass
= DisableFeaturesRequest
<I
>;
281 librados::AioCompletion
*comp
=
282 create_rados_callback
<klass
, &klass::handle_get_mirror_image
>(this);
284 int r
= image_ctx
.md_ctx
.aio_operate(RBD_MIRRORING
, comp
, &op
, &m_out_bl
);
289 template <typename I
>
290 Context
*DisableFeaturesRequest
<I
>::handle_get_mirror_image(int *result
) {
291 I
&image_ctx
= this->m_image_ctx
;
292 CephContext
*cct
= image_ctx
.cct
;
293 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
295 cls::rbd::MirrorImage mirror_image
;
298 auto it
= m_out_bl
.cbegin();
299 *result
= cls_client::mirror_image_get_finish(&it
, &mirror_image
);
302 if (*result
< 0 && *result
!= -ENOENT
) {
303 lderr(cct
) << "failed to retrieve pool mirror image: "
304 << cpp_strerror(*result
) << dendl
;
305 return handle_finish(*result
);
308 if ((mirror_image
.state
== cls::rbd::MIRROR_IMAGE_STATE_ENABLED
) && !m_force
) {
309 lderr(cct
) << "cannot disable journaling: image mirroring "
310 << "enabled and mirror pool mode set to image"
313 return handle_finish(*result
);
316 send_disable_mirror_image();
320 template <typename I
>
321 void DisableFeaturesRequest
<I
>::send_disable_mirror_image() {
322 I
&image_ctx
= this->m_image_ctx
;
323 CephContext
*cct
= image_ctx
.cct
;
325 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
327 Context
*ctx
= create_context_callback
<
328 DisableFeaturesRequest
<I
>,
329 &DisableFeaturesRequest
<I
>::handle_disable_mirror_image
>(this);
331 mirror::DisableRequest
<I
> *req
=
332 mirror::DisableRequest
<I
>::create(&image_ctx
, m_force
, true, ctx
);
336 template <typename I
>
337 Context
*DisableFeaturesRequest
<I
>::handle_disable_mirror_image(int *result
) {
338 I
&image_ctx
= this->m_image_ctx
;
339 CephContext
*cct
= image_ctx
.cct
;
340 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
343 lderr(cct
) << "failed to disable image mirroring: " << cpp_strerror(*result
)
348 send_close_journal();
352 template <typename I
>
353 void DisableFeaturesRequest
<I
>::send_close_journal() {
354 I
&image_ctx
= this->m_image_ctx
;
355 CephContext
*cct
= image_ctx
.cct
;
358 std::unique_lock locker
{image_ctx
.owner_lock
};
359 if (image_ctx
.journal
!= nullptr) {
360 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
362 std::swap(m_journal
, image_ctx
.journal
);
363 Context
*ctx
= create_context_callback
<
364 DisableFeaturesRequest
<I
>,
365 &DisableFeaturesRequest
<I
>::handle_close_journal
>(this);
367 m_journal
->close(ctx
);
372 send_remove_journal();
375 template <typename I
>
376 Context
*DisableFeaturesRequest
<I
>::handle_close_journal(int *result
) {
377 I
&image_ctx
= this->m_image_ctx
;
378 CephContext
*cct
= image_ctx
.cct
;
379 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
382 lderr(cct
) << "failed to close image journal: " << cpp_strerror(*result
)
386 ceph_assert(m_journal
!= nullptr);
390 send_remove_journal();
394 template <typename I
>
395 void DisableFeaturesRequest
<I
>::send_remove_journal() {
396 I
&image_ctx
= this->m_image_ctx
;
397 CephContext
*cct
= image_ctx
.cct
;
398 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
400 Context
*ctx
= create_context_callback
<
401 DisableFeaturesRequest
<I
>,
402 &DisableFeaturesRequest
<I
>::handle_remove_journal
>(this);
404 journal::RemoveRequest
<I
> *req
= journal::RemoveRequest
<I
>::create(
405 image_ctx
.md_ctx
, image_ctx
.id
, librbd::Journal
<>::IMAGE_CLIENT_ID
,
406 image_ctx
.op_work_queue
, ctx
);
411 template <typename I
>
412 Context
*DisableFeaturesRequest
<I
>::handle_remove_journal(int *result
) {
413 I
&image_ctx
= this->m_image_ctx
;
414 CephContext
*cct
= image_ctx
.cct
;
415 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
418 lderr(cct
) << "failed to remove image journal: " << cpp_strerror(*result
)
420 return handle_finish(*result
);
423 send_append_op_event();
427 template <typename I
>
428 void DisableFeaturesRequest
<I
>::send_append_op_event() {
429 I
&image_ctx
= this->m_image_ctx
;
430 CephContext
*cct
= image_ctx
.cct
;
432 if (!this->template append_op_event
<
433 DisableFeaturesRequest
<I
>,
434 &DisableFeaturesRequest
<I
>::handle_append_op_event
>(this)) {
435 send_remove_object_map();
438 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
441 template <typename I
>
442 Context
*DisableFeaturesRequest
<I
>::handle_append_op_event(int *result
) {
443 I
&image_ctx
= this->m_image_ctx
;
444 CephContext
*cct
= image_ctx
.cct
;
445 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
448 lderr(cct
) << "failed to commit journal entry: " << cpp_strerror(*result
)
450 return handle_finish(*result
);
453 send_remove_object_map();
457 template <typename I
>
458 void DisableFeaturesRequest
<I
>::send_remove_object_map() {
459 I
&image_ctx
= this->m_image_ctx
;
460 CephContext
*cct
= image_ctx
.cct
;
461 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
463 if ((m_features
& RBD_FEATURE_OBJECT_MAP
) == 0) {
468 Context
*ctx
= create_context_callback
<
469 DisableFeaturesRequest
<I
>,
470 &DisableFeaturesRequest
<I
>::handle_remove_object_map
>(this);
472 object_map::RemoveRequest
<I
> *req
=
473 object_map::RemoveRequest
<I
>::create(&image_ctx
, ctx
);
477 template <typename I
>
478 Context
*DisableFeaturesRequest
<I
>::handle_remove_object_map(int *result
) {
479 I
&image_ctx
= this->m_image_ctx
;
480 CephContext
*cct
= image_ctx
.cct
;
481 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
484 lderr(cct
) << "failed to remove object map: " << cpp_strerror(*result
) << dendl
;
485 return handle_finish(*result
);
492 template <typename I
>
493 void DisableFeaturesRequest
<I
>::send_set_features() {
494 I
&image_ctx
= this->m_image_ctx
;
495 CephContext
*cct
= image_ctx
.cct
;
496 ldout(cct
, 20) << this << " " << __func__
<< ": new_features="
497 << m_new_features
<< ", features_mask=" << m_features_mask
500 librados::ObjectWriteOperation op
;
501 librbd::cls_client::set_features(&op
, m_new_features
, m_features_mask
);
503 using klass
= DisableFeaturesRequest
<I
>;
504 librados::AioCompletion
*comp
=
505 create_rados_callback
<klass
, &klass::handle_set_features
>(this);
506 int r
= image_ctx
.md_ctx
.aio_operate(image_ctx
.header_oid
, comp
, &op
);
511 template <typename I
>
512 Context
*DisableFeaturesRequest
<I
>::handle_set_features(int *result
) {
513 I
&image_ctx
= this->m_image_ctx
;
514 CephContext
*cct
= image_ctx
.cct
;
515 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
517 if (*result
== -EINVAL
&& (m_features_mask
& RBD_FEATURE_JOURNALING
) != 0) {
518 // NOTE: infernalis OSDs will not accept a mask with new features, so
519 // re-attempt with a reduced mask.
520 ldout(cct
, 5) << this << " " << __func__
521 << ": re-attempt with a reduced mask" << dendl
;
522 m_features_mask
&= ~RBD_FEATURE_JOURNALING
;
527 lderr(cct
) << "failed to update features: " << cpp_strerror(*result
)
529 return handle_finish(*result
);
536 template <typename I
>
537 void DisableFeaturesRequest
<I
>::send_update_flags() {
538 I
&image_ctx
= this->m_image_ctx
;
539 CephContext
*cct
= image_ctx
.cct
;
541 if (m_disable_flags
== 0) {
542 send_notify_update();
546 ldout(cct
, 20) << this << " " << __func__
<< ": disable_flags="
547 << m_disable_flags
<< dendl
;
549 Context
*ctx
= create_context_callback
<
550 DisableFeaturesRequest
<I
>,
551 &DisableFeaturesRequest
<I
>::handle_update_flags
>(this);
553 image::SetFlagsRequest
<I
> *req
=
554 image::SetFlagsRequest
<I
>::create(&image_ctx
, 0, m_disable_flags
, ctx
);
558 template <typename I
>
559 Context
*DisableFeaturesRequest
<I
>::handle_update_flags(int *result
) {
560 I
&image_ctx
= this->m_image_ctx
;
561 CephContext
*cct
= image_ctx
.cct
;
562 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
565 lderr(cct
) << "failed to update image flags: " << cpp_strerror(*result
)
567 return handle_finish(*result
);
570 send_notify_update();
574 template <typename I
>
575 void DisableFeaturesRequest
<I
>::send_notify_update() {
576 I
&image_ctx
= this->m_image_ctx
;
577 CephContext
*cct
= image_ctx
.cct
;
578 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
580 Context
*ctx
= create_context_callback
<
581 DisableFeaturesRequest
<I
>,
582 &DisableFeaturesRequest
<I
>::handle_notify_update
>(this);
584 image_ctx
.notify_update(ctx
);
587 template <typename I
>
588 Context
*DisableFeaturesRequest
<I
>::handle_notify_update(int *result
) {
589 I
&image_ctx
= this->m_image_ctx
;
590 CephContext
*cct
= image_ctx
.cct
;
591 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
593 if (image_ctx
.exclusive_lock
== nullptr || !m_acquired_lock
) {
594 return handle_finish(*result
);
597 send_release_exclusive_lock();
601 template <typename I
>
602 void DisableFeaturesRequest
<I
>::send_release_exclusive_lock() {
603 I
&image_ctx
= this->m_image_ctx
;
604 CephContext
*cct
= image_ctx
.cct
;
605 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
607 Context
*ctx
= create_context_callback
<
608 DisableFeaturesRequest
<I
>,
609 &DisableFeaturesRequest
<I
>::handle_release_exclusive_lock
>(
610 this, image_ctx
.exclusive_lock
);
612 image_ctx
.exclusive_lock
->release_lock(ctx
);
615 template <typename I
>
616 Context
*DisableFeaturesRequest
<I
>::handle_release_exclusive_lock(int *result
) {
617 I
&image_ctx
= this->m_image_ctx
;
618 CephContext
*cct
= image_ctx
.cct
;
619 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
621 return handle_finish(*result
);
624 template <typename I
>
625 Context
*DisableFeaturesRequest
<I
>::handle_finish(int r
) {
626 I
&image_ctx
= this->m_image_ctx
;
627 CephContext
*cct
= image_ctx
.cct
;
628 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << r
<< dendl
;
631 std::unique_lock locker
{image_ctx
.owner_lock
};
632 if (image_ctx
.exclusive_lock
!= nullptr && m_requests_blocked
) {
633 image_ctx
.exclusive_lock
->unblock_requests();
636 image_ctx
.io_work_queue
->unblock_writes();
638 image_ctx
.state
->handle_prepare_lock_complete();
640 return this->create_context_finisher(r
);
643 } // namespace operation
644 } // namespace librbd
646 template class librbd::operation::DisableFeaturesRequest
<librbd::ImageCtx
>;