1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
5 #include "InstanceWatcher.h"
6 #include "ProgressContext.h"
7 #include "common/debug.h"
8 #include "common/Timer.h"
9 #include "common/errno.h"
10 #include "journal/Journaler.h"
11 #include "librbd/DeepCopyRequest.h"
12 #include "librbd/ImageCtx.h"
13 #include "librbd/ImageState.h"
14 #include "librbd/Utils.h"
15 #include "librbd/internal.h"
16 #include "librbd/journal/Types.h"
17 #include "tools/rbd_mirror/image_sync/SyncPointCreateRequest.h"
18 #include "tools/rbd_mirror/image_sync/SyncPointPruneRequest.h"
20 #define dout_context g_ceph_context
21 #define dout_subsys ceph_subsys_rbd_mirror
23 #define dout_prefix *_dout << "rbd::mirror::ImageSync: " \
24 << this << " " << __func__
29 using namespace image_sync
;
30 using librbd::util::create_async_context_callback
;
31 using librbd::util::create_context_callback
;
32 using librbd::util::unique_lock_name
;
35 class ImageSync
<I
>::ImageCopyProgressContext
: public librbd::ProgressContext
{
37 ImageCopyProgressContext(ImageSync
*image_sync
) : image_sync(image_sync
) {
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
);
45 ImageSync
*image_sync
;
49 ImageSync
<I
>::ImageSync(I
*local_image_ctx
, I
*remote_image_ctx
,
50 SafeTimer
*timer
, Mutex
*timer_lock
,
51 const std::string
&mirror_uuid
, Journaler
*journaler
,
52 MirrorPeerClientMeta
*client_meta
,
53 ContextWQ
*work_queue
,
54 InstanceWatcher
<I
> *instance_watcher
,
55 Context
*on_finish
, ProgressContext
*progress_ctx
)
56 : BaseRequest("rbd::mirror::ImageSync", local_image_ctx
->cct
, on_finish
),
57 m_local_image_ctx(local_image_ctx
), m_remote_image_ctx(remote_image_ctx
),
58 m_timer(timer
), m_timer_lock(timer_lock
), m_mirror_uuid(mirror_uuid
),
59 m_journaler(journaler
), m_client_meta(client_meta
),
60 m_work_queue(work_queue
), m_instance_watcher(instance_watcher
),
61 m_progress_ctx(progress_ctx
),
62 m_lock(unique_lock_name("ImageSync::m_lock", this)),
63 m_update_sync_point_interval(m_local_image_ctx
->cct
->_conf
.template get_val
<double>(
64 "rbd_mirror_sync_point_update_age")), m_client_meta_copy(*client_meta
) {
68 ImageSync
<I
>::~ImageSync() {
69 ceph_assert(m_image_copy_request
== nullptr);
70 ceph_assert(m_image_copy_prog_ctx
== nullptr);
71 ceph_assert(m_update_sync_ctx
== nullptr);
75 void ImageSync
<I
>::send() {
76 send_notify_sync_request();
80 void ImageSync
<I
>::cancel() {
81 Mutex::Locker
locker(m_lock
);
87 if (m_instance_watcher
->cancel_sync_request(m_local_image_ctx
->id
)) {
91 if (m_image_copy_request
!= nullptr) {
92 m_image_copy_request
->cancel();
97 void ImageSync
<I
>::send_notify_sync_request() {
98 update_progress("NOTIFY_SYNC_REQUEST");
105 BaseRequest::finish(-ECANCELED
);
109 Context
*ctx
= create_async_context_callback(
110 m_work_queue
, create_context_callback
<
111 ImageSync
<I
>, &ImageSync
<I
>::handle_notify_sync_request
>(this));
112 m_instance_watcher
->notify_sync_request(m_local_image_ctx
->id
, ctx
);
116 template <typename I
>
117 void ImageSync
<I
>::handle_notify_sync_request(int r
) {
118 dout(10) << ": r=" << r
<< dendl
;
121 if (r
== 0 && m_canceled
) {
127 BaseRequest::finish(r
);
131 send_prune_catch_up_sync_point();
134 template <typename I
>
135 void ImageSync
<I
>::send_prune_catch_up_sync_point() {
136 update_progress("PRUNE_CATCH_UP_SYNC_POINT");
138 if (m_client_meta
->sync_points
.empty()) {
139 send_create_sync_point();
145 // prune will remove sync points with missing snapshots and
146 // ensure we have a maximum of one sync point (in case we
148 Context
*ctx
= create_context_callback
<
149 ImageSync
<I
>, &ImageSync
<I
>::handle_prune_catch_up_sync_point
>(this);
150 SyncPointPruneRequest
<I
> *request
= SyncPointPruneRequest
<I
>::create(
151 m_remote_image_ctx
, false, m_journaler
, m_client_meta
, ctx
);
155 template <typename I
>
156 void ImageSync
<I
>::handle_prune_catch_up_sync_point(int r
) {
157 dout(10) << ": r=" << r
<< dendl
;
160 derr
<< ": failed to prune catch-up sync point: "
161 << cpp_strerror(r
) << dendl
;
166 send_create_sync_point();
169 template <typename I
>
170 void ImageSync
<I
>::send_create_sync_point() {
171 update_progress("CREATE_SYNC_POINT");
173 // TODO: when support for disconnecting laggy clients is added,
174 // re-connect and create catch-up sync point
175 if (m_client_meta
->sync_points
.size() > 0) {
182 Context
*ctx
= create_context_callback
<
183 ImageSync
<I
>, &ImageSync
<I
>::handle_create_sync_point
>(this);
184 SyncPointCreateRequest
<I
> *request
= SyncPointCreateRequest
<I
>::create(
185 m_remote_image_ctx
, m_mirror_uuid
, m_journaler
, m_client_meta
, ctx
);
189 template <typename I
>
190 void ImageSync
<I
>::handle_create_sync_point(int r
) {
191 dout(10) << ": r=" << r
<< dendl
;
194 derr
<< ": failed to create sync point: " << cpp_strerror(r
)
203 template <typename I
>
204 void ImageSync
<I
>::send_copy_image() {
205 librados::snap_t snap_id_start
= 0;
206 librados::snap_t snap_id_end
;
207 librbd::deep_copy::ObjectNumber object_number
;
210 RWLock::RLocker
snap_locker(m_remote_image_ctx
->snap_lock
);
211 ceph_assert(!m_client_meta
->sync_points
.empty());
212 auto &sync_point
= m_client_meta
->sync_points
.front();
213 snap_id_end
= m_remote_image_ctx
->get_snap_id(
214 cls::rbd::UserSnapshotNamespace(), sync_point
.snap_name
);
215 if (snap_id_end
== CEPH_NOSNAP
) {
216 derr
<< ": failed to locate snapshot: " << sync_point
.snap_name
<< dendl
;
218 } else if (!sync_point
.from_snap_name
.empty()) {
219 snap_id_start
= m_remote_image_ctx
->get_snap_id(
220 cls::rbd::UserSnapshotNamespace(), sync_point
.from_snap_name
);
221 if (snap_id_start
== CEPH_NOSNAP
) {
222 derr
<< ": failed to locate from snapshot: "
223 << sync_point
.from_snap_name
<< dendl
;
227 object_number
= sync_point
.object_number
;
243 Context
*ctx
= create_context_callback
<
244 ImageSync
<I
>, &ImageSync
<I
>::handle_copy_image
>(this);
245 m_image_copy_prog_ctx
= new ImageCopyProgressContext(this);
246 m_image_copy_request
= librbd::DeepCopyRequest
<I
>::create(
247 m_remote_image_ctx
, m_local_image_ctx
, snap_id_start
, snap_id_end
,
248 false, object_number
, m_work_queue
, &m_client_meta
->snap_seqs
,
249 m_image_copy_prog_ctx
, ctx
);
250 m_image_copy_request
->get();
253 update_progress("COPY_IMAGE");
255 m_image_copy_request
->send();
258 template <typename I
>
259 void ImageSync
<I
>::handle_copy_image(int r
) {
260 dout(10) << ": r=" << r
<< dendl
;
263 Mutex::Locker
timer_locker(*m_timer_lock
);
264 Mutex::Locker
locker(m_lock
);
265 m_image_copy_request
->put();
266 m_image_copy_request
= nullptr;
267 delete m_image_copy_prog_ctx
;
268 m_image_copy_prog_ctx
= nullptr;
269 if (r
== 0 && m_canceled
) {
273 if (m_update_sync_ctx
!= nullptr) {
274 m_timer
->cancel_event(m_update_sync_ctx
);
275 m_update_sync_ctx
= nullptr;
278 if (m_updating_sync_point
) {
284 if (r
== -ECANCELED
) {
285 dout(10) << ": image copy canceled" << dendl
;
289 derr
<< ": failed to copy image: " << cpp_strerror(r
) << dendl
;
294 send_flush_sync_point();
297 template <typename I
>
298 void ImageSync
<I
>::handle_copy_image_update_progress(uint64_t object_no
,
299 uint64_t object_count
) {
300 int percent
= 100 * object_no
/ object_count
;
301 update_progress("COPY_IMAGE " + stringify(percent
) + "%");
303 Mutex::Locker
locker(m_lock
);
304 m_image_copy_object_no
= object_no
;
305 m_image_copy_object_count
= object_count
;
307 if (m_update_sync_ctx
== nullptr && !m_updating_sync_point
) {
308 send_update_sync_point();
312 template <typename I
>
313 void ImageSync
<I
>::send_update_sync_point() {
314 ceph_assert(m_lock
.is_locked());
316 m_update_sync_ctx
= nullptr;
322 auto sync_point
= &m_client_meta
->sync_points
.front();
324 if (m_client_meta
->sync_object_count
== m_image_copy_object_count
&&
325 sync_point
->object_number
&&
326 (m_image_copy_object_no
- 1) == sync_point
->object_number
.get()) {
327 // update sync point did not progress since last sync
331 m_updating_sync_point
= true;
333 m_client_meta_copy
= *m_client_meta
;
334 m_client_meta
->sync_object_count
= m_image_copy_object_count
;
335 if (m_image_copy_object_no
> 0) {
336 sync_point
->object_number
= m_image_copy_object_no
- 1;
339 CephContext
*cct
= m_local_image_ctx
->cct
;
340 ldout(cct
, 20) << ": sync_point=" << *sync_point
<< dendl
;
342 bufferlist client_data_bl
;
343 librbd::journal::ClientData
client_data(*m_client_meta
);
344 encode(client_data
, client_data_bl
);
346 Context
*ctx
= create_context_callback
<
347 ImageSync
<I
>, &ImageSync
<I
>::handle_update_sync_point
>(
349 m_journaler
->update_client(client_data_bl
, ctx
);
352 template <typename I
>
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
;
358 *m_client_meta
= m_client_meta_copy
;
359 lderr(cct
) << ": failed to update client data: " << cpp_strerror(r
)
364 Mutex::Locker
timer_locker(*m_timer_lock
);
365 Mutex::Locker
locker(m_lock
);
366 m_updating_sync_point
= false;
368 if (m_image_copy_request
!= nullptr) {
369 m_update_sync_ctx
= new FunctionContext(
371 Mutex::Locker
locker(m_lock
);
372 this->send_update_sync_point();
374 m_timer
->add_event_after(m_update_sync_point_interval
,
380 send_flush_sync_point();
383 template <typename I
>
384 void ImageSync
<I
>::send_flush_sync_point() {
390 update_progress("FLUSH_SYNC_POINT");
392 m_client_meta_copy
= *m_client_meta
;
393 m_client_meta
->sync_object_count
= m_image_copy_object_count
;
394 auto sync_point
= &m_client_meta
->sync_points
.front();
395 if (m_image_copy_object_no
> 0) {
396 sync_point
->object_number
= m_image_copy_object_no
- 1;
398 sync_point
->object_number
= boost::none
;
401 dout(10) << ": sync_point=" << *sync_point
<< dendl
;
403 bufferlist client_data_bl
;
404 librbd::journal::ClientData
client_data(*m_client_meta
);
405 encode(client_data
, client_data_bl
);
407 Context
*ctx
= create_context_callback
<
408 ImageSync
<I
>, &ImageSync
<I
>::handle_flush_sync_point
>(
410 m_journaler
->update_client(client_data_bl
, ctx
);
413 template <typename I
>
414 void ImageSync
<I
>::handle_flush_sync_point(int r
) {
415 dout(10) << ": r=" << r
<< dendl
;
418 *m_client_meta
= m_client_meta_copy
;
420 derr
<< ": failed to update client data: " << cpp_strerror(r
)
426 send_prune_sync_points();
429 template <typename I
>
430 void ImageSync
<I
>::send_prune_sync_points() {
433 update_progress("PRUNE_SYNC_POINTS");
435 Context
*ctx
= create_context_callback
<
436 ImageSync
<I
>, &ImageSync
<I
>::handle_prune_sync_points
>(this);
437 SyncPointPruneRequest
<I
> *request
= SyncPointPruneRequest
<I
>::create(
438 m_remote_image_ctx
, true, m_journaler
, m_client_meta
, ctx
);
442 template <typename I
>
443 void ImageSync
<I
>::handle_prune_sync_points(int r
) {
444 dout(10) << ": r=" << r
<< dendl
;
447 derr
<< ": failed to prune sync point: "
448 << cpp_strerror(r
) << dendl
;
453 if (!m_client_meta
->sync_points
.empty()) {
461 template <typename I
>
462 void ImageSync
<I
>::update_progress(const std::string
&description
) {
463 dout(20) << ": " << description
<< dendl
;
465 if (m_progress_ctx
) {
466 m_progress_ctx
->update_progress("IMAGE_SYNC/" + description
);
470 template <typename I
>
471 void ImageSync
<I
>::finish(int r
) {
472 dout(20) << ": r=" << r
<< dendl
;
474 m_instance_watcher
->notify_sync_complete(m_local_image_ctx
->id
);
475 BaseRequest::finish(r
);
478 } // namespace mirror
481 template class rbd::mirror::ImageSync
<librbd::ImageCtx
>;