From d148c225e2433ba3eac2674c921f6ff689ab4ca2 Mon Sep 17 00:00:00 2001 From: Yingxin Cheng Date: Thu, 25 Aug 2022 21:36:27 +0800 Subject: [PATCH] crimson/os/seastore/async_cleaner: factor out JournalTrimmerImpl Signed-off-by: Yingxin Cheng --- src/crimson/os/seastore/async_cleaner.cc | 452 ++++++++++++------ src/crimson/os/seastore/async_cleaner.h | 404 +++++++--------- .../os/seastore/transaction_manager.cc | 23 +- 3 files changed, 489 insertions(+), 390 deletions(-) diff --git a/src/crimson/os/seastore/async_cleaner.cc b/src/crimson/os/seastore/async_cleaner.cc index 81b2ddb8fcf3e..ebd994897ed78 100644 --- a/src/crimson/os/seastore/async_cleaner.cc +++ b/src/crimson/os/seastore/async_cleaner.cc @@ -337,6 +337,277 @@ std::ostream &operator<<(std::ostream &os, const segments_info_t &infos) << ")"; } +void JournalTrimmerImpl::config_t::validate() const +{ + ceph_assert(max_journal_bytes <= MAX_SEG_OFF); + ceph_assert(max_journal_bytes > target_journal_dirty_bytes); + ceph_assert(max_journal_bytes > target_journal_alloc_bytes); + ceph_assert(rewrite_dirty_bytes_per_cycle > 0); + ceph_assert(rewrite_backref_bytes_per_cycle > 0); +} + +JournalTrimmerImpl::config_t +JournalTrimmerImpl::config_t::get_default( + std::size_t roll_size, journal_type_t type) +{ + assert(roll_size); + std::size_t target_dirty_bytes = 0; + std::size_t target_alloc_bytes = 0; + std::size_t max_journal_bytes = 0; + if (type == journal_type_t::SEGMENTED) { + target_dirty_bytes = 12 * roll_size; + target_alloc_bytes = 2 * roll_size; + max_journal_bytes = 16 * roll_size; + } else { + assert(type == journal_type_t::CIRCULAR); + target_dirty_bytes = roll_size / 4; + target_alloc_bytes = roll_size / 4; + max_journal_bytes = roll_size / 2; + } + return config_t{ + target_dirty_bytes, + target_alloc_bytes, + max_journal_bytes, + 1<<17,// rewrite_dirty_bytes_per_cycle + 1<<24 // rewrite_backref_bytes_per_cycle + }; +} + +JournalTrimmerImpl::config_t +JournalTrimmerImpl::config_t::get_test( + std::size_t roll_size, journal_type_t type) +{ + assert(roll_size); + std::size_t target_dirty_bytes = 0; + std::size_t target_alloc_bytes = 0; + std::size_t max_journal_bytes = 0; + if (type == journal_type_t::SEGMENTED) { + target_dirty_bytes = 2 * roll_size; + target_alloc_bytes = 2 * roll_size; + max_journal_bytes = 4 * roll_size; + } else { + assert(type == journal_type_t::CIRCULAR); + target_dirty_bytes = roll_size / 4; + target_alloc_bytes = roll_size / 4; + max_journal_bytes = roll_size / 2; + } + return config_t{ + target_dirty_bytes, + target_alloc_bytes, + max_journal_bytes, + 1<<17,// rewrite_dirty_bytes_per_cycle + 1<<24 // rewrite_backref_bytes_per_cycle + }; +} + +JournalTrimmerImpl::JournalTrimmerImpl( + BackrefManager &backref_manager, + config_t config, + journal_type_t type, + seastore_off_t roll_start, + seastore_off_t roll_size) + : backref_manager(backref_manager), + config(config), + journal_type(type), + roll_start(roll_start), + roll_size(roll_size) +{ + config.validate(); + ceph_assert(roll_start >= 0); + ceph_assert(roll_size > 0); +} + +void JournalTrimmerImpl::set_journal_head(journal_seq_t head) +{ + ceph_assert(head != JOURNAL_SEQ_NULL); + ceph_assert(journal_head == JOURNAL_SEQ_NULL || + head >= journal_head); + ceph_assert(journal_alloc_tail == JOURNAL_SEQ_NULL || + head >= journal_alloc_tail); + ceph_assert(journal_dirty_tail == JOURNAL_SEQ_NULL || + head >= journal_dirty_tail); + + journal_head = head; + background_callback->maybe_wake_background(); +} + +void JournalTrimmerImpl::update_journal_tails( + journal_seq_t dirty_tail, + journal_seq_t alloc_tail) +{ + LOG_PREFIX(JournalTrimmerImpl::update_journal_tails); + + if (dirty_tail != JOURNAL_SEQ_NULL) { + ceph_assert(journal_head == JOURNAL_SEQ_NULL || + journal_head >= dirty_tail); + if (journal_dirty_tail != JOURNAL_SEQ_NULL && + journal_dirty_tail > dirty_tail) { + ERROR("journal_dirty_tail {} => {} is backwards!", + journal_dirty_tail, dirty_tail); + ceph_abort(); + } + if (journal_dirty_tail.segment_seq == dirty_tail.segment_seq) { + DEBUG("journal_dirty_tail {} => {}", journal_dirty_tail, dirty_tail); + } else { + INFO("journal_dirty_tail {} => {}", journal_dirty_tail, dirty_tail); + } + journal_dirty_tail = dirty_tail; + } + + if (alloc_tail != JOURNAL_SEQ_NULL) { + ceph_assert(journal_head == JOURNAL_SEQ_NULL || + journal_head >= alloc_tail); + if (journal_alloc_tail != JOURNAL_SEQ_NULL && + journal_alloc_tail > alloc_tail) { + ERROR("journal_alloc_tail {} => {} is backwards!", + journal_alloc_tail, alloc_tail); + ceph_abort(); + } + if (journal_alloc_tail.segment_seq == alloc_tail.segment_seq) { + DEBUG("journal_alloc_tail {} => {}", journal_alloc_tail, alloc_tail); + } else { + INFO("journal_alloc_tail {} => {}", journal_alloc_tail, alloc_tail); + } + journal_alloc_tail = alloc_tail; + } + + background_callback->maybe_wake_background(); + background_callback->maybe_wake_blocked_io(); +} + +journal_seq_t JournalTrimmerImpl::get_tail_limit() const +{ + ceph_assert(is_ready()); + auto ret = journal_head.add_offset( + journal_type, + -static_cast(config.max_journal_bytes), + roll_start, + roll_size); + return ret; +} + +journal_seq_t JournalTrimmerImpl::get_dirty_tail_target() const +{ + ceph_assert(is_ready()); + auto ret = journal_head.add_offset( + journal_type, + -static_cast(config.target_journal_dirty_bytes), + roll_start, + roll_size); + return ret; +} + +journal_seq_t JournalTrimmerImpl::get_alloc_tail_target() const +{ + ceph_assert(is_ready()); + auto ret = journal_head.add_offset( + journal_type, + -static_cast(config.target_journal_alloc_bytes), + roll_start, + roll_size); + return ret; +} + +std::size_t JournalTrimmerImpl::get_dirty_journal_size() const +{ + if (!is_ready()) { + return 0; + } + auto ret = journal_head.relative_to( + journal_type, + journal_dirty_tail, + roll_start, + roll_size); + ceph_assert(ret >= 0); + return static_cast(ret); +} + +std::size_t JournalTrimmerImpl::get_alloc_journal_size() const +{ + if (!is_ready()) { + return 0; + } + auto ret = journal_head.relative_to( + journal_type, + journal_alloc_tail, + roll_start, + roll_size); + ceph_assert(ret >= 0); + return static_cast(ret); +} + +JournalTrimmerImpl::trim_ertr::future<> +JournalTrimmerImpl::trim_alloc() +{ + LOG_PREFIX(JournalTrimmerImpl::trim_alloc); + return repeat_eagain([this, FNAME] { + return extent_callback->with_transaction_intr( + Transaction::src_t::CLEANER_TRIM_ALLOC, + "trim_alloc", + [this, FNAME](auto &t) + { + auto target = get_alloc_tail_target(); + DEBUGT("start, alloc_tail={}, target={}", + t, journal_alloc_tail, target); + return backref_manager.merge_cached_backrefs( + t, + target, + config.rewrite_backref_bytes_per_cycle + ).si_then([this, FNAME, &t](auto trim_alloc_to) + -> ExtentCallbackInterface::submit_transaction_direct_iertr::future<> + { + DEBUGT("trim_alloc_to={}", t, trim_alloc_to); + if (trim_alloc_to != JOURNAL_SEQ_NULL) { + return extent_callback->submit_transaction_direct( + t, std::make_optional(trim_alloc_to)); + } + return seastar::now(); + }); + }); + }).safe_then([this, FNAME] { + DEBUG("finish, alloc_tail={}", journal_alloc_tail); + }); +} + +JournalTrimmerImpl::trim_ertr::future<> +JournalTrimmerImpl::trim_dirty() +{ + LOG_PREFIX(JournalTrimmerImpl::trim_dirty); + return repeat_eagain([this, FNAME] { + return extent_callback->with_transaction_intr( + Transaction::src_t::CLEANER_TRIM_DIRTY, + "trim_dirty", + [this, FNAME](auto &t) + { + auto target = get_dirty_tail_target(); + DEBUGT("start, dirty_tail={}, target={}", + t, journal_dirty_tail, target); + return extent_callback->get_next_dirty_extents( + t, + target, + config.rewrite_dirty_bytes_per_cycle + ).si_then([this, FNAME, &t](auto dirty_list) { + DEBUGT("rewrite {} dirty extents", t, dirty_list.size()); + return seastar::do_with( + std::move(dirty_list), + [this, &t](auto &dirty_list) + { + return trans_intr::do_for_each( + dirty_list, + [this, &t](auto &e) { + return extent_callback->rewrite_extent( + t, e, DIRTY_GENERATION, NULL_TIME); + }); + }); + }).si_then([this, &t] { + return extent_callback->submit_transaction_direct(t); + }); + }); + }).safe_then([this, FNAME] { + DEBUG("finish, dirty_tail={}", journal_dirty_tail); + }); +} + bool SpaceTrackerSimple::equals(const SpaceTrackerI &_other) const { LOG_PREFIX(SpaceTrackerSimple::equals); @@ -468,23 +739,18 @@ AsyncCleaner::AsyncCleaner( SegmentManagerGroupRef&& sm_group, BackrefManager &backref_manager, bool detailed, - journal_type_t type, - seastore_off_t roll_start, - seastore_off_t roll_size) + JournalTrimmerImplRef &&_trimmer) : detailed(detailed), config(config), sm_group(std::move(sm_group)), backref_manager(backref_manager), - journal_type(type), - roll_start(roll_start), - roll_size(roll_size), + trimmer(std::move(_trimmer)), ool_segment_seq_allocator( new SegmentSeqAllocator(segment_type_t::OOL)), gc_process(*this) { config.validate(); - ceph_assert(roll_start >= 0); - ceph_assert(roll_size > 0); + trimmer->set_background_callback(&gc_process); } void AsyncCleaner::register_metrics() @@ -564,10 +830,10 @@ void AsyncCleaner::register_metrics() sm::description("the size of the space is unavailable and not alive")), sm::make_counter("dirty_journal_bytes", - [this] { return get_dirty_journal_size(); }, + [this] { return trimmer->get_dirty_journal_size(); }, sm::description("the size of the journal for dirty extents")), sm::make_counter("alloc_journal_bytes", - [this] { return get_alloc_journal_size(); }, + [this] { return trimmer->get_alloc_journal_size(); }, sm::description("the size of the journal for alloc info")), sm::make_counter("projected_count", stats.projected_count, @@ -644,50 +910,6 @@ segment_id_t AsyncCleaner::allocate_segment( return NULL_SEG_ID; } -void AsyncCleaner::update_journal_tails( - journal_seq_t dirty_tail, - journal_seq_t alloc_tail) -{ - LOG_PREFIX(AsyncCleaner::update_journal_tails); - - if (dirty_tail != JOURNAL_SEQ_NULL) { - ceph_assert(journal_head == JOURNAL_SEQ_NULL || - journal_head >= dirty_tail); - if (journal_dirty_tail != JOURNAL_SEQ_NULL && - journal_dirty_tail > dirty_tail) { - ERROR("journal_dirty_tail {} => {} is backwards!", - journal_dirty_tail, dirty_tail); - ceph_abort(); - } - if (journal_dirty_tail.segment_seq == dirty_tail.segment_seq) { - DEBUG("journal_dirty_tail {} => {}", journal_dirty_tail, dirty_tail); - } else { - INFO("journal_dirty_tail {} => {}", journal_dirty_tail, dirty_tail); - } - journal_dirty_tail = dirty_tail; - } - - if (alloc_tail != JOURNAL_SEQ_NULL) { - ceph_assert(journal_head == JOURNAL_SEQ_NULL || - journal_head >= alloc_tail); - if (journal_alloc_tail != JOURNAL_SEQ_NULL && - journal_alloc_tail > alloc_tail) { - ERROR("journal_alloc_tail {} => {} is backwards!", - journal_alloc_tail, alloc_tail); - ceph_abort(); - } - if (journal_alloc_tail.segment_seq == alloc_tail.segment_seq) { - DEBUG("journal_alloc_tail {} => {}", journal_alloc_tail, alloc_tail); - } else { - INFO("journal_alloc_tail {} => {}", journal_alloc_tail, alloc_tail); - } - journal_alloc_tail = alloc_tail; - } - - gc_process.maybe_wake_background(); - gc_process.maybe_wake_blocked_io(); -} - void AsyncCleaner::close_segment(segment_id_t segment) { LOG_PREFIX(AsyncCleaner::close_segment); @@ -763,9 +985,9 @@ AsyncCleaner::gc_cycle_ret AsyncCleaner::GCProcess::run() return cleaner.do_gc_cycle(); } else { cleaner.log_gc_state("GCProcess::run(block)"); - ceph_assert(!blocking); - blocking = seastar::promise<>(); - return blocking->get_future(); + ceph_assert(!blocking_background); + blocking_background = seastar::promise<>(); + return blocking_background->get_future(); } }).then([] { return seastar::stop_iteration::no; @@ -775,25 +997,25 @@ AsyncCleaner::gc_cycle_ret AsyncCleaner::GCProcess::run() AsyncCleaner::gc_cycle_ret AsyncCleaner::do_gc_cycle() { - if (gc_should_trim_alloc()) { - return gc_trim_alloc( + if (trimmer->should_trim_alloc()) { + return trimmer->trim_alloc( ).handle_error( crimson::ct_error::assert_all{ - "GCProcess::run encountered invalid error in gc_trim_alloc" + "AsyncCleaner::do_gc_cycle encountered invalid error in trim_alloc" } ); - } else if (gc_should_trim_dirty()) { - return gc_trim_dirty( + } else if (trimmer->should_trim_dirty()) { + return trimmer->trim_dirty( ).handle_error( crimson::ct_error::assert_all{ - "GCProcess::run encountered invalid error in gc_trim_dirty" + "AsyncCleaner::do_gc_cycle encountered invalid error in trim_dirty" } ); } else if (gc_should_reclaim_space()) { return gc_reclaim_space( ).handle_error( crimson::ct_error::assert_all{ - "GCProcess::run encountered invalid error in gc_reclaim_space" + "AsyncCleaner::do_gc_cycle encountered invalid error in gc_reclaim_space" } ); } else { @@ -801,77 +1023,6 @@ AsyncCleaner::gc_cycle_ret AsyncCleaner::do_gc_cycle() } } -AsyncCleaner::gc_trim_alloc_ret -AsyncCleaner::gc_trim_alloc() -{ - LOG_PREFIX(AsyncCleaner::gc_trim_alloc); - return repeat_eagain([this, FNAME] { - return ecb->with_transaction_intr( - Transaction::src_t::CLEANER_TRIM_ALLOC, - "trim_alloc", - [this, FNAME](auto &t) - { - auto target = get_alloc_tail_target(); - DEBUGT("start, alloc_tail={}, target={}", - t, journal_alloc_tail, target); - return backref_manager.merge_cached_backrefs( - t, - target, - config.rewrite_backref_bytes_per_cycle - ).si_then([this, FNAME, &t](auto trim_alloc_to) - -> ExtentCallbackInterface::submit_transaction_direct_iertr::future<> - { - DEBUGT("trim_alloc_to={}", t, trim_alloc_to); - if (trim_alloc_to != JOURNAL_SEQ_NULL) { - return ecb->submit_transaction_direct( - t, std::make_optional(trim_alloc_to)); - } - return seastar::now(); - }); - }); - }).safe_then([this, FNAME] { - DEBUG("finish, alloc_tail={}", journal_alloc_tail); - }); -} - -AsyncCleaner::gc_trim_dirty_ret -AsyncCleaner::gc_trim_dirty() -{ - LOG_PREFIX(AsyncCleaner::gc_trim_dirty); - return repeat_eagain([this, FNAME] { - return ecb->with_transaction_intr( - Transaction::src_t::CLEANER_TRIM_DIRTY, - "trim_dirty", - [this, FNAME](auto &t) - { - auto target = get_dirty_tail_target(); - DEBUGT("start, dirty_tail={}, target={}", - t, journal_dirty_tail, target); - return ecb->get_next_dirty_extents( - t, - target, - config.rewrite_dirty_bytes_per_cycle - ).si_then([this, FNAME, &t](auto dirty_list) { - DEBUGT("rewrite {} dirty extents", t, dirty_list.size()); - return seastar::do_with( - std::move(dirty_list), - [this, &t](auto &dirty_list) - { - return trans_intr::do_for_each( - dirty_list, - [this, &t](auto &e) { - return ecb->rewrite_extent(t, e, DIRTY_GENERATION, NULL_TIME); - }); - }); - }).si_then([this, &t] { - return ecb->submit_transaction_direct(t); - }); - }); - }).safe_then([this, FNAME] { - DEBUG("finish, dirty_tail={}", journal_dirty_tail); - }); -} - AsyncCleaner::do_reclaim_space_ret AsyncCleaner::do_reclaim_space( const std::vector &backref_extents, @@ -1080,9 +1231,8 @@ AsyncCleaner::mount_ret AsyncCleaner::mount() ceph_assert(state == cleaner_state_t::STOP); state = cleaner_state_t::MOUNT; stats = {}; - journal_head = JOURNAL_SEQ_NULL; - journal_alloc_tail = JOURNAL_SEQ_NULL; - journal_dirty_tail = JOURNAL_SEQ_NULL; + + trimmer->reset(); space_tracker.reset( detailed ? @@ -1227,9 +1377,7 @@ void AsyncCleaner::start_gc() ceph_assert(state == cleaner_state_t::SCAN_SPACE); state = cleaner_state_t::READY; INFO("done, start GC, {}", gc_stat_printer_t{this, true}); - ceph_assert(journal_head != JOURNAL_SEQ_NULL); - ceph_assert(journal_alloc_tail != JOURNAL_SEQ_NULL); - ceph_assert(journal_dirty_tail != JOURNAL_SEQ_NULL); + ceph_assert(trimmer->is_ready()); gc_process.start(); } @@ -1374,7 +1522,7 @@ segment_id_t AsyncCleaner::get_next_reclaim_segment() const } for (auto& [_id, segment_info] : segments) { if (segment_info.is_closed() && - !segment_info.is_in_journal(get_journal_tail())) { + !segment_info.is_in_journal(trimmer->get_journal_tail())) { double benefit_cost = calc_gc_benefit_cost(_id, now_time, bound_time); if (benefit_cost > max_benefit_cost) { id = _id; @@ -1410,7 +1558,7 @@ AsyncCleaner::reserve_projected_usage(std::size_t projected_usage) // prepare until the prior one exits and clears this. ++stats.io_count; bool is_blocked = false; - if (should_block_on_trim()) { + if (trimmer->should_block_on_trim()) { is_blocked = true; ++stats.io_blocked_count_trim; } @@ -1447,10 +1595,10 @@ std::ostream &operator<<(std::ostream &os, AsyncCleaner::gc_stat_printer_t stats { os << "gc_stats("; if (stats.cleaner->is_ready()) { - os << "should_block_on_(trim=" << stats.cleaner->should_block_on_trim() + os << "should_block_on_(trim=" << stats.cleaner->trimmer->should_block_on_trim() << ", reclaim=" << stats.cleaner->should_block_on_reclaim() << ")" - << ", should_(trim_dirty=" << stats.cleaner->gc_should_trim_dirty() - << ", trim_alloc=" << stats.cleaner->gc_should_trim_alloc() + << ", should_(trim_dirty=" << stats.cleaner->trimmer->should_trim_dirty() + << ", trim_alloc=" << stats.cleaner->trimmer->should_trim_alloc() << ", reclaim=" << stats.cleaner->gc_should_reclaim_space() << ")"; } else { os << "not-ready"; @@ -1459,13 +1607,13 @@ std::ostream &operator<<(std::ostream &os, AsyncCleaner::gc_stat_printer_t stats << ", reclaim_ratio=" << stats.cleaner->get_reclaim_ratio() << ", alive_ratio=" << stats.cleaner->get_alive_ratio(); if (stats.detailed) { - os << ", journal_head=" << stats.cleaner->journal_head - << ", alloc_tail=" << stats.cleaner->journal_alloc_tail - << ", dirty_tail=" << stats.cleaner->journal_dirty_tail; + os << ", journal_head=" << stats.cleaner->trimmer->get_journal_head() + << ", alloc_tail=" << stats.cleaner->trimmer->get_alloc_tail() + << ", dirty_tail=" << stats.cleaner->trimmer->get_dirty_tail(); if (stats.cleaner->is_ready()) { - os << ", alloc_tail_target=" << stats.cleaner->get_alloc_tail_target() - << ", dirty_tail_target=" << stats.cleaner->get_dirty_tail_target() - << ", tail_limit=" << stats.cleaner->get_tail_limit(); + os << ", alloc_tail_target=" << stats.cleaner->trimmer->get_alloc_tail_target() + << ", dirty_tail_target=" << stats.cleaner->trimmer->get_dirty_tail_target() + << ", tail_limit=" << stats.cleaner->trimmer->get_tail_limit(); } os << ", unavailable_unreclaimable=" << stats.cleaner->get_unavailable_unreclaimable_bytes() << "B" diff --git a/src/crimson/os/seastore/async_cleaner.h b/src/crimson/os/seastore/async_cleaner.h index f4a5b09efd7b3..2b89742ee5b96 100644 --- a/src/crimson/os/seastore/async_cleaner.h +++ b/src/crimson/os/seastore/async_cleaner.h @@ -392,7 +392,7 @@ struct BackgroundListener { }; /** - * Callback interface for journal trimming + * Callback interface for Journal */ class JournalTrimmer { public: @@ -402,11 +402,6 @@ public: // set the committed journal head virtual void set_journal_head(journal_seq_t) = 0; - // get the committed journal tail - journal_seq_t get_journal_tail() const { - return std::min(get_alloc_tail(), get_dirty_tail()); - } - // get the committed journal dirty tail virtual journal_seq_t get_dirty_tail() const = 0; @@ -418,6 +413,155 @@ public: journal_seq_t dirty_tail, journal_seq_t alloc_tail) = 0; virtual ~JournalTrimmer() {} + + journal_seq_t get_journal_tail() const { + return std::min(get_alloc_tail(), get_dirty_tail()); + } + + bool is_ready() const { + return (get_journal_head() != JOURNAL_SEQ_NULL && + get_dirty_tail() != JOURNAL_SEQ_NULL && + get_alloc_tail() != JOURNAL_SEQ_NULL); + } + + std::size_t get_num_rolls() const { + if (!is_ready()) { + return 0; + } + assert(get_journal_head().segment_seq >= + get_journal_tail().segment_seq); + return get_journal_head().segment_seq + 1 - + get_journal_tail().segment_seq; + } +}; + +class BackrefManager; +class JournalTrimmerImpl; +using JournalTrimmerImplRef = std::unique_ptr; + +/** + * Journal trimming implementation + */ +class JournalTrimmerImpl : public JournalTrimmer { +public: + struct config_t { + /// Number of minimum bytes to stop trimming dirty. + std::size_t target_journal_dirty_bytes = 0; + /// Number of minimum bytes to stop trimming allocation + /// (having the corresponding backrefs unmerged) + std::size_t target_journal_alloc_bytes = 0; + /// Number of maximum bytes to block user transactions. + std::size_t max_journal_bytes = 0; + /// Number of bytes to rewrite dirty per cycle + std::size_t rewrite_dirty_bytes_per_cycle = 0; + /// Number of bytes to rewrite backref per cycle + std::size_t rewrite_backref_bytes_per_cycle = 0; + + void validate() const; + + static config_t get_default( + std::size_t roll_size, journal_type_t type); + + static config_t get_test( + std::size_t roll_size, journal_type_t type); + }; + + JournalTrimmerImpl( + BackrefManager &backref_manager, + config_t config, + journal_type_t type, + seastore_off_t roll_start, + seastore_off_t roll_size); + + ~JournalTrimmerImpl() = default; + + /* + * JournalTrimmer interfaces + */ + + journal_seq_t get_journal_head() const final { + return journal_head; + } + + void set_journal_head(journal_seq_t) final; + + journal_seq_t get_dirty_tail() const final { + return journal_dirty_tail; + } + + journal_seq_t get_alloc_tail() const final { + return journal_alloc_tail; + } + + void update_journal_tails( + journal_seq_t dirty_tail, journal_seq_t alloc_tail) final; + + journal_type_t get_journal_type() const { + return journal_type; + } + + void set_extent_callback(ExtentCallbackInterface *cb) { + extent_callback = cb; + } + + void set_background_callback(BackgroundListener *cb) { + background_callback = cb; + } + + void reset() { + journal_head = JOURNAL_SEQ_NULL; + journal_dirty_tail = JOURNAL_SEQ_NULL; + journal_alloc_tail = JOURNAL_SEQ_NULL; + } + + bool should_trim_dirty() const { + return get_dirty_tail_target() > journal_dirty_tail; + } + + bool should_trim_alloc() const { + return get_alloc_tail_target() > journal_alloc_tail; + } + + bool should_block_on_trim() const { + return get_tail_limit() > get_journal_tail(); + } + + using trim_ertr = crimson::errorator< + crimson::ct_error::input_output_error>; + trim_ertr::future<> trim_dirty(); + + trim_ertr::future<> trim_alloc(); + + static JournalTrimmerImplRef create( + BackrefManager &backref_manager, + config_t config, + journal_type_t type, + seastore_off_t roll_start, + seastore_off_t roll_size) { + return std::make_unique( + backref_manager, config, type, roll_start, roll_size); + } + + // TODO: move to private + journal_seq_t get_tail_limit() const; + journal_seq_t get_dirty_tail_target() const; + journal_seq_t get_alloc_tail_target() const; + std::size_t get_dirty_journal_size() const; + std::size_t get_alloc_journal_size() const; + +private: + ExtentCallbackInterface *extent_callback = nullptr; + BackgroundListener *background_callback = nullptr; + BackrefManager &backref_manager; + + config_t config; + journal_type_t journal_type; + seastore_off_t roll_start; + seastore_off_t roll_size; + + journal_seq_t journal_head; + journal_seq_t journal_dirty_tail; + journal_seq_t journal_alloc_tail; }; /** @@ -651,9 +795,7 @@ public: bool equals(const SpaceTrackerI &other) const; }; -class BackrefManager; - -class AsyncCleaner : public SegmentProvider, public JournalTrimmer { +class AsyncCleaner : public SegmentProvider { enum class cleaner_state_t { STOP, MOUNT, @@ -668,97 +810,36 @@ public: /// Config struct config_t { - /// Number of minimum bytes to stop trimming dirty. - std::size_t target_journal_dirty_bytes = 0; - /// Number of minimum bytes to stop trimming allocation - /// (having the corresponding backrefs unmerged) - std::size_t target_journal_alloc_bytes = 0; - /// Number of maximum bytes to block user transactions. - std::size_t max_journal_bytes = 0; - /// Ratio of maximum available space to disable reclaiming. double available_ratio_gc_max = 0; /// Ratio of minimum available space to force reclaiming. double available_ratio_hard_limit = 0; - /// Ratio of minimum reclaimable space to stop reclaiming. double reclaim_ratio_gc_threshold = 0; - /// Number of bytes to reclaim per cycle std::size_t reclaim_bytes_per_cycle = 0; - /// Number of bytes to rewrite dirty per cycle - std::size_t rewrite_dirty_bytes_per_cycle = 0; - - /// Number of bytes to rewrite backref per cycle - std::size_t rewrite_backref_bytes_per_cycle = 0; - void validate() const { - ceph_assert(max_journal_bytes <= MAX_SEG_OFF); - ceph_assert(max_journal_bytes > target_journal_dirty_bytes); - ceph_assert(max_journal_bytes > target_journal_alloc_bytes); ceph_assert(available_ratio_gc_max > available_ratio_hard_limit); ceph_assert(reclaim_bytes_per_cycle > 0); - ceph_assert(rewrite_dirty_bytes_per_cycle > 0); - ceph_assert(rewrite_backref_bytes_per_cycle > 0); } - static config_t get_default( - std::size_t roll_size, journal_type_t type) { - assert(roll_size); - std::size_t target_dirty_bytes = 0; - std::size_t target_alloc_bytes = 0; - std::size_t max_journal_bytes = 0; - if (type == journal_type_t::SEGMENTED) { - target_dirty_bytes = 12 * roll_size; - target_alloc_bytes = 2 * roll_size; - max_journal_bytes = 16 * roll_size; - } else { - assert(type == journal_type_t::CIRCULAR); - target_dirty_bytes = roll_size / 4; - target_alloc_bytes = roll_size / 4; - max_journal_bytes = roll_size / 2; - } + static config_t get_default() { return config_t{ - target_dirty_bytes, - target_alloc_bytes, - max_journal_bytes, - .15, // available_ratio_gc_max - .1, // available_ratio_hard_limit - .1, // reclaim_ratio_gc_threshold - 1<<20,// reclaim_bytes_per_cycle - 1<<17,// rewrite_dirty_bytes_per_cycle - 1<<24 // rewrite_backref_bytes_per_cycle - }; + .15, // available_ratio_gc_max + .1, // available_ratio_hard_limit + .1, // reclaim_ratio_gc_threshold + 1<<20 // reclaim_bytes_per_cycle + }; } - static config_t get_test( - std::size_t roll_size, journal_type_t type) { - assert(roll_size); - std::size_t target_dirty_bytes = 0; - std::size_t target_alloc_bytes = 0; - std::size_t max_journal_bytes = 0; - if (type == journal_type_t::SEGMENTED) { - target_dirty_bytes = 2 * roll_size; - target_alloc_bytes = 2 * roll_size; - max_journal_bytes = 4 * roll_size; - } else { - assert(type == journal_type_t::CIRCULAR); - target_dirty_bytes = roll_size / 4; - target_alloc_bytes = roll_size / 4; - max_journal_bytes = roll_size / 2; - } + static config_t get_test() { return config_t{ - target_dirty_bytes, - target_alloc_bytes, - max_journal_bytes, - .99, // available_ratio_gc_max - .2, // available_ratio_hard_limit - .6, // reclaim_ratio_gc_threshold - 1<<20,// reclaim_bytes_per_cycle - 1<<17,// rewrite_dirty_bytes_per_cycle - 1<<24 // rewrite_backref_bytes_per_cycle - }; + .99, // available_ratio_gc_max + .2, // available_ratio_hard_limit + .6, // reclaim_ratio_gc_threshold + 1<<20 // reclaim_bytes_per_cycle + }; } }; @@ -811,17 +892,7 @@ private: seastar::metrics::metric_group metrics; void register_metrics(); - journal_type_t journal_type; - - seastore_off_t roll_start; - seastore_off_t roll_size; - - journal_seq_t journal_alloc_tail; - - journal_seq_t journal_dirty_tail; - - /// the committed journal head - journal_seq_t journal_head; + JournalTrimmerImplRef trimmer; ExtentCallbackInterface *ecb = nullptr; @@ -833,20 +904,19 @@ public: SegmentManagerGroupRef&& sm_group, BackrefManager &backref_manager, bool detailed, - journal_type_t type, - seastore_off_t roll_start, - seastore_off_t roll_size); + JournalTrimmerImplRef &&trimmer); SegmentSeqAllocator& get_ool_segment_seq_allocator() { return *ool_segment_seq_allocator; } journal_type_t get_journal_type() const { - return journal_type; + return trimmer->get_journal_type(); } void set_extent_callback(ExtentCallbackInterface *cb) { ecb = cb; + trimmer->set_extent_callback(cb); } using mount_ertr = base_ertr; @@ -865,44 +935,6 @@ public: return state >= cleaner_state_t::READY; } - /* - * JournalTrimmer interfaces - */ - - journal_seq_t get_journal_head() const final { - return journal_head; - } - - void set_journal_head(journal_seq_t head) final { - ceph_assert(head != JOURNAL_SEQ_NULL); - ceph_assert(journal_head == JOURNAL_SEQ_NULL || - head >= journal_head); - ceph_assert(journal_alloc_tail == JOURNAL_SEQ_NULL || - head >= journal_alloc_tail); - ceph_assert(journal_dirty_tail == JOURNAL_SEQ_NULL || - head >= journal_dirty_tail); - - if (head.offset.get_addr_type() == paddr_types_t::SEGMENT) { - auto submitted_journal_head = segments.get_submitted_journal_head(); - ceph_assert(submitted_journal_head != JOURNAL_SEQ_NULL && - head <= submitted_journal_head); - } - - journal_head = head; - gc_process.maybe_wake_background(); - } - - journal_seq_t get_dirty_tail() const final { - return journal_dirty_tail; - } - - journal_seq_t get_alloc_tail() const final { - return journal_alloc_tail; - } - - void update_journal_tails( - journal_seq_t dirty_tail, journal_seq_t alloc_tail) final; - /* * SegmentProvider interfaces */ @@ -1001,39 +1033,6 @@ private: segment_id_t get_next_reclaim_segment() const; - journal_seq_t get_dirty_tail_target() const { - assert(is_ready()); - ceph_assert(journal_head != JOURNAL_SEQ_NULL); - auto ret = journal_head.add_offset( - journal_type, - -static_cast(config.target_journal_dirty_bytes), - roll_start, - roll_size); - return ret; - } - - journal_seq_t get_alloc_tail_target() const { - assert(is_ready()); - ceph_assert(journal_head != JOURNAL_SEQ_NULL); - auto ret = journal_head.add_offset( - journal_type, - -static_cast(config.target_journal_alloc_bytes), - roll_start, - roll_size); - return ret; - } - - journal_seq_t get_tail_limit() const { - assert(is_ready()); - ceph_assert(journal_head != JOURNAL_SEQ_NULL); - auto ret = journal_head.add_offset( - journal_type, - -static_cast(config.max_journal_bytes), - roll_start, - roll_size); - return ret; - } - struct reclaim_state_t { reclaim_gen_t generation; reclaim_gen_t target_generation; @@ -1185,14 +1184,6 @@ private: gc_cycle_ret do_gc_cycle(); - using gc_trim_dirty_ertr = base_ertr; - using gc_trim_dirty_ret = gc_trim_dirty_ertr::future<>; - gc_trim_dirty_ret gc_trim_dirty(); - - using gc_trim_alloc_ertr = base_ertr; - using gc_trim_alloc_ret = gc_trim_alloc_ertr::future<>; - gc_trim_alloc_ret gc_trim_alloc(); - using do_reclaim_space_ertr = base_ertr; using do_reclaim_space_ret = do_reclaim_space_ertr::future<>; do_reclaim_space_ret do_reclaim_space( @@ -1209,16 +1200,11 @@ private: * Segments calculations */ std::size_t get_segments_in_journal() const { - if (journal_type == journal_type_t::CIRCULAR) { - return 0; - } - auto journal_tail = get_journal_tail(); - if (journal_tail == JOURNAL_SEQ_NULL || - journal_head == JOURNAL_SEQ_NULL) { + if (trimmer->get_journal_type() == journal_type_t::CIRCULAR) { return 0; + } else { + return trimmer->get_num_rolls(); } - assert(journal_head.segment_seq >= journal_tail.segment_seq); - return journal_head.segment_seq + 1 - journal_tail.segment_seq; } std::size_t get_segments_in_journal_closed() const { auto in_journal = get_segments_in_journal(); @@ -1276,46 +1262,11 @@ private: (double)segments.get_total_bytes(); } - /* - * Journal sizes - */ - std::size_t get_dirty_journal_size() const { - if (journal_head == JOURNAL_SEQ_NULL || - journal_dirty_tail == JOURNAL_SEQ_NULL) { - return 0; - } - auto ret = journal_head.relative_to( - journal_type, - journal_dirty_tail, - roll_start, - roll_size); - ceph_assert(ret >= 0); - return static_cast(ret); - } - - std::size_t get_alloc_journal_size() const { - if (journal_head == JOURNAL_SEQ_NULL || - journal_alloc_tail == JOURNAL_SEQ_NULL) { - return 0; - } - auto ret = journal_head.relative_to( - journal_type, - journal_alloc_tail, - roll_start, - roll_size); - ceph_assert(ret >= 0); - return static_cast(ret); - } - /** * should_block_on_gc * * Encapsulates whether block pending gc. */ - bool should_block_on_trim() const { - assert(is_ready()); - return get_tail_limit() > get_journal_tail(); - } bool should_block_on_reclaim() const { assert(is_ready()); @@ -1328,7 +1279,7 @@ private: bool should_block_on_gc() const { assert(is_ready()); - return should_block_on_trim() || should_block_on_reclaim(); + return trimmer->should_block_on_trim() || should_block_on_reclaim(); } void log_gc_state(const char *caller) const; @@ -1358,15 +1309,6 @@ private: ); } - bool gc_should_trim_dirty() const { - assert(is_ready()); - return get_dirty_tail_target() > journal_dirty_tail; - } - - bool gc_should_trim_alloc() const { - assert(is_ready()); - return get_alloc_tail_target() > journal_alloc_tail; - } /** * gc_should_run * @@ -1375,8 +1317,8 @@ private: bool gc_should_run() const { ceph_assert(is_ready()); return gc_should_reclaim_space() - || gc_should_trim_dirty() - || gc_should_trim_alloc(); + || trimmer->should_trim_dirty() + || trimmer->should_trim_alloc(); } void adjust_segment_util(double old_usage, double new_usage) { diff --git a/src/crimson/os/seastore/transaction_manager.cc b/src/crimson/os/seastore/transaction_manager.cc index 1fe81c856a6e9..03bff3ccd4d10 100644 --- a/src/crimson/os/seastore/transaction_manager.cc +++ b/src/crimson/os/seastore/transaction_manager.cc @@ -662,23 +662,30 @@ TransactionManagerRef make_transaction_manager( bool cleaner_is_detailed; AsyncCleaner::config_t cleaner_config; + JournalTrimmerImpl::config_t trimmer_config; if (is_test) { cleaner_is_detailed = true; - cleaner_config = AsyncCleaner::config_t::get_test( + cleaner_config = AsyncCleaner::config_t::get_test(); + trimmer_config = JournalTrimmerImpl::config_t::get_test( roll_size, journal_type); } else { cleaner_is_detailed = false; - cleaner_config = AsyncCleaner::config_t::get_default( + cleaner_config = AsyncCleaner::config_t::get_default(); + trimmer_config = JournalTrimmerImpl::config_t::get_default( roll_size, journal_type); } + + auto ref_journal_trimmer = JournalTrimmerImpl::create( + *backref_manager, trimmer_config, + journal_type, roll_start, roll_size); + JournalTrimmer &journal_trimmer = *ref_journal_trimmer; + auto async_cleaner = std::make_unique( cleaner_config, std::move(sms), *backref_manager, cleaner_is_detailed, - journal_type, - roll_start, - roll_size); + std::move(ref_journal_trimmer)); if (journal_type == journal_type_t::SEGMENTED) { cache->set_segment_provider(*async_cleaner); @@ -686,10 +693,12 @@ TransactionManagerRef make_transaction_manager( JournalRef journal; if (journal_type == journal_type_t::SEGMENTED) { - journal = journal::make_segmented(*async_cleaner, *async_cleaner); + journal = journal::make_segmented( + *async_cleaner, + journal_trimmer); } else { journal = journal::make_circularbounded( - *async_cleaner, + journal_trimmer, static_cast(primary_device), ""); } -- 2.39.5