]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/operation/SnapshotRemoveRequest.cc
update ceph source to reef 18.2.1
[ceph.git] / ceph / src / librbd / operation / SnapshotRemoveRequest.cc
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/operation/SnapshotRemoveRequest.h"
5 #include "common/dout.h"
6 #include "common/errno.h"
7 #include "include/ceph_assert.h"
8 #include "cls/rbd/cls_rbd_client.h"
9 #include "librbd/ImageCtx.h"
10 #include "librbd/ObjectMap.h"
11 #include "librbd/Utils.h"
12 #include "librbd/image/DetachChildRequest.h"
13 #include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
14
15 #define dout_subsys ceph_subsys_rbd
16 #undef dout_prefix
17 #define dout_prefix *_dout << "librbd::SnapshotRemoveRequest: " << this << " " \
18 << __func__ << ": "
19
20 namespace librbd {
21 namespace operation {
22
23 using util::create_context_callback;
24 using util::create_rados_callback;
25
26 template <typename I>
27 SnapshotRemoveRequest<I>::SnapshotRemoveRequest(
28 I &image_ctx, Context *on_finish,
29 const cls::rbd::SnapshotNamespace &snap_namespace,
30 const std::string &snap_name, uint64_t snap_id)
31 : Request<I>(image_ctx, on_finish), m_snap_namespace(snap_namespace),
32 m_snap_name(snap_name), m_snap_id(snap_id) {
33 }
34
35 template <typename I>
36 void SnapshotRemoveRequest<I>::send_op() {
37 I &image_ctx = this->m_image_ctx;
38 CephContext *cct = image_ctx.cct;
39
40 ceph_assert(ceph_mutex_is_locked(image_ctx.owner_lock));
41 {
42 std::shared_lock image_locker{image_ctx.image_lock};
43 if (image_ctx.snap_info.find(m_snap_id) == image_ctx.snap_info.end()) {
44 lderr(cct) << "snapshot doesn't exist" << dendl;
45 this->async_complete(-ENOENT);
46 return;
47 }
48 }
49
50 trash_snap();
51 }
52
53 template <typename I>
54 bool SnapshotRemoveRequest<I>::should_complete(int r) {
55 I &image_ctx = this->m_image_ctx;
56 CephContext *cct = image_ctx.cct;
57 ldout(cct, 5) << "r=" << r << dendl;
58 if (r < 0 && r != -EBUSY) {
59 lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl;
60 }
61 return true;
62 }
63
64 template <typename I>
65 void SnapshotRemoveRequest<I>::trash_snap() {
66 I &image_ctx = this->m_image_ctx;
67 if (image_ctx.old_format) {
68 release_snap_id();
69 return;
70 } else if (cls::rbd::get_snap_namespace_type(m_snap_namespace) ==
71 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) {
72 get_snap();
73 return;
74 }
75
76 CephContext *cct = image_ctx.cct;
77 ldout(cct, 5) << dendl;
78
79 librados::ObjectWriteOperation op;
80 cls_client::snapshot_trash_add(&op, m_snap_id);
81
82 auto aio_comp = create_rados_callback<
83 SnapshotRemoveRequest<I>,
84 &SnapshotRemoveRequest<I>::handle_trash_snap>(this);
85 int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, aio_comp, &op);
86 ceph_assert(r == 0);
87 aio_comp->release();
88 }
89
90 template <typename I>
91 void SnapshotRemoveRequest<I>::handle_trash_snap(int r) {
92 I &image_ctx = this->m_image_ctx;
93 CephContext *cct = image_ctx.cct;
94 ldout(cct, 5) << "r=" << r << dendl;
95
96 if (r == -EOPNOTSUPP) {
97 // trash / clone v2 not supported
98 detach_child();
99 return;
100 } else if (r < 0 && r != -EEXIST) {
101 lderr(cct) << "failed to move snapshot to trash: " << cpp_strerror(r)
102 << dendl;
103 this->complete(r);
104 return;
105 }
106
107 m_trashed_snapshot = true;
108 get_snap();
109 }
110
111 template <typename I>
112 void SnapshotRemoveRequest<I>::get_snap() {
113 I &image_ctx = this->m_image_ctx;
114 CephContext *cct = image_ctx.cct;
115 ldout(cct, 5) << dendl;
116
117 librados::ObjectReadOperation op;
118 cls_client::snapshot_get_start(&op, m_snap_id);
119
120 auto aio_comp = create_rados_callback<
121 SnapshotRemoveRequest<I>,
122 &SnapshotRemoveRequest<I>::handle_get_snap>(this);
123 m_out_bl.clear();
124 int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, aio_comp, &op,
125 &m_out_bl);
126 ceph_assert(r == 0);
127 aio_comp->release();
128 }
129
130 template <typename I>
131 void SnapshotRemoveRequest<I>::handle_get_snap(int r) {
132 I &image_ctx = this->m_image_ctx;
133 CephContext *cct = image_ctx.cct;
134 ldout(cct, 5) << "r=" << r << dendl;
135
136 if (r == 0) {
137 cls::rbd::SnapshotInfo snap_info;
138
139 auto it = m_out_bl.cbegin();
140 r = cls_client::snapshot_get_finish(&it, &snap_info);
141 m_child_attached = (snap_info.child_count > 0);
142 if (r == 0 && m_child_attached) {
143 list_children();
144 return;
145 }
146 }
147
148 if (r < 0) {
149 lderr(cct) << "failed to retrieve snapshot: " << cpp_strerror(r)
150 << dendl;
151 this->complete(r);
152 return;
153 }
154
155 detach_child();
156 }
157
158 template <typename I>
159 void SnapshotRemoveRequest<I>::list_children() {
160 I &image_ctx = this->m_image_ctx;
161 CephContext *cct = image_ctx.cct;
162 ldout(cct, 5) << dendl;
163
164 librados::ObjectReadOperation op;
165 cls_client::children_list_start(&op, m_snap_id);
166
167 m_out_bl.clear();
168 m_child_images.clear();
169 auto aio_comp = create_rados_callback<
170 SnapshotRemoveRequest<I>,
171 &SnapshotRemoveRequest<I>::handle_list_children>(this);
172 int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, aio_comp, &op,
173 &m_out_bl);
174 ceph_assert(r == 0);
175 aio_comp->release();
176 }
177
178 template <typename I>
179 void SnapshotRemoveRequest<I>::handle_list_children(int r) {
180 I &image_ctx = this->m_image_ctx;
181 CephContext *cct = image_ctx.cct;
182 ldout(cct, 5) << "r=" << r << dendl;
183
184 if (r == 0) {
185 auto it = m_out_bl.cbegin();
186 r = cls_client::children_list_finish(&it, &m_child_images);
187 }
188
189 if (r < 0 && r != -ENOENT) {
190 lderr(cct) << "failed to retrieve child: " << cpp_strerror(r)
191 << dendl;
192 this->complete(r);
193 return;
194 }
195
196 detach_stale_child();
197 }
198
199 template <typename I>
200 void SnapshotRemoveRequest<I>::detach_stale_child() {
201 I &image_ctx = this->m_image_ctx;
202 CephContext *cct = image_ctx.cct;
203 ldout(cct, 5) << dendl;
204
205 for (auto& child_image : m_child_images) {
206 m_child_attached = true;
207 IoCtx ioctx;
208 int r = util::create_ioctx(image_ctx.md_ctx, "child image",
209 child_image.pool_id,
210 child_image.pool_namespace, &ioctx);
211 if (r == -ENOENT) {
212 librados::ObjectWriteOperation op;
213 cls_client::child_detach(&op, m_snap_id,
214 {child_image.pool_id,
215 child_image.pool_namespace,
216 child_image.image_id});
217 auto aio_comp = create_rados_callback<
218 SnapshotRemoveRequest<I>,
219 &SnapshotRemoveRequest<I>::handle_detach_stale_child>(this);
220 r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, aio_comp, &op);
221 ceph_assert(r == 0);
222 aio_comp->release();
223 return;
224 } else if (r < 0) {
225 this->async_complete(r);
226 return;
227 }
228 }
229
230 detach_child();
231 }
232
233 template <typename I>
234 void SnapshotRemoveRequest<I>::handle_detach_stale_child(int r) {
235 I &image_ctx = this->m_image_ctx;
236 CephContext *cct = image_ctx.cct;
237 ldout(cct, 5) << "r=" << r << dendl;
238
239 if (r < 0 && r != -ENOENT) {
240 lderr(cct) << "failed to detach stale child: " << cpp_strerror(r)
241 << dendl;
242 this->complete(r);
243 return;
244 }
245
246 m_child_attached = false;
247 list_children();
248 }
249
250 template <typename I>
251 void SnapshotRemoveRequest<I>::detach_child() {
252 I &image_ctx = this->m_image_ctx;
253 CephContext *cct = image_ctx.cct;
254
255 bool detach_child = false;
256 {
257 std::shared_lock image_locker{image_ctx.image_lock};
258
259 cls::rbd::ParentImageSpec our_pspec;
260 int r = image_ctx.get_parent_spec(m_snap_id, &our_pspec);
261 if (r < 0) {
262 if (r == -ENOENT) {
263 ldout(cct, 1) << "No such snapshot" << dendl;
264 } else {
265 lderr(cct) << "failed to retrieve parent spec" << dendl;
266 }
267
268 this->async_complete(r);
269 return;
270 }
271
272 if (image_ctx.parent_md.spec != our_pspec &&
273 (scan_for_parents(our_pspec) == -ENOENT)) {
274 // no other references to the parent image
275 detach_child = true;
276 }
277 }
278
279 if (!detach_child) {
280 // HEAD image or other snapshots still associated with parent
281 remove_object_map();
282 return;
283 }
284
285 ldout(cct, 5) << dendl;
286 auto ctx = create_context_callback<
287 SnapshotRemoveRequest<I>,
288 &SnapshotRemoveRequest<I>::handle_detach_child>(this);
289 auto req = image::DetachChildRequest<I>::create(image_ctx, ctx);
290 req->send();
291 }
292
293 template <typename I>
294 void SnapshotRemoveRequest<I>::handle_detach_child(int r) {
295 I &image_ctx = this->m_image_ctx;
296 CephContext *cct = image_ctx.cct;
297 ldout(cct, 5) << "r=" << r << dendl;
298
299 if (r < 0 && r != -ENOENT) {
300 lderr(cct) << "failed to detach child from parent: " << cpp_strerror(r)
301 << dendl;
302 this->complete(r);
303 return;
304 }
305
306 remove_object_map();
307 }
308
309 template <typename I>
310 void SnapshotRemoveRequest<I>::remove_object_map() {
311 I &image_ctx = this->m_image_ctx;
312 if (m_child_attached) {
313 // if a clone v2 child is attached to this snapshot, we cannot
314 // proceed. It's only an error if the snap was already in the trash
315 this->complete(m_trashed_snapshot ? 0 : -EBUSY);
316 return;
317 }
318
319 CephContext *cct = image_ctx.cct;
320
321 {
322 std::shared_lock owner_lock{image_ctx.owner_lock};
323 std::unique_lock image_locker{image_ctx.image_lock};
324 if (image_ctx.object_map != nullptr) {
325 ldout(cct, 5) << dendl;
326
327 auto ctx = create_context_callback<
328 SnapshotRemoveRequest<I>,
329 &SnapshotRemoveRequest<I>::handle_remove_object_map>(this);
330 image_ctx.object_map->snapshot_remove(m_snap_id, ctx);
331 return;
332 }
333 }
334
335 // object map disabled
336 remove_image_state();
337 }
338
339 template <typename I>
340 void SnapshotRemoveRequest<I>::handle_remove_object_map(int r) {
341 I &image_ctx = this->m_image_ctx;
342 CephContext *cct = image_ctx.cct;
343 ldout(cct, 5) << "r=" << r << dendl;
344
345 if (r < 0) {
346 lderr(cct) << "failed to remove snapshot object map: " << cpp_strerror(r)
347 << dendl;
348 this->complete(r);
349 return;
350 }
351
352 remove_image_state();
353 }
354
355 template <typename I>
356 void SnapshotRemoveRequest<I>::remove_image_state() {
357 I &image_ctx = this->m_image_ctx;
358
359 const auto* info = std::get_if<cls::rbd::MirrorSnapshotNamespace>(
360 &m_snap_namespace);
361 if (info == nullptr || info->is_orphan()) {
362 release_snap_id();
363 return;
364 }
365
366 CephContext *cct = image_ctx.cct;
367 ldout(cct, 5) << dendl;
368
369 auto ctx = create_context_callback<
370 SnapshotRemoveRequest<I>,
371 &SnapshotRemoveRequest<I>::handle_remove_image_state>(this);
372 auto req = mirror::snapshot::RemoveImageStateRequest<I>::create(
373 &image_ctx, m_snap_id, ctx);
374 req->send();
375 }
376
377 template <typename I>
378 void SnapshotRemoveRequest<I>::handle_remove_image_state(int r) {
379 I &image_ctx = this->m_image_ctx;
380 CephContext *cct = image_ctx.cct;
381 ldout(cct, 5) << "r=" << r << dendl;
382
383 if (r < 0) {
384 lderr(cct) << "failed to remove image state: " << cpp_strerror(r)
385 << dendl;
386 if (r != -ENOENT) {
387 this->complete(r);
388 return;
389 }
390 }
391
392 release_snap_id();
393 }
394
395 template <typename I>
396 void SnapshotRemoveRequest<I>::release_snap_id() {
397 I &image_ctx = this->m_image_ctx;
398
399 if (!image_ctx.data_ctx.is_valid()) {
400 remove_snap();
401 return;
402 }
403
404 CephContext *cct = image_ctx.cct;
405 ldout(cct, 5) << "snap_name=" << m_snap_name << ", "
406 << "snap_id=" << m_snap_id << dendl;
407
408 auto aio_comp = create_rados_callback<
409 SnapshotRemoveRequest<I>,
410 &SnapshotRemoveRequest<I>::handle_release_snap_id>(this);
411 image_ctx.data_ctx.aio_selfmanaged_snap_remove(m_snap_id, aio_comp);
412 aio_comp->release();
413 }
414
415 template <typename I>
416 void SnapshotRemoveRequest<I>::handle_release_snap_id(int r) {
417 I &image_ctx = this->m_image_ctx;
418 CephContext *cct = image_ctx.cct;
419 ldout(cct, 5) << "r=" << r << dendl;
420
421 if (r < 0 && r != -ENOENT) {
422 lderr(cct) << "failed to release snap id: " << cpp_strerror(r) << dendl;
423 this->complete(r);
424 return;
425 }
426
427 remove_snap();
428 }
429
430 template <typename I>
431 void SnapshotRemoveRequest<I>::remove_snap() {
432 I &image_ctx = this->m_image_ctx;
433
434 CephContext *cct = image_ctx.cct;
435 ldout(cct, 5) << dendl;
436
437 librados::ObjectWriteOperation op;
438 if (image_ctx.old_format) {
439 cls_client::old_snapshot_remove(&op, m_snap_name);
440 } else {
441 cls_client::snapshot_remove(&op, m_snap_id);
442 }
443
444 auto aio_comp = create_rados_callback<
445 SnapshotRemoveRequest<I>,
446 &SnapshotRemoveRequest<I>::handle_remove_snap>(this);
447 int r = image_ctx.md_ctx.aio_operate(image_ctx.header_oid, aio_comp, &op);
448 ceph_assert(r == 0);
449 aio_comp->release();
450 }
451
452 template <typename I>
453 void SnapshotRemoveRequest<I>::handle_remove_snap(int r) {
454 I &image_ctx = this->m_image_ctx;
455 CephContext *cct = image_ctx.cct;
456 ldout(cct, 5) << "r=" << r << dendl;
457
458 if (r < 0) {
459 lderr(cct) << "failed to remove snapshot: " << cpp_strerror(r) << dendl;
460 this->complete(r);
461 return;
462 }
463
464 remove_snap_context();
465 this->complete(0);
466 }
467
468 template <typename I>
469 void SnapshotRemoveRequest<I>::remove_snap_context() {
470 I &image_ctx = this->m_image_ctx;
471 CephContext *cct = image_ctx.cct;
472 ldout(cct, 5) << dendl;
473
474 std::unique_lock image_locker{image_ctx.image_lock};
475 image_ctx.rm_snap(m_snap_namespace, m_snap_name, m_snap_id);
476 }
477
478 template <typename I>
479 int SnapshotRemoveRequest<I>::scan_for_parents(
480 cls::rbd::ParentImageSpec &pspec) {
481 I &image_ctx = this->m_image_ctx;
482 ceph_assert(ceph_mutex_is_locked(image_ctx.image_lock));
483
484 if (pspec.pool_id != -1) {
485 std::map<uint64_t, SnapInfo>::iterator it;
486 for (it = image_ctx.snap_info.begin();
487 it != image_ctx.snap_info.end(); ++it) {
488 // skip our snap id (if checking base image, CEPH_NOSNAP won't match)
489 if (it->first == m_snap_id) {
490 continue;
491 }
492 if (it->second.parent.spec == pspec) {
493 break;
494 }
495 }
496 if (it == image_ctx.snap_info.end()) {
497 return -ENOENT;
498 }
499 }
500 return 0;
501 }
502
503 } // namespace operation
504 } // namespace librbd
505
506 template class librbd::operation::SnapshotRemoveRequest<librbd::ImageCtx>;