]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/operation/DisableFeaturesRequest.cc
97c0d2219b64c7a55f7c34ba2bf75f212dec5bf9
[ceph.git] / ceph / src / librbd / operation / DisableFeaturesRequest.cc
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/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"
18
19 #define dout_subsys ceph_subsys_rbd
20 #undef dout_prefix
21 #define dout_prefix *_dout << "librbd::DisableFeaturesRequest: "
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 DisableFeaturesRequest<I>::DisableFeaturesRequest(I &image_ctx,
32 Context *on_finish,
33 uint64_t journal_op_tid,
34 uint64_t features,
35 bool force)
36 : Request<I>(image_ctx, on_finish, journal_op_tid), m_features(features),
37 m_force(force) {
38 }
39
40 template <typename I>
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));
45
46 ldout(cct, 20) << this << " " << __func__ << ": features=" << m_features
47 << dendl;
48
49 send_prepare_lock();
50 }
51
52 template <typename I>
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;
57
58 if (r < 0) {
59 lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl;
60 }
61 return true;
62 }
63
64 template <typename I>
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;
69
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)));
74 }
75
76 template <typename I>
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;
81
82 if (*result < 0) {
83 lderr(cct) << "failed to lock image: " << cpp_strerror(*result) << dendl;
84 return this->create_context_finisher(*result);
85 }
86
87 send_block_writes();
88 return nullptr;
89 }
90
91 template <typename I>
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;
96
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));
101 }
102
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;
108
109 if (*result < 0) {
110 lderr(cct) << "failed to block writes: " << cpp_strerror(*result) << dendl;
111 return handle_finish(*result);
112 }
113 m_writes_blocked = true;
114
115 {
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;
124 }
125 }
126
127 return send_acquire_exclusive_lock(result);
128 }
129
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;
135
136 {
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;
143
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);
149 return nullptr;
150 }
151 }
152
153 return handle_acquire_exclusive_lock(result);
154 }
155
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;
161
162 image_ctx.owner_lock.lock_shared();
163 if (*result < 0) {
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);
173 }
174
175 do {
176 m_features &= image_ctx.features;
177
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);
182 }
183
184 m_new_features = image_ctx.features & ~m_features;
185 m_features_mask = m_features;
186
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;
193 *result = -EINVAL;
194 break;
195 }
196 m_features_mask |= (RBD_FEATURE_OBJECT_MAP |
197 RBD_FEATURE_FAST_DIFF |
198 RBD_FEATURE_JOURNALING);
199 }
200 if ((m_features & RBD_FEATURE_FAST_DIFF) != 0) {
201 m_disable_flags |= RBD_FLAG_FAST_DIFF_INVALID;
202 }
203 if ((m_features & RBD_FEATURE_OBJECT_MAP) != 0) {
204 m_disable_flags |= RBD_FLAG_OBJECT_MAP_INVALID;
205 }
206 } while (false);
207 image_ctx.owner_lock.unlock_shared();
208
209 if (*result < 0) {
210 return handle_finish(*result);
211 }
212
213 send_get_mirror_mode();
214 return nullptr;
215 }
216
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;
221
222 if ((m_features & RBD_FEATURE_JOURNALING) == 0) {
223 send_append_op_event();
224 return;
225 }
226
227 ldout(cct, 20) << this << " " << __func__ << dendl;
228
229 librados::ObjectReadOperation op;
230 cls_client::mirror_mode_get_start(&op);
231
232 using klass = DisableFeaturesRequest<I>;
233 librados::AioCompletion *comp =
234 create_rados_callback<klass, &klass::handle_get_mirror_mode>(this);
235 m_out_bl.clear();
236 int r = image_ctx.md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl);
237 ceph_assert(r == 0);
238 comp->release();
239 }
240
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;
246
247 if (*result == 0) {
248 auto it = m_out_bl.cbegin();
249 *result = cls_client::mirror_mode_get_finish(&it, &m_mirror_mode);
250 }
251
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);
256 }
257
258 ldout(cct, 20) << this << " " << __func__ << ": m_mirror_mode="
259 << m_mirror_mode << dendl;
260
261 send_get_mirror_image();
262 return nullptr;
263 }
264
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;
269
270 if (m_mirror_mode != cls::rbd::MIRROR_MODE_IMAGE) {
271 send_disable_mirror_image();
272 return;
273 }
274
275 ldout(cct, 20) << this << " " << __func__ << dendl;
276
277 librados::ObjectReadOperation op;
278 cls_client::mirror_image_get_start(&op, image_ctx.id);
279
280 using klass = DisableFeaturesRequest<I>;
281 librados::AioCompletion *comp =
282 create_rados_callback<klass, &klass::handle_get_mirror_image>(this);
283 m_out_bl.clear();
284 int r = image_ctx.md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl);
285 ceph_assert(r == 0);
286 comp->release();
287 }
288
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;
294
295 cls::rbd::MirrorImage mirror_image;
296
297 if (*result == 0) {
298 auto it = m_out_bl.cbegin();
299 *result = cls_client::mirror_image_get_finish(&it, &mirror_image);
300 }
301
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);
306 }
307
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"
311 << dendl;
312 *result = -EINVAL;
313 return handle_finish(*result);
314 }
315
316 send_disable_mirror_image();
317 return nullptr;
318 }
319
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;
324
325 ldout(cct, 20) << this << " " << __func__ << dendl;
326
327 Context *ctx = create_context_callback<
328 DisableFeaturesRequest<I>,
329 &DisableFeaturesRequest<I>::handle_disable_mirror_image>(this);
330
331 mirror::DisableRequest<I> *req =
332 mirror::DisableRequest<I>::create(&image_ctx, m_force, true, ctx);
333 req->send();
334 }
335
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;
341
342 if (*result < 0) {
343 lderr(cct) << "failed to disable image mirroring: " << cpp_strerror(*result)
344 << dendl;
345 // not fatal
346 }
347
348 send_close_journal();
349 return nullptr;
350 }
351
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;
356
357 {
358 std::unique_lock locker{image_ctx.owner_lock};
359 if (image_ctx.journal != nullptr) {
360 ldout(cct, 20) << this << " " << __func__ << dendl;
361
362 std::swap(m_journal, image_ctx.journal);
363 Context *ctx = create_context_callback<
364 DisableFeaturesRequest<I>,
365 &DisableFeaturesRequest<I>::handle_close_journal>(this);
366
367 m_journal->close(ctx);
368 return;
369 }
370 }
371
372 send_remove_journal();
373 }
374
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;
380
381 if (*result < 0) {
382 lderr(cct) << "failed to close image journal: " << cpp_strerror(*result)
383 << dendl;
384 }
385
386 ceph_assert(m_journal != nullptr);
387 m_journal->put();
388 m_journal = nullptr;
389
390 send_remove_journal();
391 return nullptr;
392 }
393
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;
399
400 Context *ctx = create_context_callback<
401 DisableFeaturesRequest<I>,
402 &DisableFeaturesRequest<I>::handle_remove_journal>(this);
403
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);
407
408 req->send();
409 }
410
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;
416
417 if (*result < 0) {
418 lderr(cct) << "failed to remove image journal: " << cpp_strerror(*result)
419 << dendl;
420 return handle_finish(*result);
421 }
422
423 send_append_op_event();
424 return nullptr;
425 }
426
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;
431
432 if (!this->template append_op_event<
433 DisableFeaturesRequest<I>,
434 &DisableFeaturesRequest<I>::handle_append_op_event>(this)) {
435 send_remove_object_map();
436 }
437
438 ldout(cct, 20) << this << " " << __func__ << dendl;
439 }
440
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;
446
447 if (*result < 0) {
448 lderr(cct) << "failed to commit journal entry: " << cpp_strerror(*result)
449 << dendl;
450 return handle_finish(*result);
451 }
452
453 send_remove_object_map();
454 return nullptr;
455 }
456
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;
462
463 if ((m_features & RBD_FEATURE_OBJECT_MAP) == 0) {
464 send_set_features();
465 return;
466 }
467
468 Context *ctx = create_context_callback<
469 DisableFeaturesRequest<I>,
470 &DisableFeaturesRequest<I>::handle_remove_object_map>(this);
471
472 object_map::RemoveRequest<I> *req =
473 object_map::RemoveRequest<I>::create(&image_ctx, ctx);
474 req->send();
475 }
476
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;
482
483 if (*result < 0 && *result != -ENOENT) {
484 lderr(cct) << "failed to remove object map: " << cpp_strerror(*result) << dendl;
485 return handle_finish(*result);
486 }
487
488 send_set_features();
489 return nullptr;
490 }
491
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
498 << dendl;
499
500 librados::ObjectWriteOperation op;
501 librbd::cls_client::set_features(&op, m_new_features, m_features_mask);
502
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);
507 ceph_assert(r == 0);
508 comp->release();
509 }
510
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;
516
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;
523 send_set_features();
524 }
525
526 if (*result < 0) {
527 lderr(cct) << "failed to update features: " << cpp_strerror(*result)
528 << dendl;
529 return handle_finish(*result);
530 }
531
532 send_update_flags();
533 return nullptr;
534 }
535
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;
540
541 if (m_disable_flags == 0) {
542 send_notify_update();
543 return;
544 }
545
546 ldout(cct, 20) << this << " " << __func__ << ": disable_flags="
547 << m_disable_flags << dendl;
548
549 Context *ctx = create_context_callback<
550 DisableFeaturesRequest<I>,
551 &DisableFeaturesRequest<I>::handle_update_flags>(this);
552
553 image::SetFlagsRequest<I> *req =
554 image::SetFlagsRequest<I>::create(&image_ctx, 0, m_disable_flags, ctx);
555 req->send();
556 }
557
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;
563
564 if (*result < 0) {
565 lderr(cct) << "failed to update image flags: " << cpp_strerror(*result)
566 << dendl;
567 return handle_finish(*result);
568 }
569
570 send_notify_update();
571 return nullptr;
572 }
573
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;
579
580 Context *ctx = create_context_callback<
581 DisableFeaturesRequest<I>,
582 &DisableFeaturesRequest<I>::handle_notify_update>(this);
583
584 image_ctx.notify_update(ctx);
585 }
586
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;
592
593 if (image_ctx.exclusive_lock == nullptr || !m_acquired_lock) {
594 return handle_finish(*result);
595 }
596
597 send_release_exclusive_lock();
598 return nullptr;
599 }
600
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;
606
607 Context *ctx = create_context_callback<
608 DisableFeaturesRequest<I>,
609 &DisableFeaturesRequest<I>::handle_release_exclusive_lock>(
610 this, image_ctx.exclusive_lock);
611
612 image_ctx.exclusive_lock->release_lock(ctx);
613 }
614
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;
620
621 return handle_finish(*result);
622 }
623
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;
629
630 {
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();
634 }
635
636 image_ctx.io_work_queue->unblock_writes();
637 }
638 image_ctx.state->handle_prepare_lock_complete();
639
640 return this->create_context_finisher(r);
641 }
642
643 } // namespace operation
644 } // namespace librbd
645
646 template class librbd::operation::DisableFeaturesRequest<librbd::ImageCtx>;