#include <string_view>
#include "crimson/os/seastore/logging.h"
+#include "crimson/common/config_proxy.h"
// included for get_extent_by_type
#include "crimson/os/seastore/collection_manager/collection_flat_node.h"
Cache::Cache(
ExtentReader &reader)
- : reader(reader)
+ : reader(reader),
+ lru(crimson::common::get_conf<Option::size_t>(
+ "seastore_cache_lru_size"))
{
register_metrics();
}
Cache::~Cache()
{
LOG_PREFIX(Cache::~Cache);
+ lru.clear();
for (auto &i: extents) {
ERROR("extent {} still alive", i);
}
stats.dirty_bytes,
sm::description("total bytes of dirty extents")
),
+ sm::make_counter(
+ "cache_lru_size_bytes",
+ [this] {
+ return lru.get_current_contents_bytes();
+ },
+ sm::description("total bytes pinned by the lru")
+ ),
+ sm::make_counter(
+ "cache_lru_size_extents",
+ [this] {
+ return lru.get_current_contents_extents();
+ },
+ sm::description("total extents pinned by the lru")
+ ),
}
);
if (ref->is_dirty()) {
add_to_dirty(ref);
- } else {
- ceph_assert(!ref->primary_ref_list_hook.is_linked());
- }
+ } else if (!ref->is_placeholder()) {
+ lru.add_to_lru(*ref);
+ }
DEBUG("extent {}", *ref);
}
return;
}
+ lru.remove_from_lru(*ref);
add_to_dirty(ref);
ref->state = CachedExtent::extent_state_t::DIRTY;
LOG_PREFIX(Cache::remove_extent);
DEBUG("extent {}", *ref);
assert(ref->is_valid());
- remove_from_dirty(ref);
+ if (ref->is_dirty()) {
+ remove_from_dirty(ref);
+ } else {
+ lru.remove_from_lru(*ref);
+ }
extents.erase(*ref);
}
DEBUGT("extent {}", t, *ref);
assert(ref->is_valid());
- remove_from_dirty(ref);
+ // TODO: why does this duplicate remove_extent?
+ if (ref->is_dirty()) {
+ remove_from_dirty(ref);
+ } else {
+ lru.remove_from_lru(*ref);
+ }
ref->dirty_from_or_retired_at = JOURNAL_SEQ_MAX;
invalidate_extent(t, *ref);
intrusive_ptr_release(&*prev);
intrusive_ptr_add_ref(&*next);
} else {
+ lru.remove_from_lru(*prev);
add_to_dirty(next);
}
}
root = new RootBlock();
root->state = CachedExtent::extent_state_t::CLEAN;
- add_extent(root);
+ extents.insert(*root);
}
Cache::mkfs_iertr::future<> Cache::mkfs(Transaction &t)
intrusive_ptr_release(ptr);
}
assert(stats.dirty_bytes == 0);
+ clear_lru();
return close_ertr::now();
}
"Found extent at offset {} in cache: {}",
t, offset, *ret);
t.add_to_read_set(ret);
+ touch_extent(*ret);
return ret->wait_io().then([ret] {
return get_extent_if_cached_iertr::make_ready_future<
CachedExtentRef>(ret);
DEBUGT(
"Read extent at offset {} in cache: {}",
t, offset, *ref);
+ touch_extent(*ref);
t.add_to_read_set(ref);
return get_extent_iertr::make_ready_future<TCachedExtentRef<T>>(
std::move(ref));
mark_transaction_conflicted(t, *ret.get());
return get_extent_ertr::make_ready_future<CachedExtentRef>();
} else {
+ touch_extent(*ret);
t.add_to_read_set(ret);
return get_extent_ertr::make_ready_future<CachedExtentRef>(
std::move(ret));
return ret;
}
+ void clear_lru() {
+ lru.clear();
+ }
+
void mark_delayed_extent_inline(
Transaction& t,
LogicalCachedExtentRef& ref) {
*/
CachedExtent::list dirty;
+ /**
+ * lru
+ *
+ * holds references to recently used extents
+ */
+ class LRU {
+ // max size (bytes)
+ const size_t capacity = 0;
+
+ // current size (bytes)
+ size_t contents = 0;
+
+ CachedExtent::list lru;
+
+ void trim_to_capacity() {
+ while (contents > capacity) {
+ assert(lru.size() > 0);
+ remove_from_lru(lru.front());
+ }
+ }
+ public:
+ LRU(size_t capacity) : capacity(capacity) {}
+
+ size_t get_current_contents_bytes() const {
+ return contents;
+ }
+
+ size_t get_current_contents_extents() const {
+ return lru.size();
+ }
+
+ void add_to_lru(CachedExtent &extent) {
+ assert(
+ extent.is_clean() &&
+ !extent.is_pending() &&
+ !extent.is_placeholder());
+
+ if (!extent.primary_ref_list_hook.is_linked()) {
+ contents += extent.get_length();
+ intrusive_ptr_add_ref(&extent);
+ lru.push_back(extent);
+ }
+ }
+
+ void remove_from_lru(CachedExtent &extent) {
+ assert(extent.is_clean());
+ assert(!extent.is_pending());
+ assert(!extent.is_placeholder());
+
+ if (extent.primary_ref_list_hook.is_linked()) {
+ lru.erase(lru.s_iterator_to(extent));
+ assert(contents >= extent.get_length());
+ contents -= extent.get_length();
+ intrusive_ptr_release(&extent);
+ }
+ }
+
+ void move_to_top(CachedExtent &extent) {
+ assert(
+ extent.is_clean() &&
+ !extent.is_pending() &&
+ !extent.is_placeholder());
+
+ if (extent.primary_ref_list_hook.is_linked()) {
+ lru.erase(lru.s_iterator_to(extent));
+ intrusive_ptr_release(&extent);
+ assert(contents >= extent.get_length());
+ contents -= extent.get_length();
+ }
+ add_to_lru(extent);
+ }
+
+ void clear() {
+ LOG_PREFIX(Cache::LRU::clear);
+ for (auto iter = lru.begin(); iter != lru.end();) {
+ DEBUG("clearing {}", *iter);
+ remove_from_lru(*(iter++));
+ }
+ }
+
+ ~LRU() {
+ assert(lru.empty());
+ }
+ } lru;
+
struct query_counters_t {
uint64_t access = 0;
uint64_t hit = 0;
return bp;
}
+ /// Update lru for access to ref
+ void touch_extent(CachedExtent &ext) {
+ assert(!ext.is_pending());
+ if (ext.is_clean() && !ext.is_placeholder()) {
+ lru.move_to_top(ext);
+ }
+ }
+
/// Add extent to extents handling dirty and refcounting
void add_extent(CachedExtentRef ref);