]>
Commit | Line | Data |
---|---|---|
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/errno.h" | |
8 | #include "journal/Journaler.h" | |
9 | #include "librbd/ExclusiveLock.h" | |
10 | #include "librbd/ImageCtx.h" | |
11 | #include "librbd/ObjectMap.h" | |
12 | #include "librbd/Utils.h" | |
13 | #include "librbd/journal/Types.h" | |
14 | #include "tools/rbd_mirror/image_sync/ImageCopyRequest.h" | |
15 | #include "tools/rbd_mirror/image_sync/SnapshotCopyRequest.h" | |
16 | #include "tools/rbd_mirror/image_sync/SyncPointCreateRequest.h" | |
17 | #include "tools/rbd_mirror/image_sync/SyncPointPruneRequest.h" | |
18 | ||
19 | #define dout_context g_ceph_context | |
20 | #define dout_subsys ceph_subsys_rbd_mirror | |
21 | #undef dout_prefix | |
22 | #define dout_prefix *_dout << "rbd::mirror::ImageSync: " \ | |
23 | << this << " " << __func__ | |
24 | ||
25 | namespace rbd { | |
26 | namespace mirror { | |
27 | ||
28 | using namespace image_sync; | |
29 | using librbd::util::create_context_callback; | |
30 | using librbd::util::unique_lock_name; | |
31 | ||
32 | template <typename I> | |
33 | ImageSync<I>::ImageSync(I *local_image_ctx, I *remote_image_ctx, | |
34 | SafeTimer *timer, Mutex *timer_lock, | |
35 | const std::string &mirror_uuid, Journaler *journaler, | |
36 | MirrorPeerClientMeta *client_meta, | |
37 | ContextWQ *work_queue, | |
38 | InstanceWatcher<I> *instance_watcher, | |
39 | Context *on_finish, ProgressContext *progress_ctx) | |
40 | : BaseRequest("rbd::mirror::ImageSync", local_image_ctx->cct, on_finish), | |
41 | m_local_image_ctx(local_image_ctx), m_remote_image_ctx(remote_image_ctx), | |
42 | m_timer(timer), m_timer_lock(timer_lock), m_mirror_uuid(mirror_uuid), | |
43 | m_journaler(journaler), m_client_meta(client_meta), | |
44 | m_work_queue(work_queue), m_instance_watcher(instance_watcher), | |
45 | m_progress_ctx(progress_ctx), | |
46 | m_lock(unique_lock_name("ImageSync::m_lock", this)) { | |
47 | } | |
48 | ||
49 | template <typename I> | |
50 | ImageSync<I>::~ImageSync() { | |
51 | assert(m_snapshot_copy_request == nullptr); | |
52 | assert(m_image_copy_request == nullptr); | |
53 | } | |
54 | ||
55 | template <typename I> | |
56 | void ImageSync<I>::send() { | |
57 | send_notify_sync_request(); | |
58 | } | |
59 | ||
60 | template <typename I> | |
61 | void ImageSync<I>::cancel() { | |
62 | Mutex::Locker locker(m_lock); | |
63 | ||
64 | dout(20) << dendl; | |
65 | ||
66 | m_canceled = true; | |
67 | ||
68 | if (m_instance_watcher->cancel_sync_request(m_local_image_ctx->id)) { | |
69 | return; | |
70 | } | |
71 | ||
72 | if (m_snapshot_copy_request != nullptr) { | |
73 | m_snapshot_copy_request->cancel(); | |
74 | } | |
75 | ||
76 | if (m_image_copy_request != nullptr) { | |
77 | m_image_copy_request->cancel(); | |
78 | } | |
79 | } | |
80 | ||
81 | template <typename I> | |
82 | void ImageSync<I>::send_notify_sync_request() { | |
83 | update_progress("NOTIFY_SYNC_REQUEST"); | |
84 | ||
85 | dout(20) << dendl; | |
86 | ||
87 | Context *ctx = create_context_callback< | |
88 | ImageSync<I>, &ImageSync<I>::handle_notify_sync_request>(this); | |
89 | m_instance_watcher->notify_sync_request(m_local_image_ctx->id, ctx); | |
90 | } | |
91 | ||
92 | template <typename I> | |
93 | void ImageSync<I>::handle_notify_sync_request(int r) { | |
94 | dout(20) << ": r=" << r << dendl; | |
95 | ||
96 | if (r < 0) { | |
97 | BaseRequest::finish(r); | |
98 | return; | |
99 | } | |
100 | ||
101 | send_prune_catch_up_sync_point(); | |
102 | } | |
103 | ||
104 | template <typename I> | |
105 | void ImageSync<I>::send_prune_catch_up_sync_point() { | |
106 | update_progress("PRUNE_CATCH_UP_SYNC_POINT"); | |
107 | ||
108 | if (m_client_meta->sync_points.empty()) { | |
109 | send_create_sync_point(); | |
110 | return; | |
111 | } | |
112 | ||
113 | dout(20) << dendl; | |
114 | ||
115 | // prune will remove sync points with missing snapshots and | |
116 | // ensure we have a maximum of one sync point (in case we | |
117 | // restarted) | |
118 | Context *ctx = create_context_callback< | |
119 | ImageSync<I>, &ImageSync<I>::handle_prune_catch_up_sync_point>(this); | |
120 | SyncPointPruneRequest<I> *request = SyncPointPruneRequest<I>::create( | |
121 | m_remote_image_ctx, false, m_journaler, m_client_meta, ctx); | |
122 | request->send(); | |
123 | } | |
124 | ||
125 | template <typename I> | |
126 | void ImageSync<I>::handle_prune_catch_up_sync_point(int r) { | |
127 | dout(20) << ": r=" << r << dendl; | |
128 | ||
129 | if (r < 0) { | |
130 | derr << ": failed to prune catch-up sync point: " | |
131 | << cpp_strerror(r) << dendl; | |
132 | finish(r); | |
133 | return; | |
134 | } | |
135 | ||
136 | send_create_sync_point(); | |
137 | } | |
138 | ||
139 | template <typename I> | |
140 | void ImageSync<I>::send_create_sync_point() { | |
141 | update_progress("CREATE_SYNC_POINT"); | |
142 | ||
143 | // TODO: when support for disconnecting laggy clients is added, | |
144 | // re-connect and create catch-up sync point | |
145 | if (m_client_meta->sync_points.size() > 0) { | |
146 | send_copy_snapshots(); | |
147 | return; | |
148 | } | |
149 | ||
150 | dout(20) << dendl; | |
151 | ||
152 | Context *ctx = create_context_callback< | |
153 | ImageSync<I>, &ImageSync<I>::handle_create_sync_point>(this); | |
154 | SyncPointCreateRequest<I> *request = SyncPointCreateRequest<I>::create( | |
155 | m_remote_image_ctx, m_mirror_uuid, m_journaler, m_client_meta, ctx); | |
156 | request->send(); | |
157 | } | |
158 | ||
159 | template <typename I> | |
160 | void ImageSync<I>::handle_create_sync_point(int r) { | |
161 | dout(20) << ": r=" << r << dendl; | |
162 | ||
163 | if (r < 0) { | |
164 | derr << ": failed to create sync point: " << cpp_strerror(r) | |
165 | << dendl; | |
166 | finish(r); | |
167 | return; | |
168 | } | |
169 | ||
170 | send_copy_snapshots(); | |
171 | } | |
172 | ||
173 | template <typename I> | |
174 | void ImageSync<I>::send_copy_snapshots() { | |
175 | m_lock.Lock(); | |
176 | if (m_canceled) { | |
177 | m_lock.Unlock(); | |
178 | finish(-ECANCELED); | |
179 | return; | |
180 | } | |
181 | ||
182 | dout(20) << dendl; | |
183 | ||
184 | Context *ctx = create_context_callback< | |
185 | ImageSync<I>, &ImageSync<I>::handle_copy_snapshots>(this); | |
186 | m_snapshot_copy_request = SnapshotCopyRequest<I>::create( | |
187 | m_local_image_ctx, m_remote_image_ctx, &m_snap_map, m_journaler, | |
188 | m_client_meta, m_work_queue, ctx); | |
189 | m_snapshot_copy_request->get(); | |
190 | m_lock.Unlock(); | |
191 | ||
192 | update_progress("COPY_SNAPSHOTS"); | |
193 | ||
194 | m_snapshot_copy_request->send(); | |
195 | } | |
196 | ||
197 | template <typename I> | |
198 | void ImageSync<I>::handle_copy_snapshots(int r) { | |
199 | dout(20) << ": r=" << r << dendl; | |
200 | ||
201 | { | |
202 | Mutex::Locker locker(m_lock); | |
203 | m_snapshot_copy_request->put(); | |
204 | m_snapshot_copy_request = nullptr; | |
205 | if (r == 0 && m_canceled) { | |
206 | r = -ECANCELED; | |
207 | } | |
208 | } | |
209 | ||
210 | if (r == -ECANCELED) { | |
211 | dout(10) << ": snapshot copy canceled" << dendl; | |
212 | finish(r); | |
213 | return; | |
214 | } else if (r < 0) { | |
215 | derr << ": failed to copy snapshot metadata: " << cpp_strerror(r) << dendl; | |
216 | finish(r); | |
217 | return; | |
218 | } | |
219 | ||
220 | send_copy_image(); | |
221 | } | |
222 | ||
223 | template <typename I> | |
224 | void ImageSync<I>::send_copy_image() { | |
225 | m_lock.Lock(); | |
226 | if (m_canceled) { | |
227 | m_lock.Unlock(); | |
228 | finish(-ECANCELED); | |
229 | return; | |
230 | } | |
231 | ||
232 | dout(20) << dendl; | |
233 | ||
234 | Context *ctx = create_context_callback< | |
235 | ImageSync<I>, &ImageSync<I>::handle_copy_image>(this); | |
236 | m_image_copy_request = ImageCopyRequest<I>::create( | |
237 | m_local_image_ctx, m_remote_image_ctx, m_timer, m_timer_lock, | |
238 | m_journaler, m_client_meta, &m_client_meta->sync_points.front(), | |
239 | ctx, m_progress_ctx); | |
240 | m_image_copy_request->get(); | |
241 | m_lock.Unlock(); | |
242 | ||
243 | update_progress("COPY_IMAGE"); | |
244 | ||
245 | m_image_copy_request->send(); | |
246 | } | |
247 | ||
248 | template <typename I> | |
249 | void ImageSync<I>::handle_copy_image(int r) { | |
250 | dout(20) << ": r=" << r << dendl; | |
251 | ||
252 | { | |
253 | Mutex::Locker locker(m_lock); | |
254 | m_image_copy_request->put(); | |
255 | m_image_copy_request = nullptr; | |
256 | if (r == 0 && m_canceled) { | |
257 | r = -ECANCELED; | |
258 | } | |
259 | } | |
260 | ||
261 | if (r == -ECANCELED) { | |
262 | dout(10) << ": image copy canceled" << dendl; | |
263 | finish(r); | |
264 | return; | |
265 | } else if (r < 0) { | |
266 | derr << ": failed to copy image: " << cpp_strerror(r) << dendl; | |
267 | finish(r); | |
268 | return; | |
269 | } | |
270 | ||
271 | send_copy_object_map(); | |
272 | } | |
273 | ||
274 | template <typename I> | |
275 | void ImageSync<I>::send_copy_object_map() { | |
276 | update_progress("COPY_OBJECT_MAP"); | |
277 | ||
278 | m_local_image_ctx->owner_lock.get_read(); | |
279 | m_local_image_ctx->snap_lock.get_read(); | |
280 | if (!m_local_image_ctx->test_features(RBD_FEATURE_OBJECT_MAP, | |
281 | m_local_image_ctx->snap_lock)) { | |
282 | m_local_image_ctx->snap_lock.put_read(); | |
283 | m_local_image_ctx->owner_lock.put_read(); | |
284 | send_prune_sync_points(); | |
285 | return; | |
286 | } | |
287 | ||
288 | assert(m_local_image_ctx->object_map != nullptr); | |
289 | ||
290 | assert(!m_client_meta->sync_points.empty()); | |
291 | librbd::journal::MirrorPeerSyncPoint &sync_point = | |
292 | m_client_meta->sync_points.front(); | |
293 | auto snap_id_it = m_local_image_ctx->snap_ids.find( | |
294 | {cls::rbd::UserSnapshotNamespace(), sync_point.snap_name}); | |
295 | assert(snap_id_it != m_local_image_ctx->snap_ids.end()); | |
296 | librados::snap_t snap_id = snap_id_it->second; | |
297 | ||
298 | dout(20) << ": snap_id=" << snap_id << ", " | |
299 | << "snap_name=" << sync_point.snap_name << dendl; | |
300 | ||
301 | Context *finish_op_ctx = nullptr; | |
302 | if (m_local_image_ctx->exclusive_lock != nullptr) { | |
303 | finish_op_ctx = m_local_image_ctx->exclusive_lock->start_op(); | |
304 | } | |
305 | if (finish_op_ctx == nullptr) { | |
306 | derr << ": lost exclusive lock" << dendl; | |
307 | m_local_image_ctx->snap_lock.put_read(); | |
308 | m_local_image_ctx->owner_lock.put_read(); | |
309 | finish(-EROFS); | |
310 | return; | |
311 | } | |
312 | ||
313 | // rollback the object map (copy snapshot object map to HEAD) | |
314 | RWLock::WLocker object_map_locker(m_local_image_ctx->object_map_lock); | |
315 | auto ctx = new FunctionContext([this, finish_op_ctx](int r) { | |
316 | handle_copy_object_map(r); | |
317 | finish_op_ctx->complete(0); | |
318 | }); | |
319 | m_local_image_ctx->object_map->rollback(snap_id, ctx); | |
320 | m_local_image_ctx->snap_lock.put_read(); | |
321 | m_local_image_ctx->owner_lock.put_read(); | |
322 | } | |
323 | ||
324 | template <typename I> | |
325 | void ImageSync<I>::handle_copy_object_map(int r) { | |
326 | dout(20) << dendl; | |
327 | ||
328 | assert(r == 0); | |
329 | send_refresh_object_map(); | |
330 | } | |
331 | ||
332 | template <typename I> | |
333 | void ImageSync<I>::send_refresh_object_map() { | |
334 | dout(20) << dendl; | |
335 | ||
336 | update_progress("REFRESH_OBJECT_MAP"); | |
337 | ||
338 | Context *ctx = create_context_callback< | |
339 | ImageSync<I>, &ImageSync<I>::handle_refresh_object_map>(this); | |
340 | m_object_map = m_local_image_ctx->create_object_map(CEPH_NOSNAP); | |
341 | m_object_map->open(ctx); | |
342 | } | |
343 | ||
344 | template <typename I> | |
345 | void ImageSync<I>::handle_refresh_object_map(int r) { | |
346 | dout(20) << dendl; | |
347 | ||
348 | assert(r == 0); | |
349 | { | |
350 | RWLock::WLocker snap_locker(m_local_image_ctx->snap_lock); | |
351 | std::swap(m_local_image_ctx->object_map, m_object_map); | |
352 | } | |
353 | delete m_object_map; | |
354 | ||
355 | send_prune_sync_points(); | |
356 | } | |
357 | ||
358 | template <typename I> | |
359 | void ImageSync<I>::send_prune_sync_points() { | |
360 | dout(20) << dendl; | |
361 | ||
362 | update_progress("PRUNE_SYNC_POINTS"); | |
363 | ||
364 | Context *ctx = create_context_callback< | |
365 | ImageSync<I>, &ImageSync<I>::handle_prune_sync_points>(this); | |
366 | SyncPointPruneRequest<I> *request = SyncPointPruneRequest<I>::create( | |
367 | m_remote_image_ctx, true, m_journaler, m_client_meta, ctx); | |
368 | request->send(); | |
369 | } | |
370 | ||
371 | template <typename I> | |
372 | void ImageSync<I>::handle_prune_sync_points(int r) { | |
373 | dout(20) << ": r=" << r << dendl; | |
374 | ||
375 | if (r < 0) { | |
376 | derr << ": failed to prune sync point: " | |
377 | << cpp_strerror(r) << dendl; | |
378 | finish(r); | |
379 | return; | |
380 | } | |
381 | ||
382 | if (!m_client_meta->sync_points.empty()) { | |
383 | send_copy_image(); | |
384 | return; | |
385 | } | |
386 | ||
387 | finish(0); | |
388 | } | |
389 | ||
390 | template <typename I> | |
391 | void ImageSync<I>::update_progress(const std::string &description) { | |
392 | dout(20) << ": " << description << dendl; | |
393 | ||
394 | if (m_progress_ctx) { | |
395 | m_progress_ctx->update_progress("IMAGE_SYNC/" + description); | |
396 | } | |
397 | } | |
398 | ||
399 | template <typename I> | |
400 | void ImageSync<I>::finish(int r) { | |
401 | dout(20) << ": r=" << r << dendl; | |
402 | ||
403 | m_instance_watcher->notify_sync_complete(m_local_image_ctx->id); | |
404 | BaseRequest::finish(r); | |
405 | } | |
406 | ||
407 | } // namespace mirror | |
408 | } // namespace rbd | |
409 | ||
410 | template class rbd::mirror::ImageSync<librbd::ImageCtx>; |