]> git.proxmox.com Git - ceph.git/blame - ceph/src/librbd/image/DetachChildRequest.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / librbd / image / DetachChildRequest.cc
CommitLineData
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
23namespace librbd {
24namespace image {
25
26using util::create_context_callback;
27using util::create_rados_callback;
28
29template <typename I>
30DetachChildRequest<I>::~DetachChildRequest() {
31 ceph_assert(m_parent_image_ctx == nullptr);
32}
33
34template <typename I>
35void 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
61template <typename I>
62void 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
90template <typename I>
91void 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
105template <typename I>
106void 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
123template <typename I>
124void 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
159template<typename I>
160void 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
176template<typename I>
177void 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
208template<typename I>
209void 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
220template<typename I>
221void 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
239template<typename I>
240void 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
256template<typename I>
257void 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
290template<typename I>
291void 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
304template<typename I>
305void 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
318template<typename I>
319void 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
329template<typename I>
330void 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
344template<typename I>
345void 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
362template<typename I>
363void 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
379template <typename I>
380void 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
391template class librbd::image::DetachChildRequest<librbd::ImageCtx>;