]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "librbd/image/DetachChildRequest.h" | |
5 | #include "common/dout.h" | |
6 | #include "common/errno.h" | |
7 | #include "common/WorkQueue.h" | |
8 | #include "cls/rbd/cls_rbd_client.h" | |
92f5a8d4 | 9 | #include "librbd/ExclusiveLock.h" |
11fdf7f2 TL |
10 | #include "librbd/ImageCtx.h" |
11 | #include "librbd/ImageState.h" | |
12 | #include "librbd/Operations.h" | |
13 | #include "librbd/Utils.h" | |
92f5a8d4 | 14 | #include "librbd/journal/DisabledPolicy.h" |
9f95a23c | 15 | #include "librbd/trash/RemoveRequest.h" |
11fdf7f2 TL |
16 | #include <string> |
17 | ||
18 | #define dout_subsys ceph_subsys_rbd | |
19 | #undef dout_prefix | |
20 | #define dout_prefix *_dout << "librbd::image::DetachChildRequest: " << this \ | |
21 | << " " << __func__ << ": " | |
22 | ||
23 | namespace librbd { | |
24 | namespace image { | |
25 | ||
26 | using util::create_context_callback; | |
27 | using util::create_rados_callback; | |
28 | ||
29 | template <typename I> | |
30 | DetachChildRequest<I>::~DetachChildRequest() { | |
31 | ceph_assert(m_parent_image_ctx == nullptr); | |
32 | } | |
33 | ||
34 | template <typename I> | |
35 | void DetachChildRequest<I>::send() { | |
36 | { | |
9f95a23c | 37 | std::shared_lock image_locker{m_image_ctx.image_lock}; |
11fdf7f2 TL |
38 | |
39 | // use oldest snapshot or HEAD for parent spec | |
40 | if (!m_image_ctx.snap_info.empty()) { | |
41 | m_parent_spec = m_image_ctx.snap_info.begin()->second.parent.spec; | |
42 | } else { | |
43 | m_parent_spec = m_image_ctx.parent_md.spec; | |
44 | } | |
45 | } | |
46 | ||
47 | if (m_parent_spec.pool_id == -1) { | |
48 | // ignore potential race with parent disappearing | |
49 | m_image_ctx.op_work_queue->queue(create_context_callback< | |
50 | DetachChildRequest<I>, | |
51 | &DetachChildRequest<I>::finish>(this), 0); | |
52 | return; | |
53 | } else if (!m_image_ctx.test_op_features(RBD_OPERATION_FEATURE_CLONE_CHILD)) { | |
54 | clone_v1_remove_child(); | |
55 | return; | |
56 | } | |
57 | ||
58 | clone_v2_child_detach(); | |
59 | } | |
60 | ||
61 | template <typename I> | |
62 | void DetachChildRequest<I>::clone_v2_child_detach() { | |
63 | auto cct = m_image_ctx.cct; | |
64 | ldout(cct, 5) << dendl; | |
65 | ||
66 | librados::ObjectWriteOperation op; | |
67 | cls_client::child_detach(&op, m_parent_spec.snap_id, | |
68 | {m_image_ctx.md_ctx.get_id(), | |
69 | m_image_ctx.md_ctx.get_namespace(), | |
70 | m_image_ctx.id}); | |
71 | ||
72 | int r = util::create_ioctx(m_image_ctx.md_ctx, "parent image", | |
73 | m_parent_spec.pool_id, | |
74 | m_parent_spec.pool_namespace, &m_parent_io_ctx); | |
75 | if (r < 0) { | |
76 | finish(r); | |
77 | return; | |
78 | } | |
79 | ||
80 | m_parent_header_name = util::header_name(m_parent_spec.image_id); | |
81 | ||
82 | auto aio_comp = create_rados_callback< | |
83 | DetachChildRequest<I>, | |
84 | &DetachChildRequest<I>::handle_clone_v2_child_detach>(this); | |
85 | r = m_parent_io_ctx.aio_operate(m_parent_header_name, aio_comp, &op); | |
86 | ceph_assert(r == 0); | |
87 | aio_comp->release(); | |
88 | } | |
89 | ||
90 | template <typename I> | |
91 | void DetachChildRequest<I>::handle_clone_v2_child_detach(int r) { | |
92 | auto cct = m_image_ctx.cct; | |
93 | ldout(cct, 5) << "r=" << r << dendl; | |
94 | ||
95 | if (r < 0 && r != -ENOENT) { | |
96 | lderr(cct) << "error detaching child from parent: " << cpp_strerror(r) | |
97 | << dendl; | |
98 | finish(r); | |
99 | return; | |
100 | } | |
101 | ||
102 | clone_v2_get_snapshot(); | |
103 | } | |
104 | ||
105 | template <typename I> | |
106 | void DetachChildRequest<I>::clone_v2_get_snapshot() { | |
107 | auto cct = m_image_ctx.cct; | |
108 | ldout(cct, 5) << dendl; | |
109 | ||
110 | librados::ObjectReadOperation op; | |
111 | cls_client::snapshot_get_start(&op, m_parent_spec.snap_id); | |
112 | ||
113 | m_out_bl.clear(); | |
114 | auto aio_comp = create_rados_callback< | |
115 | DetachChildRequest<I>, | |
116 | &DetachChildRequest<I>::handle_clone_v2_get_snapshot>(this); | |
117 | int r = m_parent_io_ctx.aio_operate(m_parent_header_name, aio_comp, &op, | |
118 | &m_out_bl); | |
119 | ceph_assert(r == 0); | |
120 | aio_comp->release(); | |
121 | } | |
122 | ||
123 | template <typename I> | |
124 | void DetachChildRequest<I>::handle_clone_v2_get_snapshot(int r) { | |
125 | auto cct = m_image_ctx.cct; | |
126 | ldout(cct, 5) << "r=" << r << dendl; | |
127 | ||
128 | bool remove_snapshot = false; | |
129 | if (r == 0) { | |
130 | cls::rbd::SnapshotInfo snap_info; | |
131 | auto it = m_out_bl.cbegin(); | |
132 | r = cls_client::snapshot_get_finish(&it, &snap_info); | |
133 | if (r == 0) { | |
134 | m_parent_snap_namespace = snap_info.snapshot_namespace; | |
135 | m_parent_snap_name = snap_info.name; | |
136 | ||
137 | if (cls::rbd::get_snap_namespace_type(m_parent_snap_namespace) == | |
138 | cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH && | |
139 | snap_info.child_count == 0) { | |
140 | // snapshot is in trash w/ zero children, so remove it | |
141 | remove_snapshot = true; | |
142 | } | |
143 | } | |
144 | } | |
145 | ||
146 | if (r < 0) { | |
147 | ldout(cct, 5) << "failed to retrieve snapshot: " << cpp_strerror(r) | |
148 | << dendl; | |
149 | } | |
150 | ||
151 | if (!remove_snapshot) { | |
152 | finish(0); | |
153 | return; | |
154 | } | |
155 | ||
156 | clone_v2_open_parent(); | |
157 | } | |
158 | ||
159 | template<typename I> | |
160 | void DetachChildRequest<I>::clone_v2_open_parent() { | |
161 | auto cct = m_image_ctx.cct; | |
162 | ldout(cct, 5) << dendl; | |
163 | ||
164 | m_parent_image_ctx = I::create("", m_parent_spec.image_id, nullptr, | |
165 | m_parent_io_ctx, false); | |
166 | ||
9f95a23c TL |
167 | // ensure non-primary images can be modified |
168 | m_parent_image_ctx->read_only_mask &= ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY; | |
169 | ||
11fdf7f2 TL |
170 | auto ctx = create_context_callback< |
171 | DetachChildRequest<I>, | |
172 | &DetachChildRequest<I>::handle_clone_v2_open_parent>(this); | |
173 | m_parent_image_ctx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT, ctx); | |
174 | } | |
175 | ||
176 | template<typename I> | |
177 | void DetachChildRequest<I>::handle_clone_v2_open_parent(int r) { | |
178 | auto cct = m_image_ctx.cct; | |
179 | ldout(cct, 5) << "r=" << r << dendl; | |
180 | ||
181 | if (r < 0) { | |
182 | ldout(cct, 5) << "failed to open parent for read/write: " | |
183 | << cpp_strerror(r) << dendl; | |
184 | m_parent_image_ctx->destroy(); | |
185 | m_parent_image_ctx = nullptr; | |
186 | finish(0); | |
187 | return; | |
188 | } | |
189 | ||
92f5a8d4 TL |
190 | // do not attempt to open the parent journal when removing the trash |
191 | // snapshot, because the parent may be not promoted | |
192 | if (m_parent_image_ctx->test_features(RBD_FEATURE_JOURNALING)) { | |
9f95a23c | 193 | std::unique_lock image_locker{m_parent_image_ctx->image_lock}; |
92f5a8d4 TL |
194 | m_parent_image_ctx->set_journal_policy(new journal::DisabledPolicy()); |
195 | } | |
196 | ||
197 | // disallow any proxied maintenance operations | |
198 | { | |
9f95a23c | 199 | std::shared_lock owner_locker{m_parent_image_ctx->owner_lock}; |
92f5a8d4 TL |
200 | if (m_parent_image_ctx->exclusive_lock != nullptr) { |
201 | m_parent_image_ctx->exclusive_lock->block_requests(0); | |
202 | } | |
203 | } | |
204 | ||
11fdf7f2 TL |
205 | clone_v2_remove_snapshot(); |
206 | } | |
207 | ||
208 | template<typename I> | |
209 | void DetachChildRequest<I>::clone_v2_remove_snapshot() { | |
210 | auto cct = m_image_ctx.cct; | |
211 | ldout(cct, 5) << dendl; | |
212 | ||
213 | auto ctx = create_context_callback< | |
214 | DetachChildRequest<I>, | |
215 | &DetachChildRequest<I>::handle_clone_v2_remove_snapshot>(this); | |
216 | m_parent_image_ctx->operations->snap_remove(m_parent_snap_namespace, | |
217 | m_parent_snap_name, ctx); | |
218 | } | |
219 | ||
220 | template<typename I> | |
221 | void DetachChildRequest<I>::handle_clone_v2_remove_snapshot(int r) { | |
222 | auto cct = m_image_ctx.cct; | |
223 | ldout(cct, 5) << "r=" << r << dendl; | |
224 | ||
225 | if (r < 0 && r != -ENOENT) { | |
226 | ldout(cct, 5) << "failed to remove trashed clone snapshot: " | |
227 | << cpp_strerror(r) << dendl; | |
9f95a23c TL |
228 | clone_v2_close_parent(); |
229 | return; | |
230 | } | |
231 | ||
232 | if (m_parent_image_ctx->snaps.empty()) { | |
233 | clone_v2_get_parent_trash_entry(); | |
234 | } else { | |
235 | clone_v2_close_parent(); | |
236 | } | |
237 | } | |
238 | ||
239 | template<typename I> | |
240 | void DetachChildRequest<I>::clone_v2_get_parent_trash_entry() { | |
241 | auto cct = m_image_ctx.cct; | |
242 | ldout(cct, 5) << dendl; | |
243 | ||
244 | librados::ObjectReadOperation op; | |
245 | cls_client::trash_get_start(&op, m_parent_image_ctx->id); | |
246 | ||
247 | m_out_bl.clear(); | |
248 | auto aio_comp = create_rados_callback< | |
249 | DetachChildRequest<I>, | |
250 | &DetachChildRequest<I>::handle_clone_v2_get_parent_trash_entry>(this); | |
251 | int r = m_parent_io_ctx.aio_operate(RBD_TRASH, aio_comp, &op, &m_out_bl); | |
252 | ceph_assert(r == 0); | |
253 | aio_comp->release(); | |
254 | } | |
255 | ||
256 | template<typename I> | |
257 | void DetachChildRequest<I>::handle_clone_v2_get_parent_trash_entry(int r) { | |
258 | auto cct = m_image_ctx.cct; | |
259 | ldout(cct, 5) << "r=" << r << dendl; | |
260 | ||
261 | if (r < 0 && r != -ENOENT) { | |
262 | ldout(cct, 5) << "failed to get parent trash entry: " << cpp_strerror(r) | |
263 | << dendl; | |
264 | clone_v2_close_parent(); | |
265 | return; | |
11fdf7f2 TL |
266 | } |
267 | ||
9f95a23c TL |
268 | bool in_trash = false; |
269 | ||
270 | if (r == 0) { | |
271 | cls::rbd::TrashImageSpec trash_spec; | |
272 | auto it = m_out_bl.cbegin(); | |
273 | r = cls_client::trash_get_finish(&it, &trash_spec); | |
274 | ||
275 | if (r == 0 && | |
276 | trash_spec.source == cls::rbd::TRASH_IMAGE_SOURCE_USER_PARENT && | |
277 | trash_spec.state == cls::rbd::TRASH_IMAGE_STATE_NORMAL && | |
278 | trash_spec.deferment_end_time <= ceph_clock_now()) { | |
279 | in_trash = true; | |
280 | } | |
281 | } | |
282 | ||
283 | if (in_trash) { | |
284 | clone_v2_remove_parent_from_trash(); | |
285 | } else { | |
286 | clone_v2_close_parent(); | |
287 | } | |
288 | } | |
289 | ||
290 | template<typename I> | |
291 | void DetachChildRequest<I>::clone_v2_remove_parent_from_trash() { | |
292 | auto cct = m_image_ctx.cct; | |
293 | ldout(cct, 5) << dendl; | |
294 | ||
295 | auto ctx = create_context_callback< | |
296 | DetachChildRequest<I>, | |
297 | &DetachChildRequest<I>::handle_clone_v2_remove_parent_from_trash>(this); | |
298 | auto req = librbd::trash::RemoveRequest<I>::create( | |
299 | m_parent_io_ctx, m_parent_image_ctx, m_image_ctx.op_work_queue, false, | |
300 | m_no_op, ctx); | |
301 | req->send(); | |
302 | } | |
303 | ||
304 | template<typename I> | |
305 | void DetachChildRequest<I>::handle_clone_v2_remove_parent_from_trash(int r) { | |
306 | auto cct = m_image_ctx.cct; | |
307 | ldout(cct, 5) << "r=" << r << dendl; | |
308 | ||
309 | if (r < 0) { | |
310 | ldout(cct, 5) << "failed to remove parent image:" << cpp_strerror(r) | |
311 | << dendl; | |
312 | } | |
313 | ||
314 | m_parent_image_ctx = nullptr; | |
315 | finish(0); | |
11fdf7f2 TL |
316 | } |
317 | ||
318 | template<typename I> | |
319 | void DetachChildRequest<I>::clone_v2_close_parent() { | |
320 | auto cct = m_image_ctx.cct; | |
321 | ldout(cct, 5) << dendl; | |
322 | ||
323 | auto ctx = create_context_callback< | |
324 | DetachChildRequest<I>, | |
325 | &DetachChildRequest<I>::handle_clone_v2_close_parent>(this); | |
326 | m_parent_image_ctx->state->close(ctx); | |
327 | } | |
328 | ||
329 | template<typename I> | |
330 | void DetachChildRequest<I>::handle_clone_v2_close_parent(int r) { | |
331 | auto cct = m_image_ctx.cct; | |
332 | ldout(cct, 5) << "r=" << r << dendl; | |
333 | ||
334 | if (r < 0) { | |
335 | ldout(cct, 5) << "failed to close parent image:" << cpp_strerror(r) | |
336 | << dendl; | |
337 | } | |
338 | ||
339 | m_parent_image_ctx->destroy(); | |
340 | m_parent_image_ctx = nullptr; | |
341 | finish(0); | |
342 | } | |
343 | ||
344 | template<typename I> | |
345 | void DetachChildRequest<I>::clone_v1_remove_child() { | |
346 | auto cct = m_image_ctx.cct; | |
347 | ldout(cct, 5) << dendl; | |
348 | ||
eafe8130 TL |
349 | m_parent_spec.pool_namespace = ""; |
350 | ||
11fdf7f2 TL |
351 | librados::ObjectWriteOperation op; |
352 | librbd::cls_client::remove_child(&op, m_parent_spec, m_image_ctx.id); | |
353 | ||
354 | auto aio_comp = create_rados_callback< | |
355 | DetachChildRequest<I>, | |
356 | &DetachChildRequest<I>::handle_clone_v1_remove_child>(this); | |
357 | int r = m_image_ctx.md_ctx.aio_operate(RBD_CHILDREN, aio_comp, &op); | |
358 | ceph_assert(r == 0); | |
359 | aio_comp->release(); | |
360 | } | |
361 | ||
362 | template<typename I> | |
363 | void DetachChildRequest<I>::handle_clone_v1_remove_child(int r) { | |
364 | auto cct = m_image_ctx.cct; | |
365 | ldout(cct, 5) << "r=" << r << dendl; | |
366 | ||
367 | if (r == -ENOENT) { | |
368 | r = 0; | |
369 | } else if (r < 0) { | |
370 | lderr(cct) << "failed to remove child from children list: " | |
371 | << cpp_strerror(r) << dendl; | |
372 | finish(r); | |
373 | return; | |
374 | } | |
375 | ||
376 | finish(0); | |
377 | } | |
378 | ||
379 | template <typename I> | |
380 | void DetachChildRequest<I>::finish(int r) { | |
381 | auto cct = m_image_ctx.cct; | |
382 | ldout(cct, 5) << "r=" << r << dendl; | |
383 | ||
384 | m_on_finish->complete(r); | |
385 | delete this; | |
386 | } | |
387 | ||
388 | } // namespace image | |
389 | } // namespace librbd | |
390 | ||
391 | template class librbd::image::DetachChildRequest<librbd::ImageCtx>; |