Calling check_full_extent_integrity in pin_to_extent is wrong.
By the time we partially read the extent, another transaction
might fully load the entire extent. Either by a full_extent read
or by filling the missing unloaded extent gap.
This would result in veryfing the extent crc since it's fully
loaded. However, since the extent was only partially read our
data (and crc) might be outdated.
Instead move the crc checks to read_extent prior to complete_io.
That way we would only check the crc when the extent was intended to
be fully loaded. Other users of read_extent which do not use pin_crc
(CRC_NULL), would skip this check.
Fixes: https://tracker.ceph.com/issues/73790
Fixes: https://tracker.ceph.com/issues/72864
Signed-off-by: Zhang Song <zhangsong02@qianxin.com>
Signed-off-by: Xuehan Xu <xuxuehan@qianxin.com>
Signed-off-by: Matan Breizman <mbreizma@redhat.com>
}
}
+void Cache::check_full_extent_integrity(
+ uint32_t ref_crc, uint32_t pin_crc)
+{
+ LOG_PREFIX(Cache::check_full_extent_integrity);;
+ DEBUG("checksum in the lba tree: 0x{:x}, actual checksum: 0x{:x}",
+ pin_crc,
+ ref_crc);
+ if (unlikely(pin_crc != ref_crc)) {
+ ERROR("extent checksum inconsistent, recorded: 0x{:x}, actual: 0x{:x}",
+ pin_crc,
+ ref_crc);
+ ceph_abort_msg("extent checksum inconsistent");
+ }
+}
+
CachedExtentRef Cache::alloc_new_non_data_extent_by_type(
Transaction &t, ///< [in, out] current transaction
extent_types_t type, ///< [in] type tag
/// Introspect transaction when it is being destructed
void on_transaction_destruct(Transaction& t);
+ static void check_full_extent_integrity(
+ uint32_t ref_crc, uint32_t pin_crc);
+
/// Read the extent in range offset~length,
/// must be called exclusively for an extent,
/// also see do_read_extent_maybe_partial().
extent->on_clean_read();
SUBDEBUG(seastore_cache, "read extent 0x{:x}~0x{:x} done -- {}",
offset, length, *extent);
+
+ if (pin_crc != CRC_NULL) {
+ SUBDEBUG(seastore_cache, "read extent 0x{:x}~0x{:x} veryfing integrity -- {}",
+ offset, length, *extent);
+ // We must check the integrity here prior to complete_io.
+ // Previously, concurrent transaction could have checked
+ // crc of non matching extent data.
+ // See: https://tracker.ceph.com/issues/73790
+ assert(extent->is_fully_loaded());
+ check_full_extent_integrity(extent->last_committed_crc, pin_crc);
+ }
} else {
extent->last_committed_crc = CRC_NULL;
SUBDEBUG(seastore_cache, "read extent 0x{:x}~0x{:x} done (partial) -- {}",
);
}
-void TransactionManager::check_full_extent_integrity(
- Transaction &t, uint32_t ref_crc, uint32_t pin_crc)
-{
- LOG_PREFIX(TransactionManager::check_full_extent_integrity);
- SUBDEBUGT(seastore_tm,
- "checksum in the lba tree: 0x{:x}, actual checksum: 0x{:x}",
- t,
- pin_crc,
- ref_crc);
- if (unlikely(pin_crc != ref_crc)) {
- SUBERRORT(seastore_tm,
- "extent checksum inconsistent, recorded: 0x{:x}, actual: 0x{:x}",
- t,
- pin_crc,
- ref_crc);
- ceph_abort_msg("extent checksum inconsistent");
- }
-}
-
seastar::future<> TransactionManager::flush(OrderingHandle &handle)
{
LOG_PREFIX(TransactionManager::flush);
}),
pin.get_checksum()
);
- if (ref->is_fully_loaded()) {
- check_full_extent_integrity(t, ref->calc_crc32c(), pin.get_checksum());
- } else {
- assert(!full_extent_integrity_check);
- }
SUBDEBUGT(seastore_tm, "got extent -- {} fully_loaded: {}",
t, *ref, ref->is_fully_loaded());
co_return std::move(ref);
}
- static void check_full_extent_integrity(
- Transaction &t, uint32_t ref_crc, uint32_t pin_crc);
-
/**
* pin_to_extent_by_type
*
SUBDEBUGT(seastore_tm, "got extent -- {} fully_loaded: {}",
t, *ref, ref->is_fully_loaded());
assert(ref->is_fully_loaded());
- check_full_extent_integrity(t, ref->calc_crc32c(), pin.get_checksum());
co_return ref->template cast<LogicalChildNode>();
}