A large virtual address range may contain only a small number of page-state transitions. Reading one pagemap entry for every virtual page exposes that state at page granularity, but it also makes user space inspect a long sequence of entries. Linux PAGEMAP_SCAN moves the filtering into the kernel and reports matching spans as struct page_region records.

The interface is an ioctl() on /proc/PID/pagemap. A request supplies an address interval, category predicates, a return mask, and an output vector. The kernel walks page tables and emits contiguous regions whose selected page properties match the request. This changes the shape of page-table inspection from a stream of per-page values into a filtered range query.

Category masks separate selection from returned state

struct pm_scan_arg contains three category fields used to select entries: category_mask, category_anyof_mask, and category_inverted. It also contains return_mask, which controls which category bits are copied into each returned region.

category_mask requires all selected bits to match. category_anyof_mask allows a page to qualify when any selected category is present. category_inverted reverses the sense of selected categories before the matching rules are applied. These fields permit a scan to express conditions in the kernel instead of returning every entry and discarding most of them in user space.

Current kernel documentation includes categories such as PAGE_IS_PRESENT, PAGE_IS_SWAPPED, PAGE_IS_FILE, PAGE_IS_PFNZERO, PAGE_IS_HUGE, PAGE_IS_SOFT_DIRTY, PAGE_IS_WPALLOWED, and PAGE_IS_WRITTEN. Newer kernels can expose additional categories, so applications need to treat the supported UAPI of the running kernel as the actual boundary.

The selection mask and return mask serve different purposes. A process can filter on one set of properties while requesting a different subset of category information in the result. Keeping that distinction explicit prevents the query predicate from becoming coupled to the result representation.

Results describe runs rather than individual PTE records

Each output struct page_region contains start, end, and categories. Adjacent pages with compatible returned state can be represented as one region. A sparse pattern of state changes can therefore produce far fewer records than the number of pages traversed.

This representation does not change the granularity of the underlying information. The interface reports page-table state with page-size granularity; range coalescing is a compact encoding of consecutive pages with the same relevant classification.

The output vector has a finite vec_len. A scan can stop because that vector is full, because max_pages has been reached, or because the requested interval has been consumed. walk_end reports the address at which the walk ended. Code that scans a large mapping must therefore treat walk_end as continuation state rather than assuming one call necessarily covers the full interval.

That continuation rule is also important when page tables change concurrently. Multiple calls do not form a snapshot transaction. A target process can fault pages in, reclaim them, remap ranges, or alter write state between calls. walk_end makes iteration precise, but it does not freeze the address space.

PAGE_IS_SOFT_DIRTY exposes the existing soft-dirty mechanism through the scan

Linux soft-dirty tracking marks page-table entries after writes. The traditional workflow clears soft-dirty state through /proc/PID/clear_refs, allows the workload to run, then inspects bit 55 in /proc/PID/pagemap for relevant entries.

PAGEMAP_SCAN can select PAGE_IS_SOFT_DIRTY and return ranges carrying that category. This removes the need to decode every 64-bit pagemap entry merely to locate pages whose soft-dirty state matches a query.

The semantic limits of soft-dirty tracking remain. Clearing soft-dirty state clears writable PTE state so a later write can fault and cause the kernel to mark the entry again. Mapping changes can also affect the signal: newly created or expanded mappings are marked soft-dirty so user space can detect address-space renewal. A scan is a more structured retrieval interface; it does not convert soft-dirty into a hardware write log or an immutable history.

PAGE_IS_WRITTEN belongs to userfaultfd asynchronous write protection

PAGE_IS_WRITTEN represents a separate dirty-tracking path. Kernel documentation defines it for VMAs registered with userfaultfd write-protect mode and asynchronous write protection enabled. It reports pages written since write protection was applied.

The setup requires a userfaultfd configured with UFFD_FEATURE_WP_ASYNC, followed by registration of the target memory with UFFDIO_REGISTER_MODE_WP. Pages can then be write-protected, and later scans can select PAGE_IS_WRITTEN to locate writes.

This state is not a generic dirty bit for arbitrary VMAs. Outside the required userfaultfd registration and feature state, applications cannot assign the same meaning to PAGE_IS_WRITTEN. The category is part of a coordinated tracking protocol between user space and the kernel.

Kernel documentation describes this path as a better-performing alternative to soft-dirty for normal pages and notes that it avoids the VMA-merging issue associated with soft-dirty tracking. Huge-page cases still require care because THP or Hugetlb mappings can cause extra pages to be reported.

A scan can combine observation with write protection

PM_SCAN_WP_MATCHING asks the kernel to write-protect pages that match the scan criteria. The operation can be combined with result collection, allowing the same page-table walk to report matching ranges and apply asynchronous userfaultfd write protection.

PM_SCAN_CHECK_WPASYNC adds a stricter boundary: the operation aborts if it encounters pages that do not have asynchronous write protection available. This is useful when silently applying a partial protection model would break the dirty-tracking protocol.

Combining collection and protection reduces the gap that would exist between a separate scan and a later write-protection pass. The guarantee is scoped to the operation the kernel performs; it does not make unrelated user-space processing atomic with subsequent writes. Consumers still need a protocol for defining tracking epochs and deciding when collected state is complete enough for checkpointing, migration, replication, or incremental persistence.

Pagemap access remains a privileged observation boundary

Opening another process’s /proc/PID/pagemap is subject to procfs access controls. Page-table information can expose sensitive process state, and Linux has progressively restricted portions of pagemap data such as physical frame numbers.

PAGEMAP_SCAN does not bypass those controls. The ioctl operates on an already opened pagemap file descriptor and inherits the security boundary around obtaining that descriptor. Container, ptrace, procfs mount, capability, and LSM policy can therefore determine whether a process can inspect a target at all.

Applications also need to avoid treating page categories as stronger guarantees than they provide. PAGE_IS_PRESENT describes residency state observed during the walk, not future residency. PAGE_IS_FILE classifies backing, not application ownership. PAGE_IS_HUGE identifies huge-page mapping state, not a promise that the mapping will remain huge. Page-table state is inherently dynamic unless a separate mechanism stabilizes the target.

Range queries make page-state tracking proportional to transitions

The central property of PAGEMAP_SCAN is not a new kind of memory state. Its value comes from moving page-table filtering, range coalescing, and optional write-protection work into one kernel interface.

For workloads that track dirty memory or classify large mappings, this can replace per-entry decoding with queries that return only relevant address spans. walk_end, finite output vectors, explicit category masks, and the userfaultfd requirements around PAGE_IS_WRITTEN define the operational boundaries. The result is a compact page-state primitive whose correctness still depends on treating concurrent address-space changes and tracking epochs as explicit parts of the surrounding system.