RE:(2) [RFC PATCH v3 0/2] ext4: speed up fast commit on random writes

From: Daejun Park

Date: Thu Jul 23 2026 - 04:30:58 EST


On Jul 22, 2026, at 01:27, Andreas Dilger <adilger@xxxxxxxxx> wrote:
> Have you looked at implementing a more dynamic structure like another
> extent status tree, rbtree, xarray or or similar instead of having a
> fixed-size array? [...] It would be useful to do some testing of how
> many separate ranges gets worse than the fallback.

Thanks -- I implemented the dynamic structure; it replaces the array and the
tunable entirely. v4 tracks the modified ranges in a per-inode rbtree, which
answered both of your questions.

It can't be the shared extent status tree: I probed whether the shrinker
reclaims a mapped es entry for a modified-but-not-yet-committed range, and it
does (448 in one short run under mild pressure). That "modified" mark is not
re-derivable, so marking the shared tree would silently lose modifications -- the
tracker has to own its structure.

So it is a private rbtree, reusing ext4's own machinery (generic rb_*, as the
extent status tree already does for per-inode ranges) -- idiomatic, no new
dependency. The node is a dedicated 32-byte struct with its own slab cache, not
extent_status: extent_status carries an 8-byte es_pblk that fast commit never
uses, and its cache is SLAB_RECLAIM_ACCOUNT, which would misaccount these
non-reclaimable nodes. The rb_root is inline in the inode, so only per-range
nodes are allocated. (I also built and compared a maple-tree version:
behaviour-identical, within ~15% on memory, denser only at the range counts this
series already caps -- so the rbtree, with no dependency, is the choice.)

On "how many is too many": the cap is EXT4_FC_SNAPSHOT_MAX_RANGES (2048), the
limit the snapshot already enforces on emitted ranges -- reused directly, not a
new constant. Each disjoint tracked range snapshots to at least one ADD_RANGE,
so a tracked set larger than that can never fit under the snapshot cap: the fast
commit would overrun it and fall back regardless. That count is exactly where
tracking stops paying off, so at the cap the transaction falls back to a full
commit. (Verified: R=4096 disjoint writes/fsync falls back there; R=1024 tracks
precisely.)

Thanks,
Daejun