crimson/os/seastore/random_block_manager: try to allocate consecutive rbm space when rewriting extents

Add the address of the last allocation as the hint to the current round
of allocation in rbm

Signed-off-by: Xuehan Xu <xuxuehan@qianxin.com>
This commit is contained in:
Xuehan Xu 2025-09-03 16:10:28 +08:00
parent eb9e9b9d06
commit d86fb5fd80
15 changed files with 161 additions and 37 deletions

View File

@ -1388,15 +1388,11 @@ do_reclaim_space_ret do_reclaim_space(
&reclaimed, &t, modify_time, target_generation] {
DEBUGT("reclaim {} extents", t, extents.size());
// rewrite live extents
return trans_intr::do_for_each(
extents,
[&extent_callback, modify_time, &t,
&reclaimed, target_generation](auto ext)
{
reclaimed += ext->get_length();
return extent_callback.rewrite_extent(
t, ext, target_generation, modify_time);
});
for (auto &ext : extents) {
reclaimed += ext->get_length();
}
return extent_callback.rewrite_extents(
t, extents, target_generation, modify_time);
});
}).si_then([&extent_callback, &t] {
return extent_callback.submit_transaction_direct(t);

View File

@ -351,6 +351,14 @@ public:
rewrite_gen_t target_generation,
sea_time_point modify_time) = 0;
using rewrite_extents_iertr = base_iertr;
using rewrite_extents_ret = rewrite_extents_iertr::future<>;
virtual rewrite_extents_ret rewrite_extents(
Transaction &t,
std::vector<CachedExtentRef> &extents,
rewrite_gen_t target_generation,
sea_time_point modify_time) = 0;
/**
* promote_extent
*
@ -1898,10 +1906,11 @@ public:
return paddr;
}
std::list<alloc_paddr_result> alloc_paddrs(extent_len_t length) {
std::list<alloc_paddr_result> alloc_paddrs(
extent_len_t length, paddr_t hint) {
// TODO: implement allocation strategy (dirty metadata and multiple devices)
auto rbs = rb_group->get_rb_managers();
auto ret = rbs[0]->alloc_extents(length);
auto ret = rbs[0]->alloc_extents(length, hint);
if (!ret.empty()) {
stats.used_bytes += length;
}

View File

@ -1236,6 +1236,7 @@ CachedExtentRef Cache::alloc_new_non_data_extent_by_type(
extent_len_t length, ///< [in] length
placement_hint_t hint, ///< [in] user hint
rewrite_gen_t gen, ///< [in] rewrite generation
paddr_t paddr_hint,
bool is_tracked
)
{
@ -1243,7 +1244,7 @@ CachedExtentRef Cache::alloc_new_non_data_extent_by_type(
SUBDEBUGT(seastore_cache, "allocate {} 0x{:x}B, hint={}, gen={}",
t, type, length, hint, rewrite_gen_printer_t{gen});
ceph_assert(get_extent_category(type) == data_category_t::METADATA);
auto opt = alloc_option_t{hint, gen, is_tracked};
auto opt = alloc_option_t{hint, gen, is_tracked, paddr_hint};
switch (type) {
case extent_types_t::ROOT:
ceph_assert(0 == "ROOT is never directly alloc'd");

View File

@ -1295,6 +1295,7 @@ public:
extent_len_t length, ///< [in] length
placement_hint_t hint, ///< [in] user hint
rewrite_gen_t gen, ///< [in] rewrite generation
paddr_t paddr_hint,
bool is_tracked
);

View File

@ -126,7 +126,9 @@ public:
virtual paddr_t alloc_paddr(extent_len_t length) = 0;
virtual std::list<alloc_paddr_result> alloc_paddrs(extent_len_t length) = 0;
virtual std::list<alloc_paddr_result> alloc_paddrs(
extent_len_t length,
paddr_t hint) = 0;
using alloc_write_ertr = base_ertr;
using alloc_write_iertr = trans_iertr<alloc_write_ertr>;
@ -189,7 +191,7 @@ public:
return make_delayed_temp_paddr(0);
}
std::list<alloc_paddr_result> alloc_paddrs(extent_len_t length) final {
std::list<alloc_paddr_result> alloc_paddrs(extent_len_t length, paddr_t) final {
return {alloc_paddr_result{make_delayed_temp_paddr(0), length}};
}
@ -256,9 +258,10 @@ public:
return rb_cleaner->alloc_paddr(length);
}
std::list<alloc_paddr_result> alloc_paddrs(extent_len_t length) final {
std::list<alloc_paddr_result> alloc_paddrs(
extent_len_t length, paddr_t hint) final {
assert(rb_cleaner);
return rb_cleaner->alloc_paddrs(length);
return rb_cleaner->alloc_paddrs(length, hint);
}
bool can_inplace_rewrite(Transaction& t,
@ -450,6 +453,7 @@ public:
placement_hint_t hint;
rewrite_gen_t gen;
bool is_tracked;
paddr_t paddr_hint = P_ADDR_NULL;
write_policy_t write_policy = write_policy_t::WRITE_BACK;
#ifdef UNIT_TESTS_BUILT
std::optional<paddr_t> external_paddr = std::nullopt;
@ -528,7 +532,8 @@ public:
#endif
{
assert(category == data_category_t::DATA);
auto addrs = get_writer(opt.hint, category, opt.gen)->alloc_paddrs(length);
auto addrs = get_writer(opt.hint, category, opt.gen)->alloc_paddrs(
length, opt.paddr_hint);
for (auto &ext : addrs) {
auto left = ext.len;
while (left > 0) {

View File

@ -77,7 +77,7 @@ public:
using allocate_ret_bare = std::list<alloc_paddr_result>;
using allo_extents_ret = allocate_ertr::future<allocate_ret_bare>;
virtual allocate_ret_bare alloc_extents(size_t size) = 0;
virtual allocate_ret_bare alloc_extents(size_t size, paddr_t hint) = 0;
virtual void mark_space_used(paddr_t paddr, size_t len) = 0;
virtual void mark_space_free(paddr_t paddr, size_t len) = 0;

View File

@ -99,6 +99,15 @@ extent_len_t AvlAllocator::find_block(
if (p != extent_size_tree.rend()) {
max_size = p->end - p->start;
}
const auto compare = extent_tree.key_comp();
auto rs = extent_tree.lower_bound(extent_range_t{start, size}, compare);
if (rs != extent_tree.end()) {
uint64_t offset = rs->start;
if (offset + size <= rs->end) {
start = offset;
return size;
}
}
assert(max_size);
if (max_size <= size) {
@ -210,7 +219,7 @@ std::optional<interval_set<rbm_abs_addr>> AvlAllocator::alloc_extent(
}
std::optional<interval_set<rbm_abs_addr>> AvlAllocator::alloc_extents(
size_t size)
size_t size, rbm_abs_addr hint)
{
LOG_PREFIX(AvlAllocator::alloc_extents);
if (available_size < size) {
@ -224,10 +233,10 @@ std::optional<interval_set<rbm_abs_addr>> AvlAllocator::alloc_extents(
interval_set<rbm_abs_addr> result;
auto try_to_alloc_block = [this, &result, FNAME] (uint64_t alloc_size)
auto try_to_alloc_block = [this, hint, &result, FNAME] (uint64_t alloc_size)
{
rbm_abs_addr start = hint;
while (alloc_size) {
rbm_abs_addr start = 0;
extent_len_t len = find_block(std::min(max_alloc_size, alloc_size), start);
ceph_assert(len);
_remove_from_tree(start, len);

View File

@ -65,7 +65,7 @@ public:
std::optional<interval_set<rbm_abs_addr>> alloc_extent(
size_t size) final;
std::optional<interval_set<rbm_abs_addr>> alloc_extents(
size_t size) final;
size_t size, rbm_abs_addr hint) final;
void free_extent(rbm_abs_addr addr, size_t size) final;
void mark_extent_used(rbm_abs_addr addr, size_t size) final;

View File

@ -66,11 +66,13 @@ paddr_t BlockRBManager::alloc_extent(size_t size)
}
BlockRBManager::allocate_ret_bare
BlockRBManager::alloc_extents(size_t size)
BlockRBManager::alloc_extents(size_t size, paddr_t hint)
{
LOG_PREFIX(BlockRBManager::alloc_extents);
assert(allocator);
auto alloc = allocator->alloc_extents(size);
rbm_abs_addr rbm_hint =
(hint == P_ADDR_NULL ? 0 : convert_paddr_to_abs_addr(hint));
auto alloc = allocator->alloc_extents(size, rbm_hint);
if (!alloc) {
return {};
}

View File

@ -58,7 +58,8 @@ public:
*/
paddr_t alloc_extent(size_t size) override; // allocator, return blocks
allocate_ret_bare alloc_extents(size_t size) override; // allocator, return blocks
allocate_ret_bare alloc_extents(
size_t size, paddr_t hint) override; // allocator, return blocks
void complete_allocation(paddr_t addr, size_t size) override;

View File

@ -35,7 +35,7 @@ public:
*
*/
virtual std::optional<interval_set<rbm_abs_addr>> alloc_extents(
size_t size) = 0;
size_t size, rbm_abs_addr hint) = 0;
/**
* free_extent

View File

@ -861,10 +861,12 @@ TransactionManager::get_next_dirty_extents(
return cache->get_next_dirty_extents(t, seq, max_bytes);
}
TransactionManager::rewrite_extent_ret
TransactionManager::rewrite_extent_iertr::future<
std::vector<CachedExtentRef>>
TransactionManager::rewrite_logical_extent(
Transaction& t,
LogicalChildNodeRef extent)
LogicalChildNodeRef extent,
paddr_t paddr_hint)
{
LOG_PREFIX(TransactionManager::rewrite_logical_extent);
if (extent->has_been_invalidated()) {
@ -890,6 +892,7 @@ TransactionManager::rewrite_logical_extent(
extent->get_user_hint(),
// get target rewrite generation
extent->get_rewrite_generation(),
paddr_hint,
is_tracked)->cast<LogicalChildNode>();
assert(nextent->get_write_policy() != write_policy_t::WRITE_THROUGH);
nextent->rewrite(t, *extent, 0);
@ -921,6 +924,7 @@ TransactionManager::rewrite_logical_extent(
extent->get_paddr(),
*nextent
);
co_return std::vector<CachedExtentRef>{nextent};
} else {
assert(get_extent_category(extent->get_type()) == data_category_t::DATA);
@ -938,6 +942,7 @@ TransactionManager::rewrite_logical_extent(
// get target rewrite generation
extent->get_rewrite_generation(),
is_tracked,
paddr_hint,
// WRITH_THROUGH is only effective for client io, so
// always set the write policy to WRITE_BACK here
write_policy_t::WRITE_BACK
@ -996,6 +1001,7 @@ TransactionManager::rewrite_logical_extent(
off += nextent->get_length();
left -= nextent->get_length();
}
co_return std::move(extents);
}
}
@ -1062,7 +1068,9 @@ TransactionManager::rewrite_extent_ret TransactionManager::rewrite_extent(
auto fut = rewrite_extent_iertr::now();
if (extent->is_logical()) {
assert(is_logical_type(extent->get_type()));
fut = rewrite_logical_extent(t, extent->cast<LogicalChildNode>());
fut = rewrite_logical_extent(
t, extent->cast<LogicalChildNode>(), P_ADDR_NULL
).discard_result();
} else if (is_backref_node(extent->get_type())) {
fut = backref_manager->rewrite_extent(t, extent);
} else {
@ -1237,6 +1245,7 @@ TransactionManager::promote_extent(
placement_hint_t::HOT,
INIT_GENERATION,
true,
P_ADDR_NULL,
write_policy_t::WRITE_BACK
});
t.touch_laddr_prefix(orig_ext->get_laddr().get_object_prefix());
@ -1284,6 +1293,7 @@ TransactionManager::promote_extent(
orig_ext->get_length(),
placement_hint_t::HOT,
INIT_GENERATION,
P_ADDR_NULL,
true);
auto lext = promoted_extent->cast<LogicalChildNode>();
lext->set_laddr(orig_ext->get_laddr());
@ -1321,6 +1331,83 @@ TransactionManager::promote_extent(
t, *mapping.direct_cursor, std::move(promoted_extents));
}
TransactionManager::rewrite_extents_ret TransactionManager::rewrite_extents(
Transaction &t,
std::vector<CachedExtentRef> &extents,
rewrite_gen_t target_generation,
sea_time_point modify_time)
{
LOG_PREFIX(TransactionManager::rewrite_extents);
return seastar::do_with(
P_ADDR_NULL,
L_ADDR_NULL,
[this, &t, target_generation, modify_time, &extents, FNAME]
(auto &paddr_hint, auto &next_laddr) {
return trans_intr::do_for_each(
extents,
[this, &t, target_generation, modify_time, FNAME,
&paddr_hint, &next_laddr](auto &extent) {
{
auto updated = cache->update_extent_from_transaction(t, extent);
if (!updated) {
DEBUGT("extent is already retired, skipping -- {}", t, *extent);
return rewrite_extent_iertr::now();
}
extent = updated;
ceph_assert(!extent->is_pending_io());
}
assert(extent->is_valid() && !extent->is_initial_pending());
if (extent->is_stable_dirty()) {
if (epm->can_inplace_rewrite(t, extent)) {
DEBUGT("delta overwriting extent -- {}", t, *extent);
t.add_inplace_rewrite_extent(extent);
extent->set_inplace_rewrite_generation();
return rewrite_extent_iertr::now();
}
if (extent->get_version() == 1 && extent->has_mutation()) {
t.get_rewrite_stats().account_n_dirty();
} else {
// extent->get_version() > 1 or DIRTY
t.get_rewrite_stats().account_dirty(extent->get_version());
}
extent->set_target_rewrite_generation(INIT_GENERATION);
} else {
extent->set_target_rewrite_generation(target_generation);
ceph_assert(modify_time != NULL_TIME);
extent->set_modify_time(modify_time);
}
if (is_backref_node(extent->get_type())) {
DEBUGT("rewriting backref extent -- {}", t, *extent);
return backref_manager->rewrite_extent(t, extent);
}
if (extent->get_type() == extent_types_t::ROOT) {
DEBUGT("rewriting root extent -- {}", t, *extent);
cache->duplicate_for_write(t, extent);
return rewrite_extent_iertr::now();
}
if (extent->is_logical()) {
auto ext = extent->template cast<LogicalChildNode>();
if (next_laddr != ext->get_laddr()) {
paddr_hint = P_ADDR_NULL;
}
next_laddr = (ext->get_laddr() + ext->get_length()).checked_to_laddr();
return rewrite_logical_extent(t, ext, paddr_hint
).si_then([&paddr_hint](auto nlextents) {
for (auto &nlextent : nlextents) {
paddr_hint = nlextent->get_paddr() + nlextent->get_length();
}
});
} else {
DEBUGT("rewriting physical extent -- {}", t, *extent);
return lba_manager->rewrite_extent(t, extent);
}
});
});
}
TransactionManager::demote_region_ret
TransactionManager::demote_region(
Transaction &t,
@ -1337,6 +1424,7 @@ TransactionManager::demote_region(
crimson::ct_error::assert_all("unexpected enoent"));
auto it = co_await resolve_cursor_to_mapping(t, std::move(cursor));
demote_region_res_t ret{0, 0, false};
std::vector<CachedExtentRef> extents;
while ((ret.demoted_size + ret.evicted_size) < max_proceed_size) {
if (it.is_end() || it.get_key().get_object_prefix() != prefix) {
ret.complete = true;
@ -1360,8 +1448,7 @@ TransactionManager::demote_region(
auto extent = co_await read_cursor_by_type(
t, it.direct_cursor, it.get_extent_type());
ret.evicted_size += extent->get_length();
extent->set_target_rewrite_generation(epm->get_max_hot_gen() + 1);
co_await rewrite_logical_extent(t, extent);
extents.push_back(extent);
it = co_await it.next();
} else {
DEBUGT("skip {}", t, it);
@ -1369,6 +1456,10 @@ TransactionManager::demote_region(
}
}
co_await rewrite_extents(
t, extents, epm->get_max_hot_gen() + 1,
seastar::lowres_system_clock::now());
co_return ret;
}

View File

@ -615,7 +615,7 @@ public:
auto exts = cache->alloc_new_data_extents<T>(
t, len,
{
placement_hint, INIT_GENERATION, false,
placement_hint, INIT_GENERATION, false, P_ADDR_NULL,
epm->get_write_policy(T::TYPE, len)
});
// user must initialize the logical extent themselves
@ -954,6 +954,13 @@ public:
rewrite_gen_t target_generation,
sea_time_point modify_time) final;
using ExtentCallbackInterface::rewrite_extents_ret;
rewrite_extents_ret rewrite_extents(
Transaction &t,
std::vector<CachedExtentRef> &extents,
rewrite_gen_t target_generation,
sea_time_point modify_time) final;
using ExtentCallbackInterface::promote_extent_ret;
promote_extent_ret promote_extent(
Transaction &t,
@ -1709,9 +1716,11 @@ private:
Transaction &t,
LBAMapping mapping);
rewrite_extent_ret rewrite_logical_extent(
rewrite_extent_iertr::future<std::vector<CachedExtentRef>>
rewrite_logical_extent(
Transaction& t,
LogicalChildNodeRef extent);
LogicalChildNodeRef extent,
paddr_t hint);
submit_transaction_direct_ret do_submit_transaction(
Transaction &t,

View File

@ -328,7 +328,7 @@ struct lba_btree_test : btree_test_base {
lba_btree_update([=, this](auto &btree, auto &t) {
auto extents = cache->alloc_new_data_extents<TestBlock>(
t, TestBlock::SIZE,
{placement_hint_t::HOT, 0, false,
{placement_hint_t::HOT, 0, false, P_ADDR_NULL,
write_policy_t::WRITE_BACK, get_paddr()});
return seastar::do_with(
std::move(extents),
@ -548,7 +548,7 @@ struct btree_lba_manager_test : btree_test_base {
[=, this](auto &t) {
auto extents = cache->alloc_new_data_extents<TestBlock>(
t, TestBlock::SIZE,
{placement_hint_t::HOT, 0, false,
{placement_hint_t::HOT, 0, false, P_ADDR_NULL,
write_policy_t::WRITE_BACK, get_paddr()});
return seastar::do_with(
std::vector<LogicalChildNodeRef>(

View File

@ -58,7 +58,7 @@ struct allocator_test_t :
return allocator->alloc_extent(size);
}
auto allocates(size_t size) {
return allocator->alloc_extents(size);
return allocator->alloc_extents(size, 0);
}
void free(uint64_t start, uint64_t length) {
allocator->free_extent(start, length);