#include "crimson/os/seastore/async_cleaner.h"
#include "crimson/os/seastore/backref_manager.h"
+#include "crimson/os/seastore/lba_manager.h"
#include "crimson/os/seastore/transaction_manager.h"
SET_SUBSYS(seastore_cleaner);
config_t config,
backend_type_t type,
device_off_t roll_start,
- device_off_t roll_size)
- : backref_manager(backref_manager),
+ device_off_t roll_size,
+ bool tail_include_alloc)
+ : JournalTrimmer(tail_include_alloc),
+ backref_manager(backref_manager),
config(config),
backend_type(type),
roll_start(roll_start),
}
}
- if (alloc_tail != JOURNAL_SEQ_NULL) {
+ if (tail_include_alloc && alloc_tail != JOURNAL_SEQ_NULL) {
ceph_assert(journal_head == JOURNAL_SEQ_NULL ||
journal_head >= alloc_tail);
if (journal_alloc_tail != JOURNAL_SEQ_NULL &&
std::size_t JournalTrimmerImpl::get_alloc_journal_size() const
{
- if (!background_callback->is_ready()) {
+ if (!background_callback->is_ready() ||
+ !tail_include_alloc) {
return 0;
}
auto ret = journal_head.relative_to(
});
}
-bool SegmentCleaner::check_usage()
+bool SegmentCleaner::check_usage(bool)
{
SpaceTrackerIRef tracker(space_tracker->make_empty());
extent_callback->with_transaction_weak(
RBMCleaner::RBMCleaner(
RBMDeviceGroupRef&& rb_group,
BackrefManager &backref_manager,
+ LBAManager &lba_manager,
bool detailed)
: detailed(detailed),
rb_group(std::move(rb_group)),
- backref_manager(backref_manager)
+ backref_manager(backref_manager),
+ lba_manager(lba_manager)
{}
void RBMCleaner::print(std::ostream &os, bool is_detailed) const
});
}
-bool RBMCleaner::check_usage()
+bool RBMCleaner::check_usage(bool has_cold_tier)
{
assert(detailed);
const auto& rbms = rb_group->get_rb_managers();
extent_callback->with_transaction_weak(
"check_usage",
CACHE_HINT_NOCACHE,
- [this, &tracker, &rbms](auto &t) {
- return backref_manager.scan_mapped_space(
- t,
- [&tracker, &rbms](
- paddr_t paddr,
- paddr_t backref_key,
- extent_len_t len,
- extent_types_t type,
- laddr_t laddr)
- {
- for (auto rbm : rbms) {
- if (rbm->get_device_id() == paddr.get_device_id()) {
- if (is_backref_node(type)) {
- assert(laddr == L_ADDR_NULL);
- assert(backref_key.is_absolute_random_block()
- || backref_key == P_ADDR_MIN);
- tracker.allocate(
- paddr,
- len);
- } else if (laddr == L_ADDR_NULL) {
- assert(backref_key == P_ADDR_NULL);
- tracker.release(
- paddr,
- len);
- } else {
- assert(backref_key == P_ADDR_NULL);
- tracker.allocate(
- paddr,
- len);
- }
- }
- }
- });
+ [this, &tracker, &rbms, has_cold_tier](auto &t) {
+ if (has_cold_tier) {
+ return backref_manager.scan_mapped_space(
+ t,
+ [&tracker, &rbms](
+ paddr_t paddr,
+ paddr_t backref_key,
+ extent_len_t len,
+ extent_types_t type,
+ laddr_t laddr)
+ {
+ for (auto rbm : rbms) {
+ if (rbm->get_device_id() == paddr.get_device_id()) {
+ if (is_backref_node(type)) {
+ assert(laddr == L_ADDR_NULL);
+ assert(backref_key.is_absolute_random_block()
+ || backref_key == P_ADDR_MIN);
+ tracker.allocate(
+ paddr,
+ len);
+ } else if (laddr == L_ADDR_NULL) {
+ assert(backref_key == P_ADDR_NULL);
+ tracker.release(
+ paddr,
+ len);
+ } else {
+ assert(backref_key == P_ADDR_NULL);
+ tracker.allocate(
+ paddr,
+ len);
+ }
+ }
+ }
+ });
+ } else {
+ return lba_manager.scan_mapped_space(
+ t,
+ [&tracker, &rbms](
+ paddr_t paddr,
+ extent_len_t len,
+ extent_types_t type,
+ laddr_t laddr)
+ {
+ for (auto rbm : rbms) {
+ if (rbm->get_device_id() == paddr.get_device_id()) {
+ tracker.allocate(paddr, len);
+ }
+ }
+ });
+ }
}).unsafe_get();
return equals(tracker);
}
*/
class JournalTrimmer {
public:
+ JournalTrimmer(bool tail_include_alloc)
+ : tail_include_alloc(tail_include_alloc) {}
// get the committed journal head
virtual journal_seq_t get_journal_head() const = 0;
virtual ~JournalTrimmer() {}
journal_seq_t get_journal_tail() const {
- return std::min(get_alloc_tail(), get_dirty_tail());
+ if (tail_include_alloc) {
+ return std::min(get_alloc_tail(), get_dirty_tail());
+ } else {
+ return get_dirty_tail();
+ }
}
virtual std::size_t get_trim_size_per_cycle() const = 0;
bool check_is_ready() const {
return (get_journal_head() != JOURNAL_SEQ_NULL &&
get_dirty_tail() != JOURNAL_SEQ_NULL &&
- get_alloc_tail() != JOURNAL_SEQ_NULL);
+ (get_alloc_tail() != JOURNAL_SEQ_NULL ||
+ !tail_include_alloc));
}
std::size_t get_num_rolls() const {
return get_journal_head_sequence() + 1 -
get_journal_tail().segment_seq;
}
+protected:
+ bool tail_include_alloc = true;
};
class BackrefManager;
+class LBAManager;
class JournalTrimmerImpl;
using JournalTrimmerImplRef = std::unique_ptr<JournalTrimmerImpl>;
config_t config,
backend_type_t type,
device_off_t roll_start,
- device_off_t roll_size);
+ device_off_t roll_size,
+ bool tail_include_alloc);
~JournalTrimmerImpl() = default;
config_t config,
backend_type_t type,
device_off_t roll_start,
- device_off_t roll_size) {
+ device_off_t roll_size,
+ bool tail_include_alloc) {
return std::make_unique<JournalTrimmerImpl>(
- backref_manager, config, type, roll_start, roll_size);
+ backref_manager, config, type, roll_start,
+ roll_size, tail_include_alloc);
}
struct stat_printer_t {
return target <= journal_dirty_tail;
}
+ bool can_drop_backref() const {
+ return get_backend_type() == backend_type_t::RANDOM_BLOCK;
+ }
+
bool should_trim_alloc() const {
+ if (can_drop_backref()) {
+ return false;
+ }
return get_alloc_tail_target() > journal_alloc_tail;
}
#endif
// test only
- virtual bool check_usage() = 0;
+ virtual bool check_usage(bool has_cold_tier) = 0;
struct stat_printer_t {
const AsyncCleaner &cleaner;
// Testing interfaces
- bool check_usage() final;
+ bool check_usage(bool has_cold_tier) final;
private:
/*
RBMCleaner(
RBMDeviceGroupRef&& rb_group,
BackrefManager &backref_manager,
+ LBAManager &lba_manager,
bool detailed);
static RBMCleanerRef create(
RBMDeviceGroupRef&& rb_group,
BackrefManager &backref_manager,
+ LBAManager &lba_manager,
bool detailed) {
return std::make_unique<RBMCleaner>(
- std::move(rb_group), backref_manager, detailed);
+ std::move(rb_group), backref_manager, lba_manager, detailed);
}
RBMDeviceGroup* get_rb_group() {
// Testing interfaces
- bool check_usage() final;
+ bool check_usage(bool has_cold_tier) final;
bool check_usage_is_empty() const final {
// TODO
const bool detailed;
RBMDeviceGroupRef rb_group;
BackrefManager &backref_manager;
+ LBAManager &lba_manager;
struct {
/**
extent->get_paddr(),
extent->get_length(),
extent->get_type()));
- backref_entries.emplace_back(
- backref_entry_t::create_retire(
- extent->get_paddr(),
- extent->get_length(),
- extent->get_type()));
+ if (!can_drop_backref()) {
+ backref_entries.emplace_back(
+ backref_entry_t::create_retire(
+ extent->get_paddr(),
+ extent->get_length(),
+ extent->get_type()));
+ }
} else if (is_backref_node(extent->get_type())) {
// The retire alloc deltas are used to identify the invalid backref extent
// deltas during replay when using CircularBoundedJournal, see
extent->get_paddr(),
extent->get_length(),
extent->get_type()));
- remove_backref_extent(extent->get_paddr());
+ if (!can_drop_backref()) {
+ remove_backref_extent(extent->get_paddr());
+ }
} else {
ERRORT("Got unexpected extent type: {}", t, *extent);
ceph_abort_msg("imposible");
i->get_length(),
i->get_type()));
+ if (can_drop_backref()) {
+ continue;
+ }
+
// Note: commit extents and backref allocations in the same place
// Note: remapping is split into 2 steps, retire and alloc, they must be
// committed atomically together
record.push_back(std::move(delta));
}
- apply_backref_mset(backref_entries);
- t.set_backref_entries(std::move(backref_entries));
+ if (!can_drop_backref()) {
+ apply_backref_mset(backref_entries);
+ t.set_backref_entries(std::move(backref_entries));
+ }
ceph_assert(t.get_fresh_block_stats().num ==
t.inline_block_list.size() +
i->complete_io();
epm.commit_space_used(i->get_paddr(), i->get_length());
+ if (can_drop_backref()) {
+ return;
+ }
// Note: commit extents and backref allocations in the same place
if (is_backref_mapped_type(i->get_type())) {
DEBUGT("backref_entry alloc {}~0x{:x}",
last_commit = start_seq;
- apply_backref_byseq(t.move_backref_entries(), start_seq);
- commit_backref_entries(std::move(backref_entries), start_seq);
+ if (!can_drop_backref()) {
+ apply_backref_byseq(t.move_backref_entries(), start_seq);
+ commit_backref_entries(std::move(backref_entries), start_seq);
+ }
}
void Cache::init()
{
LOG_PREFIX(Cache::replay_delta);
assert(dirty_tail != JOURNAL_SEQ_NULL);
- assert(alloc_tail != JOURNAL_SEQ_NULL);
+ if (!can_drop_backref()) {
+ assert(alloc_tail != JOURNAL_SEQ_NULL);
+ }
ceph_assert(modify_time != NULL_TIME);
// FIXME: This is specific to the segmented implementation
// replay alloc
if (delta.type == extent_types_t::ALLOC_INFO) {
+ if (can_drop_backref()) {
+ return replay_delta_ertr::make_ready_future<
+ std::pair<bool, CachedExtentRef>>(std::make_pair(false, nullptr));
+ }
+
if (journal_seq < alloc_tail) {
DEBUG("journal_seq {} < alloc_tail {}, don't replay {}",
journal_seq, alloc_tail, delta);
return query_cache(offset);
}
+ bool can_drop_backref() const {
+ return epm.is_pure_rbm();
+ }
+
private:
using get_extent_ertr = base_ertr;
template <typename T>
return primary_device->get_backend_type();
}
+
+ bool is_pure_rbm() const {
+ return get_main_backend_type() == backend_type_t::RANDOM_BLOCK &&
+ // as of now, cold tier can only be segmented.
+ !background_process.has_cold_tier();
+ }
+
// Testing interfaces
void test_init_no_background(Device *test_device) {
// Testing interfaces
bool check_usage() {
- return main_cleaner->check_usage() &&
- (!has_cold_tier() || cold_cleaner->check_usage());
+ return main_cleaner->check_usage(has_cold_tier()) &&
+ (!has_cold_tier() || cold_cleaner->check_usage(true));
}
seastar::future<> run_until_halt();
template <> struct fmt::formatter<crimson::os::seastore::dirty_io_stats_printer_t> : fmt::ostream_formatter {};
template <> struct fmt::formatter<crimson::os::seastore::extent_types_t> : fmt::ostream_formatter {};
template <> struct fmt::formatter<crimson::os::seastore::journal_seq_t> : fmt::ostream_formatter {};
+template <> struct fmt::formatter<crimson::os::seastore::backend_type_t> : fmt::ostream_formatter {};
template <> struct fmt::formatter<crimson::os::seastore::journal_tail_delta_t> : fmt::ostream_formatter {};
template <> struct fmt::formatter<crimson::os::seastore::laddr_t> : fmt::ostream_formatter {};
template <> struct fmt::formatter<crimson::os::seastore::laddr_offset_t> : fmt::ostream_formatter {};
}
}).si_then([this, &t] {
epm->start_scan_space();
- return backref_manager->scan_mapped_space(
- t,
- [this](
- paddr_t paddr,
- paddr_t backref_key,
- extent_len_t len,
- extent_types_t type,
- laddr_t laddr) {
- assert(paddr.is_absolute());
- if (is_backref_node(type)) {
- assert(laddr == L_ADDR_NULL);
- assert(backref_key.is_absolute() || backref_key == P_ADDR_MIN);
- backref_manager->cache_new_backref_extent(paddr, backref_key, type);
+ if (can_drop_backref()) {
+ return lba_manager->scan_mapped_space(
+ t,
+ [this](
+ paddr_t paddr,
+ extent_len_t len,
+ extent_types_t type,
+ laddr_t laddr) {
+ assert(paddr.is_absolute());
cache->update_tree_extents_num(type, 1);
epm->mark_space_used(paddr, len);
- } else if (laddr == L_ADDR_NULL) {
- assert(backref_key == P_ADDR_NULL);
- cache->update_tree_extents_num(type, -1);
- epm->mark_space_free(paddr, len);
- } else {
- assert(backref_key == P_ADDR_NULL);
- cache->update_tree_extents_num(type, 1);
- epm->mark_space_used(paddr, len);
- }
- });
+ });
+ } else {
+ return backref_manager->scan_mapped_space(
+ t,
+ [this](
+ paddr_t paddr,
+ paddr_t backref_key,
+ extent_len_t len,
+ extent_types_t type,
+ laddr_t laddr) {
+ assert(paddr.is_absolute());
+ if (is_backref_node(type)) {
+ assert(laddr == L_ADDR_NULL);
+ assert(backref_key.is_absolute() || backref_key == P_ADDR_MIN);
+ backref_manager->cache_new_backref_extent(
+ paddr, backref_key, type);
+ cache->update_tree_extents_num(type, 1);
+ epm->mark_space_used(paddr, len);
+ } else if (laddr == L_ADDR_NULL) {
+ assert(backref_key == P_ADDR_NULL);
+ cache->update_tree_extents_num(type, -1);
+ epm->mark_space_free(paddr, len);
+ } else {
+ assert(backref_key == P_ADDR_NULL);
+ cache->update_tree_extents_num(type, 1);
+ epm->mark_space_used(paddr, len);
+ }
+ });
+ }
});
});
}).safe_then([this] {
shard_stats_t& shard_stats,
bool is_test)
{
+ LOG_PREFIX(make_transaction_manager);
rewrite_gen_t hot_tier_generations = crimson::common::get_conf<uint64_t>(
"seastore_hot_tier_generations");
rewrite_gen_t cold_tier_generations = crimson::common::get_conf<uint64_t>(
roll_size, backend_type);
}
+ bool pure_rbm_backend =
+ (p_backend_type == backend_type_t::RANDOM_BLOCK) && !cold_sms;
auto journal_trimmer = JournalTrimmerImpl::create(
*backref_manager, trimmer_config,
- backend_type, roll_start, roll_size);
+ backend_type, roll_start, roll_size,
+ !pure_rbm_backend);
AsyncCleanerRef cleaner;
JournalRef journal;
cleaner = RBMCleaner::create(
std::move(rbs),
*backref_manager,
+ *lba_manager,
cleaner_is_detailed);
journal = journal::make_circularbounded(
*journal_trimmer,
std::move(cold_segment_cleaner));
epm->set_primary_device(primary_device);
+ INFO("main backend type: {}, cold tier: {}",
+ epm->get_main_backend_type(), (bool)cold_sms);
return std::make_unique<TransactionManager>(
std::move(journal),
std::move(cache),
shard_stats_t& shard_stats;
+ bool can_drop_backref() const {
+ return cache->can_drop_backref();
+ }
+
using LBALeafNode = lba::LBALeafNode;
struct unlinked_child_t {
LBAMapping mapping;
mutable segment_info_t tmp_info;
- btree_test_base() = default;
+ btree_test_base() : JournalTrimmer(true) {}
/*
* JournalTrimmer interfaces
uint64_t block_size;
WritePipeline pipeline;
- cbjournal_test_t() = default;
+ cbjournal_test_t() : JournalTrimmer(true) {}
/*
* JournalTrimmer interfaces
mutable segment_info_t tmp_info;
- journal_test_t() = default;
+ journal_test_t() : JournalTrimmer(true) {}
/*
* JournalTrimmer interfaces