<< ")";
}
+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<seastore_off_t>(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<seastore_off_t>(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<seastore_off_t>(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<std::size_t>(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<std::size_t>(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<journal_seq_t>(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);
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()
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,
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);
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;
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 {
}
}
-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<journal_seq_t>(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<CachedExtentRef> &backref_extents,
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 ?
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();
}
}
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;
// 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;
}
{
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";
<< ", 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"
};
/**
- * Callback interface for journal trimming
+ * Callback interface for Journal
*/
class JournalTrimmer {
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;
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<JournalTrimmerImpl>;
+
+/**
+ * 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<JournalTrimmerImpl>(
+ 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;
};
/**
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,
/// 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
+ };
}
};
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;
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;
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
*/
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<seastore_off_t>(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<seastore_off_t>(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<seastore_off_t>(config.max_journal_bytes),
- roll_start,
- roll_size);
- return ret;
- }
-
struct reclaim_state_t {
reclaim_gen_t generation;
reclaim_gen_t target_generation;
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(
* 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();
(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<std::size_t>(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<std::size_t>(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());
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;
);
}
- 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
*
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) {