Files
Natalia Portillo 598d9224a7 Bound the block caches by bytes held instead of by entry count
The read caches sized themselves as MAX_CACHE_SIZE / (SectorSize << shift),
but ctx->shift is only ever assigned on the DDT v1 path, and the context is
zeroed at open. On every DDT v2 image the divisor therefore collapsed to the
sector size and the limit came out as 262144 entries, while each block_cache
entry holds a whole decompressed data block of SectorSize << dataShift bytes,
8 MiB with the default data_shift of 12. That is a ceiling of roughly 2 TiB
rather than the intended 512 MiB, so the count limit was never reached: a
sequential sector-by-sector pass cached the entire decompressed image and
freed none of it until aaruf_close(). Comparing two 40 GB images this way was
killed by the OOM reaper.

An entry count cannot express a memory budget when the values vary in size,
and here they vary by five orders of magnitude: block_header_cache holds
BlockHeader structs while block_cache holds megabyte payloads, yet both were
given the same limit. Track the byte size of each value instead and evict
least-recently-used entries until the cache is back under budget, and drop the
block geometry from cache initialization entirely. The block cache gets the
512 MiB MAX_CACHE_SIZE it always intended; block headers get a separate 8 MiB
budget, which covers far more blocks than the payload cache can hold.

The entry just inserted is never evicted, so callers may keep using the
pointer they handed over for the rest of the call. That also removes the
use-after-free that a zero limit used to cause: the old eviction loop started
at the hash head and could free the entry just added, after which the caller
read from freed memory.
2026-09-02 00:03:22 +01:00
..
2025-12-23 08:23:40 +00:00
2026-04-04 16:32:25 +01:00
2025-10-03 01:49:44 +01:00