]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/mirror/snapshot/PromoteRequest.cc
f9cf4b5b973dd67d66dcf228bb1df16a978581f2
[ceph.git] / ceph / src / librbd / mirror / snapshot / PromoteRequest.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/mirror/snapshot/PromoteRequest.h"
5 #include "common/Timer.h"
6 #include "common/dout.h"
7 #include "common/errno.h"
8 #include "cls/rbd/cls_rbd_client.h"
9 #include "librbd/ExclusiveLock.h"
10 #include "librbd/ImageCtx.h"
11 #include "librbd/ImageState.h"
12 #include "librbd/Operations.h"
13 #include "librbd/Utils.h"
14 #include "librbd/image/ListWatchersRequest.h"
15 #include "librbd/mirror/snapshot/CreateNonPrimaryRequest.h"
16 #include "librbd/mirror/snapshot/CreatePrimaryRequest.h"
17 #include "librbd/mirror/snapshot/Utils.h"
18
19 #define dout_subsys ceph_subsys_rbd
20 #undef dout_prefix
21 #define dout_prefix *_dout << "librbd::mirror::snapshot::PromoteRequest: " \
22 << this << " " << __func__ << ": "
23
24 namespace librbd {
25 namespace mirror {
26 namespace snapshot {
27
28 using librbd::util::create_async_context_callback;
29 using librbd::util::create_context_callback;
30 using librbd::util::create_rados_callback;
31
32 template <typename I>
33 void PromoteRequest<I>::send() {
34 CephContext *cct = m_image_ctx->cct;
35 bool requires_orphan = false;
36 if (!util::can_create_primary_snapshot(m_image_ctx, false, true,
37 &requires_orphan,
38 &m_rollback_snap_id)) {
39 lderr(cct) << "cannot promote" << dendl;
40 finish(-EINVAL);
41 return;
42 } else if (m_rollback_snap_id == CEPH_NOSNAP && !requires_orphan) {
43 create_promote_snapshot();
44 return;
45 }
46
47 ldout(cct, 20) << "requires_orphan=" << requires_orphan << ", "
48 << "rollback_snap_id=" << m_rollback_snap_id << dendl;
49 create_orphan_snapshot();
50 }
51
52 template <typename I>
53 void PromoteRequest<I>::create_orphan_snapshot() {
54 CephContext *cct = m_image_ctx->cct;
55 ldout(cct, 20) << dendl;
56
57 auto ctx = create_context_callback<
58 PromoteRequest<I>,
59 &PromoteRequest<I>::handle_create_orphan_snapshot>(this);
60
61 auto req = CreateNonPrimaryRequest<I>::create(
62 m_image_ctx, false, "", CEPH_NOSNAP, {}, {}, nullptr, ctx);
63 req->send();
64 }
65
66 template <typename I>
67 void PromoteRequest<I>::handle_create_orphan_snapshot(int r) {
68 CephContext *cct = m_image_ctx->cct;
69 ldout(cct, 20) << "r=" << r << dendl;
70
71 if (r < 0) {
72 lderr(cct) << "failed to create orphan snapshot: " << cpp_strerror(r)
73 << dendl;
74 finish(r);
75 return;
76 }
77
78 list_watchers();
79 }
80
81 template <typename I>
82 void PromoteRequest<I>::list_watchers() {
83 CephContext *cct = m_image_ctx->cct;
84 ldout(cct, 20) << dendl;
85
86 auto ctx = create_context_callback<
87 PromoteRequest<I>,
88 &PromoteRequest<I>::handle_list_watchers>(this);
89
90 m_watchers.clear();
91 auto flags = librbd::image::LIST_WATCHERS_FILTER_OUT_MY_INSTANCE |
92 librbd::image::LIST_WATCHERS_MIRROR_INSTANCES_ONLY;
93 auto req = librbd::image::ListWatchersRequest<I>::create(
94 *m_image_ctx, flags, &m_watchers, ctx);
95 req->send();
96 }
97
98 template <typename I>
99 void PromoteRequest<I>::handle_list_watchers(int r) {
100 CephContext *cct = m_image_ctx->cct;
101 ldout(cct, 20) << "r=" << r << dendl;
102
103 if (r < 0) {
104 lderr(cct) << "failed to list watchers: " << cpp_strerror(r)
105 << dendl;
106 finish(r);
107 return;
108 }
109
110 if (m_watchers.empty()) {
111 acquire_exclusive_lock();
112 return;
113 }
114
115 wait_update_notify();
116 }
117
118 template <typename I>
119 void PromoteRequest<I>::wait_update_notify() {
120 CephContext *cct = m_image_ctx->cct;
121 ldout(cct, 20) << dendl;
122
123 ImageCtx::get_timer_instance(cct, &m_timer, &m_timer_lock);
124
125 std::lock_guard timer_lock{*m_timer_lock};
126
127 m_scheduler_ticks = 5;
128
129 int r = m_image_ctx->state->register_update_watcher(&m_update_watch_ctx,
130 &m_update_watcher_handle);
131 if (r < 0) {
132 lderr(cct) << "failed to register update watcher: " << cpp_strerror(r)
133 << dendl;
134 finish(r);
135 return;
136 }
137
138 scheduler_unregister_update_watcher();
139 }
140
141 template <typename I>
142 void PromoteRequest<I>::handle_update_notify() {
143 CephContext *cct = m_image_ctx->cct;
144 ldout(cct, 20) << dendl;
145
146 std::lock_guard timer_lock{*m_timer_lock};
147 m_scheduler_ticks = 0;
148 }
149
150 template <typename I>
151 void PromoteRequest<I>::scheduler_unregister_update_watcher() {
152 ceph_assert(ceph_mutex_is_locked(*m_timer_lock));
153
154 CephContext *cct = m_image_ctx->cct;
155 ldout(cct, 20) << "scheduler_ticks=" << m_scheduler_ticks << dendl;
156
157 if (m_scheduler_ticks > 0) {
158 m_scheduler_ticks--;
159 m_timer->add_event_after(1, new LambdaContext([this](int) {
160 scheduler_unregister_update_watcher();
161 }));
162 return;
163 }
164
165 m_image_ctx->op_work_queue->queue(new LambdaContext([this](int) {
166 unregister_update_watcher();
167 }), 0);
168 }
169
170 template <typename I>
171 void PromoteRequest<I>::unregister_update_watcher() {
172 CephContext *cct = m_image_ctx->cct;
173 ldout(cct, 20) << dendl;
174
175 auto ctx = create_context_callback<
176 PromoteRequest<I>,
177 &PromoteRequest<I>::handle_unregister_update_watcher>(this);
178
179 m_image_ctx->state->unregister_update_watcher(m_update_watcher_handle, ctx);
180 }
181
182 template <typename I>
183 void PromoteRequest<I>::handle_unregister_update_watcher(int r) {
184 CephContext *cct = m_image_ctx->cct;
185 ldout(cct, 20) << "r=" << r << dendl;
186
187 if (r < 0) {
188 lderr(cct) << "failed to unregister update watcher: " << cpp_strerror(r)
189 << dendl;
190 finish(r);
191 return;
192 }
193
194 list_watchers();
195 }
196
197 template <typename I>
198 void PromoteRequest<I>::acquire_exclusive_lock() {
199 {
200 std::unique_lock locker{m_image_ctx->owner_lock};
201 if (m_image_ctx->exclusive_lock != nullptr &&
202 !m_image_ctx->exclusive_lock->is_lock_owner()) {
203 CephContext *cct = m_image_ctx->cct;
204 ldout(cct, 20) << dendl;
205
206 m_lock_acquired = true;
207 m_image_ctx->exclusive_lock->block_requests(0);
208
209 auto ctx = create_context_callback<
210 PromoteRequest<I>,
211 &PromoteRequest<I>::handle_acquire_exclusive_lock>(this);
212
213 m_image_ctx->exclusive_lock->acquire_lock(ctx);
214 return;
215 }
216 }
217
218 rollback();
219 }
220
221 template <typename I>
222 void PromoteRequest<I>::handle_acquire_exclusive_lock(int r) {
223 CephContext *cct = m_image_ctx->cct;
224 ldout(cct, 20) << "r=" << r << dendl;
225
226 if (r < 0) {
227 lderr(cct) << "failed to acquire exclusive lock: " << cpp_strerror(r)
228 << dendl;
229 finish(r);
230 return;
231 } else {
232 std::unique_lock locker{m_image_ctx->owner_lock};
233 if (m_image_ctx->exclusive_lock != nullptr &&
234 !m_image_ctx->exclusive_lock->is_lock_owner()) {
235 lderr(cct) << "failed to acquire exclusive lock" << dendl;
236 r = m_image_ctx->exclusive_lock->get_unlocked_op_error();
237 locker.unlock();
238 finish(r);
239 return;
240 }
241 }
242
243 rollback();
244 }
245
246 template <typename I>
247 void PromoteRequest<I>::rollback() {
248 if (m_rollback_snap_id == CEPH_NOSNAP) {
249 create_promote_snapshot();
250 return;
251 }
252
253 CephContext *cct = m_image_ctx->cct;
254 ldout(cct, 20) << dendl;
255
256 std::shared_lock owner_locker{m_image_ctx->owner_lock};
257 std::shared_lock image_locker{m_image_ctx->image_lock};
258
259 auto info = m_image_ctx->get_snap_info(m_rollback_snap_id);
260 ceph_assert(info != nullptr);
261 auto snap_namespace = info->snap_namespace;
262 auto snap_name = info->name;
263
264 image_locker.unlock();
265
266 auto ctx = create_async_context_callback(
267 *m_image_ctx, create_context_callback<
268 PromoteRequest<I>, &PromoteRequest<I>::handle_rollback>(this));
269
270 m_image_ctx->operations->execute_snap_rollback(snap_namespace, snap_name,
271 m_progress_ctx, ctx);
272 }
273
274 template <typename I>
275 void PromoteRequest<I>::handle_rollback(int r) {
276 CephContext *cct = m_image_ctx->cct;
277 ldout(cct, 20) << "r=" << r << dendl;
278
279 if (r < 0) {
280 lderr(cct) << "failed to rollback: " << cpp_strerror(r) << dendl;
281 finish(r);
282 return;
283 }
284
285 create_promote_snapshot();
286 }
287
288 template <typename I>
289 void PromoteRequest<I>::create_promote_snapshot() {
290 CephContext *cct = m_image_ctx->cct;
291 ldout(cct, 20) << dendl;
292
293 auto ctx = create_context_callback<
294 PromoteRequest<I>,
295 &PromoteRequest<I>::handle_create_promote_snapshot>(this);
296
297 auto req = CreatePrimaryRequest<I>::create(
298 m_image_ctx, m_global_image_id, CEPH_NOSNAP,
299 (snapshot::CREATE_PRIMARY_FLAG_IGNORE_EMPTY_PEERS |
300 snapshot::CREATE_PRIMARY_FLAG_FORCE), nullptr, ctx);
301 req->send();
302 }
303
304 template <typename I>
305 void PromoteRequest<I>::handle_create_promote_snapshot(int r) {
306 CephContext *cct = m_image_ctx->cct;
307 ldout(cct, 20) << "r=" << r << dendl;
308
309 if (r < 0) {
310 lderr(cct) << "failed to create promote snapshot: " << cpp_strerror(r)
311 << dendl;
312 finish(r);
313 return;
314 }
315
316 disable_non_primary_feature();
317 }
318
319 template <typename I>
320 void PromoteRequest<I>::disable_non_primary_feature() {
321 CephContext *cct = m_image_ctx->cct;
322 ldout(cct, 10) << dendl;
323
324 // remove the non-primary feature flag so that the image can be
325 // R/W by standard RBD clients
326 librados::ObjectWriteOperation op;
327 cls_client::set_features(&op, 0U, RBD_FEATURE_NON_PRIMARY);
328
329 auto aio_comp = create_rados_callback<
330 PromoteRequest<I>,
331 &PromoteRequest<I>::handle_disable_non_primary_feature>(this);
332 int r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, aio_comp,
333 &op);
334 ceph_assert(r == 0);
335 aio_comp->release();
336 }
337
338 template <typename I>
339 void PromoteRequest<I>::handle_disable_non_primary_feature(int r) {
340 CephContext *cct = m_image_ctx->cct;
341 ldout(cct, 10) << "r=" << r << dendl;
342
343 if (r < 0) {
344 lderr(cct) << "failed to disable non-primary feature: "
345 << cpp_strerror(r) << dendl;
346 finish(r);
347 return;
348 }
349
350 release_exclusive_lock();
351 }
352
353 template <typename I>
354 void PromoteRequest<I>::release_exclusive_lock() {
355 if (m_lock_acquired) {
356 std::unique_lock locker{m_image_ctx->owner_lock};
357 if (m_image_ctx->exclusive_lock != nullptr) {
358 CephContext *cct = m_image_ctx->cct;
359 ldout(cct, 20) << dendl;
360
361 m_image_ctx->exclusive_lock->unblock_requests();
362
363 auto ctx = create_context_callback<
364 PromoteRequest<I>,
365 &PromoteRequest<I>::handle_release_exclusive_lock>(this);
366
367 m_image_ctx->exclusive_lock->release_lock(ctx);
368 return;
369 }
370 }
371
372 finish(0);
373 }
374
375 template <typename I>
376 void PromoteRequest<I>::handle_release_exclusive_lock(int r) {
377 CephContext *cct = m_image_ctx->cct;
378 ldout(cct, 20) << "r=" << r << dendl;
379
380 if (r < 0) {
381 lderr(cct) << "failed to release exclusive lock: " << cpp_strerror(r)
382 << dendl;
383 finish(r);
384 return;
385 }
386
387 finish(0);
388 }
389
390 template <typename I>
391 void PromoteRequest<I>::finish(int r) {
392 CephContext *cct = m_image_ctx->cct;
393 ldout(cct, 20) << "r=" << r << dendl;
394
395 m_on_finish->complete(r);
396 delete this;
397 }
398
399 } // namespace snapshot
400 } // namespace mirror
401 } // namespace librbd
402
403 template class librbd::mirror::snapshot::PromoteRequest<librbd::ImageCtx>;