]>
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 "ImageSync.h" | |
31f18b77 | 5 | #include "InstanceWatcher.h" |
7c673cae | 6 | #include "ProgressContext.h" |
11fdf7f2 TL |
7 | #include "common/debug.h" |
8 | #include "common/Timer.h" | |
7c673cae | 9 | #include "common/errno.h" |
11fdf7f2 | 10 | #include "librbd/DeepCopyRequest.h" |
7c673cae | 11 | #include "librbd/ImageCtx.h" |
11fdf7f2 | 12 | #include "librbd/ImageState.h" |
7c673cae | 13 | #include "librbd/Utils.h" |
11fdf7f2 | 14 | #include "librbd/internal.h" |
9f95a23c | 15 | #include "tools/rbd_mirror/Threads.h" |
7c673cae FG |
16 | #include "tools/rbd_mirror/image_sync/SyncPointCreateRequest.h" |
17 | #include "tools/rbd_mirror/image_sync/SyncPointPruneRequest.h" | |
9f95a23c | 18 | #include "tools/rbd_mirror/image_sync/Types.h" |
7c673cae FG |
19 | |
20 | #define dout_context g_ceph_context | |
21 | #define dout_subsys ceph_subsys_rbd_mirror | |
22 | #undef dout_prefix | |
23 | #define dout_prefix *_dout << "rbd::mirror::ImageSync: " \ | |
24 | << this << " " << __func__ | |
25 | ||
26 | namespace rbd { | |
27 | namespace mirror { | |
28 | ||
29 | using namespace image_sync; | |
28e407b8 | 30 | using librbd::util::create_async_context_callback; |
7c673cae FG |
31 | using librbd::util::create_context_callback; |
32 | using librbd::util::unique_lock_name; | |
33 | ||
11fdf7f2 TL |
34 | template <typename I> |
35 | class ImageSync<I>::ImageCopyProgressContext : public librbd::ProgressContext { | |
36 | public: | |
37 | ImageCopyProgressContext(ImageSync *image_sync) : image_sync(image_sync) { | |
38 | } | |
39 | ||
40 | int update_progress(uint64_t object_no, uint64_t object_count) override { | |
41 | image_sync->handle_copy_image_update_progress(object_no, object_count); | |
42 | return 0; | |
43 | } | |
44 | ||
45 | ImageSync *image_sync; | |
46 | }; | |
47 | ||
7c673cae | 48 | template <typename I> |
9f95a23c TL |
49 | ImageSync<I>::ImageSync( |
50 | Threads<I>* threads, | |
51 | I *local_image_ctx, | |
52 | I *remote_image_ctx, | |
53 | const std::string &local_mirror_uuid, | |
54 | image_sync::SyncPointHandler* sync_point_handler, | |
55 | InstanceWatcher<I> *instance_watcher, | |
56 | ProgressContext *progress_ctx, | |
57 | Context *on_finish) | |
58 | : CancelableRequest("rbd::mirror::ImageSync", local_image_ctx->cct, | |
59 | on_finish), | |
60 | m_threads(threads), | |
61 | m_local_image_ctx(local_image_ctx), | |
62 | m_remote_image_ctx(remote_image_ctx), | |
63 | m_local_mirror_uuid(local_mirror_uuid), | |
64 | m_sync_point_handler(sync_point_handler), | |
65 | m_instance_watcher(instance_watcher), | |
31f18b77 | 66 | m_progress_ctx(progress_ctx), |
9f95a23c TL |
67 | m_lock(ceph::make_mutex(unique_lock_name("ImageSync::m_lock", this))), |
68 | m_update_sync_point_interval( | |
69 | m_local_image_ctx->cct->_conf.template get_val<double>( | |
70 | "rbd_mirror_sync_point_update_age")) { | |
7c673cae FG |
71 | } |
72 | ||
73 | template <typename I> | |
74 | ImageSync<I>::~ImageSync() { | |
11fdf7f2 TL |
75 | ceph_assert(m_image_copy_request == nullptr); |
76 | ceph_assert(m_image_copy_prog_ctx == nullptr); | |
77 | ceph_assert(m_update_sync_ctx == nullptr); | |
7c673cae FG |
78 | } |
79 | ||
80 | template <typename I> | |
81 | void ImageSync<I>::send() { | |
31f18b77 | 82 | send_notify_sync_request(); |
7c673cae FG |
83 | } |
84 | ||
85 | template <typename I> | |
86 | void ImageSync<I>::cancel() { | |
9f95a23c | 87 | std::lock_guard locker{m_lock}; |
7c673cae | 88 | |
11fdf7f2 | 89 | dout(10) << dendl; |
7c673cae FG |
90 | |
91 | m_canceled = true; | |
92 | ||
31f18b77 FG |
93 | if (m_instance_watcher->cancel_sync_request(m_local_image_ctx->id)) { |
94 | return; | |
95 | } | |
96 | ||
7c673cae FG |
97 | if (m_image_copy_request != nullptr) { |
98 | m_image_copy_request->cancel(); | |
99 | } | |
100 | } | |
101 | ||
31f18b77 FG |
102 | template <typename I> |
103 | void ImageSync<I>::send_notify_sync_request() { | |
104 | update_progress("NOTIFY_SYNC_REQUEST"); | |
105 | ||
11fdf7f2 | 106 | dout(10) << dendl; |
31f18b77 | 107 | |
9f95a23c | 108 | m_lock.lock(); |
28e407b8 | 109 | if (m_canceled) { |
9f95a23c TL |
110 | m_lock.unlock(); |
111 | CancelableRequest::finish(-ECANCELED); | |
28e407b8 AA |
112 | return; |
113 | } | |
114 | ||
115 | Context *ctx = create_async_context_callback( | |
9f95a23c | 116 | m_threads->work_queue, create_context_callback< |
28e407b8 | 117 | ImageSync<I>, &ImageSync<I>::handle_notify_sync_request>(this)); |
31f18b77 | 118 | m_instance_watcher->notify_sync_request(m_local_image_ctx->id, ctx); |
9f95a23c | 119 | m_lock.unlock(); |
31f18b77 FG |
120 | } |
121 | ||
122 | template <typename I> | |
123 | void ImageSync<I>::handle_notify_sync_request(int r) { | |
11fdf7f2 | 124 | dout(10) << ": r=" << r << dendl; |
31f18b77 | 125 | |
9f95a23c | 126 | m_lock.lock(); |
28e407b8 AA |
127 | if (r == 0 && m_canceled) { |
128 | r = -ECANCELED; | |
129 | } | |
9f95a23c | 130 | m_lock.unlock(); |
28e407b8 | 131 | |
31f18b77 | 132 | if (r < 0) { |
9f95a23c | 133 | CancelableRequest::finish(r); |
31f18b77 FG |
134 | return; |
135 | } | |
136 | ||
137 | send_prune_catch_up_sync_point(); | |
138 | } | |
139 | ||
7c673cae FG |
140 | template <typename I> |
141 | void ImageSync<I>::send_prune_catch_up_sync_point() { | |
142 | update_progress("PRUNE_CATCH_UP_SYNC_POINT"); | |
143 | ||
9f95a23c | 144 | if (m_sync_point_handler->get_sync_points().empty()) { |
7c673cae FG |
145 | send_create_sync_point(); |
146 | return; | |
147 | } | |
148 | ||
11fdf7f2 | 149 | dout(10) << dendl; |
7c673cae FG |
150 | |
151 | // prune will remove sync points with missing snapshots and | |
152 | // ensure we have a maximum of one sync point (in case we | |
153 | // restarted) | |
154 | Context *ctx = create_context_callback< | |
155 | ImageSync<I>, &ImageSync<I>::handle_prune_catch_up_sync_point>(this); | |
156 | SyncPointPruneRequest<I> *request = SyncPointPruneRequest<I>::create( | |
9f95a23c | 157 | m_remote_image_ctx, false, m_sync_point_handler, ctx); |
7c673cae FG |
158 | request->send(); |
159 | } | |
160 | ||
161 | template <typename I> | |
162 | void ImageSync<I>::handle_prune_catch_up_sync_point(int r) { | |
11fdf7f2 | 163 | dout(10) << ": r=" << r << dendl; |
7c673cae FG |
164 | |
165 | if (r < 0) { | |
166 | derr << ": failed to prune catch-up sync point: " | |
167 | << cpp_strerror(r) << dendl; | |
168 | finish(r); | |
169 | return; | |
170 | } | |
171 | ||
172 | send_create_sync_point(); | |
173 | } | |
174 | ||
175 | template <typename I> | |
176 | void ImageSync<I>::send_create_sync_point() { | |
177 | update_progress("CREATE_SYNC_POINT"); | |
178 | ||
179 | // TODO: when support for disconnecting laggy clients is added, | |
180 | // re-connect and create catch-up sync point | |
9f95a23c | 181 | if (!m_sync_point_handler->get_sync_points().empty()) { |
11fdf7f2 | 182 | send_copy_image(); |
7c673cae FG |
183 | return; |
184 | } | |
185 | ||
11fdf7f2 | 186 | dout(10) << dendl; |
7c673cae FG |
187 | |
188 | Context *ctx = create_context_callback< | |
189 | ImageSync<I>, &ImageSync<I>::handle_create_sync_point>(this); | |
190 | SyncPointCreateRequest<I> *request = SyncPointCreateRequest<I>::create( | |
9f95a23c | 191 | m_remote_image_ctx, m_local_mirror_uuid, m_sync_point_handler, ctx); |
7c673cae FG |
192 | request->send(); |
193 | } | |
194 | ||
195 | template <typename I> | |
196 | void ImageSync<I>::handle_create_sync_point(int r) { | |
11fdf7f2 | 197 | dout(10) << ": r=" << r << dendl; |
7c673cae FG |
198 | |
199 | if (r < 0) { | |
200 | derr << ": failed to create sync point: " << cpp_strerror(r) | |
201 | << dendl; | |
202 | finish(r); | |
203 | return; | |
204 | } | |
205 | ||
11fdf7f2 | 206 | send_copy_image(); |
7c673cae FG |
207 | } |
208 | ||
209 | template <typename I> | |
11fdf7f2 TL |
210 | void ImageSync<I>::send_copy_image() { |
211 | librados::snap_t snap_id_start = 0; | |
212 | librados::snap_t snap_id_end; | |
213 | librbd::deep_copy::ObjectNumber object_number; | |
214 | int r = 0; | |
9f95a23c TL |
215 | |
216 | m_snap_seqs_copy = m_sync_point_handler->get_snap_seqs(); | |
217 | m_sync_points_copy = m_sync_point_handler->get_sync_points(); | |
218 | ceph_assert(!m_sync_points_copy.empty()); | |
219 | auto &sync_point = m_sync_points_copy.front(); | |
220 | ||
7c673cae | 221 | { |
9f95a23c | 222 | std::shared_lock image_locker{m_remote_image_ctx->image_lock}; |
11fdf7f2 TL |
223 | snap_id_end = m_remote_image_ctx->get_snap_id( |
224 | cls::rbd::UserSnapshotNamespace(), sync_point.snap_name); | |
225 | if (snap_id_end == CEPH_NOSNAP) { | |
226 | derr << ": failed to locate snapshot: " << sync_point.snap_name << dendl; | |
227 | r = -ENOENT; | |
228 | } else if (!sync_point.from_snap_name.empty()) { | |
229 | snap_id_start = m_remote_image_ctx->get_snap_id( | |
230 | cls::rbd::UserSnapshotNamespace(), sync_point.from_snap_name); | |
231 | if (snap_id_start == CEPH_NOSNAP) { | |
232 | derr << ": failed to locate from snapshot: " | |
233 | << sync_point.from_snap_name << dendl; | |
234 | r = -ENOENT; | |
235 | } | |
7c673cae | 236 | } |
11fdf7f2 | 237 | object_number = sync_point.object_number; |
7c673cae | 238 | } |
11fdf7f2 | 239 | if (r < 0) { |
7c673cae FG |
240 | finish(r); |
241 | return; | |
242 | } | |
243 | ||
9f95a23c | 244 | m_lock.lock(); |
7c673cae | 245 | if (m_canceled) { |
9f95a23c | 246 | m_lock.unlock(); |
7c673cae FG |
247 | finish(-ECANCELED); |
248 | return; | |
249 | } | |
250 | ||
11fdf7f2 | 251 | dout(10) << dendl; |
7c673cae FG |
252 | |
253 | Context *ctx = create_context_callback< | |
254 | ImageSync<I>, &ImageSync<I>::handle_copy_image>(this); | |
11fdf7f2 TL |
255 | m_image_copy_prog_ctx = new ImageCopyProgressContext(this); |
256 | m_image_copy_request = librbd::DeepCopyRequest<I>::create( | |
257 | m_remote_image_ctx, m_local_image_ctx, snap_id_start, snap_id_end, | |
9f95a23c | 258 | 0, false, object_number, m_threads->work_queue, &m_snap_seqs_copy, |
11fdf7f2 | 259 | m_image_copy_prog_ctx, ctx); |
7c673cae | 260 | m_image_copy_request->get(); |
9f95a23c | 261 | m_lock.unlock(); |
7c673cae FG |
262 | |
263 | update_progress("COPY_IMAGE"); | |
264 | ||
265 | m_image_copy_request->send(); | |
266 | } | |
267 | ||
268 | template <typename I> | |
269 | void ImageSync<I>::handle_copy_image(int r) { | |
11fdf7f2 | 270 | dout(10) << ": r=" << r << dendl; |
7c673cae FG |
271 | |
272 | { | |
9f95a23c | 273 | std::scoped_lock locker{m_threads->timer_lock, m_lock}; |
7c673cae FG |
274 | m_image_copy_request->put(); |
275 | m_image_copy_request = nullptr; | |
11fdf7f2 TL |
276 | delete m_image_copy_prog_ctx; |
277 | m_image_copy_prog_ctx = nullptr; | |
7c673cae FG |
278 | if (r == 0 && m_canceled) { |
279 | r = -ECANCELED; | |
280 | } | |
11fdf7f2 TL |
281 | |
282 | if (m_update_sync_ctx != nullptr) { | |
9f95a23c | 283 | m_threads->timer->cancel_event(m_update_sync_ctx); |
11fdf7f2 TL |
284 | m_update_sync_ctx = nullptr; |
285 | } | |
286 | ||
287 | if (m_updating_sync_point) { | |
288 | m_ret_val = r; | |
289 | return; | |
290 | } | |
7c673cae FG |
291 | } |
292 | ||
293 | if (r == -ECANCELED) { | |
294 | dout(10) << ": image copy canceled" << dendl; | |
295 | finish(r); | |
296 | return; | |
297 | } else if (r < 0) { | |
298 | derr << ": failed to copy image: " << cpp_strerror(r) << dendl; | |
299 | finish(r); | |
300 | return; | |
301 | } | |
302 | ||
11fdf7f2 | 303 | send_flush_sync_point(); |
7c673cae FG |
304 | } |
305 | ||
306 | template <typename I> | |
11fdf7f2 TL |
307 | void ImageSync<I>::handle_copy_image_update_progress(uint64_t object_no, |
308 | uint64_t object_count) { | |
309 | int percent = 100 * object_no / object_count; | |
310 | update_progress("COPY_IMAGE " + stringify(percent) + "%"); | |
7c673cae | 311 | |
9f95a23c | 312 | std::lock_guard locker{m_lock}; |
11fdf7f2 TL |
313 | m_image_copy_object_no = object_no; |
314 | m_image_copy_object_count = object_count; | |
7c673cae | 315 | |
11fdf7f2 TL |
316 | if (m_update_sync_ctx == nullptr && !m_updating_sync_point) { |
317 | send_update_sync_point(); | |
318 | } | |
319 | } | |
7c673cae | 320 | |
11fdf7f2 TL |
321 | template <typename I> |
322 | void ImageSync<I>::send_update_sync_point() { | |
9f95a23c | 323 | ceph_assert(ceph_mutex_is_locked(m_lock)); |
7c673cae | 324 | |
11fdf7f2 TL |
325 | m_update_sync_ctx = nullptr; |
326 | ||
327 | if (m_canceled) { | |
328 | return; | |
31f18b77 | 329 | } |
11fdf7f2 | 330 | |
9f95a23c TL |
331 | ceph_assert(!m_sync_points_copy.empty()); |
332 | auto sync_point = &m_sync_points_copy.front(); | |
11fdf7f2 | 333 | |
9f95a23c | 334 | if (sync_point->object_number && |
11fdf7f2 TL |
335 | (m_image_copy_object_no - 1) == sync_point->object_number.get()) { |
336 | // update sync point did not progress since last sync | |
31f18b77 FG |
337 | return; |
338 | } | |
339 | ||
11fdf7f2 TL |
340 | m_updating_sync_point = true; |
341 | ||
11fdf7f2 TL |
342 | if (m_image_copy_object_no > 0) { |
343 | sync_point->object_number = m_image_copy_object_no - 1; | |
344 | } | |
345 | ||
9f95a23c TL |
346 | auto ctx = create_context_callback< |
347 | ImageSync<I>, &ImageSync<I>::handle_update_sync_point>(this); | |
348 | m_sync_point_handler->update_sync_points(m_snap_seqs_copy, | |
349 | m_sync_points_copy, false, ctx); | |
7c673cae FG |
350 | } |
351 | ||
352 | template <typename I> | |
11fdf7f2 TL |
353 | void ImageSync<I>::handle_update_sync_point(int r) { |
354 | CephContext *cct = m_local_image_ctx->cct; | |
355 | ldout(cct, 20) << ": r=" << r << dendl; | |
7c673cae | 356 | |
11fdf7f2 | 357 | { |
9f95a23c | 358 | std::scoped_lock locker{m_threads->timer_lock, m_lock}; |
11fdf7f2 TL |
359 | m_updating_sync_point = false; |
360 | ||
361 | if (m_image_copy_request != nullptr) { | |
9f95a23c | 362 | m_update_sync_ctx = new LambdaContext( |
11fdf7f2 | 363 | [this](int r) { |
9f95a23c | 364 | std::lock_guard locker{m_lock}; |
11fdf7f2 TL |
365 | this->send_update_sync_point(); |
366 | }); | |
9f95a23c TL |
367 | m_threads->timer->add_event_after( |
368 | m_update_sync_point_interval, m_update_sync_ctx); | |
11fdf7f2 TL |
369 | return; |
370 | } | |
371 | } | |
372 | ||
373 | send_flush_sync_point(); | |
7c673cae FG |
374 | } |
375 | ||
376 | template <typename I> | |
11fdf7f2 TL |
377 | void ImageSync<I>::send_flush_sync_point() { |
378 | if (m_ret_val < 0) { | |
379 | finish(m_ret_val); | |
380 | return; | |
381 | } | |
7c673cae | 382 | |
11fdf7f2 TL |
383 | update_progress("FLUSH_SYNC_POINT"); |
384 | ||
9f95a23c TL |
385 | ceph_assert(!m_sync_points_copy.empty()); |
386 | auto sync_point = &m_sync_points_copy.front(); | |
387 | ||
11fdf7f2 TL |
388 | if (m_image_copy_object_no > 0) { |
389 | sync_point->object_number = m_image_copy_object_no - 1; | |
390 | } else { | |
391 | sync_point->object_number = boost::none; | |
392 | } | |
393 | ||
9f95a23c TL |
394 | auto ctx = create_context_callback< |
395 | ImageSync<I>, &ImageSync<I>::handle_flush_sync_point>(this); | |
396 | m_sync_point_handler->update_sync_points(m_snap_seqs_copy, | |
397 | m_sync_points_copy, false, ctx); | |
7c673cae FG |
398 | } |
399 | ||
400 | template <typename I> | |
11fdf7f2 TL |
401 | void ImageSync<I>::handle_flush_sync_point(int r) { |
402 | dout(10) << ": r=" << r << dendl; | |
7c673cae | 403 | |
11fdf7f2 | 404 | if (r < 0) { |
11fdf7f2 TL |
405 | derr << ": failed to update client data: " << cpp_strerror(r) |
406 | << dendl; | |
407 | finish(r); | |
408 | return; | |
7c673cae | 409 | } |
7c673cae FG |
410 | |
411 | send_prune_sync_points(); | |
412 | } | |
413 | ||
414 | template <typename I> | |
415 | void ImageSync<I>::send_prune_sync_points() { | |
11fdf7f2 | 416 | dout(10) << dendl; |
7c673cae FG |
417 | |
418 | update_progress("PRUNE_SYNC_POINTS"); | |
419 | ||
420 | Context *ctx = create_context_callback< | |
421 | ImageSync<I>, &ImageSync<I>::handle_prune_sync_points>(this); | |
422 | SyncPointPruneRequest<I> *request = SyncPointPruneRequest<I>::create( | |
9f95a23c | 423 | m_remote_image_ctx, true, m_sync_point_handler, ctx); |
7c673cae FG |
424 | request->send(); |
425 | } | |
426 | ||
427 | template <typename I> | |
428 | void ImageSync<I>::handle_prune_sync_points(int r) { | |
11fdf7f2 | 429 | dout(10) << ": r=" << r << dendl; |
7c673cae FG |
430 | |
431 | if (r < 0) { | |
432 | derr << ": failed to prune sync point: " | |
433 | << cpp_strerror(r) << dendl; | |
434 | finish(r); | |
435 | return; | |
436 | } | |
437 | ||
9f95a23c | 438 | if (!m_sync_point_handler->get_sync_points().empty()) { |
7c673cae FG |
439 | send_copy_image(); |
440 | return; | |
441 | } | |
442 | ||
443 | finish(0); | |
444 | } | |
445 | ||
446 | template <typename I> | |
447 | void ImageSync<I>::update_progress(const std::string &description) { | |
448 | dout(20) << ": " << description << dendl; | |
449 | ||
450 | if (m_progress_ctx) { | |
451 | m_progress_ctx->update_progress("IMAGE_SYNC/" + description); | |
452 | } | |
453 | } | |
454 | ||
31f18b77 FG |
455 | template <typename I> |
456 | void ImageSync<I>::finish(int r) { | |
457 | dout(20) << ": r=" << r << dendl; | |
458 | ||
459 | m_instance_watcher->notify_sync_complete(m_local_image_ctx->id); | |
9f95a23c | 460 | CancelableRequest::finish(r); |
31f18b77 FG |
461 | } |
462 | ||
7c673cae FG |
463 | } // namespace mirror |
464 | } // namespace rbd | |
465 | ||
466 | template class rbd::mirror::ImageSync<librbd::ImageCtx>; |