Pages and nodes are alike for the purposes of this note and we will speak here only of pages. A page frame on the disk will be occupied by successive tenant pages. When a page is sold back to a bank the bank uses its range key to rescind all outstanding keys to that page. This note is about how the kernel rescinds keys.
In the page header there is an allocation count, (once it was 16 bits but now 32 bits), that is duplicated in unprepared keys to the page. Keys whose AC does not match that in the page header are observed as DK(0) outside the kernel and in fact replaced by DK(0) as soon as the kernel notices them, generally in an attempt to prepare them. The page header is something that stays with the page thru checkpoint restarts. When the AC is incremented in the page header, extant keys to the page appear to vanish.
Actually there is a bit in the page header that remembers whether any prepared key to the page has become unprepared, for if there are no such events, zapping the keys on the page’s backchain suffices to locate and rescind all keys to the page. Ditto nodes. The 32 bit AC thus will not increase more quickly than I/O. The range key returns an error if the AC cannot be incremented. Such a page is “worn out” and cannot be resold.
It is easy also to imagine a background kernel job that every few years:
Almost exactly the same goes for nodes except nodes have in addition to the AC the call count which is duplicated in current resume keys to root nodes of domains. This count is incremented in the node when a resume key is invoked, unless no resume keys for this node have been unprepared. This ensures that other resume keys die upon restarting a waiting domain.
The page headers on modern disks live in periodically placed page sized pots on the disk, scattered regularly among the pages. One pot read gets a bunch of headers. They are cached in RAM as are node pots and user pages. Node headers live in node pots next to their nodes.
All of this logic might be simplified with a classic object table with an entry per object. But we avoid several costs with our allocation count logic: