i->state = CachedExtent::extent_state_t::CLEAN;
assert(i->is_logical());
i->clear_modified_region();
- touch_extent(*i, &trans_src, t.get_cache_hint());
+ touch_extent_fully(*i, &trans_src, t.get_cache_hint());
DEBUGT("inplace rewrite ool block is commmitted -- {}", t, *i);
}
if (i->is_stable_dirty()) {
add_to_dirty(i, &t_src);
} else {
- touch_extent(*i, &t_src, t.get_cache_hint());
+ touch_extent_fully(*i, &t_src, t.get_cache_hint());
}
alloc_delta.alloc_blk_ranges.emplace_back(
i->invalidate_hints();
add_extent(i);
const auto t_src = t.get_src();
- touch_extent(*i, &t_src, t.get_cache_hint());
+ touch_extent_fully(*i, &t_src, t.get_cache_hint());
i->complete_io();
epm.commit_space_used(i->get_paddr(), i->get_length());
ext.cast<LogicalCachedExtent>()->set_laddr(laddr);
}
// replay is not included by the cache hit metrics
- touch_extent(ext, nullptr, CACHE_HINT_TOUCH);
+ touch_extent_fully(ext, nullptr, CACHE_HINT_TOUCH);
},
nullptr) :
_get_extent_if_cached(
return ret->wait_io().then([ret] {
return get_extent_if_cached_iertr::make_ready_future<CachedExtentRef>(ret);
});
- }
+ } // result == Transaction::get_extent_ret::ABSENT
assert(paddr.is_absolute());
// get_extent_ret::ABSENT from transaction
}
t.add_to_read_set(ret);
- touch_extent(*ret, &t_src, t.get_cache_hint());
+ touch_extent_fully(*ret, &t_src, t.get_cache_hint());
if (!ret->is_fully_loaded()) {
SUBDEBUGT(seastore_cache,
"{} {}~0x{:x} is present without fully loaded in cache -- {}",
t, T::TYPE, offset, length);
auto f = [&t, this, t_src](CachedExtent &ext) {
t.add_to_read_set(CachedExtentRef(&ext));
- touch_extent(ext, &t_src, t.get_cache_hint());
+ touch_extent_fully(ext, &t_src, t.get_cache_hint());
};
return trans_intr::make_interruptible(
do_get_caching_extent<T>(
SUBTRACET(seastore_cache, "{} {}~0x{:x} is absent on t, query cache ...",
t, T::TYPE, offset, length);
const auto t_src = t.get_src();
- auto f = [&t, this, t_src](CachedExtent &ext) {
+ auto f = [&t, this, partial_off, partial_len, t_src](CachedExtent &ext) {
// XXX: is_stable_dirty() may not be linked in lba tree
assert(ext.is_stable());
assert(T::TYPE == ext.get_type());
++stats.access.load_absent;
t.add_to_read_set(CachedExtentRef(&ext));
- touch_extent(ext, &t_src, t.get_cache_hint());
+ touch_extent_by_range(
+ ext, &t_src, t.get_cache_hint(),
+ partial_off, partial_len);
};
return trans_intr::make_interruptible(
do_get_caching_extent<T>(
++stats.access.cache_lru;
}
if (ret.is_paddr_known) {
- touch_extent(extent, &t_src, t.get_cache_hint());
+ touch_extent_fully(extent, &t_src, t.get_cache_hint());
} else {
needs_touch = true;
}
t.maybe_add_to_read_set_step_2(target_extent.get());
}
if (needs_touch) {
- touch_extent(*target_extent, &t_src, t.get_cache_hint());
+ touch_extent_fully(*target_extent, &t_src, t.get_cache_hint());
}
return get_extent_iertr::now();
});
++stats.access.cache_lru;
}
if (ret.is_paddr_known) {
- touch_extent(*p_extent, &t_src, t.get_cache_hint());
+ touch_extent_fully(*p_extent, &t_src, t.get_cache_hint());
} else {
needs_touch = true;
}
t.maybe_add_to_read_set_step_2(p_extent);
}
if (needs_touch) {
- touch_extent(*p_extent, &t_src, t.get_cache_hint());
+ touch_extent_fully(*p_extent, &t_src, t.get_cache_hint());
}
return get_extent_iertr::make_ready_future<CachedExtentRef>(
CachedExtentRef(p_extent));
t, type, offset, length, laddr);
auto f = [&t, this, t_src](CachedExtent &ext) {
t.add_to_read_set(CachedExtentRef(&ext));
- touch_extent(ext, &t_src, t.get_cache_hint());
+ touch_extent_fully(ext, &t_src, t.get_cache_hint());
};
return trans_intr::make_interruptible(
do_get_caching_extent_by_type(
++stats.access.load_absent;
t.add_to_read_set(CachedExtentRef(&ext));
- touch_extent(ext, &t_src, t.get_cache_hint());
+ touch_extent_fully(ext, &t_src, t.get_cache_hint());
};
return trans_intr::make_interruptible(
do_get_caching_extent_by_type(
}
private:
- /// Update extent pinboard for access to ref
- void touch_extent(
+ void touch_extent_fully(
CachedExtent &ext,
const Transaction::src_t* p_src,
cache_hint_t hint)
+ {
+ touch_extent_by_range(ext, p_src, hint, 0, ext.get_length());
+ }
+
+ /// Update extent pinboard for access to ref
+ ///
+ /// The extent must be touched before read_extent(), otherwise it is
+ /// incorrect to detect sequential read using load_start and load_length.
+ void touch_extent_by_range(
+ CachedExtent &ext,
+ const Transaction::src_t* p_src,
+ cache_hint_t hint,
+ extent_len_t load_start,
+ extent_len_t load_length)
{
assert(ext.get_paddr().is_absolute());
if (hint == CACHE_HINT_NOCACHE && is_logical_type(ext.get_type())) {
return;
}
if (ext.is_stable_clean() && !ext.is_placeholder()) {
- pinboard->move_to_top(ext, p_src);
+ pinboard->move_to_top(ext, p_src, load_start, load_length);
}
}
cache_state = state;
}
+ extent_len_t get_last_touch_end() const {
+ return last_touch_end;
+ }
+
+ void set_last_touch_end(extent_len_t touch_end) {
+ assert(touch_end != 0);
+ last_touch_end = touch_end;
+ }
+
private:
template <typename T>
friend class read_set_item_t;
// or the rewrite generation for the fresh write
rewrite_gen_t rewrite_generation = NULL_GENERATION;
+ // save the end offset of the most recent of extent touching,
+ // see Cache::touch_extent_by_range() and ExtentPinboardTwoQ.
+ extent_len_t last_touch_end = 0;
+
// This field is unused when the ExtentPinboard use LRU algorithm
extent_2q_state_t cache_state = extent_2q_state_t::Fresh;
void move_to_top(
CachedExtent &extent,
- const Transaction::src_t* p_src) final {
+ const Transaction::src_t* p_src,
+ extent_len_t /*load_start*/,
+ extent_len_t /*load_length*/) final {
if (extent.is_linked_to_list()) {
lru.move_to_top(extent, p_src);
} else {
clear();
}
- bool accessed_recently(laddr_t laddr) {
+ enum class AccessMode {
+ Missing,
+ ContinueFromLastEnd,
+ Again
+ };
+
+ AccessMode accessed_recently(laddr_t laddr, extent_len_t load_start) {
auto iter = index.find(laddr);
if (iter == index.end()) {
- return false;
+ return AccessMode::Missing;
}
+ // Current read start offset is same as the last access end offset,
+ // we treat this access as sequential read.
+ auto &last_end = iter->second->last_access_end;
+ assert(last_end != 0);
+ auto ret = last_end == load_start
+ ? AccessMode::ContinueFromLastEnd
+ : AccessMode::Again;
remove(iter);
- return true;
+ return ret;
}
- void add(laddr_t laddr, extent_len_t loaded_length) {
+ void add(laddr_t laddr,
+ extent_len_t loaded_length,
+ extent_len_t access_end) {
assert(laddr != L_ADDR_NULL);
assert(loaded_length != 0);
+ assert(access_end != 0);
assert(!index.contains(laddr));
- index[laddr] = queue.emplace(queue.end(), laddr, loaded_length);
+ index[laddr] = queue.emplace(
+ queue.end(), laddr, loaded_length, access_end);
current_size += loaded_length;
trim_to(capacity);
}
private:
struct entry_t {
- entry_t(laddr_t laddr, extent_len_t loaded_length)
- : laddr(laddr), loaded_length(loaded_length) {}
+ entry_t(laddr_t laddr,
+ extent_len_t loaded_length,
+ extent_len_t access_end)
+ : laddr(laddr),
+ loaded_length(loaded_length),
+ last_access_end(access_end) {}
laddr_t laddr;
extent_len_t loaded_length;
+ extent_len_t last_access_end;
};
using entry_queue_t = std::list<entry_t>;
void move_to_top(
CachedExtent &extent,
- const Transaction::src_t* p_src) final {
+ const Transaction::src_t* p_src,
+ extent_len_t load_start,
+ extent_len_t load_length) final {
auto state = extent.get_2q_state();
auto type = extent.get_type();
if (extent.is_linked_to_list()) {
} else { // the logical extent which is not in warm_in and not in hot
ceph_assert(state == extent_2q_state_t::Fresh);
auto lext = extent.cast<LogicalCachedExtent>();
- if (warm_out.accessed_recently(lext->get_laddr())) {
+ auto m = warm_out.accessed_recently(lext->get_laddr(), load_start);
+ using AccessMode = IndexedFifoQueue::AccessMode;
+ if (m == AccessMode::Again) {
// This extent was accessed recently, consider it's hot enough to
// promote to hot queue.
extent.set_2q_state(extent_2q_state_t::Hot);
extent.set_2q_state(extent_2q_state_t::WarmIn);
auto trimmed_extents = warm_in.add_to_top(extent, p_src);
on_update_warm_in(trimmed_extents);
- hit_queue(overall_hits.absent, p_src, type);
+ if (m == AccessMode::Missing) {
+ hit_queue(overall_hits.absent, p_src, type);
+ } else { // m == AccessMode::ContinueFromLastEnd
+ hit_queue(overall_hits.sequential_absent, p_src, type);
+ }
}
}
+ auto end = load_start + load_length;
+ assert(end != 0);
+ // The last end update should be in the same continuation with touching
+ // extent, subsequent touch will cover the prior touch end.
+ extent.set_last_touch_end(end);
}
void increase_cached_size(
ceph_assert(is_logical_type(extent->get_type()));
extent->set_2q_state(extent_2q_state_t::Fresh);
auto lext = extent->cast<LogicalCachedExtent>();
+ auto laddr = lext->get_laddr();
auto len = extent->get_loaded_length();
+ auto end = extent->get_last_touch_end();
// the extents evicted from warm_in queue will be recorded
// in warm_out FIFO queue as recently accessed extents.
- warm_out.add(lext->get_laddr(), len);
+ warm_out.add(laddr, len, end);
}
}
// 2Q cache algorithm:
// Workflow:
// 1. New non-logical extents enter warm_in first, physical extents
// are placed into hot queue directly
- // 2. On warm_in eviction, add extent's metadata(laddr, loaded length)
- // to warm_out queue
- // 3. If accessed while in warm_out, extent promotes to hot queue
+ // 2. On warm_in eviction, add extent's metadata(laddr, loaded length and
+ // last access end) to warm_out queue
+ // 3. If the extent in warm_out is accessed again and the load start doesn't
+ // match the last accessed end recorded in the warm_out, promote extent
+ // to the hot queue, otherwise add add extent to warm_in queue
// 4. Hot queue manages extents using LRU algorithm
ExtentQueue warm_in;
IndexedFifoQueue warm_out;
QueueCounter hot_hits;
QueueCounter absent;
QueueCounter hot_absent;
+ QueueCounter sequential_absent;
};
mutable hit_stats_t overall_hits;
mutable hit_stats_t last_hits;
virtual void register_metrics() = 0;
virtual void move_to_top(
CachedExtent &extent,
- const Transaction::src_t *p_src) = 0;
+ const Transaction::src_t *p_src,
+ extent_len_t load_start,
+ extent_len_t load_length) = 0;
virtual void remove(CachedExtent &extent) = 0;
virtual void get_stats(
cache_stats_t &stats,