1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "tools/rbd_mirror/image_deleter/TrashWatcher.h"
5 #include "include/rbd_types.h"
6 #include "cls/rbd/cls_rbd_client.h"
7 #include "common/debug.h"
8 #include "common/errno.h"
9 #include "common/Timer.h"
10 #include "librbd/ImageCtx.h"
11 #include "librbd/Utils.h"
12 #include "tools/rbd_mirror/Threads.h"
13 #include "tools/rbd_mirror/image_deleter/Types.h"
15 #define dout_context g_ceph_context
16 #define dout_subsys ceph_subsys_rbd_mirror
18 #define dout_prefix *_dout << "rbd::mirror::image_deleter::TrashWatcher: " \
19 << this << " " << __func__ << ": "
21 using librbd::util::create_context_callback
;
22 using librbd::util::create_rados_callback
;
26 namespace image_deleter
{
30 const size_t MAX_RETURN
= 1024;
32 } // anonymous namespace
35 TrashWatcher
<I
>::TrashWatcher(librados::IoCtx
&io_ctx
, Threads
<I
> *threads
,
36 TrashListener
& trash_listener
)
37 : librbd::TrashWatcher
<I
>(io_ctx
, threads
->work_queue
),
38 m_io_ctx(io_ctx
), m_threads(threads
), m_trash_listener(trash_listener
),
39 m_lock(librbd::util::unique_lock_name(
40 "rbd::mirror::image_deleter::TrashWatcher", this)) {
44 void TrashWatcher
<I
>::init(Context
*on_finish
) {
48 Mutex::Locker
locker(m_lock
);
49 m_on_init_finish
= on_finish
;
51 ceph_assert(!m_trash_list_in_progress
);
52 m_trash_list_in_progress
= true;
59 void TrashWatcher
<I
>::shut_down(Context
*on_finish
) {
63 Mutex::Locker
timer_locker(m_threads
->timer_lock
);
64 Mutex::Locker
locker(m_lock
);
66 ceph_assert(!m_shutting_down
);
67 m_shutting_down
= true;
68 if (m_timer_ctx
!= nullptr) {
69 m_threads
->timer
->cancel_event(m_timer_ctx
);
70 m_timer_ctx
= nullptr;
74 auto ctx
= new FunctionContext([this, on_finish
](int r
) {
75 unregister_watcher(on_finish
);
77 m_async_op_tracker
.wait_for_ops(ctx
);
81 void TrashWatcher
<I
>::handle_image_added(const std::string
&image_id
,
82 const cls::rbd::TrashImageSpec
& spec
) {
83 dout(10) << "image_id=" << image_id
<< dendl
;
85 Mutex::Locker
locker(m_lock
);
86 add_image(image_id
, spec
);
90 void TrashWatcher
<I
>::handle_image_removed(const std::string
&image_id
) {
91 // ignore removals -- the image deleter will ignore -ENOENTs
95 void TrashWatcher
<I
>::handle_rewatch_complete(int r
) {
96 dout(5) << "r=" << r
<< dendl
;
98 if (r
== -EBLACKLISTED
) {
99 dout(0) << "detected client is blacklisted" << dendl
;
101 } else if (r
== -ENOENT
) {
102 dout(5) << "trash directory deleted" << dendl
;
104 derr
<< "unexpected error re-registering trash directory watch: "
105 << cpp_strerror(r
) << dendl
;
107 schedule_trash_list(30);
110 template <typename I
>
111 void TrashWatcher
<I
>::create_trash() {
114 Mutex::Locker
locker(m_lock
);
115 ceph_assert(m_trash_list_in_progress
);
118 librados::ObjectWriteOperation op
;
121 m_async_op_tracker
.start_op();
122 auto aio_comp
= create_rados_callback
<
123 TrashWatcher
<I
>, &TrashWatcher
<I
>::handle_create_trash
>(this);
124 int r
= m_io_ctx
.aio_operate(RBD_TRASH
, aio_comp
, &op
);
129 template <typename I
>
130 void TrashWatcher
<I
>::handle_create_trash(int r
) {
131 dout(20) << "r=" << r
<< dendl
;
133 Mutex::Locker
locker(m_lock
);
134 ceph_assert(m_trash_list_in_progress
);
137 Context
* on_init_finish
= nullptr;
138 if (r
== -EBLACKLISTED
|| r
== -ENOENT
) {
139 if (r
== -EBLACKLISTED
) {
140 dout(0) << "detected client is blacklisted" << dendl
;
142 dout(0) << "detected pool no longer exists" << dendl
;
145 Mutex::Locker
locker(m_lock
);
146 std::swap(on_init_finish
, m_on_init_finish
);
147 m_trash_list_in_progress
= false;
148 } else if (r
< 0 && r
!= -EEXIST
) {
149 derr
<< "failed to create trash object: " << cpp_strerror(r
) << dendl
;
151 Mutex::Locker
locker(m_lock
);
152 m_trash_list_in_progress
= false;
155 schedule_trash_list(30);
160 m_async_op_tracker
.finish_op();
161 if (on_init_finish
!= nullptr) {
162 on_init_finish
->complete(r
);
166 template <typename I
>
167 void TrashWatcher
<I
>::register_watcher() {
169 Mutex::Locker
locker(m_lock
);
170 ceph_assert(m_trash_list_in_progress
);
173 // if the watch registration is in-flight, let the watcher
174 // handle the transition -- only (re-)register if it's not registered
175 if (!this->is_unregistered()) {
180 // first time registering or the watch failed
182 m_async_op_tracker
.start_op();
184 Context
*ctx
= create_context_callback
<
185 TrashWatcher
, &TrashWatcher
<I
>::handle_register_watcher
>(this);
186 this->register_watch(ctx
);
189 template <typename I
>
190 void TrashWatcher
<I
>::handle_register_watcher(int r
) {
191 dout(5) << "r=" << r
<< dendl
;
194 Mutex::Locker
locker(m_lock
);
195 ceph_assert(m_trash_list_in_progress
);
197 m_trash_list_in_progress
= false;
201 Context
*on_init_finish
= nullptr;
204 } else if (r
== -EBLACKLISTED
) {
205 dout(0) << "detected client is blacklisted" << dendl
;
207 Mutex::Locker
locker(m_lock
);
208 std::swap(on_init_finish
, m_on_init_finish
);
210 derr
<< "unexpected error registering trash directory watch: "
211 << cpp_strerror(r
) << dendl
;
212 schedule_trash_list(10);
215 m_async_op_tracker
.finish_op();
216 if (on_init_finish
!= nullptr) {
217 on_init_finish
->complete(r
);
221 template <typename I
>
222 void TrashWatcher
<I
>::unregister_watcher(Context
* on_finish
) {
225 m_async_op_tracker
.start_op();
226 Context
*ctx
= new FunctionContext([this, on_finish
](int r
) {
227 handle_unregister_watcher(r
, on_finish
);
229 this->unregister_watch(ctx
);
232 template <typename I
>
233 void TrashWatcher
<I
>::handle_unregister_watcher(int r
, Context
* on_finish
) {
234 dout(5) << "unregister_watcher: r=" << r
<< dendl
;
236 derr
<< "error unregistering watcher for trash directory: "
237 << cpp_strerror(r
) << dendl
;
239 m_async_op_tracker
.finish_op();
240 on_finish
->complete(0);
243 template <typename I
>
244 void TrashWatcher
<I
>::trash_list(bool initial_request
) {
245 if (initial_request
) {
246 m_async_op_tracker
.start_op();
247 m_last_image_id
= "";
250 dout(5) << "last_image_id=" << m_last_image_id
<< dendl
;
253 Mutex::Locker
locker(m_lock
);
254 ceph_assert(m_trash_list_in_progress
);
257 librados::ObjectReadOperation op
;
258 librbd::cls_client::trash_list_start(&op
, m_last_image_id
, MAX_RETURN
);
260 librados::AioCompletion
*aio_comp
= create_rados_callback
<
261 TrashWatcher
<I
>, &TrashWatcher
<I
>::handle_trash_list
>(this);
263 int r
= m_io_ctx
.aio_operate(RBD_TRASH
, aio_comp
, &op
, &m_out_bl
);
268 template <typename I
>
269 void TrashWatcher
<I
>::handle_trash_list(int r
) {
270 dout(5) << "r=" << r
<< dendl
;
272 std::map
<std::string
, cls::rbd::TrashImageSpec
> images
;
274 auto bl_it
= m_out_bl
.cbegin();
275 r
= librbd::cls_client::trash_list_finish(&bl_it
, &images
);
278 Context
*on_init_finish
= nullptr;
280 Mutex::Locker
locker(m_lock
);
281 ceph_assert(m_trash_list_in_progress
);
283 for (auto& image
: images
) {
284 add_image(image
.first
, image
.second
);
286 } else if (r
== -ENOENT
) {
290 if (r
== -EBLACKLISTED
) {
291 dout(0) << "detected client is blacklisted during trash refresh" << dendl
;
292 m_trash_list_in_progress
= false;
293 std::swap(on_init_finish
, m_on_init_finish
);
294 } else if (r
>= 0 && images
.size() < MAX_RETURN
) {
295 m_trash_list_in_progress
= false;
296 std::swap(on_init_finish
, m_on_init_finish
);
298 m_trash_list_in_progress
= false;
302 if (r
>= 0 && images
.size() == MAX_RETURN
) {
303 m_last_image_id
= images
.rbegin()->first
;
306 } else if (r
< 0 && r
!= -EBLACKLISTED
) {
307 derr
<< "failed to retrieve trash directory: " << cpp_strerror(r
) << dendl
;
308 schedule_trash_list(10);
311 m_async_op_tracker
.finish_op();
312 if (on_init_finish
!= nullptr) {
313 on_init_finish
->complete(r
);
317 template <typename I
>
318 void TrashWatcher
<I
>::schedule_trash_list(double interval
) {
319 Mutex::Locker
timer_locker(m_threads
->timer_lock
);
320 Mutex::Locker
locker(m_lock
);
321 if (m_shutting_down
|| m_trash_list_in_progress
|| m_timer_ctx
!= nullptr) {
322 if (m_trash_list_in_progress
&& !m_deferred_trash_list
) {
323 dout(5) << "deferring refresh until in-flight refresh completes" << dendl
;
324 m_deferred_trash_list
= true;
330 m_timer_ctx
= m_threads
->timer
->add_event_after(
332 new FunctionContext([this](int r
) {
333 process_trash_list();
337 template <typename I
>
338 void TrashWatcher
<I
>::process_trash_list() {
341 ceph_assert(m_threads
->timer_lock
.is_locked());
342 ceph_assert(m_timer_ctx
!= nullptr);
343 m_timer_ctx
= nullptr;
346 Mutex::Locker
locker(m_lock
);
347 ceph_assert(!m_trash_list_in_progress
);
348 m_trash_list_in_progress
= true;
351 // execute outside of the timer's lock
352 m_async_op_tracker
.start_op();
353 Context
*ctx
= new FunctionContext([this](int r
) {
355 m_async_op_tracker
.finish_op();
357 m_threads
->work_queue
->queue(ctx
, 0);
360 template <typename I
>
361 void TrashWatcher
<I
>::add_image(const std::string
& image_id
,
362 const cls::rbd::TrashImageSpec
& spec
) {
363 if (spec
.source
!= cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING
) {
367 ceph_assert(m_lock
.is_locked());
368 auto& deferment_end_time
= spec
.deferment_end_time
;
369 dout(10) << "image_id=" << image_id
<< ", "
370 << "deferment_end_time=" << deferment_end_time
<< dendl
;
372 m_async_op_tracker
.start_op();
373 auto ctx
= new FunctionContext([this, image_id
, deferment_end_time
](int r
) {
374 m_trash_listener
.handle_trash_image(image_id
, deferment_end_time
);
375 m_async_op_tracker
.finish_op();
377 m_threads
->work_queue
->queue(ctx
, 0);
380 } // namespace image_deleter;
381 } // namespace mirror
384 template class rbd::mirror::image_deleter::TrashWatcher
<librbd::ImageCtx
>;