]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
crimson/os/seastore: move Cache::LRU out of Cache
authorZhang Song <zhangsong02@qianxin.com>
Wed, 18 Jun 2025 07:18:56 +0000 (15:18 +0800)
committerZhang Song <zhangsong02@qianxin.com>
Fri, 18 Jul 2025 03:53:27 +0000 (11:53 +0800)
Signed-off-by: Zhang Song <zhangsong02@qianxin.com>
src/common/options/crimson.yaml.in
src/crimson/os/seastore/CMakeLists.txt
src/crimson/os/seastore/cache.cc
src/crimson/os/seastore/cache.h
src/crimson/os/seastore/cached_extent.h
src/crimson/os/seastore/extent_pinboard.cc [new file with mode: 0644]
src/crimson/os/seastore/extent_pinboard.h [new file with mode: 0644]

index 5999758e22935be0c08885169efba460cc316a00..94676490635ec5f95d0609b8dde00a0dd993c9d1 100644 (file)
@@ -147,7 +147,7 @@ options:
   level: advanced
   desc: Max size in bytes that an extent can be, 0 to disable
   default: 0
-- name: seastore_cache_lru_size
+- name: seastore_cachepin_size_pershard
   type: size
   level: advanced
   desc: Size in bytes of extents to keep in cache (per reactor).
index d5ca12d8f0670dd7638bd075e9f63482bb4737cc..97eae1d575cda1952c868c5568725bdfcfdece2d 100644 (file)
@@ -9,6 +9,7 @@ set(crimson_seastore_srcs
   transaction_manager.cc
   transaction.cc
   cache.cc
+  extent_pinboard.cc
   root_block.cc
   lba_manager.cc
   async_cleaner.cc
index a639d83a78bdb64eebb3a7d5c3b8d9bfc40ea510..1e1abdc41fb3a3658093e7aaa08f7a2ecf938293 100644 (file)
@@ -31,11 +31,10 @@ namespace crimson::os::seastore {
 Cache::Cache(
   ExtentPlacementManager &epm)
   : epm(epm),
-    lru(crimson::common::get_conf<Option::size_t>(
-         "seastore_cache_lru_size"))
+    pinboard(create_extent_pinboard(
+      crimson::common::get_conf<Option::size_t>(
+       "seastore_cachepin_size_pershard")))
 {
-  LOG_PREFIX(Cache::Cache);
-  INFO("created, lru_capacity=0x{:x}B", lru.get_capacity_bytes());
   register_metrics();
   segment_providers_by_device_id.resize(DEVICE_ID_MAX, nullptr);
 }
@@ -503,23 +502,11 @@ void Cache::register_metrics()
         stats.dirty_bytes,
         sm::description("total bytes of dirty extents")
       ),
-      sm::make_counter(
-       "cache_lru_size_bytes",
-       [this] {
-         return lru.get_current_size_bytes();
-       },
-       sm::description("total bytes pinned by the lru")
-      ),
-      sm::make_counter(
-       "cache_lru_num_extents",
-       [this] {
-         return lru.get_current_num_extents();
-       },
-       sm::description("total extents pinned by the lru")
-      ),
     }
   );
 
+  pinboard->register_metrics();
+
   /**
    * tree stats
    */
@@ -744,7 +731,7 @@ void Cache::mark_dirty(CachedExtentRef ref)
     return;
   }
 
-  lru.remove_from_lru(*ref);
+  pinboard->remove(*ref);
   ref->state = CachedExtent::extent_state_t::DIRTY;
   add_to_dirty(ref, nullptr);
 }
@@ -873,7 +860,7 @@ void Cache::remove_extent(
     remove_from_dirty(ref, p_src);
   } else if (!ref->is_placeholder()) {
     assert(ref->get_paddr().is_absolute());
-    lru.remove_from_lru(*ref);
+    pinboard->remove(*ref);
   }
   extents_index.erase(*ref);
 }
@@ -909,7 +896,7 @@ void Cache::commit_replace_extent(
   } else if (prev->is_stable_dirty()) {
     replace_dirty(next, prev, t_src);
   } else {
-    lru.remove_from_lru(*prev);
+    pinboard->remove(*prev);
     add_to_dirty(next, &t_src);
   }
 
