From afde4c6dcb94dbbed154764454c5ea57cc6e2cdb Mon Sep 17 00:00:00 2001 From: Zhang Song Date: Wed, 3 Sep 2025 16:05:38 +0800 Subject: [PATCH] crimson/os/seastore: add write through policy Write extents larger than some threshold to the cold tier directly Signed-off-by: Zhang Song Signed-off-by: Xuehan Xu --- src/common/options/crimson.yaml.in | 5 +++ src/crimson/os/seastore/cache.cc | 16 +++++----- src/crimson/os/seastore/cache.h | 19 ++++++----- src/crimson/os/seastore/cached_extent.h | 19 ++++++++++- .../os/seastore/extent_placement_manager.h | 32 ++++++++++++++++--- src/crimson/os/seastore/seastore_types.cc | 10 ++++++ src/crimson/os/seastore/seastore_types.h | 8 +++++ .../os/seastore/transaction_manager.cc | 24 ++++++++++---- src/crimson/os/seastore/transaction_manager.h | 6 +++- .../seastore/test_btree_lba_manager.cc | 8 +++-- .../seastore/test_transaction_manager.cc | 1 + 11 files changed, 117 insertions(+), 31 deletions(-) diff --git a/src/common/options/crimson.yaml.in b/src/common/options/crimson.yaml.in index 755fb0d5f94..1c7c3a18240 100644 --- a/src/common/options/crimson.yaml.in +++ b/src/common/options/crimson.yaml.in @@ -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 diff --git a/src/crimson/os/seastore/cache.cc b/src/crimson/os/seastore/cache.cc index 5bcd0adf5b6..10db3a0732b 100644 --- a/src/crimson/os/seastore/cache.cc +++ b/src/crimson/os/seastore/cache.cc @@ -1264,28 +1264,26 @@ std::vector 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 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); diff --git a/src/crimson/os/seastore/cache.h b/src/crimson/os/seastore/cache.h index 6f893bc6c3c..df3fdf90e98 100644 --- a/src/crimson/os/seastore/cache.h +++ b/src/crimson/os/seastore/cache.h @@ -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(); 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 ); /** diff --git a/src/crimson/os/seastore/cached_extent.h b/src/crimson/os/seastore/cached_extent.h index 3d69ea73f32..d40e9e632bf 100644 --- a/src/crimson/os/seastore/cached_extent.h +++ b/src/crimson/os/seastore/cached_extent.h @@ -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 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; diff --git a/src/crimson/os/seastore/extent_placement_manager.h b/src/crimson/os/seastore/extent_placement_manager.h index 3b94ae99384..210e8d38442 100644 --- a/src/crimson/os/seastore/extent_placement_manager.h +++ b/src/crimson/os/seastore/extent_placement_manager.h @@ -370,7 +370,9 @@ public: ool_segment_seq_allocator( std::make_unique(segment_type_t::OOL)), max_data_allocation_size(crimson::common::get_conf( - "seastore_max_data_allocation_size")) + "seastore_max_data_allocation_size")), + write_through_size(crimson::common::get_conf( + "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 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; diff --git a/src/crimson/os/seastore/seastore_types.cc b/src/crimson/os/seastore/seastore_types.cc index df83d1e5825..65f9a39b12a 100644 --- a/src/crimson/os/seastore/seastore_types.cc +++ b/src/crimson/os/seastore/seastore_types.cc @@ -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. diff --git a/src/crimson/os/seastore/seastore_types.h b/src/crimson/os/seastore/seastore_types.h index 92661894dd8..4f02157111d 100644 --- a/src/crimson/os/seastore/seastore_types.h +++ b/src/crimson/os/seastore/seastore_types.h @@ -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 : f template <> struct fmt::formatter : fmt::ostream_formatter {}; template <> struct fmt::formatter : fmt::ostream_formatter {}; template <> struct fmt::formatter : fmt::ostream_formatter {}; +template <> struct fmt::formatter : fmt::ostream_formatter {}; #endif namespace fmt { diff --git a/src/crimson/os/seastore/transaction_manager.cc b/src/crimson/os/seastore/transaction_manager.cc index 5dbfee0d8fa..a32573a1cc9 100644 --- a/src/crimson/os/seastore/transaction_manager.cc +++ b/src/crimson/os/seastore/transaction_manager.cc @@ -878,6 +878,7 @@ TransactionManager::rewrite_logical_extent( // get target rewrite generation extent->get_rewrite_generation(), is_tracked)->cast(); + 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(); + 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()); diff --git a/src/crimson/os/seastore/transaction_manager.h b/src/crimson/os/seastore/transaction_manager.h index 07c1a11f455..5c88925a7dd 100644 --- a/src/crimson/os/seastore/transaction_manager.h +++ b/src/crimson/os/seastore/transaction_manager.h @@ -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, 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) { diff --git a/src/test/crimson/seastore/test_btree_lba_manager.cc b/src/test/crimson/seastore/test_btree_lba_manager.cc index c79ce6e5e25..ee27a850e76 100644 --- a/src/test/crimson/seastore/test_btree_lba_manager.cc +++ b/src/test/crimson/seastore/test_btree_lba_manager.cc @@ -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( - 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( - 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( extents.begin(), extents.end()), diff --git a/src/test/crimson/seastore/test_transaction_manager.cc b/src/test/crimson/seastore/test_transaction_manager.cc index 834304bcdb4..f5b5c9f77a3 100644 --- a/src/test/crimson/seastore/test_transaction_manager.cc +++ b/src/test/crimson/seastore/test_transaction_manager.cc @@ -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: {}, "