]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
crimson/os/seastore: add write through policy
authorZhang Song <zhangsong02@qianxin.com>
Wed, 3 Sep 2025 08:05:38 +0000 (16:05 +0800)
committerXuehan Xu <xuxuehan@qianxin.com>
Fri, 31 Jul 2026 01:40:19 +0000 (09:40 +0800)
Write extents larger than some threshold to the cold tier directly

Signed-off-by: Zhang Song <zhangsong02@qianxin.com>
Signed-off-by: Xuehan Xu <xuxuehan@qianxin.com>
src/common/options/crimson.yaml.in
src/crimson/os/seastore/cache.cc
src/crimson/os/seastore/cache.h
src/crimson/os/seastore/cached_extent.h
src/crimson/os/seastore/extent_placement_manager.h
src/crimson/os/seastore/seastore_types.cc
src/crimson/os/seastore/seastore_types.h
src/crimson/os/seastore/transaction_manager.cc
src/crimson/os/seastore/transaction_manager.h
src/test/crimson/seastore/test_btree_lba_manager.cc
src/test/crimson/seastore/test_transaction_manager.cc

index 755fb0d5f9461b82949a558da35ec24128716c83..1c7c3a18240d1b211cc3cf370ef75d5143173b8f 100644 (file)
@@ -388,3 +388,8 @@ options:
   level: advanced
   desc: Size in bytes of extents to be demoted from logical bucket
   default: 2_M
+- name: seastore_write_through_size
+  type: size
+  level: dev
+  desc: Select write through policy when data length is greater than this value.
+  default: 512_K
index 5bcd0adf5b6a955b7acf5c613195e2ac47e41fd4..10db3a0732bf67c51b72bf49510100e51595d773 100644 (file)
@@ -1264,28 +1264,26 @@ std::vector<CachedExtentRef> Cache::alloc_new_data_extents_by_type(
   Transaction &t,        ///< [in, out] current transaction
   extent_types_t type,   ///< [in] type tag
   extent_len_t length,   ///< [in] length
-  placement_hint_t hint, ///< [in] user hint
-  rewrite_gen_t gen,      ///< [in] rewrite generation
-  bool is_tracked
+  alloc_option_t opt     ///< [in] allocation options
 )
 {
   LOG_PREFIX(Cache::alloc_new_data_extents_by_type);
   SUBDEBUGT(seastore_cache, "allocate {} 0x{:x}B, hint={}, gen={}",
-            t, type, length, hint, rewrite_gen_printer_t{gen});
+            t, type, length, opt.hint, rewrite_gen_printer_t{opt.gen});
   ceph_assert(get_extent_category(type) == data_category_t::DATA);
   std::vector<CachedExtentRef> res;
   switch (type) {
   case extent_types_t::OBJECT_DATA_BLOCK:
     {
       auto extents = alloc_new_data_extents<
-       ObjectDataBlock>(t, length, {hint, gen, is_tracked});
+       ObjectDataBlock>(t, length, std::move(opt));
       res.insert(res.begin(), extents.begin(), extents.end());
     }
     return res;
   case extent_types_t::TEST_BLOCK:
     {
       auto extents = alloc_new_data_extents<
-       TestBlock>(t, length, {hint, gen, is_tracked});
+       TestBlock>(t, length, std::move(opt));
       res.insert(res.begin(), extents.begin(), extents.end());
     }
     return res;
@@ -2200,7 +2198,8 @@ void Cache::init()
              P_ADDR_ROOT,
              PLACEMENT_HINT_NULL,
              NULL_GENERATION,
-             TRANS_ID_NULL);
+             TRANS_ID_NULL,
+            write_policy_t::WRITE_BACK);
   root->set_modify_time(seastar::lowres_system_clock::now());
   INFO("init root -- {}", *root);
   add_extent(root);