@@ -1949,20 +1936,20 @@ Cache::close_ertr::future<> Cache::close()
 {
   LOG_PREFIX(Cache::close);
   INFO("close with {}({}B) dirty, dirty_from={}, alloc_from={}, "
-       "{}({}B) lru, totally {}({}B) indexed extents",
+       "{}({}B) pinned extents, totally {}({}B) indexed extents",
        dirty.size(),
        stats.dirty_bytes,
        get_oldest_dirty_from().value_or(JOURNAL_SEQ_NULL),
        get_oldest_backref_dirty_from().value_or(JOURNAL_SEQ_NULL),
-       lru.get_current_num_extents(),
-       lru.get_current_size_bytes(),
+       pinboard->get_current_num_extents(),
+       pinboard->get_current_size_bytes(),
        extents_index.size(),
        extents_index.get_bytes());
   root.reset();
   clear_dirty();
   backref_extents.clear();
   backref_entryrefs_by_seq.clear();
-  lru.clear();
+  pinboard->clear();
   return close_ertr::now();
 }
 
@@ -2362,7 +2349,7 @@ cache_stats_t Cache::get_stats(
   LOG_PREFIX(Cache::get_stats);
 
   cache_stats_t ret;
-  lru.get_stats(ret, report_detail, seconds);
+  pinboard->get_stats(ret, report_detail, seconds);
 
   /*
    * dirty stats
@@ -2534,100 +2521,4 @@ cache_stats_t Cache::get_stats(
   return ret;
 }
 
-void Cache::LRU::get_stats(
-  cache_stats_t &stats,
-  bool report_detail,
-  double seconds) const
-{
-  LOG_PREFIX(Cache::LRU::get_stats);
-
-  stats.lru_sizes = cache_size_stats_t{current_size, lru.size()};
-  stats.lru_io = overall_io;
-  stats.lru_io.minus(last_overall_io);
-
-  if (report_detail && seconds != 0) {
-    counter_by_src_t<counter_by_extent_t<cache_io_stats_t> >
-      _trans_io_by_src_ext = trans_io_by_src_ext;
-    counter_by_src_t<cache_io_stats_t> trans_io_by_src;
-    cache_io_stats_t trans_io;
-    for (uint8_t _src=0; _src<TRANSACTION_TYPE_MAX; ++_src) {
-      auto src = static_cast<transaction_type_t>(_src);
-      auto& io_by_ext = get_by_src(_trans_io_by_src_ext, src);
-      const auto& last_io_by_ext = get_by_src(last_trans_io_by_src_ext, src);
-      auto& trans_io_per_src = get_by_src(trans_io_by_src, src);
-      for (uint8_t _ext=0; _ext<EXTENT_TYPES_MAX; ++_ext) {
-        auto ext = static_cast<extent_types_t>(_ext);
-        auto& extent_io = get_by_ext(io_by_ext, ext);
-        const auto& last_extent_io = get_by_ext(last_io_by_ext, ext);
-        extent_io.minus(last_extent_io);
-        trans_io_per_src.add(extent_io);
-      }
-      trans_io.add(trans_io_per_src);
-    }
-    cache_io_stats_t other_io = stats.lru_io;
-    other_io.minus(trans_io);
-
-    std::ostringstream oss;
-    oss << "\nlru total" << stats.lru_sizes;
-    cache_size_stats_t data_sizes;
-    cache_size_stats_t mdat_sizes;
-    cache_size_stats_t phys_sizes;
-    for (uint8_t _ext=0; _ext<EXTENT_TYPES_MAX; ++_ext) {
-      auto ext = static_cast<extent_types_t>(_ext);
-      const auto& extent_sizes = get_by_ext(sizes_by_ext, ext);
-      if (is_data_type(ext)) {
-        data_sizes.add(extent_sizes);
-      } else if (is_logical_metadata_type(ext)) {
-        mdat_sizes.add(extent_sizes);
-      } else if (is_physical_type(ext)) {
-        phys_sizes.add(extent_sizes);
-      }
-    }
-    oss << "\n  data" << data_sizes
-        << "\n  mdat" << mdat_sizes
-        << "\n  phys" << phys_sizes;
-
-    oss << "\nlru io: trans-"
-        << cache_io_stats_printer_t{seconds, trans_io}
-        << "; other-"
-        << cache_io_stats_printer_t{seconds, other_io};
-    for (uint8_t _src=0; _src<TRANSACTION_TYPE_MAX; ++_src) {
-      auto src = static_cast<transaction_type_t>(_src);
-      const auto& trans_io_per_src = get_by_src(trans_io_by_src, src);
-      if (trans_io_per_src.is_empty()) {
-        continue;
-      }
-      cache_io_stats_t data_io;
-      cache_io_stats_t mdat_io;
-      cache_io_stats_t phys_io;
-      const auto& io_by_ext = get_by_src(_trans_io_by_src_ext, src);
-      for (uint8_t _ext=0; _ext<EXTENT_TYPES_MAX; ++_ext) {
-        auto ext = static_cast<extent_types_t>(_ext);
-        const auto extent_io = get_by_ext(io_by_ext, ext);
-        if (is_data_type(ext)) {
-          data_io.add(extent_io);
-        } else if (is_logical_metadata_type(ext)) {
-          mdat_io.add(extent_io);
-        } else if (is_physical_type(ext)) {
-          phys_io.add(extent_io);
-        }
-      }
-      oss << "\n  " << src << ": "
-          << cache_io_stats_printer_t{seconds, trans_io_per_src}
-          << "\n    data: "
-          << cache_io_stats_printer_t{seconds, data_io}
-          << "\n    mdat: "
-          << cache_io_stats_printer_t{seconds, mdat_io}
-          << "\n    phys: "
-          << cache_io_stats_printer_t{seconds, phys_io};
-    }
-
-    INFO("{}", oss.str());
-
-    last_trans_io_by_src_ext = trans_io_by_src_ext;
-  }
-
-  last_overall_io = overall_io;
-}
-
 }
index dc7e25bcf6d7149a7e1e2202c1e5df076644a56c..d14f76d6f4628766d05e330948504e5c73755252 100644 (file)
@@ -19,6 +19,7 @@
 #include "crimson/os/seastore/segment_manager.h"
 #include "crimson/os/seastore/transaction.h"
 #include "crimson/os/seastore/linked_tree_node.h"
+#include "crimson/os/seastore/extent_pinboard.h"
 
 namespace crimson::os::seastore::backref {
 class BtreeBackrefManager;
@@ -1573,7 +1574,7 @@ public:
   }
 
 private:
-  /// Update lru for access to ref
+  /// Update extent pinboard for access to ref
   void touch_extent(
       CachedExtent &ext,
       const Transaction::src_t* p_src,
@@ -1584,7 +1585,7 @@ private:
       return;
     }
     if (ext.is_stable_clean() && !ext.is_placeholder()) {
-      lru.move_to_top(ext, p_src);
+      pinboard->move_to_top(ext, p_src);
     }
   }
 
@@ -1641,159 +1642,7 @@ private:
   friend class crimson::os::seastore::backref::BtreeBackrefManager;
   friend class crimson::os::seastore::BackrefManager;
 
-  /**
-   * lru
-   *
-   * holds references to recently used extents
-   */
-  class LRU {
-    // max size (bytes)
-    const size_t capacity = 0;
-
-    // current size (bytes)
-    size_t current_size = 0;
-
-    counter_by_extent_t<cache_size_stats_t> sizes_by_ext;
-    cache_io_stats_t overall_io;
-    counter_by_src_t<counter_by_extent_t<cache_io_stats_t> >
-      trans_io_by_src_ext;
-
-    mutable cache_io_stats_t last_overall_io;
-    mutable cache_io_stats_t last_trans_io;
-    mutable counter_by_src_t<counter_by_extent_t<cache_io_stats_t> >
-      last_trans_io_by_src_ext;
-
-    CachedExtent::primary_ref_list lru;
-
-    void do_remove_from_lru(
-        CachedExtent &extent,
-        const Transaction::src_t* p_src) {
-      assert(extent.is_stable_clean() && !extent.is_placeholder());
-      assert(extent.primary_ref_list_hook.is_linked());
-      assert(lru.size() > 0);
-      auto extent_loaded_length = extent.get_loaded_length();
-      assert(current_size >= extent_loaded_length);
-
-      lru.erase(lru.s_iterator_to(extent));
-      current_size -= extent_loaded_length;
-      get_by_ext(sizes_by_ext, extent.get_type()).account_out(extent_loaded_length);
-      overall_io.out_sizes.account_in(extent_loaded_length);
-      if (p_src) {
-        get_by_ext(
-          get_by_src(trans_io_by_src_ext, *p_src),
-          extent.get_type()
-        ).out_sizes.account_in(extent_loaded_length);
-      }
-      intrusive_ptr_release(&extent);
-    }
-
-    void trim_to_capacity(
-        const Transaction::src_t* p_src) {
-      while (current_size > capacity) {
-        do_remove_from_lru(lru.front(), p_src);
-      }
-    }
-
-  public:
-    LRU(size_t capacity) : capacity(capacity) {}
-
-    size_t get_capacity_bytes() const {
-      return capacity;
-    }
-
-    size_t get_current_size_bytes() const {
-      return current_size;
-    }
-
-    size_t get_current_num_extents() const {
-      return lru.size();
-    }
-
-    void get_stats(
-        cache_stats_t &stats,
-        bool report_detail,
-        double seconds) const;
-
-    void remove_from_lru(CachedExtent &extent) {
-      assert(extent.is_stable_clean());
-      assert(!extent.is_placeholder());
-
-      if (extent.primary_ref_list_hook.is_linked()) {
-        do_remove_from_lru(extent, nullptr);
-      }
-    }
-
-    void move_to_top(
-        CachedExtent &extent,
-        const Transaction::src_t* p_src) {
-      assert(extent.is_stable_clean());
-      assert(!extent.is_placeholder());
-
-      auto extent_loaded_length = extent.get_loaded_length();
-      if (extent.primary_ref_list_hook.is_linked()) {
-        // present, move to top (back)
-        assert(lru.size() > 0);
-        assert(current_size >= extent_loaded_length);
-        lru.erase(lru.s_iterator_to(extent));
-        lru.push_back(extent);
-      } else {
-        // absent, add to top (back)
-        if (extent_loaded_length > 0) {
-          current_size += extent_loaded_length;
-          overall_io.in_sizes.account_in(extent_loaded_length);
-          if (p_src) {
-            get_by_ext(
-              get_by_src(trans_io_by_src_ext, *p_src),
-              extent.get_type()
-            ).in_sizes.account_in(extent_loaded_length);
-          }
-        } // else: the extent isn't loaded upon touch_extent()/on_cache(),
-          //       account the io later in increase_cached_size() upon read_extent()
-       get_by_ext(sizes_by_ext, extent.get_type()).account_in(extent_loaded_length);
-        intrusive_ptr_add_ref(&extent);
-        lru.push_back(extent);
-
-        trim_to_capacity(p_src);
-      }
-    }
-
-    void increase_cached_size(
-      CachedExtent &extent,
-      extent_len_t increased_length,
-      const Transaction::src_t* p_src) {
-      assert(extent.is_data_stable());
-
-      if (extent.primary_ref_list_hook.is_linked()) {
-        assert(extent.is_stable_clean());
-        assert(!extent.is_placeholder());
-        // present, increase size
-        assert(lru.size() > 0);
-        current_size += increased_length;
-        get_by_ext(sizes_by_ext, extent.get_type()).account_parital_in(increased_length);
-        overall_io.in_sizes.account_in(increased_length);
-        if (p_src) {
-          get_by_ext(
-            get_by_src(trans_io_by_src_ext, *p_src),
-            extent.get_type()
-          ).in_sizes.account_in(increased_length);
-        }
-
-        trim_to_capacity(nullptr);
-      }
-    }
-
-    void clear() {
-      LOG_PREFIX(Cache::LRU::clear);
-      for (auto iter = lru.begin(); iter != lru.end();) {
-       SUBDEBUG(seastore_cache, "clearing {}", *iter);
-       do_remove_from_lru(*(iter++), nullptr);
-      }
-    }
-
-    ~LRU() {
-      clear();
-    }
-  } lru;
+  ExtentPinboardRef pinboard;
 
   struct query_counters_t {
     uint64_t access = 0;
@@ -2023,7 +1872,7 @@ private:
     load_ranges_t to_read = extent->load_ranges(offset, length);
     auto new_length = extent->get_loaded_length();
     assert(new_length > old_length);
-    lru.increase_cached_size(*extent, new_length - old_length, p_src);
+    pinboard->increase_cached_size(*extent, new_length - old_length, p_src);
     return seastar::do_with(to_read.ranges, [extent, this, FNAME](auto &read_ranges) {
       return ExtentPlacementManager::read_ertr::parallel_for_each(
           read_ranges, [extent, this, FNAME](auto &read_range) {
index 10c4aa704c810c1906a9f884d6b2a0eb628839af..b3defca307309685d4c850c78b2364f828d8b14e 100644 (file)
@@ -1011,6 +1011,7 @@ protected:
   }
 
   friend class Cache;
+  friend class ExtentPinboardLRU;
   template <typename T, typename... Args>
   static TCachedExtentRef<T> make_cached_extent_ref(
     Args&&... args) {
diff --git a/src/crimson/os/seastore/extent_pinboard.cc b/src/crimson/os/seastore/extent_pinboard.cc
new file mode 100644 (file)
index 0000000..2f842d3
--- /dev/null
@@ -0,0 +1,289 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "crimson/os/seastore/extent_pinboard.h"
+#include "crimson/os/seastore/transaction.h"
+
+SET_SUBSYS(seastore_cache);
+
+namespace crimson::os::seastore {
+
+class ExtentPinboardLRU : public ExtentPinboard {
+  // max size (bytes)
+  const std::size_t capacity = 0;
+
+  // current size (bytes)
+  std::size_t current_size = 0;
+
+  counter_by_extent_t<cache_size_stats_t> sizes_by_ext;
+  cache_io_stats_t overall_io;
+  counter_by_src_t<counter_by_extent_t<cache_io_stats_t> >
+    trans_io_by_src_ext;
+
+  mutable cache_io_stats_t last_overall_io;
+  mutable cache_io_stats_t last_trans_io;
+  mutable counter_by_src_t<counter_by_extent_t<cache_io_stats_t> >
+    last_trans_io_by_src_ext;
+
+  CachedExtent::primary_ref_list lru;
+
+  seastar::metrics::metric_group metrics;
+
+  void do_remove_from_lru(
+    CachedExtent &extent,
+    const Transaction::src_t* p_src) {
+    assert(extent.is_stable_clean());
+    assert(!extent.is_placeholder());
+    assert(extent.primary_ref_list_hook.is_linked());
+    assert(lru.size() > 0);
+    auto extent_loaded_length = extent.get_loaded_length();
+    assert(current_size >= extent_loaded_length);
+
+    lru.erase(lru.s_iterator_to(extent));
+    current_size -= extent_loaded_length;
+    get_by_ext(sizes_by_ext, extent.get_type()).account_out(extent_loaded_length);
+    overall_io.out_sizes.account_in(extent_loaded_length);
+    if (p_src) {
+      get_by_ext(
+        get_by_src(trans_io_by_src_ext, *p_src),
+        extent.get_type()
+      ).out_sizes.account_in(extent_loaded_length);
+    }
+    intrusive_ptr_release(&extent);
+  }
+
+  void trim_to_capacity(
+    const Transaction::src_t* p_src) {
+    while (current_size > capacity) {
+      do_remove_from_lru(lru.front(), p_src);
+    }
+  }
+
+public:
+  ExtentPinboardLRU(std::size_t capacity) : capacity(capacity) {
+    LOG_PREFIX(ExtentPinboardLRU::ExtentPinboardLRU);
+    INFO("created, lru_capacity=0x{:x}B", capacity);
+  }
+
+  std::size_t get_capacity_bytes() const {
+    return capacity;
+  }
+
+  std::size_t get_current_size_bytes() const final {
+    return current_size;
+  }
+
+  std::size_t get_current_num_extents() const final {
+    return lru.size();
+  }
+
+  void register_metrics() final {
+    namespace sm = seastar::metrics;
+    metrics.add_group(
+      "cache",
+      {
+        sm::make_counter(
+          "lru_size_bytes",
+          [this] {
+            return get_current_size_bytes();
+          },
+          sm::description("total bytes pinned by the lru")
+        ),
+        sm::make_counter(
+          "lru_num_extents",
+          [this] {
+            return get_current_num_extents();
+          },
+          sm::description("total extents pinned by the lru")
+        ),
+      }
+    );
+  }
+
+  void get_stats(
+    cache_stats_t &stats,
+    bool report_detail,
+    double seconds) const final;
+
+  void remove(CachedExtent &extent) {
+    assert(extent.is_stable_clean());
+    assert(!extent.is_placeholder());
+
+    if (extent.primary_ref_list_hook.is_linked()) {
+      do_remove_from_lru(extent, nullptr);
+    }
+  }
+
+  void move_to_top(
+    CachedExtent &extent,
+    const Transaction::src_t* p_src) {
+    assert(extent.is_stable_clean());
+    assert(!extent.is_placeholder());
+
+    auto extent_loaded_length = extent.get_loaded_length();
+    if (extent.primary_ref_list_hook.is_linked()) {
+      // present, move to top (back)
+      assert(lru.size() > 0);
+      assert(current_size >= extent_loaded_length);
+      lru.erase(lru.s_iterator_to(extent));
+      lru.push_back(extent);
+    } else {
+      // absent, add to top (back)
+      if (extent_loaded_length > 0) {
+        current_size += extent_loaded_length;
+        overall_io.in_sizes.account_in(extent_loaded_length);
+        if (p_src) {
+          get_by_ext(
+            get_by_src(trans_io_by_src_ext, *p_src),
+            extent.get_type()
+          ).in_sizes.account_in(extent_loaded_length);
+        }
+      } // else: the extent isn't loaded upon touch_extent()/on_cache(),
+        //       account the io later in increase_cached_size() upon read_extent()
+      get_by_ext(sizes_by_ext, extent.get_type()).account_in(extent_loaded_length);
+      intrusive_ptr_add_ref(&extent);
+      lru.push_back(extent);
+
+      trim_to_capacity(p_src);
+    }
+  }
+
+  void increase_cached_size(
+    CachedExtent &extent,
+    extent_len_t increased_length,
+    const Transaction::src_t* p_src) final {
+    assert(extent.is_data_stable());
+
+    if (extent.primary_ref_list_hook.is_linked()) {
+      assert(extent.is_stable_clean());
+      assert(!extent.is_placeholder());
+      // present, increase size
+      assert(lru.size() > 0);
+      current_size += increased_length;
+      get_by_ext(sizes_by_ext, extent.get_type()).account_parital_in(increased_length);
+      overall_io.in_sizes.account_in(increased_length);
+      if (p_src) {
+        get_by_ext(
+          get_by_src(trans_io_by_src_ext, *p_src),
+          extent.get_type()
+        ).in_sizes.account_in(increased_length);
+      }
+
+      trim_to_capacity(nullptr);
+    }
+  }
+
+  void clear() final {
+    LOG_PREFIX(ExtentPinboardLRU::clear);
+    for (auto iter = lru.begin(); iter != lru.end();) {
+      SUBDEBUG(seastore_cache, "clearing {}", *iter);
+      do_remove_from_lru(*(iter++), nullptr);
+    }
+  }
+
+  ~ExtentPinboardLRU() {
+    clear();
+  }
+};
+
+ExtentPinboardRef create_extent_pinboard(std::size_t capacity) {
+  return std::make_unique<ExtentPinboardLRU>(capacity);
+}
+
+void ExtentPinboardLRU::get_stats(
+  cache_stats_t &stats,
+  bool report_detail,
+  double seconds) const
+{
+  LOG_PREFIX(Cache::LRU::get_stats);
+
+  stats.lru_sizes = cache_size_stats_t{current_size, lru.size()};
+  stats.lru_io = overall_io;
+  stats.lru_io.minus(last_overall_io);
+
+  if (report_detail && seconds != 0) {
+    counter_by_src_t<counter_by_extent_t<cache_io_stats_t> >
+      _trans_io_by_src_ext = trans_io_by_src_ext;
+    counter_by_src_t<cache_io_stats_t> trans_io_by_src;
+    cache_io_stats_t trans_io;
+    for (uint8_t _src=0; _src<TRANSACTION_TYPE_MAX; ++_src) {
+      auto src = static_cast<transaction_type_t>(_src);
+      auto& io_by_ext = get_by_src(_trans_io_by_src_ext, src);
+      const auto& last_io_by_ext = get_by_src(last_trans_io_by_src_ext, src);
+      auto& trans_io_per_src = get_by_src(trans_io_by_src, src);
+      for (uint8_t _ext=0; _ext<EXTENT_TYPES_MAX; ++_ext) {
+        auto ext = static_cast<extent_types_t>(_ext);
+        auto& extent_io = get_by_ext(io_by_ext, ext);
+        const auto& last_extent_io = get_by_ext(last_io_by_ext, ext);
+        extent_io.minus(last_extent_io);
+        trans_io_per_src.add(extent_io);
+      }
+      trans_io.add(trans_io_per_src);
+    }
+    cache_io_stats_t other_io = stats.lru_io;
+    other_io.minus(trans_io);
+
+    std::ostringstream oss;
+    oss << "\nlru total" << stats.lru_sizes;
+    cache_size_stats_t data_sizes;
+    cache_size_stats_t mdat_sizes;
+    cache_size_stats_t phys_sizes;
+    for (uint8_t _ext=0; _ext<EXTENT_TYPES_MAX; ++_ext) {
+      auto ext = static_cast<extent_types_t>(_ext);
+      const auto& extent_sizes = get_by_ext(sizes_by_ext, ext);
+      if (is_data_type(ext)) {
+        data_sizes.add(extent_sizes);
+      } else if (is_logical_metadata_type(ext)) {
+        mdat_sizes.add(extent_sizes);
+      } else if (is_physical_type(ext)) {
+        phys_sizes.add(extent_sizes);
+      }
+    }
+    oss << "\n  data" << data_sizes
+        << "\n  mdat" << mdat_sizes
+        << "\n  phys" << phys_sizes;
+
+    oss << "\nlru io: trans-"
+        << cache_io_stats_printer_t{seconds, trans_io}
+        << "; other-"
+        << cache_io_stats_printer_t{seconds, other_io};
+    for (uint8_t _src=0; _src<TRANSACTION_TYPE_MAX; ++_src) {
+      auto src = static_cast<transaction_type_t>(_src);
+      const auto& trans_io_per_src = get_by_src(trans_io_by_src, src);
+      if (trans_io_per_src.is_empty()) {
+        continue;
+      }
+      cache_io_stats_t data_io;
+      cache_io_stats_t mdat_io;
+      cache_io_stats_t phys_io;
+      const auto& io_by_ext = get_by_src(_trans_io_by_src_ext, src);
+      for (uint8_t _ext=0; _ext<EXTENT_TYPES_MAX; ++_ext) {
+        auto ext = static_cast<extent_types_t>(_ext);
+        const auto extent_io = get_by_ext(io_by_ext, ext);
+        if (is_data_type(ext)) {
+          data_io.add(extent_io);
+        } else if (is_logical_metadata_type(ext)) {
+          mdat_io.add(extent_io);
+        } else if (is_physical_type(ext)) {
+          phys_io.add(extent_io);
+        }
+      }
+      oss << "\n  " << src << ": "
+          << cache_io_stats_printer_t{seconds, trans_io_per_src}
+          << "\n    data: "
+          << cache_io_stats_printer_t{seconds, data_io}
+          << "\n    mdat: "
+          << cache_io_stats_printer_t{seconds, mdat_io}
+          << "\n    phys: "
+          << cache_io_stats_printer_t{seconds, phys_io};
+    }
+
+    INFO("{}", oss.str());
+
+    last_trans_io_by_src_ext = trans_io_by_src_ext;
+  }
+
+  last_overall_io = overall_io;
+}
+
+} // namespace crimson::os::seastore
diff --git a/src/crimson/os/seastore/extent_pinboard.h b/src/crimson/os/seastore/extent_pinboard.h
new file mode 100644 (file)
index 0000000..6e6d2f3
--- /dev/null
@@ -0,0 +1,33 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include "crimson/os/seastore/cached_extent.h"
+#include "crimson/os/seastore/transaction.h"
+
+namespace crimson::os::seastore {
+
+struct ExtentPinboard {
+  virtual ~ExtentPinboard() = default;
+  virtual void register_metrics() = 0;
+  virtual void move_to_top(
+    CachedExtent &extent,
+    const Transaction::src_t *p_src) = 0;
+  virtual void remove(CachedExtent &extent) = 0;
+  virtual void get_stats(
+    cache_stats_t &stats,
+    bool report_detail,
+    double seconds) const = 0;
+  virtual std::size_t get_current_size_bytes() const = 0;
+  virtual std::size_t get_current_num_extents() const = 0;
+  virtual void increase_cached_size(
+    CachedExtent &extent,
+    extent_len_t increased_length,
+    const Transaction::src_t *p_src) = 0;
+  virtual void clear() = 0;
+};
+using ExtentPinboardRef = std::unique_ptr<ExtentPinboard>;
+ExtentPinboardRef create_extent_pinboard(std::size_t capacity);
+
+} // namespace crimson::os::seastore