Buffer cache state model (source code)

= Buffer cache state model

Use one fixed-size <buffered device block> per <buffer cache key>, serialized operations, successful reads and writes, and no outside changes to the backing contents. Record an absent state or a reserved key with validity $v$, dirty flag $D$, and retained-reference count $r$. The <bits> $v,D$ describe contents, while the nonnegative <integer> $r$ describes lifetime. Every <dirty buffer> is valid, so $D=1$ implies $v=1$. Reservation begins at $(v,D,r)=(0,0,1)$; successful fill changes only $v$ to 1. Modifying a valid retained structure sets $D=1$. Releasing a caller changes $r$, not $v$ or $D$.

\Image[/buffer-cache-block-state-transitions.png]
{title=<Buffer cache validity>, <dirty buffers> and retained references impose different state-transition rules}

The first diagram separates contents transitions from reference transitions. <Buffer cache writeback> temporarily retains the structure while transferring its contents; that internal reference is acquired and released inside the single serialized operation shown. <Buffer cache eviction> requires a valid <clean buffer> with $r=0$. The diagram omits read errors, explicit cancellation of invalid reservations, concurrent writes, invalidation and journal ordering. In particular, a journal can forbid otherwise possible <buffer cache writeback> until its <write-ahead logging> dependencies are satisfied.

For a small independent check, bound $r$ to $0,1,2$. Starting absent, allow reservation, acquisition, successful fill, modification, successful <buffer cache writeback>, release of a valid retained structure, and eligible <buffer cache eviction>. Exactly nine abstract states are reachable: absent, two invalid reservations with $r=1,2$, and six valid states combining $D=0,1$ with $r=0,1,2$. Every transition preserves $D\le v$; the only eviction source has $(v,D,r)=(1,0,0)$. This bounded <finite-state machine> check supports these safety rules, not a claim that work always completes.