@@ -2624,7 +2623,8 @@ Cache::_get_absent_extent_by_type(
            offset,
            PLACEMENT_HINT_NULL,
            NULL_GENERATION,
-           TRANS_ID_NULL);
+           TRANS_ID_NULL,
+           write_policy_t::WRITE_BACK);
   DEBUGT("{} length=0x{:x} is absent, add extent ... -- {}",
     t, type, length, *ret);
   add_extent(ret);
index 6f893bc6c3c116d041d2cea9c4788195fad3a327..df3fdf90e98b7edb5842b2c922338ea398ac1730 100644 (file)
@@ -857,7 +857,8 @@ private:
              offset,
              PLACEMENT_HINT_NULL,
              NULL_GENERATION,
-             TRANS_ID_NULL);
+             TRANS_ID_NULL,
+             write_policy_t::WRITE_BACK);
     SUBDEBUGT(seastore_cache,
        "{} {}~0x{:x} is absent, add extent and reading range 0x{:x}~0x{:x} ... -- {}",
        t, T::TYPE, offset, length, partial_off, partial_len, *ret);
@@ -896,7 +897,8 @@ private:
                 offset,
                 PLACEMENT_HINT_NULL,
                 NULL_GENERATION,
-               TRANS_ID_NULL);
+               TRANS_ID_NULL,
+               write_policy_t::WRITE_BACK);
       SUBDEBUG(seastore_cache,
           "{} {}~0x{:x} is absent, add extent and reading range 0x{:x}~0x{:x} ... -- {}",
           T::TYPE, offset, length, partial_off, partial_len, *ret);
@@ -1175,7 +1177,8 @@ public:
               result->paddr,
               opt.hint,
               result->gen,
-             t.get_trans_id());
+             t.get_trans_id(),
+             write_policy_t::WRITE_BACK);
     t.add_fresh_extent(ret);
     SUBDEBUGT(seastore_cache,
               "allocated {} 0x{:x}B extent at {}, hint={}, gen={} -- {}",
@@ -1213,7 +1216,8 @@ public:
                 result.paddr,
                 opt.hint,
                 result.gen,
-                t.get_trans_id());
+                t.get_trans_id(),
+               opt.write_policy);
       t.add_fresh_extent(ret);
       SUBDEBUGT(seastore_cache,
                 "allocated {} 0x{:x}B extent at {}, hint={}, gen={} -- {}",
@@ -1255,7 +1259,8 @@ public:
              remap_paddr,
              PLACEMENT_HINT_NULL,
              NULL_GENERATION,
-              t.get_trans_id());
+              t.get_trans_id(),
+             write_policy_t::WRITE_BACK);
 
     auto extent = ext->template cast<T>();
     extent->set_laddr(remap_laddr);
@@ -1297,9 +1302,7 @@ public:
     Transaction &t,        ///< [in, out] current transaction
     extent_types_t type,   ///< [in] type tag
     extent_len_t length,   ///< [in] length
