Files
erofs-freebsd-out-tree/docs/pre9-lzma-cache-review-resolution.md
T
2026-08-13 10:44:59 +02:00

3.2 KiB

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.