]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "include/compat.h" | |
5 | #include "BootstrapRequest.h" | |
6 | #include "CloseImageRequest.h" | |
7 | #include "CreateImageRequest.h" | |
8 | #include "IsPrimaryRequest.h" | |
9 | #include "OpenImageRequest.h" | |
10 | #include "OpenLocalImageRequest.h" | |
11 | #include "common/debug.h" | |
12 | #include "common/dout.h" | |
13 | #include "common/errno.h" | |
14 | #include "common/WorkQueue.h" | |
15 | #include "cls/rbd/cls_rbd_client.h" | |
16 | #include "journal/Journaler.h" | |
17 | #include "librbd/ImageCtx.h" | |
18 | #include "librbd/ImageState.h" | |
19 | #include "librbd/internal.h" | |
20 | #include "librbd/Journal.h" | |
21 | #include "librbd/Utils.h" | |
22 | #include "librbd/journal/Types.h" | |
23 | #include "tools/rbd_mirror/ProgressContext.h" | |
24 | #include "tools/rbd_mirror/ImageSyncThrottler.h" | |
25 | ||
26 | #define dout_context g_ceph_context | |
27 | #define dout_subsys ceph_subsys_rbd_mirror | |
28 | #undef dout_prefix | |
29 | #define dout_prefix *_dout << "rbd::mirror::image_replayer::BootstrapRequest: " \ | |
30 | << this << " " << __func__ | |
31 | ||
32 | namespace rbd { | |
33 | namespace mirror { | |
34 | namespace image_replayer { | |
35 | ||
36 | using librbd::util::create_context_callback; | |
37 | using librbd::util::create_rados_callback; | |
38 | using librbd::util::unique_lock_name; | |
39 | ||
40 | template <typename I> | |
41 | BootstrapRequest<I>::BootstrapRequest( | |
42 | librados::IoCtx &local_io_ctx, | |
43 | librados::IoCtx &remote_io_ctx, | |
44 | std::shared_ptr<ImageSyncThrottler<I>> image_sync_throttler, | |
45 | I **local_image_ctx, | |
46 | const std::string &local_image_id, | |
47 | const std::string &remote_image_id, | |
48 | const std::string &global_image_id, | |
49 | ContextWQ *work_queue, SafeTimer *timer, | |
50 | Mutex *timer_lock, | |
51 | const std::string &local_mirror_uuid, | |
52 | const std::string &remote_mirror_uuid, | |
53 | Journaler *journaler, | |
54 | MirrorPeerClientMeta *client_meta, | |
55 | Context *on_finish, | |
56 | bool *do_resync, | |
57 | rbd::mirror::ProgressContext *progress_ctx) | |
58 | : BaseRequest("rbd::mirror::image_replayer::BootstrapRequest", | |
59 | reinterpret_cast<CephContext*>(local_io_ctx.cct()), on_finish), | |
60 | m_local_io_ctx(local_io_ctx), m_remote_io_ctx(remote_io_ctx), | |
61 | m_image_sync_throttler(image_sync_throttler), | |
62 | m_local_image_ctx(local_image_ctx), m_local_image_id(local_image_id), | |
63 | m_remote_image_id(remote_image_id), m_global_image_id(global_image_id), | |
64 | m_work_queue(work_queue), m_timer(timer), m_timer_lock(timer_lock), | |
65 | m_local_mirror_uuid(local_mirror_uuid), | |
66 | m_remote_mirror_uuid(remote_mirror_uuid), m_journaler(journaler), | |
67 | m_client_meta(client_meta), m_progress_ctx(progress_ctx), | |
68 | m_do_resync(do_resync), | |
69 | m_lock(unique_lock_name("BootstrapRequest::m_lock", this)) { | |
70 | } | |
71 | ||
72 | template <typename I> | |
73 | BootstrapRequest<I>::~BootstrapRequest() { | |
74 | assert(m_remote_image_ctx == nullptr); | |
75 | } | |
76 | ||
77 | template <typename I> | |
78 | void BootstrapRequest<I>::send() { | |
79 | *m_do_resync = false; | |
80 | ||
81 | get_remote_tag_class(); | |
82 | } | |
83 | ||
84 | template <typename I> | |
85 | void BootstrapRequest<I>::cancel() { | |
86 | dout(20) << dendl; | |
87 | ||
88 | Mutex::Locker locker(m_lock); | |
89 | m_canceled = true; | |
90 | ||
91 | m_image_sync_throttler->cancel_sync(m_local_io_ctx, m_local_image_id); | |
92 | } | |
93 | ||
94 | template <typename I> | |
95 | void BootstrapRequest<I>::get_remote_tag_class() { | |
96 | dout(20) << dendl; | |
97 | ||
98 | update_progress("GET_REMOTE_TAG_CLASS"); | |
99 | ||
100 | Context *ctx = create_context_callback< | |
101 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_get_remote_tag_class>( | |
102 | this); | |
103 | m_journaler->get_client(librbd::Journal<>::IMAGE_CLIENT_ID, &m_client, ctx); | |
104 | } | |
105 | ||
106 | template <typename I> | |
107 | void BootstrapRequest<I>::handle_get_remote_tag_class(int r) { | |
108 | dout(20) << ": r=" << r << dendl; | |
109 | ||
110 | if (r < 0) { | |
111 | derr << ": failed to retrieve remote client: " << cpp_strerror(r) << dendl; | |
112 | finish(r); | |
113 | return; | |
114 | } | |
115 | ||
116 | librbd::journal::ClientData client_data; | |
117 | bufferlist::iterator it = m_client.data.begin(); | |
118 | try { | |
119 | ::decode(client_data, it); | |
120 | } catch (const buffer::error &err) { | |
121 | derr << ": failed to decode remote client meta data: " << err.what() | |
122 | << dendl; | |
123 | finish(-EBADMSG); | |
124 | return; | |
125 | } | |
126 | ||
127 | librbd::journal::ImageClientMeta *client_meta = | |
128 | boost::get<librbd::journal::ImageClientMeta>(&client_data.client_meta); | |
129 | if (client_meta == nullptr) { | |
130 | derr << ": unknown remote client registration" << dendl; | |
131 | finish(-EINVAL); | |
132 | return; | |
133 | } | |
134 | ||
135 | m_remote_tag_class = client_meta->tag_class; | |
136 | dout(10) << ": remote tag class=" << m_remote_tag_class << dendl; | |
137 | ||
138 | get_client(); | |
139 | } | |
140 | ||
141 | template <typename I> | |
142 | void BootstrapRequest<I>::get_client() { | |
143 | dout(20) << dendl; | |
144 | ||
145 | update_progress("GET_CLIENT"); | |
146 | ||
147 | Context *ctx = create_context_callback< | |
148 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_get_client>( | |
149 | this); | |
150 | m_journaler->get_client(m_local_mirror_uuid, &m_client, ctx); | |
151 | } | |
152 | ||
153 | template <typename I> | |
154 | void BootstrapRequest<I>::handle_get_client(int r) { | |
155 | dout(20) << ": r=" << r << dendl; | |
156 | ||
157 | if (r == -ENOENT) { | |
158 | dout(10) << ": client not registered" << dendl; | |
159 | } else if (r < 0) { | |
160 | derr << ": failed to retrieve client: " << cpp_strerror(r) << dendl; | |
161 | finish(r); | |
162 | return; | |
163 | } else if (decode_client_meta()) { | |
164 | // skip registration if it already exists | |
165 | open_remote_image(); | |
166 | return; | |
167 | } | |
168 | ||
169 | register_client(); | |
170 | } | |
171 | ||
172 | template <typename I> | |
173 | void BootstrapRequest<I>::register_client() { | |
174 | dout(20) << dendl; | |
175 | ||
176 | update_progress("REGISTER_CLIENT"); | |
177 | ||
178 | // record an place-holder record | |
179 | librbd::journal::ClientData client_data{ | |
180 | librbd::journal::MirrorPeerClientMeta{m_local_image_id}}; | |
181 | bufferlist client_data_bl; | |
182 | ::encode(client_data, client_data_bl); | |
183 | ||
184 | Context *ctx = create_context_callback< | |
185 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_register_client>( | |
186 | this); | |
187 | m_journaler->register_client(client_data_bl, ctx); | |
188 | } | |
189 | ||
190 | template <typename I> | |
191 | void BootstrapRequest<I>::handle_register_client(int r) { | |
192 | dout(20) << ": r=" << r << dendl; | |
193 | ||
194 | if (r < 0) { | |
195 | derr << ": failed to register with remote journal: " << cpp_strerror(r) | |
196 | << dendl; | |
197 | finish(r); | |
198 | return; | |
199 | } | |
200 | ||
201 | *m_client_meta = librbd::journal::MirrorPeerClientMeta(m_local_image_id); | |
202 | open_remote_image(); | |
203 | } | |
204 | ||
205 | template <typename I> | |
206 | void BootstrapRequest<I>::open_remote_image() { | |
207 | dout(20) << dendl; | |
208 | ||
209 | update_progress("OPEN_REMOTE_IMAGE"); | |
210 | ||
211 | Context *ctx = create_context_callback< | |
212 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_open_remote_image>( | |
213 | this); | |
214 | OpenImageRequest<I> *request = OpenImageRequest<I>::create( | |
215 | m_remote_io_ctx, &m_remote_image_ctx, m_remote_image_id, false, | |
216 | ctx); | |
217 | request->send(); | |
218 | } | |
219 | ||
220 | template <typename I> | |
221 | void BootstrapRequest<I>::handle_open_remote_image(int r) { | |
222 | dout(20) << ": r=" << r << dendl; | |
223 | ||
224 | if (r < 0) { | |
225 | derr << ": failed to open remote image: " << cpp_strerror(r) << dendl; | |
226 | assert(m_remote_image_ctx == nullptr); | |
227 | finish(r); | |
228 | return; | |
229 | } | |
230 | ||
231 | is_primary(); | |
232 | } | |
233 | ||
234 | template <typename I> | |
235 | void BootstrapRequest<I>::is_primary() { | |
236 | dout(20) << dendl; | |
237 | ||
238 | update_progress("OPEN_REMOTE_IMAGE"); | |
239 | ||
240 | Context *ctx = create_context_callback< | |
241 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_is_primary>( | |
242 | this); | |
243 | IsPrimaryRequest<I> *request = IsPrimaryRequest<I>::create(m_remote_image_ctx, | |
244 | &m_primary, ctx); | |
245 | request->send(); | |
246 | } | |
247 | ||
248 | template <typename I> | |
249 | void BootstrapRequest<I>::handle_is_primary(int r) { | |
250 | dout(20) << ": r=" << r << dendl; | |
251 | ||
252 | if (r < 0) { | |
253 | derr << ": error querying remote image primary status: " << cpp_strerror(r) | |
254 | << dendl; | |
255 | m_ret_val = r; | |
256 | close_remote_image(); | |
257 | return; | |
258 | } | |
259 | ||
260 | if (!m_primary) { | |
261 | dout(5) << ": remote image is not primary -- skipping image replay" | |
262 | << dendl; | |
263 | m_ret_val = -EREMOTEIO; | |
264 | update_client_state(); | |
265 | return; | |
266 | } | |
267 | ||
268 | if (m_local_image_id.empty()) { | |
269 | create_local_image(); | |
270 | return; | |
271 | } | |
272 | ||
273 | open_local_image(); | |
274 | } | |
275 | ||
276 | template <typename I> | |
277 | void BootstrapRequest<I>::update_client_state() { | |
278 | if (m_client_meta->state == librbd::journal::MIRROR_PEER_STATE_REPLAYING) { | |
279 | // state already set for replaying upon failover | |
280 | close_remote_image(); | |
281 | return; | |
282 | } | |
283 | ||
284 | dout(20) << dendl; | |
285 | update_progress("UPDATE_CLIENT_STATE"); | |
286 | ||
287 | librbd::journal::MirrorPeerClientMeta client_meta(*m_client_meta); | |
288 | client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING; | |
289 | ||
290 | librbd::journal::ClientData client_data(client_meta); | |
291 | bufferlist data_bl; | |
292 | ::encode(client_data, data_bl); | |
293 | ||
294 | Context *ctx = create_context_callback< | |
295 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_update_client_state>( | |
296 | this); | |
297 | m_journaler->update_client(data_bl, ctx); | |
298 | } | |
299 | ||
300 | template <typename I> | |
301 | void BootstrapRequest<I>::handle_update_client_state(int r) { | |
302 | dout(20) << ": r=" << r << dendl; | |
303 | if (r < 0) { | |
304 | derr << ": failed to update client: " << cpp_strerror(r) << dendl; | |
305 | } else { | |
306 | m_client_meta->state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;; | |
307 | } | |
308 | ||
309 | close_remote_image(); | |
310 | } | |
311 | ||
312 | template <typename I> | |
313 | void BootstrapRequest<I>::open_local_image() { | |
314 | dout(20) << dendl; | |
315 | ||
316 | update_progress("OPEN_LOCAL_IMAGE"); | |
317 | ||
318 | Context *ctx = create_context_callback< | |
319 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_open_local_image>( | |
320 | this); | |
321 | OpenLocalImageRequest<I> *request = OpenLocalImageRequest<I>::create( | |
322 | m_local_io_ctx, m_local_image_ctx, m_local_image_id, m_work_queue, | |
323 | ctx); | |
324 | request->send(); | |
325 | } | |
326 | ||
327 | template <typename I> | |
328 | void BootstrapRequest<I>::handle_open_local_image(int r) { | |
329 | dout(20) << ": r=" << r << dendl; | |
330 | ||
331 | if (r == -ENOENT) { | |
332 | assert(*m_local_image_ctx == nullptr); | |
333 | dout(10) << ": local image missing" << dendl; | |
334 | create_local_image(); | |
335 | return; | |
336 | } else if (r == -EREMOTEIO) { | |
337 | assert(*m_local_image_ctx == nullptr); | |
338 | dout(10) << "local image is primary -- skipping image replay" << dendl; | |
339 | m_ret_val = r; | |
340 | close_remote_image(); | |
341 | return; | |
342 | } else if (r < 0) { | |
343 | assert(*m_local_image_ctx == nullptr); | |
344 | derr << ": failed to open local image: " << cpp_strerror(r) << dendl; | |
345 | m_ret_val = r; | |
346 | close_remote_image(); | |
347 | return; | |
348 | } | |
349 | ||
350 | I *local_image_ctx = (*m_local_image_ctx); | |
351 | { | |
352 | RWLock::RLocker snap_locker(local_image_ctx->snap_lock); | |
353 | if (local_image_ctx->journal == nullptr) { | |
354 | derr << ": local image does not support journaling" << dendl; | |
355 | m_ret_val = -EINVAL; | |
356 | close_local_image(); | |
357 | return; | |
358 | } | |
359 | ||
360 | r = (*m_local_image_ctx)->journal->is_resync_requested(m_do_resync); | |
361 | if (r < 0) { | |
362 | derr << ": failed to check if a resync was requested" << dendl; | |
363 | m_ret_val = r; | |
364 | close_local_image(); | |
365 | return; | |
366 | } | |
367 | } | |
368 | ||
369 | if (*m_do_resync) { | |
370 | close_remote_image(); | |
371 | return; | |
372 | } | |
373 | ||
374 | if (m_client.state == cls::journal::CLIENT_STATE_DISCONNECTED) { | |
375 | dout(10) << ": client flagged disconnected -- skipping bootstrap" << dendl; | |
376 | // The caller is expected to detect disconnect initializing remote journal. | |
377 | m_ret_val = 0; | |
378 | close_remote_image(); | |
379 | return; | |
380 | } | |
381 | ||
382 | update_client_image(); | |
383 | } | |
384 | ||
385 | template <typename I> | |
386 | void BootstrapRequest<I>::create_local_image() { | |
387 | dout(20) << dendl; | |
388 | ||
389 | m_local_image_id = ""; | |
390 | update_progress("CREATE_LOCAL_IMAGE"); | |
391 | ||
392 | m_remote_image_ctx->snap_lock.get_read(); | |
393 | std::string image_name = m_remote_image_ctx->name; | |
394 | m_remote_image_ctx->snap_lock.put_read(); | |
395 | ||
396 | Context *ctx = create_context_callback< | |
397 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_create_local_image>( | |
398 | this); | |
399 | CreateImageRequest<I> *request = CreateImageRequest<I>::create( | |
400 | m_local_io_ctx, m_work_queue, m_global_image_id, m_remote_mirror_uuid, | |
401 | image_name, m_remote_image_ctx, &m_local_image_id, ctx); | |
402 | request->send(); | |
403 | } | |
404 | ||
405 | template <typename I> | |
406 | void BootstrapRequest<I>::handle_create_local_image(int r) { | |
407 | dout(20) << ": r=" << r << dendl; | |
408 | ||
409 | if (r < 0) { | |
410 | derr << ": failed to create local image: " << cpp_strerror(r) << dendl; | |
411 | m_ret_val = r; | |
412 | close_remote_image(); | |
413 | return; | |
414 | } | |
415 | ||
416 | open_local_image(); | |
417 | } | |
418 | ||
419 | template <typename I> | |
420 | void BootstrapRequest<I>::update_client_image() { | |
421 | dout(20) << dendl; | |
422 | ||
423 | update_progress("UPDATE_CLIENT_IMAGE"); | |
424 | ||
425 | if (m_client_meta->image_id == (*m_local_image_ctx)->id) { | |
426 | // already registered local image with remote journal | |
427 | get_remote_tags(); | |
428 | return; | |
429 | } | |
430 | m_local_image_id = (*m_local_image_ctx)->id; | |
431 | ||
432 | dout(20) << dendl; | |
433 | ||
434 | librbd::journal::MirrorPeerClientMeta client_meta{m_local_image_id}; | |
435 | client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING; | |
436 | ||
437 | librbd::journal::ClientData client_data(client_meta); | |
438 | bufferlist data_bl; | |
439 | ::encode(client_data, data_bl); | |
440 | ||
441 | Context *ctx = create_context_callback< | |
442 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_update_client_image>( | |
443 | this); | |
444 | m_journaler->update_client(data_bl, ctx); | |
445 | } | |
446 | ||
447 | template <typename I> | |
448 | void BootstrapRequest<I>::handle_update_client_image(int r) { | |
449 | dout(20) << ": r=" << r << dendl; | |
450 | ||
451 | if (r < 0) { | |
452 | derr << ": failed to update client: " << cpp_strerror(r) << dendl; | |
453 | m_ret_val = r; | |
454 | close_local_image(); | |
455 | return; | |
456 | } | |
457 | ||
458 | if (m_canceled) { | |
459 | dout(10) << ": request canceled" << dendl; | |
460 | m_ret_val = -ECANCELED; | |
461 | close_local_image(); | |
462 | return; | |
463 | } | |
464 | ||
465 | *m_client_meta = {m_local_image_id}; | |
466 | m_client_meta->state = librbd::journal::MIRROR_PEER_STATE_SYNCING; | |
467 | get_remote_tags(); | |
468 | } | |
469 | ||
470 | template <typename I> | |
471 | void BootstrapRequest<I>::get_remote_tags() { | |
472 | dout(20) << dendl; | |
473 | ||
474 | update_progress("GET_REMOTE_TAGS"); | |
475 | ||
476 | if (m_client_meta->state == librbd::journal::MIRROR_PEER_STATE_SYNCING) { | |
477 | // optimization -- no need to compare remote tags if we just created | |
478 | // the image locally or sync was interrupted | |
479 | image_sync(); | |
480 | return; | |
481 | } | |
482 | ||
483 | dout(20) << dendl; | |
484 | ||
485 | Context *ctx = create_context_callback< | |
486 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_get_remote_tags>(this); | |
487 | m_journaler->get_tags(m_remote_tag_class, &m_remote_tags, ctx); | |
488 | } | |
489 | ||
490 | template <typename I> | |
491 | void BootstrapRequest<I>::handle_get_remote_tags(int r) { | |
492 | dout(20) << ": r=" << r << dendl; | |
493 | ||
494 | if (r < 0) { | |
495 | derr << ": failed to retrieve remote tags: " << cpp_strerror(r) << dendl; | |
496 | m_ret_val = r; | |
497 | close_local_image(); | |
498 | return; | |
499 | } | |
500 | ||
501 | if (m_canceled) { | |
502 | dout(10) << ": request canceled" << dendl; | |
503 | m_ret_val = -ECANCELED; | |
504 | close_local_image(); | |
505 | return; | |
506 | } | |
507 | ||
508 | // At this point, the local image was existing, non-primary, and replaying; | |
509 | // and the remote image is primary. Attempt to link the local image's most | |
510 | // recent tag to the remote image's tag chain. | |
511 | uint64_t local_tag_tid; | |
512 | librbd::journal::TagData local_tag_data; | |
513 | I *local_image_ctx = (*m_local_image_ctx); | |
514 | { | |
515 | RWLock::RLocker snap_locker(local_image_ctx->snap_lock); | |
516 | if (local_image_ctx->journal == nullptr) { | |
517 | derr << ": local image does not support journaling" << dendl; | |
518 | m_ret_val = -EINVAL; | |
519 | close_local_image(); | |
520 | return; | |
521 | } | |
522 | ||
523 | local_tag_tid = local_image_ctx->journal->get_tag_tid(); | |
524 | local_tag_data = local_image_ctx->journal->get_tag_data(); | |
525 | dout(20) << ": local tag " << local_tag_tid << ": " | |
526 | << local_tag_data << dendl; | |
527 | } | |
528 | ||
529 | bool remote_tag_data_valid = false; | |
530 | librbd::journal::TagData remote_tag_data; | |
531 | boost::optional<uint64_t> remote_orphan_tag_tid = | |
532 | boost::make_optional<uint64_t>(false, 0U); | |
533 | bool reconnect_orphan = false; | |
534 | ||
535 | // decode the remote tags | |
536 | for (auto &remote_tag : m_remote_tags) { | |
537 | if (local_tag_data.predecessor.commit_valid && | |
538 | local_tag_data.predecessor.mirror_uuid == m_remote_mirror_uuid && | |
539 | local_tag_data.predecessor.tag_tid > remote_tag.tid) { | |
540 | dout(20) << ": skipping processed predecessor remote tag " | |
541 | << remote_tag.tid << dendl; | |
542 | continue; | |
543 | } | |
544 | ||
545 | try { | |
546 | bufferlist::iterator it = remote_tag.data.begin(); | |
547 | ::decode(remote_tag_data, it); | |
548 | remote_tag_data_valid = true; | |
549 | } catch (const buffer::error &err) { | |
550 | derr << ": failed to decode remote tag " << remote_tag.tid << ": " | |
551 | << err.what() << dendl; | |
552 | m_ret_val = -EBADMSG; | |
553 | close_local_image(); | |
554 | return; | |
555 | } | |
556 | ||
557 | dout(10) << ": decoded remote tag " << remote_tag.tid << ": " | |
558 | << remote_tag_data << dendl; | |
559 | ||
560 | if (!local_tag_data.predecessor.commit_valid) { | |
561 | // newly synced local image (no predecessor) replays from the first tag | |
562 | if (remote_tag_data.mirror_uuid != librbd::Journal<>::LOCAL_MIRROR_UUID) { | |
563 | dout(20) << ": skipping non-primary remote tag" << dendl; | |
564 | continue; | |
565 | } | |
566 | ||
567 | dout(20) << ": using initial primary remote tag" << dendl; | |
568 | break; | |
569 | } | |
570 | ||
571 | if (local_tag_data.mirror_uuid == librbd::Journal<>::ORPHAN_MIRROR_UUID) { | |
572 | // demotion last available local epoch | |
573 | ||
574 | if (remote_tag_data.mirror_uuid == local_tag_data.mirror_uuid && | |
575 | remote_tag_data.predecessor.commit_valid && | |
576 | remote_tag_data.predecessor.tag_tid == | |
577 | local_tag_data.predecessor.tag_tid) { | |
578 | // demotion matches remote epoch | |
579 | ||
580 | if (remote_tag_data.predecessor.mirror_uuid == m_local_mirror_uuid && | |
581 | local_tag_data.predecessor.mirror_uuid == | |
582 | librbd::Journal<>::LOCAL_MIRROR_UUID) { | |
583 | // local demoted and remote has matching event | |
584 | dout(20) << ": found matching local demotion tag" << dendl; | |
585 | remote_orphan_tag_tid = remote_tag.tid; | |
586 | continue; | |
587 | } | |
588 | ||
589 | if (local_tag_data.predecessor.mirror_uuid == m_remote_mirror_uuid && | |
590 | remote_tag_data.predecessor.mirror_uuid == | |
591 | librbd::Journal<>::LOCAL_MIRROR_UUID) { | |
592 | // remote demoted and local has matching event | |
593 | dout(20) << ": found matching remote demotion tag" << dendl; | |
594 | remote_orphan_tag_tid = remote_tag.tid; | |
595 | continue; | |
596 | } | |
597 | } | |
598 | ||
599 | if (remote_tag_data.mirror_uuid == librbd::Journal<>::LOCAL_MIRROR_UUID && | |
600 | remote_tag_data.predecessor.mirror_uuid == librbd::Journal<>::ORPHAN_MIRROR_UUID && | |
601 | remote_tag_data.predecessor.commit_valid && remote_orphan_tag_tid && | |
602 | remote_tag_data.predecessor.tag_tid == *remote_orphan_tag_tid) { | |
603 | // remote promotion tag chained to remote/local demotion tag | |
604 | dout(20) << ": found chained remote promotion tag" << dendl; | |
605 | reconnect_orphan = true; | |
606 | break; | |
607 | } | |
608 | ||
609 | // promotion must follow demotion | |
610 | remote_orphan_tag_tid = boost::none; | |
611 | } | |
612 | } | |
613 | ||
614 | if (remote_tag_data_valid && | |
615 | local_tag_data.mirror_uuid == m_remote_mirror_uuid) { | |
616 | dout(20) << ": local image is in clean replay state" << dendl; | |
617 | } else if (reconnect_orphan) { | |
618 | dout(20) << ": remote image was demoted/promoted" << dendl; | |
619 | } else { | |
620 | derr << ": split-brain detected -- skipping image replay" << dendl; | |
621 | m_ret_val = -EEXIST; | |
622 | close_local_image(); | |
623 | return; | |
624 | } | |
625 | ||
626 | image_sync(); | |
627 | } | |
628 | ||
629 | template <typename I> | |
630 | void BootstrapRequest<I>::image_sync() { | |
631 | if (m_client_meta->state == librbd::journal::MIRROR_PEER_STATE_REPLAYING) { | |
632 | // clean replay state -- no image sync required | |
633 | close_remote_image(); | |
634 | return; | |
635 | } | |
636 | ||
637 | dout(20) << dendl; | |
638 | update_progress("IMAGE_SYNC"); | |
639 | ||
640 | Context *ctx = create_context_callback< | |
641 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_image_sync>( | |
642 | this); | |
643 | ||
644 | { | |
645 | Mutex::Locker locker(m_lock); | |
646 | if (!m_canceled) { | |
647 | m_image_sync_throttler->start_sync(*m_local_image_ctx, | |
648 | m_remote_image_ctx, m_timer, | |
649 | m_timer_lock, | |
650 | m_local_mirror_uuid, m_journaler, | |
651 | m_client_meta, m_work_queue, ctx, | |
652 | m_progress_ctx); | |
653 | return; | |
654 | } | |
655 | } | |
656 | ||
657 | dout(10) << ": request canceled" << dendl; | |
658 | m_ret_val = -ECANCELED; | |
659 | close_remote_image(); | |
660 | } | |
661 | ||
662 | template <typename I> | |
663 | void BootstrapRequest<I>::handle_image_sync(int r) { | |
664 | dout(20) << ": r=" << r << dendl; | |
665 | ||
666 | if (m_canceled) { | |
667 | dout(10) << ": request canceled" << dendl; | |
668 | m_ret_val = -ECANCELED; | |
669 | } | |
670 | ||
671 | if (r < 0) { | |
672 | derr << ": failed to sync remote image: " << cpp_strerror(r) << dendl; | |
673 | m_ret_val = r; | |
674 | } | |
675 | ||
676 | close_remote_image(); | |
677 | } | |
678 | ||
679 | template <typename I> | |
680 | void BootstrapRequest<I>::close_local_image() { | |
681 | dout(20) << dendl; | |
682 | ||
683 | update_progress("CLOSE_LOCAL_IMAGE"); | |
684 | ||
685 | Context *ctx = create_context_callback< | |
686 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_close_local_image>( | |
687 | this); | |
688 | CloseImageRequest<I> *request = CloseImageRequest<I>::create( | |
689 | m_local_image_ctx, ctx); | |
690 | request->send(); | |
691 | } | |
692 | ||
693 | template <typename I> | |
694 | void BootstrapRequest<I>::handle_close_local_image(int r) { | |
695 | dout(20) << ": r=" << r << dendl; | |
696 | ||
697 | if (r < 0) { | |
698 | derr << ": error encountered closing local image: " << cpp_strerror(r) | |
699 | << dendl; | |
700 | } | |
701 | ||
702 | close_remote_image(); | |
703 | } | |
704 | ||
705 | template <typename I> | |
706 | void BootstrapRequest<I>::close_remote_image() { | |
707 | dout(20) << dendl; | |
708 | ||
709 | update_progress("CLOSE_REMOTE_IMAGE"); | |
710 | ||
711 | Context *ctx = create_context_callback< | |
712 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_close_remote_image>( | |
713 | this); | |
714 | CloseImageRequest<I> *request = CloseImageRequest<I>::create( | |
715 | &m_remote_image_ctx, ctx); | |
716 | request->send(); | |
717 | } | |
718 | ||
719 | template <typename I> | |
720 | void BootstrapRequest<I>::handle_close_remote_image(int r) { | |
721 | dout(20) << ": r=" << r << dendl; | |
722 | ||
723 | if (r < 0) { | |
724 | derr << ": error encountered closing remote image: " << cpp_strerror(r) | |
725 | << dendl; | |
726 | } | |
727 | ||
728 | finish(m_ret_val); | |
729 | } | |
730 | ||
731 | template <typename I> | |
732 | bool BootstrapRequest<I>::decode_client_meta() { | |
733 | dout(20) << dendl; | |
734 | ||
735 | librbd::journal::ClientData client_data; | |
736 | bufferlist::iterator it = m_client.data.begin(); | |
737 | try { | |
738 | ::decode(client_data, it); | |
739 | } catch (const buffer::error &err) { | |
740 | derr << ": failed to decode client meta data: " << err.what() << dendl; | |
741 | return false; | |
742 | } | |
743 | ||
744 | librbd::journal::MirrorPeerClientMeta *client_meta = | |
745 | boost::get<librbd::journal::MirrorPeerClientMeta>(&client_data.client_meta); | |
746 | if (client_meta == nullptr) { | |
747 | derr << ": unknown peer registration" << dendl; | |
748 | return false; | |
749 | } else if (!client_meta->image_id.empty()) { | |
750 | // have an image id -- use that to open the image | |
751 | m_local_image_id = client_meta->image_id; | |
752 | } | |
753 | ||
754 | *m_client_meta = *client_meta; | |
755 | ||
756 | dout(20) << ": client found: image_id=" << m_local_image_id | |
757 | << ", client_meta=" << *m_client_meta << dendl; | |
758 | return true; | |
759 | } | |
760 | ||
761 | template <typename I> | |
762 | void BootstrapRequest<I>::update_progress(const std::string &description) { | |
763 | dout(20) << ": " << description << dendl; | |
764 | ||
765 | if (m_progress_ctx) { | |
766 | m_progress_ctx->update_progress(description); | |
767 | } | |
768 | } | |
769 | ||
770 | } // namespace image_replayer | |
771 | } // namespace mirror | |
772 | } // namespace rbd | |
773 | ||
774 | template class rbd::mirror::image_replayer::BootstrapRequest<librbd::ImageCtx>; |