]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
crimson/os/seastore/async_cleaner: factor out JournalTrimmerImpl
authorYingxin Cheng <yingxin.cheng@intel.com>
Thu, 25 Aug 2022 13:36:27 +0000 (21:36 +0800)
committerYingxin Cheng <yingxin.cheng@intel.com>
Fri, 26 Aug 2022 09:50:19 +0000 (17:50 +0800)
Signed-off-by: Yingxin Cheng <yingxin.cheng@intel.com>
src/crimson/os/seastore/async_cleaner.cc
src/crimson/os/seastore/async_cleaner.h
src/crimson/os/seastore/transaction_manager.cc

index 81b2ddb8fcf3e897cb6a71d5bc9645bc0b1ebc4d..ebd994897ed78712ce4e3a5b6b45ad766a8fe49e 100644 (file)
@@ -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<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);
@@ -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<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,
@@ -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"
index f4a5b09efd7b3f90ce2d7b3bf95ab346a5699b96..2b89742ee5b96b57ccf9be6a04914243177dc132 100644 (file)
@@ -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<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;
 };
 
 /**
@@ -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<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;
@@ -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<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());
@@ -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) {
index 1fe81c856a6e923c8ba16b250f0197018d7eb4f1..03bff3ccd4d10d87b3769cf1babbce2e385413f1 100644 (file)
@@ -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<AsyncCleaner>(
     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<random_block_device::RBMDevice*>(primary_device),
       "");
   }