-    placement_hint_t hint, ///< [in] user hint
-    rewrite_gen_t gen,     ///< [in] rewrite generation
-    bool is_tracked
+    alloc_option_t opt     ///< [in] allocation options
     );
 
   /**
index 3d69ea73f32cc2079682cfb7e9ef0d4873bfb076..d40e9e632bfa87d399a1e8044d3e02110ee6b442 100644 (file)
@@ -360,12 +360,14 @@ public:
             paddr_t paddr,
             placement_hint_t hint,
             rewrite_gen_t gen,
-           transaction_id_t trans_id) {
+            transaction_id_t trans_id,
+            write_policy_t policy) {
     state = _state;
     set_paddr(paddr);
     user_hint = hint;
     rewrite_generation = gen;
     pending_for_transaction = trans_id;
+    write_policy = policy;
   }
 
   void set_modify_time(sea_time_point t) {
@@ -537,6 +539,7 @@ public:
        << ", last_committed_crc=" << last_committed_crc
        << ", refcount=" << use_count()
        << ", user_hint=" << user_hint
+       << ", write_policy=" << write_policy
        << ", rewrite_gen=" << rewrite_gen_printer_t{rewrite_generation}
        << ", pending_io=";
     if (is_pending_io()) {
@@ -916,6 +919,18 @@ public:
     is_shadow = b;
   }
 
+  write_policy_t get_write_policy() const {
+    return write_policy;
+  }
+
+  void set_write_policy(write_policy_t w) {
+    write_policy = w;
+  }
+
+  void reset_write_policy() {
+    write_policy = write_policy_t::WRITE_BACK;
+  }
+
 private:
   template <typename T>
   friend class read_set_item_t;
@@ -1027,6 +1042,8 @@ private:
 
   placement_hint_t user_hint = PLACEMENT_HINT_NULL;
 
+  write_policy_t write_policy = write_policy_t::WRITE_BACK;
+
   // the target rewrite generation for the followup rewrite
   // or the rewrite generation for the fresh write
   rewrite_gen_t rewrite_generation = NULL_GENERATION;
index 3b94ae9938481b5b8da7ac06a310ea3a971ffb26..210e8d3844213fe7101cce4c2248fcb9db4d6d19 100644 (file)
@@ -370,7 +370,9 @@ public:
       ool_segment_seq_allocator(
           std::make_unique<SegmentSeqAllocator>(segment_type_t::OOL)),
       max_data_allocation_size(crimson::common::get_conf<Option::size_t>(
-         "seastore_max_data_allocation_size"))
+         "seastore_max_data_allocation_size")),
+      write_through_size(crimson::common::get_conf<Option::size_t>(
+         "seastore_write_through_size"))
   {
     LOG_PREFIX(ExtentPlacementManager::ExtentPlacementManager);
     devices_by_id.resize(DEVICE_ID_MAX, nullptr);
@@ -448,6 +450,7 @@ public:
     placement_hint_t hint;
     rewrite_gen_t gen;
     bool is_tracked;
+    write_policy_t write_policy = write_policy_t::WRITE_BACK;
 #ifdef UNIT_TESTS_BUILT
     std::optional<paddr_t> external_paddr = std::nullopt;
 #endif
@@ -468,7 +471,8 @@ public:
     assert(opt.gen == INIT_GENERATION || opt.hint == placement_hint_t::REWRITE);
 
     data_category_t category = get_extent_category(type);
-    opt.gen = adjust_generation(category, type, opt.hint, opt.gen, opt.is_tracked);
+    opt.gen = adjust_generation(
+      category, type, opt.hint, opt.gen, opt.write_policy, opt.is_tracked);
 
     paddr_t addr;
 #ifdef UNIT_TESTS_BUILT
@@ -508,7 +512,8 @@ public:
     assert(opt.gen == INIT_GENERATION || opt.hint == placement_hint_t::REWRITE);
 
     data_category_t category = get_extent_category(type);
-    opt.gen = adjust_generation(category, type, opt.hint, opt.gen, opt.is_tracked);
+    opt.gen = adjust_generation(
+      category, type, opt.hint, opt.gen, opt.write_policy, opt.is_tracked);
     assert(opt.gen != INLINE_GENERATION);
 
     // XXX: bp might be extended to point to different memory (e.g. PMem)
@@ -546,6 +551,13 @@ public:
     return allocs;
   }
 
+  write_policy_t get_write_policy(extent_types_t type, extent_len_t length) const {
+    if (has_cold_tier() && length >= write_through_size && is_data_type(type)) {
+      return write_policy_t::WRITE_THROUGH;
+    }
+    return write_policy_t::WRITE_BACK;
+  }
+
 #ifdef UNIT_TESTS_BUILT
   void prefill_fragmented_devices() {
     LOG_PREFIX(ExtentPlacementManager::prefill_fragmented_devices);
@@ -715,6 +727,7 @@ private:
       extent_types_t type,
       placement_hint_t hint,
       rewrite_gen_t gen,
+      write_policy_t policy,
       bool is_tracked) {
     assert(is_real_type(type));
     if (is_root_type(type)) {
@@ -744,10 +757,20 @@ private:
         }
       } else {
         assert(category == data_category_t::DATA);
-        gen = OOL_GENERATION;
+        if (background_process.has_cold_tier() &&
+            policy == write_policy_t::WRITE_THROUGH) {
+          gen = hot_tier_generations;
+        } else {
+          assert(policy != write_policy_t::WRITE_THROUGH);
+          gen = OOL_GENERATION;
+        }
       }
     } else if (background_process.has_cold_tier()) {
       gen = background_process.adjust_generation(gen);
+      if (gen <= hot_tier_generations &&
+          policy == write_policy_t::WRITE_THROUGH) {
+        gen = hot_tier_generations;
+      }
     }
 
     if (is_tracked && gen >= hot_tier_generations &&
@@ -1316,6 +1339,7 @@ private:
   // TODO: drop once paddr->journal_seq_t is introduced
   SegmentSeqAllocatorRef ool_segment_seq_allocator;
   extent_len_t max_data_allocation_size = 0;
+  std::size_t write_through_size = 0;
 
   friend class ::transaction_manager_test_t;
   friend class Cache;
index df83d1e5825da042453f5762563500b9f46e31e5..65f9a39b12adaae8a64470b2e0e00e86d3c40b72 100644 (file)
@@ -1224,6 +1224,16 @@ std::ostream& operator<<(std::ostream& out, placement_hint_t h)
   }
 }
 
+std::ostream& operator<<(std::ostream& out, write_policy_t w)
+{
+  switch(w) {
+  case write_policy_t::WRITE_BACK:
+    return out << "WRITE_BACK";
+  case write_policy_t::WRITE_THROUGH:
+    return out << "WRITE_THROUGH";
+  }
+}
+
 bool can_delay_allocation(device_type_t type) {
   // Some types of device may not support delayed allocation, for example PMEM.
   // All types of device currently support delayed allocation.
index 92661894dd87f2ef79ee0566cfb2d9ae9b93c804..4f02157111dcd4e7f18c32ebdd087e401562286c 100644 (file)
@@ -2070,6 +2070,13 @@ constexpr bool is_real_type(extent_types_t type) {
 
 std::ostream &operator<<(std::ostream &out, extent_types_t t);
 
+enum class write_policy_t {
+  WRITE_BACK,
+  WRITE_THROUGH
+};
+
+std::ostream& operator<<(std::ostream& out, write_policy_t w);
+
 /**
  * rewrite_gen_t
  *
@@ -3654,6 +3661,7 @@ template <> struct fmt::formatter<crimson::os::seastore::transaction_type_t> : f
 template <> struct fmt::formatter<crimson::os::seastore::write_result_t> : fmt::ostream_formatter {};
 template <> struct fmt::formatter<crimson::os::seastore::omap_type_t> : fmt::ostream_formatter {};
 template <> struct fmt::formatter<ceph::buffer::list> : fmt::ostream_formatter {};
+template <> struct fmt::formatter<crimson::os::seastore::write_policy_t> : fmt::ostream_formatter {};
 #endif
 
 namespace fmt {
index 5dbfee0d8fa023e23eb4159b23923f46a24250be..a32573a1cc92984dab26fd8ae8328a690e483f7d 100644 (file)
@@ -878,6 +878,7 @@ TransactionManager::rewrite_logical_extent(
       // get target rewrite generation
       extent->get_rewrite_generation(),
       is_tracked)->cast<LogicalChildNode>();
+    assert(nextent->get_write_policy() != write_policy_t::WRITE_THROUGH);
     nextent->rewrite(t, *extent, 0);
 
     DEBUGT("rewriting meta -- {} to {}", t, *extent, *nextent);
@@ -919,10 +920,15 @@ TransactionManager::rewrite_logical_extent(
       t,
       extent->get_type(),
       extent->get_length(),
-      extent->get_user_hint(),
-      // get target rewrite generation
-      extent->get_rewrite_generation(),
-      is_tracked);
+      {
+        extent->get_user_hint(),
+        // get target rewrite generation
+        extent->get_rewrite_generation(),
+        is_tracked,
+        // WRITH_THROUGH is only effective for client io, so
+        // always set the write policy to WRITE_BACK here
+        write_policy_t::WRITE_BACK
+      });
     extent_len_t off = 0;
     auto left = extent->get_length();
     extent_ref_count_t refcount = 0;
@@ -934,6 +940,7 @@ TransactionManager::rewrite_logical_extent(
     t.force_rewrite_conflict = (extents.size() > 1);
     for (auto &_nextent : extents) {
       auto nextent = _nextent->template cast<LogicalChildNode>();
+      assert(nextent->get_write_policy() != write_policy_t::WRITE_THROUGH);
       bool first_extent = (off == 0);
       ceph_assert(left >= nextent->get_length());
       nextent->rewrite(t, *extent, off);
@@ -1213,9 +1220,12 @@ TransactionManager::promote_extent(
       t,
       orig_ext->get_type(),
       orig_ext->get_length(),
-      placement_hint_t::HOT,
-      INIT_GENERATION,
-      true);
+      {
+        placement_hint_t::HOT,
+        INIT_GENERATION,
+        true,
+        write_policy_t::WRITE_BACK
+      });
 
     promoted_extents.reserve(promoted_raw_extents.size());
 
index 07c1a11f455d71fc7bb9ee93b49913d3ab1230ef..5c88925a7ddc9ccfe0485b5103ff8785050c2bf6 100644 (file)
@@ -591,7 +591,11 @@ public:
     SUBDEBUGT(seastore_tm, "{} hint {}~0x{:x} phint={} ...",
               t, T::TYPE, laddr_hint, len, placement_hint);
     auto exts = cache->alloc_new_data_extents<T>(
-      t, len, {placement_hint, INIT_GENERATION});
+      t, len,
+      {
+        placement_hint, INIT_GENERATION, false,
+        epm->get_write_policy(T::TYPE, len)
+      });
     // user must initialize the logical extent themselves
     assert(is_user_transaction(t.get_src()));
     for (auto& ext : exts) {
index c79ce6e5e25b06f4dd0c1414e739dcadddb67fb2..ee27a850e76aeedc0ac8e73501aeabbd6e643c38 100644 (file)
@@ -327,7 +327,9 @@ struct lba_btree_test : btree_test_base {
     check.emplace(addr, get_map_val(len, TestBlock::TYPE));
     lba_btree_update([=, this](auto &btree, auto &t) {
       auto extents = cache->alloc_new_data_extents<TestBlock>(
-         t, TestBlock::SIZE, {placement_hint_t::HOT, 0, false, get_paddr()});
+         t, TestBlock::SIZE,
+         {placement_hint_t::HOT, 0, false,
+          write_policy_t::WRITE_BACK, get_paddr()});
       return seastar::do_with(
        std::move(extents),
        [this, addr, &t, len, &btree](auto &extents) {
@@ -545,7 +547,9 @@ struct btree_lba_manager_test : btree_test_base {
       *t.t,
       [=, this](auto &t) {
        auto extents = cache->alloc_new_data_extents<TestBlock>(
-           t, TestBlock::SIZE, {placement_hint_t::HOT, 0, false, get_paddr()});
+           t, TestBlock::SIZE,
+           {placement_hint_t::HOT, 0, false,
+            write_policy_t::WRITE_BACK, get_paddr()});
        return seastar::do_with(
          std::vector<LogicalChildNodeRef>(
            extents.begin(), extents.end()),
index 834304bcdb4d2e53157fce720a118950653b0534..f5b5c9f77a373f07588740cb5d14f3cfc3694b88 100644 (file)
@@ -1150,6 +1150,7 @@ struct transaction_manager_test_t :
               t,
               placement_hint_t::HOT,
               gen,
+             write_policy_t::WRITE_BACK,
              false);
             if (expected_generations[t][gen] != epm_gen) {
               logger().error("caller: {}, extent type: {}, input generation: {}, "