Files
erofs-freebsd-out-tree/docs/pre9-lzma-cache-review-resolution.md
T
2026-08-18 09:20:44 +02:00

63 lines
3.2 KiB
Markdown

# Pre9 LZMA cache review resolution
This document records the follow-up to commit `61f4709`, which cached one
decoded LZMA extent per vnode.
## Findings addressed
- The cache is now one entry in `struct erofs_mount`, rather than one entry in
every `struct erofs_node`. Retained decoded memory is bounded by one extent
per mounted filesystem, with the number of mounts controlled by mount
privileges. Keeping many ordinary file descriptors open cannot create more
cache entries.
- Cache initialization is performed in `erofs_mountfs()` immediately after
mount allocation. `erofs_sb_free()` destroys it on every mount failure path
and after successful `vflush()` during unmount. Node reclaim no longer owns
cache cleanup.
- `EROFS_MAP_META` is an explicit ineligibility condition. Metadata-backed
tailpacking therefore remains on its existing read path.
- `packed_inode` and `metabox_en` are explicit ineligibility conditions. The
mount-private backing objects cannot populate or consume the user-data
cache.
- The cache type and helper names are now `erofs_zextent_cache` and
`z_erofs_extent_cache_*`, distinguishing decoded extents from Linux's
managed compressed-page cache names.
- The key retains the existing physical/logical extent fields, device id,
flags, and algorithm, and adds the stable inode `nid`. The `data` pointer is
the validity bit, so NID zero is not treated as an empty key.
## Copy bound and concurrency
`z_erofs_read_uio()` limits each output request to `MAXPHYS`. The generic
`z_erofs_read_data()` API can receive a larger request, so cache eligibility
rejects any request whose mapped portion exceeds `MAXPHYS`. Both cache helper
entry points also contain `KASSERT(len <= MAXPHYS)` checks. The largest copy
performed while holding `z_extent_cache_lock` is therefore `MAXPHYS`; the
12 MiB `Z_EROFS_PCLUSTER_MAX_DSIZE` limit remains an on-disk decoded-extent
allocation bound, not a mutex-copy bound.
Allocation, compressed reads, and decompression happen outside the mutex. A
cache hit copies while holding the mutex, and a miss publishes and copies
under the same mutex before the previous allocation is freed. Concurrent
misses may decode duplicate extents and may replace one another, but pointer
access and replacement remain serialized. A refcounted immutable cache entry
was deliberately not introduced in this first stage.
## Resource tradeoff
The per-mount entry is intentionally a small first-stage design. It removes
the unbounded-per-open-vnode retention introduced by `61f4709`, but it can
thrash when many files are read concurrently on one mount. Retention lasts
until unmount, and the retained allocation is capped by the existing EROFS
format constant `Z_EROFS_PCLUSTER_MAX_DSIZE` (12 MiB). A FreeBSD shrinker or
pressure callback is deferred; adding one would require a broader memory
accounting and lifecycle design than this corrective commit.
## Validation scope
This correction is statically validated only. QEMU validation remains
required for complete SHA-256 reads, same-file concurrent reads, close/reopen,
unmount cleanup, metadata tailpacking, and memory-pressure behavior. The
existing Pre9 manual test report predates this correction and must not be
reported as runtime validation of this commit.