1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "librbd/cache/ObjectCacherObjectDispatch.h"
5 #include "common/errno.h"
6 #include "common/WorkQueue.h"
7 #include "librbd/ImageCtx.h"
8 #include "librbd/Journal.h"
9 #include "librbd/Utils.h"
10 #include "librbd/LibrbdWriteback.h"
11 #include "librbd/io/ObjectDispatchSpec.h"
12 #include "librbd/io/ObjectDispatcher.h"
13 #include "librbd/io/Utils.h"
14 #include "osd/osd_types.h"
15 #include "osdc/WritebackHandler.h"
18 #define dout_subsys ceph_subsys_rbd
20 #define dout_prefix *_dout << "librbd::cache::ObjectCacherObjectDispatch: " \
21 << this << " " << __func__ << ": "
28 typedef std::vector
<ObjectExtent
> ObjectExtents
;
30 } // anonymous namespace
33 struct ObjectCacherObjectDispatch
<I
>::C_InvalidateCache
: public Context
{
34 ObjectCacherObjectDispatch
* dispatcher
;
38 C_InvalidateCache(ObjectCacherObjectDispatch
* dispatcher
,
39 bool purge_on_error
, Context
*on_finish
)
40 : dispatcher(dispatcher
), purge_on_error(purge_on_error
),
41 on_finish(on_finish
) {
44 void finish(int r
) override
{
45 ceph_assert(dispatcher
->m_cache_lock
.is_locked());
46 auto cct
= dispatcher
->m_image_ctx
->cct
;
48 if (r
== -EBLACKLISTED
) {
49 lderr(cct
) << "blacklisted during flush (purging)" << dendl
;
50 dispatcher
->m_object_cacher
->purge_set(dispatcher
->m_object_set
);
51 } else if (r
< 0 && purge_on_error
) {
52 lderr(cct
) << "failed to invalidate cache (purging): "
53 << cpp_strerror(r
) << dendl
;
54 dispatcher
->m_object_cacher
->purge_set(dispatcher
->m_object_set
);
56 lderr(cct
) << "failed to invalidate cache: " << cpp_strerror(r
) << dendl
;
59 auto unclean
= dispatcher
->m_object_cacher
->release_set(
60 dispatcher
->m_object_set
);
64 lderr(cct
) << "could not release all objects from cache: "
65 << unclean
<< " bytes remain" << dendl
;
71 on_finish
->complete(r
);
76 ObjectCacherObjectDispatch
<I
>::ObjectCacherObjectDispatch(
78 : m_image_ctx(image_ctx
),
79 m_cache_lock(util::unique_lock_name(
80 "librbd::cache::ObjectCacherObjectDispatch::cache_lock", this)) {
84 ObjectCacherObjectDispatch
<I
>::~ObjectCacherObjectDispatch() {
85 delete m_object_cacher
;
88 delete m_writeback_handler
;
92 void ObjectCacherObjectDispatch
<I
>::init() {
93 auto cct
= m_image_ctx
->cct
;
94 ldout(cct
, 5) << dendl
;
97 ldout(cct
, 5) << "enabling caching..." << dendl
;
98 m_writeback_handler
= new LibrbdWriteback(m_image_ctx
, m_cache_lock
);
100 uint64_t init_max_dirty
= m_image_ctx
->cache_max_dirty
;
101 if (m_image_ctx
->cache_writethrough_until_flush
) {
106 m_image_ctx
->config
.template get_val
<Option::size_t>("rbd_cache_size");
108 m_image_ctx
->config
.template get_val
<Option::size_t>("rbd_cache_target_dirty");
110 m_image_ctx
->config
.template get_val
<double>("rbd_cache_max_dirty_age");
111 auto block_writes_upfront
=
112 m_image_ctx
->config
.template get_val
<bool>("rbd_cache_block_writes_upfront");
113 auto max_dirty_object
=
114 m_image_ctx
->config
.template get_val
<uint64_t>("rbd_cache_max_dirty_object");
116 ldout(cct
, 5) << "Initial cache settings:"
117 << " size=" << cache_size
118 << " num_objects=" << 10
119 << " max_dirty=" << init_max_dirty
120 << " target_dirty=" << target_dirty
121 << " max_dirty_age=" << max_dirty_age
<< dendl
;
123 m_object_cacher
= new ObjectCacher(cct
, m_image_ctx
->perfcounter
->get_name(),
124 *m_writeback_handler
, m_cache_lock
,
125 nullptr, nullptr, cache_size
,
126 10, /* reset this in init */
127 init_max_dirty
, target_dirty
,
128 max_dirty_age
, block_writes_upfront
);
130 // size object cache appropriately
131 if (max_dirty_object
== 0) {
132 max_dirty_object
= std::min
<uint64_t>(
133 2000, std::max
<uint64_t>(10, cache_size
/ 100 /
134 sizeof(ObjectCacher::Object
)));
136 ldout(cct
, 5) << " cache bytes " << cache_size
137 << " -> about " << max_dirty_object
<< " objects" << dendl
;
138 m_object_cacher
->set_max_objects(max_dirty_object
);
140 m_object_set
= new ObjectCacher::ObjectSet(nullptr,
141 m_image_ctx
->data_ctx
.get_id(), 0);
142 m_object_cacher
->start();
143 m_cache_lock
.Unlock();
145 // add ourself to the IO object dispatcher chain
146 m_image_ctx
->io_object_dispatcher
->register_object_dispatch(this);
149 template <typename I
>
150 void ObjectCacherObjectDispatch
<I
>::shut_down(Context
* on_finish
) {
151 auto cct
= m_image_ctx
->cct
;
152 ldout(cct
, 5) << dendl
;
154 // chain shut down in reverse order
156 // shut down the cache
157 on_finish
= new FunctionContext([this, on_finish
](int r
) {
158 m_object_cacher
->stop();
159 on_finish
->complete(r
);
162 // ensure we aren't holding the cache lock post-flush
163 on_finish
= util::create_async_context_callback(*m_image_ctx
, on_finish
);
165 // invalidate any remaining cache entries
166 on_finish
= new C_InvalidateCache(this, true, on_finish
);
168 // flush all pending writeback state
170 m_object_cacher
->release_set(m_object_set
);
171 m_object_cacher
->flush_set(m_object_set
, on_finish
);
172 m_cache_lock
.Unlock();
175 template <typename I
>
176 bool ObjectCacherObjectDispatch
<I
>::read(
177 const std::string
&oid
, uint64_t object_no
, uint64_t object_off
,
178 uint64_t object_len
, librados::snap_t snap_id
, int op_flags
,
179 const ZTracer::Trace
&parent_trace
, ceph::bufferlist
* read_data
,
180 io::ExtentMap
* extent_map
, int* object_dispatch_flags
,
181 io::DispatchResult
* dispatch_result
, Context
** on_finish
,
182 Context
* on_dispatched
) {
183 // IO chained in reverse order
184 auto cct
= m_image_ctx
->cct
;
185 ldout(cct
, 20) << "object_no=" << object_no
<< " " << object_off
<< "~"
186 << object_len
<< dendl
;
188 // ensure we aren't holding the cache lock post-read
189 on_dispatched
= util::create_async_context_callback(*m_image_ctx
,
192 m_image_ctx
->snap_lock
.get_read();
193 auto rd
= m_object_cacher
->prepare_read(snap_id
, read_data
, op_flags
);
194 m_image_ctx
->snap_lock
.put_read();
196 ObjectExtent
extent(oid
, object_no
, object_off
, object_len
, 0);
197 extent
.oloc
.pool
= m_image_ctx
->data_ctx
.get_id();
198 extent
.buffer_extents
.push_back({0, object_len
});
199 rd
->extents
.push_back(extent
);
201 ZTracer::Trace
trace(parent_trace
);
202 *dispatch_result
= io::DISPATCH_RESULT_COMPLETE
;
205 int r
= m_object_cacher
->readx(rd
, m_object_set
, on_dispatched
, &trace
);
206 m_cache_lock
.Unlock();
208 on_dispatched
->complete(r
);
213 template <typename I
>
214 bool ObjectCacherObjectDispatch
<I
>::discard(
215 const std::string
&oid
, uint64_t object_no
, uint64_t object_off
,
216 uint64_t object_len
, const ::SnapContext
&snapc
, int discard_flags
,
217 const ZTracer::Trace
&parent_trace
, int* object_dispatch_flags
,
218 uint64_t* journal_tid
, io::DispatchResult
* dispatch_result
,
219 Context
** on_finish
, Context
* on_dispatched
) {
220 auto cct
= m_image_ctx
->cct
;
221 ldout(cct
, 20) << "object_no=" << object_no
<< " " << object_off
<< "~"
222 << object_len
<< dendl
;
224 ObjectExtents object_extents
;
225 object_extents
.emplace_back(oid
, object_no
, object_off
, object_len
, 0);
227 // discard the cache state after changes are committed to disk (and to
228 // prevent races w/ readahead)
229 auto ctx
= *on_finish
;
230 *on_finish
= new FunctionContext(
231 [this, object_extents
, ctx
](int r
) {
233 m_object_cacher
->discard_set(m_object_set
, object_extents
);
234 m_cache_lock
.Unlock();
239 // ensure we aren't holding the cache lock post-write
240 on_dispatched
= util::create_async_context_callback(*m_image_ctx
,
243 *dispatch_result
= io::DISPATCH_RESULT_CONTINUE
;
245 // ensure any in-flight writeback is complete before advancing
246 // the discard request
248 m_object_cacher
->discard_writeback(m_object_set
, object_extents
,
250 m_cache_lock
.Unlock();
254 template <typename I
>
255 bool ObjectCacherObjectDispatch
<I
>::write(
256 const std::string
&oid
, uint64_t object_no
, uint64_t object_off
,
257 ceph::bufferlist
&& data
, const ::SnapContext
&snapc
, int op_flags
,
258 const ZTracer::Trace
&parent_trace
, int* object_dispatch_flags
,
259 uint64_t* journal_tid
, io::DispatchResult
* dispatch_result
,
260 Context
** on_finish
, Context
* on_dispatched
) {
261 auto cct
= m_image_ctx
->cct
;
262 ldout(cct
, 20) << "object_no=" << object_no
<< " " << object_off
<< "~"
263 << data
.length() << dendl
;
265 // ensure we aren't holding the cache lock post-write
266 on_dispatched
= util::create_async_context_callback(*m_image_ctx
,
269 m_image_ctx
->snap_lock
.get_read();
270 ObjectCacher::OSDWrite
*wr
= m_object_cacher
->prepare_write(
271 snapc
, data
, ceph::real_time::min(), op_flags
, *journal_tid
);
272 m_image_ctx
->snap_lock
.put_read();
274 ObjectExtent
extent(oid
, 0, object_off
, data
.length(), 0);
275 extent
.oloc
.pool
= m_image_ctx
->data_ctx
.get_id();
276 extent
.buffer_extents
.push_back({0, data
.length()});
277 wr
->extents
.push_back(extent
);
279 ZTracer::Trace
trace(parent_trace
);
280 *dispatch_result
= io::DISPATCH_RESULT_COMPLETE
;
283 m_object_cacher
->writex(wr
, m_object_set
, on_dispatched
, &trace
);
284 m_cache_lock
.Unlock();
288 template <typename I
>
289 bool ObjectCacherObjectDispatch
<I
>::write_same(
290 const std::string
&oid
, uint64_t object_no
, uint64_t object_off
,
291 uint64_t object_len
, io::Extents
&& buffer_extents
, ceph::bufferlist
&& data
,
292 const ::SnapContext
&snapc
, int op_flags
,
293 const ZTracer::Trace
&parent_trace
, int* object_dispatch_flags
,
294 uint64_t* journal_tid
, io::DispatchResult
* dispatch_result
,
295 Context
** on_finish
, Context
* on_dispatched
) {
296 auto cct
= m_image_ctx
->cct
;
297 ldout(cct
, 20) << "object_no=" << object_no
<< " " << object_off
<< "~"
298 << object_len
<< dendl
;
300 // ObjectCacher doesn't support write-same so convert to regular write
301 ObjectExtent
extent(oid
, 0, object_off
, object_len
, 0);
302 extent
.buffer_extents
= std::move(buffer_extents
);
305 io::util::assemble_write_same_extent(extent
, data
, &ws_data
, true);
307 return write(oid
, object_no
, object_off
, std::move(ws_data
), snapc
,
308 op_flags
, parent_trace
, object_dispatch_flags
, journal_tid
,
309 dispatch_result
, on_finish
, on_dispatched
);
312 template <typename I
>
313 bool ObjectCacherObjectDispatch
<I
>::compare_and_write(
314 const std::string
&oid
, uint64_t object_no
, uint64_t object_off
,
315 ceph::bufferlist
&& cmp_data
, ceph::bufferlist
&& write_data
,
316 const ::SnapContext
&snapc
, int op_flags
,
317 const ZTracer::Trace
&parent_trace
, uint64_t* mismatch_offset
,
318 int* object_dispatch_flags
, uint64_t* journal_tid
,
319 io::DispatchResult
* dispatch_result
, Context
** on_finish
,
320 Context
* on_dispatched
) {
321 auto cct
= m_image_ctx
->cct
;
322 ldout(cct
, 20) << "object_no=" << object_no
<< " " << object_off
<< "~"
323 << cmp_data
.length() << dendl
;
325 // pass-through the compare-and-write request since it's not a supported
326 // operation of the ObjectCacher
328 // ensure we aren't holding the cache lock post-flush
329 on_dispatched
= util::create_async_context_callback(*m_image_ctx
,
332 // flush any pending writes from the cache
333 ZTracer::Trace
trace(parent_trace
);
334 *dispatch_result
= io::DISPATCH_RESULT_CONTINUE
;
336 ObjectExtents object_extents
;
337 object_extents
.emplace_back(oid
, object_no
, object_off
, cmp_data
.length(),
340 Mutex::Locker
cache_locker(m_cache_lock
);
341 m_object_cacher
->flush_set(m_object_set
, object_extents
, &trace
,
346 template <typename I
>
347 bool ObjectCacherObjectDispatch
<I
>::flush(
348 io::FlushSource flush_source
, const ZTracer::Trace
&parent_trace
,
349 io::DispatchResult
* dispatch_result
, Context
** on_finish
,
350 Context
* on_dispatched
) {
351 auto cct
= m_image_ctx
->cct
;
352 ldout(cct
, 20) << dendl
;
354 // ensure we aren't holding the cache lock post-flush
355 on_dispatched
= util::create_async_context_callback(*m_image_ctx
,
359 if (flush_source
== io::FLUSH_SOURCE_USER
&& !m_user_flushed
&&
360 m_image_ctx
->cache_writethrough_until_flush
&&
361 m_image_ctx
->cache_max_dirty
> 0) {
362 m_user_flushed
= true;
363 m_object_cacher
->set_max_dirty(m_image_ctx
->cache_max_dirty
);
364 ldout(cct
, 5) << "saw first user flush, enabling writeback" << dendl
;
367 *dispatch_result
= io::DISPATCH_RESULT_CONTINUE
;
368 m_object_cacher
->flush_set(m_object_set
, on_dispatched
);
369 m_cache_lock
.Unlock();
373 template <typename I
>
374 bool ObjectCacherObjectDispatch
<I
>::invalidate_cache(Context
* on_finish
) {
375 auto cct
= m_image_ctx
->cct
;
376 ldout(cct
, 5) << dendl
;
378 // ensure we aren't holding the cache lock post-flush
379 on_finish
= util::create_async_context_callback(*m_image_ctx
, on_finish
);
381 // invalidate any remaining cache entries
382 on_finish
= new C_InvalidateCache(this, false, on_finish
);
385 m_object_cacher
->release_set(m_object_set
);
386 m_object_cacher
->flush_set(m_object_set
, on_finish
);
387 m_cache_lock
.Unlock();
391 template <typename I
>
392 bool ObjectCacherObjectDispatch
<I
>::reset_existence_cache(
393 Context
* on_finish
) {
394 auto cct
= m_image_ctx
->cct
;
395 ldout(cct
, 5) << dendl
;
398 m_object_cacher
->clear_nonexistence(m_object_set
);
399 m_cache_lock
.Unlock();
405 } // namespace librbd
407 template class librbd::cache::ObjectCacherObjectDispatch
<librbd::ImageCtx
>;