LOG_PREFIX(Cache::duplicate_for_write);
assert(i->is_fully_loaded());
+#ifndef NDEBUG
+ if (i->is_logical()) {
+ assert(static_cast<LogicalCachedExtent&>(*i).has_laddr());
+ assert(static_cast<LogicalCachedExtent&>(*i).is_seen_by_users());
+ }
+#endif
+
if (i->is_mutable()) {
return i;
}
if (i->is_exist_clean()) {
assert(i->is_logical());
- assert(static_cast<LogicalCachedExtent&>(*i).has_laddr());
i->version++;
i->state = CachedExtent::extent_state_t::EXIST_MUTATION_PENDING;
i->last_committed_crc = i->calc_crc32c();
ret->version++;
ret->state = CachedExtent::extent_state_t::MUTATION_PENDING;
if (i->is_logical()) {
- auto& lextent = static_cast<LogicalCachedExtent&>(*i);
- assert(lextent.has_laddr());
+ auto& stable_extent = static_cast<LogicalCachedExtent&>(*i);
assert(ret->is_logical());
- static_cast<LogicalCachedExtent&>(*ret).set_laddr(lextent.get_laddr());
+ auto& mutate_extent = static_cast<LogicalCachedExtent&>(*ret);
+ mutate_extent.set_laddr(stable_extent.get_laddr());
+ assert(mutate_extent.is_seen_by_users());
}
t.add_mutated_extent(ret);
/// used to wait while in-progress commit completes
std::optional<seastar::shared_promise<>> io_wait_promise;
+
void set_io_wait() {
ceph_assert(!io_wait_promise);
io_wait_promise = seastar::shared_promise<>();
}
+
void complete_io() {
ceph_assert(io_wait_promise);
io_wait_promise->set_value();
*/
class LogicalCachedExtent : public CachedExtent {
public:
- template <typename... T>
- LogicalCachedExtent(T&&... t) : CachedExtent(std::forward<T>(t)...) {}
+ using CachedExtent::CachedExtent;
+
+ LogicalCachedExtent(const LogicalCachedExtent& other)
+ : CachedExtent(other),
+ seen_by_users(other.seen_by_users) {}
+
+ LogicalCachedExtent(const LogicalCachedExtent& other, share_buffer_t s)
+ : CachedExtent(other, s),
+ seen_by_users(other.seen_by_users) {}
void on_rewrite(Transaction &t, CachedExtent &extent, extent_len_t off) final {
assert(get_type() == extent.get_type());
auto &lextent = (LogicalCachedExtent&)extent;
set_laddr((lextent.get_laddr() + off).checked_to_laddr());
+ seen_by_users = lextent.seen_by_users;
do_on_rewrite(t, lextent);
}
virtual void do_on_rewrite(Transaction &t, LogicalCachedExtent &extent) {}
+ bool is_seen_by_users() const {
+ return seen_by_users;
+ }
+
+ // handled under TransactionManager,
+ // user should not set this by themselves.
+ void set_seen_by_users() {
+ assert(!seen_by_users);
+ seen_by_users = true;
+ }
+
bool has_laddr() const {
return laddr != L_ADDR_NULL;
}
// the logical address of the extent, and if shared,
// it is the intermediate_base, see BtreeLBAMapping comments.
laddr_t laddr = L_ADDR_NULL;
+
+ // whether the extent has been seen by users since OSD startup,
+ // otherwise, an extent can still be alive if it's dirty or pinned by lru.
+ //
+ // user must initialize the logical extent upon the first access.
+ bool seen_by_users = false;
};
using LogicalCachedExtentRef = TCachedExtentRef<LogicalCachedExtent>;
static_assert(is_logical_type(T::TYPE));
assert(is_aligned(partial_off, get_block_size()));
assert(is_aligned(partial_len, get_block_size()));
+ // must be user-oriented required by maybe_init
+ assert(is_user_transaction(t.get_src()));
extent_len_t direct_partial_off = partial_off;
bool is_clone = pin->is_clone();
return cache->read_extent_maybe_partial(
t, std::move(extent), direct_partial_off, partial_len);
}).si_then([maybe_init=std::move(maybe_init)](auto extent) {
- maybe_init(*extent);
- return extent;
- });
+ if (!extent->is_seen_by_users()) {
+ maybe_init(*extent);
+ extent->set_seen_by_users();
+ }
+ return std::move(extent);
+ });
} else {
return this->pin_to_extent<T>(
t, std::move(std::get<0>(ret)),
laddr_t laddr_hint,
extent_len_t len,
placement_hint_t placement_hint = placement_hint_t::HOT) {
+ static_assert(is_logical_metadata_type(T::TYPE));
LOG_PREFIX(TransactionManager::alloc_non_data_extent);
SUBDEBUGT(seastore_tm, "{} hint {}~0x{:x} phint={} ...",
t, T::TYPE, laddr_hint, len, placement_hint);
len,
placement_hint,
INIT_GENERATION);
+ // user must initialize the logical extent themselves.
+ assert(is_user_transaction(t.get_src()));
+ ext->set_seen_by_users();
return lba_manager->alloc_extent(
t,
laddr_hint,
laddr_t laddr_hint,
extent_len_t len,
placement_hint_t placement_hint = placement_hint_t::HOT) {
+ static_assert(is_data_type(T::TYPE));
LOG_PREFIX(TransactionManager::alloc_data_extents);
SUBDEBUGT(seastore_tm, "{} hint {}~0x{:x} phint={} ...",
t, T::TYPE, laddr_hint, len, placement_hint);
len,
placement_hint,
INIT_GENERATION);
+ // user must initialize the logical extent themselves
+ assert(is_user_transaction(t.get_src()));
+ for (auto& ext : exts) {
+ ext->set_seen_by_users();
+ }
return lba_manager->alloc_extents(
t,
laddr_hint,
LBAMappingRef &&pin,
std::array<remap_entry, N> remaps) {
static_assert(std::is_base_of_v<LogicalChildNode, T>);
+ // data extents don't need maybe_init yet, currently,
+ static_assert(is_data_type(T::TYPE));
+ // must be user-oriented required by (the potential) maybe_init
+ assert(is_user_transaction(t.get_src()));
#ifndef NDEBUG
std::sort(remaps.begin(), remaps.end(),
} else {
auto ret = get_extent_if_linked<T>(t, pin->duplicate());
if (ret.index() == 1) {
- return std::move(std::get<1>(ret));
+ return std::get<1>(ret
+ ).si_then([](auto extent) {
+ if (!extent->is_seen_by_users()) {
+ // Note, no maybe_init available for data extents
+ extent->set_seen_by_users();
+ }
+ return std::move(extent);
+ });
} else {
// absent
return base_iertr::make_ready_future<TCachedExtentRef<T>>();
original_bptr = ext->get_bptr();
}
if (ext) {
+ assert(ext->is_seen_by_users());
cache->retire_extent(t, ext);
} else {
cache->retire_absent_extent_addr(t, original_paddr, original_len);
remap_len,
original_laddr,
original_bptr);
+ // user must initialize the logical extent themselves.
+ extent->set_seen_by_users();
extents.emplace_back(std::move(extent));
}
});
extent_len_t partial_len,
lextent_init_func_t<T> &&maybe_init) {
static_assert(is_logical_type(T::TYPE));
+ // must be user-oriented required by maybe_init
+ assert(is_user_transaction(t.get_src()));
using ret = pin_to_extent_ret<T>;
auto &pref = *pin;
auto direct_length = pref.is_indirect() ?
pref.link_child(&extent);
extent.maybe_set_intermediate_laddr(pref);
maybe_init(extent);
+ extent.set_seen_by_users();
}
).si_then([FNAME, &t, pin=std::move(pin), this](auto ref) mutable -> ret {
if (ref->is_fully_loaded()) {
SUBTRACET(seastore_tm, "getting absent extent from pin {} type {} ...",
t, *pin, type);
assert(is_logical_type(type));
+ assert(is_background_transaction(t.get_src()));
auto &pref = *pin;
laddr_t direct_key;
extent_len_t direct_length;
assert(!pref.get_parent()->is_pending());
pref.link_child(&lextent);
lextent.maybe_set_intermediate_laddr(pref);
+ // No change to extent::seen_by_user because this path is only
+ // for background cleaning.
}
).si_then([FNAME, &t, pin=std::move(pin), this](auto ref) {
auto crc = ref->calc_crc32c();