1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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"
15 #define dout_subsys ceph_subsys_rbd
17 #define dout_prefix *_dout << "librbd::SnapshotRemoveRequest: " << this << " " \
23 using util::create_context_callback
;
24 using util::create_rados_callback
;
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
) {
36 void SnapshotRemoveRequest
<I
>::send_op() {
37 I
&image_ctx
= this->m_image_ctx
;
38 CephContext
*cct
= image_ctx
.cct
;
40 ceph_assert(ceph_mutex_is_locked(image_ctx
.owner_lock
));
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
);
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
;
65 void SnapshotRemoveRequest
<I
>::trash_snap() {
66 I
&image_ctx
= this->m_image_ctx
;
67 if (image_ctx
.old_format
) {
70 } else if (cls::rbd::get_snap_namespace_type(m_snap_namespace
) ==
71 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH
) {
76 CephContext
*cct
= image_ctx
.cct
;
77 ldout(cct
, 5) << dendl
;
79 librados::ObjectWriteOperation op
;
80 cls_client::snapshot_trash_add(&op
, m_snap_id
);
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
);
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
;
96 if (r
== -EOPNOTSUPP
) {
97 // trash / clone v2 not supported
100 } else if (r
< 0 && r
!= -EEXIST
) {
101 lderr(cct
) << "failed to move snapshot to trash: " << cpp_strerror(r
)
107 m_trashed_snapshot
= true;
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
;
117 librados::ObjectReadOperation op
;
118 cls_client::snapshot_get_start(&op
, m_snap_id
);
120 auto aio_comp
= create_rados_callback
<
121 SnapshotRemoveRequest
<I
>,
122 &SnapshotRemoveRequest
<I
>::handle_get_snap
>(this);
124 int r
= image_ctx
.md_ctx
.aio_operate(image_ctx
.header_oid
, aio_comp
, &op
,
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
;
137 cls::rbd::SnapshotInfo snap_info
;
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
) {
149 lderr(cct
) << "failed to retrieve snapshot: " << cpp_strerror(r
)
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
;
164 librados::ObjectReadOperation op
;
165 cls_client::children_list_start(&op
, m_snap_id
);
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
,
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
;
185 auto it
= m_out_bl
.cbegin();
186 r
= cls_client::children_list_finish(&it
, &m_child_images
);
189 if (r
< 0 && r
!= -ENOENT
) {
190 lderr(cct
) << "failed to retrieve child: " << cpp_strerror(r
)
196 detach_stale_child();
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
;
205 for (auto& child_image
: m_child_images
) {
206 m_child_attached
= true;
208 int r
= util::create_ioctx(image_ctx
.md_ctx
, "child image",
210 child_image
.pool_namespace
, &ioctx
);
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
);
225 this->async_complete(r
);
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
;
239 if (r
< 0 && r
!= -ENOENT
) {
240 lderr(cct
) << "failed to detach stale child: " << cpp_strerror(r
)
246 m_child_attached
= false;
250 template <typename I
>
251 void SnapshotRemoveRequest
<I
>::detach_child() {
252 I
&image_ctx
= this->m_image_ctx
;
253 CephContext
*cct
= image_ctx
.cct
;
255 bool detach_child
= false;
257 std::shared_lock image_locker
{image_ctx
.image_lock
};
259 cls::rbd::ParentImageSpec our_pspec
;
260 int r
= image_ctx
.get_parent_spec(m_snap_id
, &our_pspec
);
263 ldout(cct
, 1) << "No such snapshot" << dendl
;
265 lderr(cct
) << "failed to retrieve parent spec" << dendl
;
268 this->async_complete(r
);
272 if (image_ctx
.parent_md
.spec
!= our_pspec
&&
273 (scan_for_parents(our_pspec
) == -ENOENT
)) {
274 // no other references to the parent image
280 // HEAD image or other snapshots still associated with parent
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
);
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
;
299 if (r
< 0 && r
!= -ENOENT
) {
300 lderr(cct
) << "failed to detach child from parent: " << cpp_strerror(r
)
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
);
319 CephContext
*cct
= image_ctx
.cct
;
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
;
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
);
335 // object map disabled
336 remove_image_state();
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
;
346 lderr(cct
) << "failed to remove snapshot object map: " << cpp_strerror(r
)
352 remove_image_state();
355 template <typename I
>
356 void SnapshotRemoveRequest
<I
>::remove_image_state() {
357 I
&image_ctx
= this->m_image_ctx
;
359 const auto* info
= std::get_if
<cls::rbd::MirrorSnapshotNamespace
>(
361 if (info
== nullptr || info
->is_orphan()) {
366 CephContext
*cct
= image_ctx
.cct
;
367 ldout(cct
, 5) << dendl
;
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
);
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
;
384 lderr(cct
) << "failed to remove image state: " << cpp_strerror(r
)
395 template <typename I
>
396 void SnapshotRemoveRequest
<I
>::release_snap_id() {
397 I
&image_ctx
= this->m_image_ctx
;
399 if (!image_ctx
.data_ctx
.is_valid()) {
404 CephContext
*cct
= image_ctx
.cct
;
405 ldout(cct
, 5) << "snap_name=" << m_snap_name
<< ", "
406 << "snap_id=" << m_snap_id
<< dendl
;
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
);
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
;
421 if (r
< 0 && r
!= -ENOENT
) {
422 lderr(cct
) << "failed to release snap id: " << cpp_strerror(r
) << dendl
;
430 template <typename I
>
431 void SnapshotRemoveRequest
<I
>::remove_snap() {
432 I
&image_ctx
= this->m_image_ctx
;
434 CephContext
*cct
= image_ctx
.cct
;
435 ldout(cct
, 5) << dendl
;
437 librados::ObjectWriteOperation op
;
438 if (image_ctx
.old_format
) {
439 cls_client::old_snapshot_remove(&op
, m_snap_name
);
441 cls_client::snapshot_remove(&op
, m_snap_id
);
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
);
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
;
459 lderr(cct
) << "failed to remove snapshot: " << cpp_strerror(r
) << dendl
;
464 remove_snap_context();
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
;
474 std::unique_lock image_locker
{image_ctx
.image_lock
};
475 image_ctx
.rm_snap(m_snap_namespace
, m_snap_name
, m_snap_id
);
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
));
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
) {
492 if (it
->second
.parent
.spec
== pspec
) {
496 if (it
== image_ctx
.snap_info
.end()) {
503 } // namespace operation
504 } // namespace librbd
506 template class librbd::operation::SnapshotRemoveRequest
<librbd::ImageCtx
>;