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/ImageDispatcherInterface.h"
15 #include "librbd/journal/RemoveRequest.h"
16 #include "librbd/journal/TypeTraits.h"
17 #include "librbd/mirror/DisableRequest.h"
18 #include "librbd/object_map/RemoveRequest.h"
20 #define dout_subsys ceph_subsys_rbd
22 #define dout_prefix *_dout << "librbd::DisableFeaturesRequest: "
27 using util::create_async_context_callback
;
28 using util::create_context_callback
;
29 using util::create_rados_callback
;
32 DisableFeaturesRequest
<I
>::DisableFeaturesRequest(I
&image_ctx
,
34 uint64_t journal_op_tid
,
37 : Request
<I
>(image_ctx
, on_finish
, journal_op_tid
), m_features(features
),
42 void DisableFeaturesRequest
<I
>::send_op() {
43 I
&image_ctx
= this->m_image_ctx
;
44 CephContext
*cct
= image_ctx
.cct
;
45 ceph_assert(ceph_mutex_is_locked(image_ctx
.owner_lock
));
47 ldout(cct
, 20) << this << " " << __func__
<< ": features=" << m_features
54 bool DisableFeaturesRequest
<I
>::should_complete(int r
) {
55 I
&image_ctx
= this->m_image_ctx
;
56 CephContext
*cct
= image_ctx
.cct
;
57 ldout(cct
, 20) << this << " " << __func__
<< " r=" << r
<< dendl
;
60 lderr(cct
) << "encountered error: " << cpp_strerror(r
) << dendl
;
66 void DisableFeaturesRequest
<I
>::send_prepare_lock() {
67 I
&image_ctx
= this->m_image_ctx
;
68 CephContext
*cct
= image_ctx
.cct
;
69 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
71 image_ctx
.state
->prepare_lock(create_async_context_callback(
72 image_ctx
, create_context_callback
<
73 DisableFeaturesRequest
<I
>,
74 &DisableFeaturesRequest
<I
>::handle_prepare_lock
>(this)));
78 Context
*DisableFeaturesRequest
<I
>::handle_prepare_lock(int *result
) {
79 I
&image_ctx
= this->m_image_ctx
;
80 CephContext
*cct
= image_ctx
.cct
;
81 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
84 lderr(cct
) << "failed to lock image: " << cpp_strerror(*result
) << dendl
;
85 return this->create_context_finisher(*result
);
93 void DisableFeaturesRequest
<I
>::send_block_writes() {
94 I
&image_ctx
= this->m_image_ctx
;
95 CephContext
*cct
= image_ctx
.cct
;
96 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
98 std::unique_lock locker
{image_ctx
.owner_lock
};
99 image_ctx
.io_image_dispatcher
->block_writes(create_context_callback
<
100 DisableFeaturesRequest
<I
>,
101 &DisableFeaturesRequest
<I
>::handle_block_writes
>(this));
104 template <typename I
>
105 Context
*DisableFeaturesRequest
<I
>::handle_block_writes(int *result
) {
106 I
&image_ctx
= this->m_image_ctx
;
107 CephContext
*cct
= image_ctx
.cct
;
108 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
111 lderr(cct
) << "failed to block writes: " << cpp_strerror(*result
) << dendl
;
112 return handle_finish(*result
);
114 m_writes_blocked
= true;
117 std::unique_lock locker
{image_ctx
.owner_lock
};
118 // avoid accepting new requests from peers while we manipulate
119 // the image features
120 if (image_ctx
.exclusive_lock
!= nullptr &&
121 (image_ctx
.journal
== nullptr ||
122 !image_ctx
.journal
->is_journal_replaying())) {
123 image_ctx
.exclusive_lock
->block_requests(0);
124 m_requests_blocked
= true;
128 return send_acquire_exclusive_lock(result
);
131 template <typename I
>
132 Context
*DisableFeaturesRequest
<I
>::send_acquire_exclusive_lock(int *result
) {
133 I
&image_ctx
= this->m_image_ctx
;
134 CephContext
*cct
= image_ctx
.cct
;
135 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
138 std::unique_lock locker
{image_ctx
.owner_lock
};
139 // if disabling features w/ exclusive lock supported, we need to
140 // acquire the lock to temporarily block IO against the image
141 if (image_ctx
.exclusive_lock
!= nullptr &&
142 !image_ctx
.exclusive_lock
->is_lock_owner()) {
143 m_acquired_lock
= true;
145 Context
*ctx
= create_context_callback
<
146 DisableFeaturesRequest
<I
>,
147 &DisableFeaturesRequest
<I
>::handle_acquire_exclusive_lock
>(
148 this, image_ctx
.exclusive_lock
);
149 image_ctx
.exclusive_lock
->acquire_lock(ctx
);
154 return handle_acquire_exclusive_lock(result
);
157 template <typename I
>
158 Context
*DisableFeaturesRequest
<I
>::handle_acquire_exclusive_lock(int *result
) {
159 I
&image_ctx
= this->m_image_ctx
;
160 CephContext
*cct
= image_ctx
.cct
;
161 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
163 image_ctx
.owner_lock
.lock_shared();
165 lderr(cct
) << "failed to lock image: " << cpp_strerror(*result
) << dendl
;
166 image_ctx
.owner_lock
.unlock_shared();
167 return handle_finish(*result
);
168 } else if (image_ctx
.exclusive_lock
!= nullptr &&
169 !image_ctx
.exclusive_lock
->is_lock_owner()) {
170 lderr(cct
) << "failed to acquire exclusive lock" << dendl
;
171 *result
= image_ctx
.exclusive_lock
->get_unlocked_op_error();
172 image_ctx
.owner_lock
.unlock_shared();
173 return handle_finish(*result
);
177 m_features
&= image_ctx
.features
;
179 // interlock object-map and fast-diff together
180 if (((m_features
& RBD_FEATURE_OBJECT_MAP
) != 0) ||
181 ((m_features
& RBD_FEATURE_FAST_DIFF
) != 0)) {
182 m_features
|= (RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
);
185 m_new_features
= image_ctx
.features
& ~m_features
;
186 m_features_mask
= m_features
;
188 if ((m_features
& RBD_FEATURE_EXCLUSIVE_LOCK
) != 0) {
189 if ((m_new_features
& RBD_FEATURE_OBJECT_MAP
) != 0 ||
190 (m_new_features
& RBD_FEATURE_JOURNALING
) != 0) {
191 lderr(cct
) << "cannot disable exclusive-lock. object-map "
192 "or journaling must be disabled before "
193 "disabling exclusive-lock." << dendl
;
197 m_features_mask
|= (RBD_FEATURE_OBJECT_MAP
|
198 RBD_FEATURE_FAST_DIFF
|
199 RBD_FEATURE_JOURNALING
);
201 if ((m_features
& RBD_FEATURE_FAST_DIFF
) != 0) {
202 m_disable_flags
|= RBD_FLAG_FAST_DIFF_INVALID
;
204 if ((m_features
& RBD_FEATURE_OBJECT_MAP
) != 0) {
205 m_disable_flags
|= RBD_FLAG_OBJECT_MAP_INVALID
;
208 image_ctx
.owner_lock
.unlock_shared();
211 return handle_finish(*result
);
214 send_get_mirror_mode();
218 template <typename I
>
219 void DisableFeaturesRequest
<I
>::send_get_mirror_mode() {
220 I
&image_ctx
= this->m_image_ctx
;
221 CephContext
*cct
= image_ctx
.cct
;
223 if ((m_features
& RBD_FEATURE_JOURNALING
) == 0) {
224 send_append_op_event();
228 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
230 librados::ObjectReadOperation op
;
231 cls_client::mirror_mode_get_start(&op
);
233 using klass
= DisableFeaturesRequest
<I
>;
234 librados::AioCompletion
*comp
=
235 create_rados_callback
<klass
, &klass::handle_get_mirror_mode
>(this);
237 int r
= image_ctx
.md_ctx
.aio_operate(RBD_MIRRORING
, comp
, &op
, &m_out_bl
);
242 template <typename I
>
243 Context
*DisableFeaturesRequest
<I
>::handle_get_mirror_mode(int *result
) {
244 I
&image_ctx
= this->m_image_ctx
;
245 CephContext
*cct
= image_ctx
.cct
;
246 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
249 auto it
= m_out_bl
.cbegin();
250 *result
= cls_client::mirror_mode_get_finish(&it
, &m_mirror_mode
);
253 if (*result
< 0 && *result
!= -ENOENT
) {
254 lderr(cct
) << "failed to retrieve pool mirror mode: "
255 << cpp_strerror(*result
) << dendl
;
256 return handle_finish(*result
);
259 ldout(cct
, 20) << this << " " << __func__
<< ": m_mirror_mode="
260 << m_mirror_mode
<< dendl
;
262 send_get_mirror_image();
266 template <typename I
>
267 void DisableFeaturesRequest
<I
>::send_get_mirror_image() {
268 I
&image_ctx
= this->m_image_ctx
;
269 CephContext
*cct
= image_ctx
.cct
;
271 if (m_mirror_mode
!= cls::rbd::MIRROR_MODE_IMAGE
) {
272 send_disable_mirror_image();
276 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
278 librados::ObjectReadOperation op
;
279 cls_client::mirror_image_get_start(&op
, image_ctx
.id
);
281 using klass
= DisableFeaturesRequest
<I
>;
282 librados::AioCompletion
*comp
=
283 create_rados_callback
<klass
, &klass::handle_get_mirror_image
>(this);
285 int r
= image_ctx
.md_ctx
.aio_operate(RBD_MIRRORING
, comp
, &op
, &m_out_bl
);
290 template <typename I
>
291 Context
*DisableFeaturesRequest
<I
>::handle_get_mirror_image(int *result
) {
292 I
&image_ctx
= this->m_image_ctx
;
293 CephContext
*cct
= image_ctx
.cct
;
294 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
296 cls::rbd::MirrorImage mirror_image
;
299 auto it
= m_out_bl
.cbegin();
300 *result
= cls_client::mirror_image_get_finish(&it
, &mirror_image
);
303 if (*result
< 0 && *result
!= -ENOENT
) {
304 lderr(cct
) << "failed to retrieve pool mirror image: "
305 << cpp_strerror(*result
) << dendl
;
306 return handle_finish(*result
);
309 if (mirror_image
.state
== cls::rbd::MIRROR_IMAGE_STATE_ENABLED
&&
310 mirror_image
.mode
== cls::rbd::MIRROR_IMAGE_MODE_JOURNAL
&& !m_force
) {
311 lderr(cct
) << "cannot disable journaling: journal-based mirroring "
312 << "enabled and mirror pool mode set to image"
315 return handle_finish(*result
);
318 if (mirror_image
.mode
!= cls::rbd::MIRROR_IMAGE_MODE_JOURNAL
) {
319 send_close_journal();
321 send_disable_mirror_image();
326 template <typename I
>
327 void DisableFeaturesRequest
<I
>::send_disable_mirror_image() {
328 I
&image_ctx
= this->m_image_ctx
;
329 CephContext
*cct
= image_ctx
.cct
;
331 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
333 Context
*ctx
= create_context_callback
<
334 DisableFeaturesRequest
<I
>,
335 &DisableFeaturesRequest
<I
>::handle_disable_mirror_image
>(this);
337 mirror::DisableRequest
<I
> *req
=
338 mirror::DisableRequest
<I
>::create(&image_ctx
, m_force
, true, ctx
);
342 template <typename I
>
343 Context
*DisableFeaturesRequest
<I
>::handle_disable_mirror_image(int *result
) {
344 I
&image_ctx
= this->m_image_ctx
;
345 CephContext
*cct
= image_ctx
.cct
;
346 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
349 lderr(cct
) << "failed to disable image mirroring: " << cpp_strerror(*result
)
354 send_close_journal();
358 template <typename I
>
359 void DisableFeaturesRequest
<I
>::send_close_journal() {
360 I
&image_ctx
= this->m_image_ctx
;
361 CephContext
*cct
= image_ctx
.cct
;
364 std::unique_lock locker
{image_ctx
.owner_lock
};
365 if (image_ctx
.journal
!= nullptr) {
366 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
368 std::swap(m_journal
, image_ctx
.journal
);
369 Context
*ctx
= create_context_callback
<
370 DisableFeaturesRequest
<I
>,
371 &DisableFeaturesRequest
<I
>::handle_close_journal
>(this);
373 m_journal
->close(ctx
);
378 send_remove_journal();
381 template <typename I
>
382 Context
*DisableFeaturesRequest
<I
>::handle_close_journal(int *result
) {
383 I
&image_ctx
= this->m_image_ctx
;
384 CephContext
*cct
= image_ctx
.cct
;
385 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
388 lderr(cct
) << "failed to close image journal: " << cpp_strerror(*result
)
392 ceph_assert(m_journal
!= nullptr);
396 send_remove_journal();
400 template <typename I
>
401 void DisableFeaturesRequest
<I
>::send_remove_journal() {
402 I
&image_ctx
= this->m_image_ctx
;
403 CephContext
*cct
= image_ctx
.cct
;
404 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
406 Context
*ctx
= create_context_callback
<
407 DisableFeaturesRequest
<I
>,
408 &DisableFeaturesRequest
<I
>::handle_remove_journal
>(this);
410 typename
journal::TypeTraits
<I
>::ContextWQ
* context_wq
;
411 Journal
<I
>::get_work_queue(cct
, &context_wq
);
413 journal::RemoveRequest
<I
> *req
= journal::RemoveRequest
<I
>::create(
414 image_ctx
.md_ctx
, image_ctx
.id
, librbd::Journal
<>::IMAGE_CLIENT_ID
,
420 template <typename I
>
421 Context
*DisableFeaturesRequest
<I
>::handle_remove_journal(int *result
) {
422 I
&image_ctx
= this->m_image_ctx
;
423 CephContext
*cct
= image_ctx
.cct
;
424 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
427 lderr(cct
) << "failed to remove image journal: " << cpp_strerror(*result
)
429 return handle_finish(*result
);
432 send_append_op_event();
436 template <typename I
>
437 void DisableFeaturesRequest
<I
>::send_append_op_event() {
438 I
&image_ctx
= this->m_image_ctx
;
439 CephContext
*cct
= image_ctx
.cct
;
441 if (!this->template append_op_event
<
442 DisableFeaturesRequest
<I
>,
443 &DisableFeaturesRequest
<I
>::handle_append_op_event
>(this)) {
444 send_remove_object_map();
447 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
450 template <typename I
>
451 Context
*DisableFeaturesRequest
<I
>::handle_append_op_event(int *result
) {
452 I
&image_ctx
= this->m_image_ctx
;
453 CephContext
*cct
= image_ctx
.cct
;
454 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
457 lderr(cct
) << "failed to commit journal entry: " << cpp_strerror(*result
)
459 return handle_finish(*result
);
462 send_remove_object_map();
466 template <typename I
>
467 void DisableFeaturesRequest
<I
>::send_remove_object_map() {
468 I
&image_ctx
= this->m_image_ctx
;
469 CephContext
*cct
= image_ctx
.cct
;
470 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
472 if ((m_features
& RBD_FEATURE_OBJECT_MAP
) == 0) {
477 Context
*ctx
= create_context_callback
<
478 DisableFeaturesRequest
<I
>,
479 &DisableFeaturesRequest
<I
>::handle_remove_object_map
>(this);
481 object_map::RemoveRequest
<I
> *req
=
482 object_map::RemoveRequest
<I
>::create(&image_ctx
, ctx
);
486 template <typename I
>
487 Context
*DisableFeaturesRequest
<I
>::handle_remove_object_map(int *result
) {
488 I
&image_ctx
= this->m_image_ctx
;
489 CephContext
*cct
= image_ctx
.cct
;
490 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
492 if (*result
< 0 && *result
!= -ENOENT
) {
493 lderr(cct
) << "failed to remove object map: " << cpp_strerror(*result
) << dendl
;
494 return handle_finish(*result
);
501 template <typename I
>
502 void DisableFeaturesRequest
<I
>::send_set_features() {
503 I
&image_ctx
= this->m_image_ctx
;
504 CephContext
*cct
= image_ctx
.cct
;
505 ldout(cct
, 20) << this << " " << __func__
<< ": new_features="
506 << m_new_features
<< ", features_mask=" << m_features_mask
509 librados::ObjectWriteOperation op
;
510 librbd::cls_client::set_features(&op
, m_new_features
, m_features_mask
);
512 using klass
= DisableFeaturesRequest
<I
>;
513 librados::AioCompletion
*comp
=
514 create_rados_callback
<klass
, &klass::handle_set_features
>(this);
515 int r
= image_ctx
.md_ctx
.aio_operate(image_ctx
.header_oid
, comp
, &op
);
520 template <typename I
>
521 Context
*DisableFeaturesRequest
<I
>::handle_set_features(int *result
) {
522 I
&image_ctx
= this->m_image_ctx
;
523 CephContext
*cct
= image_ctx
.cct
;
524 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
526 if (*result
== -EINVAL
&& (m_features_mask
& RBD_FEATURE_JOURNALING
) != 0) {
527 // NOTE: infernalis OSDs will not accept a mask with new features, so
528 // re-attempt with a reduced mask.
529 ldout(cct
, 5) << this << " " << __func__
530 << ": re-attempt with a reduced mask" << dendl
;
531 m_features_mask
&= ~RBD_FEATURE_JOURNALING
;
536 lderr(cct
) << "failed to update features: " << cpp_strerror(*result
)
538 return handle_finish(*result
);
545 template <typename I
>
546 void DisableFeaturesRequest
<I
>::send_update_flags() {
547 I
&image_ctx
= this->m_image_ctx
;
548 CephContext
*cct
= image_ctx
.cct
;
550 if (m_disable_flags
== 0) {
551 send_notify_update();
555 ldout(cct
, 20) << this << " " << __func__
<< ": disable_flags="
556 << m_disable_flags
<< dendl
;
558 Context
*ctx
= create_context_callback
<
559 DisableFeaturesRequest
<I
>,
560 &DisableFeaturesRequest
<I
>::handle_update_flags
>(this);
562 image::SetFlagsRequest
<I
> *req
=
563 image::SetFlagsRequest
<I
>::create(&image_ctx
, 0, m_disable_flags
, ctx
);
567 template <typename I
>
568 Context
*DisableFeaturesRequest
<I
>::handle_update_flags(int *result
) {
569 I
&image_ctx
= this->m_image_ctx
;
570 CephContext
*cct
= image_ctx
.cct
;
571 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
574 lderr(cct
) << "failed to update image flags: " << cpp_strerror(*result
)
576 return handle_finish(*result
);
579 send_notify_update();
583 template <typename I
>
584 void DisableFeaturesRequest
<I
>::send_notify_update() {
585 I
&image_ctx
= this->m_image_ctx
;
586 CephContext
*cct
= image_ctx
.cct
;
587 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
589 Context
*ctx
= create_context_callback
<
590 DisableFeaturesRequest
<I
>,
591 &DisableFeaturesRequest
<I
>::handle_notify_update
>(this);
593 image_ctx
.notify_update(ctx
);
596 template <typename I
>
597 Context
*DisableFeaturesRequest
<I
>::handle_notify_update(int *result
) {
598 I
&image_ctx
= this->m_image_ctx
;
599 CephContext
*cct
= image_ctx
.cct
;
600 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
602 if (image_ctx
.exclusive_lock
== nullptr || !m_acquired_lock
) {
603 return handle_finish(*result
);
606 send_release_exclusive_lock();
610 template <typename I
>
611 void DisableFeaturesRequest
<I
>::send_release_exclusive_lock() {
612 I
&image_ctx
= this->m_image_ctx
;
613 CephContext
*cct
= image_ctx
.cct
;
614 ldout(cct
, 20) << this << " " << __func__
<< dendl
;
616 Context
*ctx
= create_context_callback
<
617 DisableFeaturesRequest
<I
>,
618 &DisableFeaturesRequest
<I
>::handle_release_exclusive_lock
>(
619 this, image_ctx
.exclusive_lock
);
621 image_ctx
.exclusive_lock
->release_lock(ctx
);
624 template <typename I
>
625 Context
*DisableFeaturesRequest
<I
>::handle_release_exclusive_lock(int *result
) {
626 I
&image_ctx
= this->m_image_ctx
;
627 CephContext
*cct
= image_ctx
.cct
;
628 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
630 return handle_finish(*result
);
633 template <typename I
>
634 Context
*DisableFeaturesRequest
<I
>::handle_finish(int r
) {
635 I
&image_ctx
= this->m_image_ctx
;
636 CephContext
*cct
= image_ctx
.cct
;
637 ldout(cct
, 20) << this << " " << __func__
<< ": r=" << r
<< dendl
;
640 std::unique_lock locker
{image_ctx
.owner_lock
};
641 if (image_ctx
.exclusive_lock
!= nullptr && m_requests_blocked
) {
642 image_ctx
.exclusive_lock
->unblock_requests();
645 image_ctx
.io_image_dispatcher
->unblock_writes();
647 image_ctx
.state
->handle_prepare_lock_complete();
649 return this->create_context_finisher(r
);
652 } // namespace operation
653 } // namespace librbd
655 template class librbd::operation::DisableFeaturesRequest
<librbd::ImageCtx
>;