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"
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"
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;
30   	using librbd::util::create_async_context_callback;
31   	using librbd::util::create_context_callback;
32   	using librbd::util::unique_lock_name;
33   	
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   	
48   	template <typename I>
49   	ImageSync<I>::ImageSync(I *local_image_ctx, I *remote_image_ctx,
50   	                        SafeTimer *timer, ceph::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(ceph::make_mutex(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) {
65   	}
66   	
67   	template <typename I>
(1) Event exn_spec_violation: An exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE" is thrown but the throw list "throw()" doesn't allow it to be thrown. This will cause a call to unexpected() which usually calls terminate().
Also see events: [fun_call_w_exception]
68   	ImageSync<I>::~ImageSync() {
69   	  ceph_assert(m_image_copy_request == nullptr);
(2) Event fun_call_w_exception: Called function throws an exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE". [details]
Also see events: [exn_spec_violation]
70   	  ceph_assert(m_image_copy_prog_ctx == nullptr);
71   	  ceph_assert(m_update_sync_ctx == nullptr);
72   	}
73   	
74   	template <typename I>
75   	void ImageSync<I>::send() {
76   	  send_notify_sync_request();
77   	}
78   	
79   	template <typename I>
80   	void ImageSync<I>::cancel() {
81   	  std::lock_guard locker{m_lock};
82   	
83   	  dout(10) << dendl;
84   	
85   	  m_canceled = true;
86   	
87   	  if (m_instance_watcher->cancel_sync_request(m_local_image_ctx->id)) {
88   	    return;
89   	  }
90   	
91   	  if (m_image_copy_request != nullptr) {
92   	    m_image_copy_request->cancel();
93   	  }
94   	}
95   	
96   	template <typename I>
97   	void ImageSync<I>::send_notify_sync_request() {
98   	  update_progress("NOTIFY_SYNC_REQUEST");
99   	
100  	  dout(10) << dendl;
101  	
102  	  m_lock.lock();
103  	  if (m_canceled) {
104  	    m_lock.unlock();
105  	    BaseRequest::finish(-ECANCELED);
106  	    return;
107  	  }
108  	
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);
113  	  m_lock.unlock();
114  	}
115  	
116  	template <typename I>
117  	void ImageSync<I>::handle_notify_sync_request(int r) {
118  	  dout(10) << ": r=" << r << dendl;
119  	
120  	  m_lock.lock();
121  	  if (r == 0 && m_canceled) {
122  	    r = -ECANCELED;
123  	  }
124  	  m_lock.unlock();
125  	
126  	  if (r < 0) {
127  	    BaseRequest::finish(r);
128  	    return;
129  	  }
130  	
131  	  send_prune_catch_up_sync_point();
132  	}
133  	
134  	template <typename I>
135  	void ImageSync<I>::send_prune_catch_up_sync_point() {
136  	  update_progress("PRUNE_CATCH_UP_SYNC_POINT");
137  	
138  	  if (m_client_meta->sync_points.empty()) {
139  	    send_create_sync_point();
140  	    return;
141  	  }
142  	
143  	  dout(10) << dendl;
144  	
145  	  // prune will remove sync points with missing snapshots and
146  	  // ensure we have a maximum of one sync point (in case we
147  	  // restarted)
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);
152  	  request->send();
153  	}
154  	
155  	template <typename I>
156  	void ImageSync<I>::handle_prune_catch_up_sync_point(int r) {
157  	  dout(10) << ": r=" << r << dendl;
158  	
159  	  if (r < 0) {
160  	    derr << ": failed to prune catch-up sync point: "
161  	         << cpp_strerror(r) << dendl;
162  	    finish(r);
163  	    return;
164  	  }
165  	
166  	  send_create_sync_point();
167  	}
168  	
169  	template <typename I>
170  	void ImageSync<I>::send_create_sync_point() {
171  	  update_progress("CREATE_SYNC_POINT");
172  	
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) {
176  	    send_copy_image();
177  	    return;
178  	  }
179  	
180  	  dout(10) << dendl;
181  	
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);
186  	  request->send();
187  	}
188  	
189  	template <typename I>
190  	void ImageSync<I>::handle_create_sync_point(int r) {
191  	  dout(10) << ": r=" << r << dendl;
192  	
193  	  if (r < 0) {
194  	    derr << ": failed to create sync point: " << cpp_strerror(r)
195  	         << dendl;
196  	    finish(r);
197  	    return;
198  	  }
199  	
200  	  send_copy_image();
201  	}
202  	
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;
208  	  int r = 0;
209  	  {
210  	    std::shared_lock image_locker{m_remote_image_ctx->image_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;
217  	      r = -ENOENT;
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;
224  	        r = -ENOENT;
225  	      }
226  	    }
227  	    object_number = sync_point.object_number;
228  	  }
229  	  if (r < 0) {
230  	    finish(r);
231  	    return;
232  	  }
233  	
234  	  m_lock.lock();
235  	  if (m_canceled) {
236  	    m_lock.unlock();
237  	    finish(-ECANCELED);
238  	    return;
239  	  }
240  	
241  	  dout(10) << dendl;
242  	
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();
251  	  m_lock.unlock();
252  	
253  	  update_progress("COPY_IMAGE");
254  	
255  	  m_image_copy_request->send();
256  	}
257  	
258  	template <typename I>
259  	void ImageSync<I>::handle_copy_image(int r) {
260  	  dout(10) << ": r=" << r << dendl;
261  	
262  	  {
263  	    std::scoped_lock locker{*m_timer_lock, m_lock};
264  	    m_image_copy_request->put();
265  	    m_image_copy_request = nullptr;
266  	    delete m_image_copy_prog_ctx;
267  	    m_image_copy_prog_ctx = nullptr;
268  	    if (r == 0 && m_canceled) {
269  	      r = -ECANCELED;
270  	    }
271  	
272  	    if (m_update_sync_ctx != nullptr) {
273  	      m_timer->cancel_event(m_update_sync_ctx);
274  	      m_update_sync_ctx = nullptr;
275  	    }
276  	
277  	    if (m_updating_sync_point) {
278  	      m_ret_val = r;
279  	      return;
280  	    }
281  	  }
282  	
283  	  if (r == -ECANCELED) {
284  	    dout(10) << ": image copy canceled" << dendl;
285  	    finish(r);
286  	    return;
287  	  } else if (r < 0) {
288  	    derr << ": failed to copy image: " << cpp_strerror(r) << dendl;
289  	    finish(r);
290  	    return;
291  	  }
292  	
293  	  send_flush_sync_point();
294  	}
295  	
296  	template <typename I>
297  	void ImageSync<I>::handle_copy_image_update_progress(uint64_t object_no,
298  	                                                     uint64_t object_count) {
299  	  int percent = 100 * object_no / object_count;
300  	  update_progress("COPY_IMAGE " + stringify(percent) + "%");
301  	
302  	  std::lock_guard locker{m_lock};
303  	  m_image_copy_object_no = object_no;
304  	  m_image_copy_object_count = object_count;
305  	
306  	  if (m_update_sync_ctx == nullptr && !m_updating_sync_point) {
307  	    send_update_sync_point();
308  	  }
309  	}
310  	
311  	template <typename I>
312  	void ImageSync<I>::send_update_sync_point() {
313  	  ceph_assert(ceph_mutex_is_locked(m_lock));
314  	
315  	  m_update_sync_ctx = nullptr;
316  	
317  	  if (m_canceled) {
318  	    return;
319  	  }
320  	
321  	  auto sync_point = &m_client_meta->sync_points.front();
322  	
323  	  if (m_client_meta->sync_object_count == m_image_copy_object_count &&
324  	      sync_point->object_number &&
325  	      (m_image_copy_object_no - 1) == sync_point->object_number.get()) {
326  	    // update sync point did not progress since last sync
327  	    return;
328  	  }
329  	
330  	  m_updating_sync_point = true;
331  	
332  	  m_client_meta_copy = *m_client_meta;
333  	  m_client_meta->sync_object_count = m_image_copy_object_count;
334  	  if (m_image_copy_object_no > 0) {
335  	    sync_point->object_number = m_image_copy_object_no - 1;
336  	  }
337  	
338  	  CephContext *cct = m_local_image_ctx->cct;
339  	  ldout(cct, 20) << ": sync_point=" << *sync_point << dendl;
340  	
341  	  bufferlist client_data_bl;
342  	  librbd::journal::ClientData client_data(*m_client_meta);
343  	  encode(client_data, client_data_bl);
344  	
345  	  Context *ctx = create_context_callback<
346  	    ImageSync<I>, &ImageSync<I>::handle_update_sync_point>(
347  	      this);
348  	  m_journaler->update_client(client_data_bl, ctx);
349  	}
350  	
351  	template <typename I>
352  	void ImageSync<I>::handle_update_sync_point(int r) {
353  	  CephContext *cct = m_local_image_ctx->cct;
354  	  ldout(cct, 20) << ": r=" << r << dendl;
355  	
356  	  if (r < 0) {
357  	    *m_client_meta = m_client_meta_copy;
358  	    lderr(cct) << ": failed to update client data: " << cpp_strerror(r)
359  	               << dendl;
360  	  }
361  	
362  	  {
363  	    std::scoped_lock locker{*m_timer_lock, m_lock};
364  	    m_updating_sync_point = false;
365  	
366  	    if (m_image_copy_request != nullptr) {
367  	      m_update_sync_ctx = new LambdaContext(
368  	        [this](int r) {
369  		  std::lock_guard locker{m_lock};
370  	          this->send_update_sync_point();
371  	        });
372  	      m_timer->add_event_after(m_update_sync_point_interval,
373  	                               m_update_sync_ctx);
374  	      return;
375  	    }
376  	  }
377  	
378  	  send_flush_sync_point();
379  	}
380  	
381  	template <typename I>
382  	void ImageSync<I>::send_flush_sync_point() {
383  	  if (m_ret_val < 0) {
384  	    finish(m_ret_val);
385  	    return;
386  	  }
387  	
388  	  update_progress("FLUSH_SYNC_POINT");
389  	
390  	  m_client_meta_copy = *m_client_meta;
391  	  m_client_meta->sync_object_count = m_image_copy_object_count;
392  	  auto sync_point = &m_client_meta->sync_points.front();
393  	  if (m_image_copy_object_no > 0) {
394  	    sync_point->object_number = m_image_copy_object_no - 1;
395  	  } else {
396  	    sync_point->object_number = boost::none;
397  	  }
398  	
399  	  dout(10) << ": sync_point=" << *sync_point << dendl;
400  	
401  	  bufferlist client_data_bl;
402  	  librbd::journal::ClientData client_data(*m_client_meta);
403  	  encode(client_data, client_data_bl);
404  	
405  	  Context *ctx = create_context_callback<
406  	    ImageSync<I>, &ImageSync<I>::handle_flush_sync_point>(
407  	      this);
408  	  m_journaler->update_client(client_data_bl, ctx);
409  	}
410  	
411  	template <typename I>
412  	void ImageSync<I>::handle_flush_sync_point(int r) {
413  	  dout(10) << ": r=" << r << dendl;
414  	
415  	  if (r < 0) {
416  	    *m_client_meta = m_client_meta_copy;
417  	
418  	    derr << ": failed to update client data: " << cpp_strerror(r)
419  	         << dendl;
420  	    finish(r);
421  	    return;
422  	  }
423  	
424  	  send_prune_sync_points();
425  	}
426  	
427  	template <typename I>
428  	void ImageSync<I>::send_prune_sync_points() {
429  	  dout(10) << dendl;
430  	
431  	  update_progress("PRUNE_SYNC_POINTS");
432  	
433  	  Context *ctx = create_context_callback<
434  	    ImageSync<I>, &ImageSync<I>::handle_prune_sync_points>(this);
435  	  SyncPointPruneRequest<I> *request = SyncPointPruneRequest<I>::create(
436  	    m_remote_image_ctx, true, m_journaler, m_client_meta, ctx);
437  	  request->send();
438  	}
439  	
440  	template <typename I>
441  	void ImageSync<I>::handle_prune_sync_points(int r) {
442  	  dout(10) << ": r=" << r << dendl;
443  	
444  	  if (r < 0) {
445  	    derr << ": failed to prune sync point: "
446  	         << cpp_strerror(r) << dendl;
447  	    finish(r);
448  	    return;
449  	  }
450  	
451  	  if (!m_client_meta->sync_points.empty()) {
452  	    send_copy_image();
453  	    return;
454  	  }
455  	
456  	  finish(0);
457  	}
458  	
459  	template <typename I>
460  	void ImageSync<I>::update_progress(const std::string &description) {
461  	  dout(20) << ": " << description << dendl;
462  	
463  	  if (m_progress_ctx) {
464  	    m_progress_ctx->update_progress("IMAGE_SYNC/" + description);
465  	  }
466  	}
467  	
468  	template <typename I>
469  	void ImageSync<I>::finish(int r) {
470  	  dout(20) << ": r=" << r << dendl;
471  	
472  	  m_instance_watcher->notify_sync_complete(m_local_image_ctx->id);
473  	  BaseRequest::finish(r);
474  	}
475  	
476  	} // namespace mirror
477  	} // namespace rbd
478  	
479  	template class rbd::mirror::ImageSync<librbd::ImageCtx>;
480