1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "librbd/ObjectMap.h"
5 #include "librbd/BlockGuard.h"
6 #include "librbd/ExclusiveLock.h"
7 #include "librbd/ImageCtx.h"
8 #include "librbd/object_map/RefreshRequest.h"
9 #include "librbd/object_map/ResizeRequest.h"
10 #include "librbd/object_map/SnapshotCreateRequest.h"
11 #include "librbd/object_map/SnapshotRemoveRequest.h"
12 #include "librbd/object_map/SnapshotRollbackRequest.h"
13 #include "librbd/object_map/UnlockRequest.h"
14 #include "librbd/object_map/UpdateRequest.h"
15 #include "librbd/Utils.h"
16 #include "common/dout.h"
17 #include "common/errno.h"
18 #include "common/WorkQueue.h"
20 #include "include/rados/librados.hpp"
22 #include "cls/lock/cls_lock_client.h"
23 #include "cls/rbd/cls_rbd_types.h"
24 #include "include/stringify.h"
25 #include "osdc/Striper.h"
28 #define dout_subsys ceph_subsys_rbd
30 #define dout_prefix *_dout << "librbd::ObjectMap: " << this << " " << __func__ \
35 using librbd::util::create_context_callback
;
38 ObjectMap
<I
>::ObjectMap(I
&image_ctx
, uint64_t snap_id
)
39 : RefCountedObject(image_ctx
.cct
),
40 m_image_ctx(image_ctx
), m_snap_id(snap_id
),
41 m_lock(ceph::make_shared_mutex(util::unique_lock_name("librbd::ObjectMap::lock", this))),
42 m_update_guard(new UpdateGuard(m_image_ctx
.cct
)) {
46 ObjectMap
<I
>::~ObjectMap() {
47 delete m_update_guard
;
51 int ObjectMap
<I
>::aio_remove(librados::IoCtx
&io_ctx
, const std::string
&image_id
,
52 librados::AioCompletion
*c
) {
53 return io_ctx
.aio_remove(object_map_name(image_id
, CEPH_NOSNAP
), c
);
57 std::string ObjectMap
<I
>::object_map_name(const std::string
&image_id
,
59 std::string
oid(RBD_OBJECT_MAP_PREFIX
+ image_id
);
60 if (snap_id
!= CEPH_NOSNAP
) {
61 std::stringstream snap_suffix
;
62 snap_suffix
<< "." << std::setfill('0') << std::setw(16) << std::hex
64 oid
+= snap_suffix
.str();
70 bool ObjectMap
<I
>::is_compatible(const file_layout_t
& layout
, uint64_t size
) {
71 uint64_t object_count
= Striper::get_num_objects(layout
, size
);
72 return (object_count
<= cls::rbd::MAX_OBJECT_MAP_OBJECT_COUNT
);
76 uint8_t ObjectMap
<I
>::operator[](uint64_t object_no
) const
78 std::shared_lock locker
{m_lock
};
79 ceph_assert(object_no
< m_object_map
.size());
80 return m_object_map
[object_no
];
84 bool ObjectMap
<I
>::object_may_exist(uint64_t object_no
) const
86 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.image_lock
));
88 // Fall back to default logic if object map is disabled or invalid
89 if (!m_image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
,
90 m_image_ctx
.image_lock
)) {
95 int r
= m_image_ctx
.test_flags(m_image_ctx
.snap_id
,
96 RBD_FLAG_OBJECT_MAP_INVALID
,
97 m_image_ctx
.image_lock
, &flags_set
);
98 if (r
< 0 || flags_set
) {
102 uint8_t state
= (*this)[object_no
];
103 bool exists
= (state
!= OBJECT_NONEXISTENT
);
104 ldout(m_image_ctx
.cct
, 20) << "object_no=" << object_no
<< " r=" << exists
109 template <typename I
>
110 bool ObjectMap
<I
>::object_may_not_exist(uint64_t object_no
) const
112 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.image_lock
));
114 // Fall back to default logic if object map is disabled or invalid
115 if (!m_image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
,
116 m_image_ctx
.image_lock
)) {
121 int r
= m_image_ctx
.test_flags(m_image_ctx
.snap_id
,
122 RBD_FLAG_OBJECT_MAP_INVALID
,
123 m_image_ctx
.image_lock
, &flags_set
);
124 if (r
< 0 || flags_set
) {
128 uint8_t state
= (*this)[object_no
];
129 bool nonexistent
= (state
!= OBJECT_EXISTS
&& state
!= OBJECT_EXISTS_CLEAN
);
130 ldout(m_image_ctx
.cct
, 20) << "object_no=" << object_no
<< " r="
131 << nonexistent
<< dendl
;
135 template <typename I
>
136 bool ObjectMap
<I
>::update_required(const ceph::BitVector
<2>::Iterator
& it
,
138 ceph_assert(ceph_mutex_is_locked(m_lock
));
140 if ((state
== new_state
) ||
141 (new_state
== OBJECT_PENDING
&& state
== OBJECT_NONEXISTENT
) ||
142 (new_state
== OBJECT_NONEXISTENT
&& state
!= OBJECT_PENDING
)) {
148 template <typename I
>
149 void ObjectMap
<I
>::open(Context
*on_finish
) {
150 Context
*ctx
= create_context_callback
<Context
>(on_finish
, this);
152 auto req
= object_map::RefreshRequest
<I
>::create(
153 m_image_ctx
, &m_lock
, &m_object_map
, m_snap_id
, ctx
);
157 template <typename I
>
158 void ObjectMap
<I
>::close(Context
*on_finish
) {
159 Context
*ctx
= create_context_callback
<Context
>(on_finish
, this);
161 if (m_snap_id
!= CEPH_NOSNAP
) {
162 m_image_ctx
.op_work_queue
->queue(ctx
, 0);
166 ctx
= new LambdaContext([this, ctx
](int r
) {
167 auto req
= object_map::UnlockRequest
<I
>::create(m_image_ctx
, ctx
);
171 // ensure the block guard for aio updates is empty before unlocking
173 m_async_op_tracker
.wait_for_ops(ctx
);
176 template <typename I
>
177 bool ObjectMap
<I
>::set_object_map(ceph::BitVector
<2> &target_object_map
) {
178 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.owner_lock
));
179 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.image_lock
));
180 ceph_assert(m_image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
,
181 m_image_ctx
.image_lock
));
182 std::unique_lock locker
{m_lock
};
183 m_object_map
= target_object_map
;
187 template <typename I
>
188 void ObjectMap
<I
>::rollback(uint64_t snap_id
, Context
*on_finish
) {
189 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.image_lock
));
191 std::unique_lock locker
{m_lock
};
192 Context
*ctx
= create_context_callback
<Context
>(on_finish
, this);
194 object_map::SnapshotRollbackRequest
*req
=
195 new object_map::SnapshotRollbackRequest(m_image_ctx
, snap_id
, ctx
);
199 template <typename I
>
200 void ObjectMap
<I
>::snapshot_add(uint64_t snap_id
, Context
*on_finish
) {
201 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.image_lock
));
202 ceph_assert((m_image_ctx
.features
& RBD_FEATURE_OBJECT_MAP
) != 0);
203 ceph_assert(snap_id
!= CEPH_NOSNAP
);
205 Context
*ctx
= create_context_callback
<Context
>(on_finish
, this);
207 object_map::SnapshotCreateRequest
*req
=
208 new object_map::SnapshotCreateRequest(m_image_ctx
, &m_lock
, &m_object_map
,
213 template <typename I
>
214 void ObjectMap
<I
>::snapshot_remove(uint64_t snap_id
, Context
*on_finish
) {
215 ceph_assert(ceph_mutex_is_wlocked(m_image_ctx
.image_lock
));
216 ceph_assert((m_image_ctx
.features
& RBD_FEATURE_OBJECT_MAP
) != 0);
217 ceph_assert(snap_id
!= CEPH_NOSNAP
);
219 Context
*ctx
= create_context_callback
<Context
>(on_finish
, this);
221 object_map::SnapshotRemoveRequest
*req
=
222 new object_map::SnapshotRemoveRequest(m_image_ctx
, &m_lock
, &m_object_map
,
227 template <typename I
>
228 void ObjectMap
<I
>::aio_save(Context
*on_finish
) {
229 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.owner_lock
));
230 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.image_lock
));
231 ceph_assert(m_image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
,
232 m_image_ctx
.image_lock
));
233 std::shared_lock locker
{m_lock
};
235 librados::ObjectWriteOperation op
;
236 if (m_snap_id
== CEPH_NOSNAP
) {
237 rados::cls::lock::assert_locked(&op
, RBD_LOCK_NAME
, LOCK_EXCLUSIVE
, "", "");
239 cls_client::object_map_save(&op
, m_object_map
);
241 Context
*ctx
= create_context_callback
<Context
>(on_finish
, this);
243 std::string
oid(object_map_name(m_image_ctx
.id
, m_snap_id
));
244 librados::AioCompletion
*comp
= util::create_rados_callback(ctx
);
246 int r
= m_image_ctx
.md_ctx
.aio_operate(oid
, comp
, &op
);
251 template <typename I
>
252 void ObjectMap
<I
>::aio_resize(uint64_t new_size
, uint8_t default_object_state
,
253 Context
*on_finish
) {
254 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.owner_lock
));
255 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.image_lock
));
256 ceph_assert(m_image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
,
257 m_image_ctx
.image_lock
));
258 ceph_assert(m_image_ctx
.image_watcher
!= NULL
);
259 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
260 m_image_ctx
.exclusive_lock
->is_lock_owner());
262 Context
*ctx
= create_context_callback
<Context
>(on_finish
, this);
264 object_map::ResizeRequest
*req
= new object_map::ResizeRequest(
265 m_image_ctx
, &m_lock
, &m_object_map
, m_snap_id
, new_size
,
266 default_object_state
, ctx
);
270 template <typename I
>
271 void ObjectMap
<I
>::detained_aio_update(UpdateOperation
&&op
) {
272 CephContext
*cct
= m_image_ctx
.cct
;
273 ldout(cct
, 20) << dendl
;
275 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.image_lock
));
276 ceph_assert(ceph_mutex_is_wlocked(m_lock
));
278 BlockGuardCell
*cell
;
279 int r
= m_update_guard
->detain({op
.start_object_no
, op
.end_object_no
},
282 lderr(cct
) << "failed to detain object map update: " << cpp_strerror(r
)
284 m_image_ctx
.op_work_queue
->queue(op
.on_finish
, r
);
285 m_async_op_tracker
.finish_op();
288 ldout(cct
, 20) << "detaining object map update due to in-flight update: "
289 << "start=" << op
.start_object_no
<< ", "
290 << "end=" << op
.end_object_no
<< ", "
291 << (op
.current_state
?
292 stringify(static_cast<uint32_t>(*op
.current_state
)) :
294 << "->" << static_cast<uint32_t>(op
.new_state
) << dendl
;
298 ldout(cct
, 20) << "in-flight update cell: " << cell
<< dendl
;
299 Context
*on_finish
= op
.on_finish
;
300 Context
*ctx
= new LambdaContext([this, cell
, on_finish
](int r
) {
301 handle_detained_aio_update(cell
, r
, on_finish
);
303 aio_update(CEPH_NOSNAP
, op
.start_object_no
, op
.end_object_no
, op
.new_state
,
304 op
.current_state
, op
.parent_trace
, op
.ignore_enoent
, ctx
);
307 template <typename I
>
308 void ObjectMap
<I
>::handle_detained_aio_update(BlockGuardCell
*cell
, int r
,
309 Context
*on_finish
) {
310 CephContext
*cct
= m_image_ctx
.cct
;
311 ldout(cct
, 20) << "cell=" << cell
<< ", r=" << r
<< dendl
;
313 typename
UpdateGuard::BlockOperations block_ops
;
314 m_update_guard
->release(cell
, &block_ops
);
317 std::shared_lock image_locker
{m_image_ctx
.image_lock
};
318 std::unique_lock locker
{m_lock
};
319 for (auto &op
: block_ops
) {
320 detained_aio_update(std::move(op
));
324 on_finish
->complete(r
);
325 m_async_op_tracker
.finish_op();
328 template <typename I
>
329 void ObjectMap
<I
>::aio_update(uint64_t snap_id
, uint64_t start_object_no
,
330 uint64_t end_object_no
, uint8_t new_state
,
331 const boost::optional
<uint8_t> ¤t_state
,
332 const ZTracer::Trace
&parent_trace
,
333 bool ignore_enoent
, Context
*on_finish
) {
334 ceph_assert(ceph_mutex_is_locked(m_image_ctx
.image_lock
));
335 ceph_assert((m_image_ctx
.features
& RBD_FEATURE_OBJECT_MAP
) != 0);
336 ceph_assert(m_image_ctx
.image_watcher
!= nullptr);
337 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
338 m_image_ctx
.exclusive_lock
->is_lock_owner());
339 ceph_assert(start_object_no
< end_object_no
);
341 CephContext
*cct
= m_image_ctx
.cct
;
342 ldout(cct
, 20) << "start=" << start_object_no
<< ", "
343 << "end=" << end_object_no
<< ", "
345 stringify(static_cast<uint32_t>(*current_state
)) : "")
346 << "->" << static_cast<uint32_t>(new_state
) << dendl
;
347 if (snap_id
== CEPH_NOSNAP
) {
348 ceph_assert(ceph_mutex_is_wlocked(m_lock
));
349 end_object_no
= std::min(end_object_no
, m_object_map
.size());
350 if (start_object_no
>= end_object_no
) {
351 ldout(cct
, 20) << "skipping update of invalid object map" << dendl
;
352 m_image_ctx
.op_work_queue
->queue(on_finish
, 0);
356 auto it
= m_object_map
.begin() + start_object_no
;
357 auto end_it
= m_object_map
.begin() + end_object_no
;
358 for (; it
!= end_it
; ++it
) {
359 if (update_required(it
, new_state
)) {
364 ldout(cct
, 20) << "object map update not required" << dendl
;
365 m_image_ctx
.op_work_queue
->queue(on_finish
, 0);
370 auto req
= object_map::UpdateRequest
<I
>::create(
371 m_image_ctx
, &m_lock
, &m_object_map
, snap_id
, start_object_no
,
372 end_object_no
, new_state
, current_state
, parent_trace
, ignore_enoent
,
377 } // namespace librbd
379 template class librbd::ObjectMap
<librbd::ImageCtx
>;