Linux Page Owner: Attribute Growing Page Use to Allocation Call Paths

Use page_owner to identify allocation call paths retaining pages at equivalent workload checkpoints, then investigate the ownership contract behind the growing path. The largest allocation stack is not automatically a leak. Growth can reflect a bounded cache, an unfinished operation, or a genuine missing release; the experiment must distinguish them.

This procedure uses upstream Linux v6.12 and its page-owner debugfs interfaces. It concerns kernel page-allocation provenance, not a replacement for per-process RSS accounting or individual slab-object tracing. No kernel was built or page-owner capture collected for this article. The capture steps and interpretation examples are proposed methods.

Verify boot-time coverage and cost

The v6.12 configuration makes CONFIG_PAGE_OWNER depend on debug-kernel and stack-trace support and selects the supporting facilities. Building it in is not sufficient: the page-owner documentation requires page_owner=on at boot to enable tracking.

Use a recoverable diagnostic image with a known memory budget. Tracking needs metadata and stack storage, so its enabled footprint belongs in the test configuration. Do not cite the documentation’s historical example size measurements as the overhead of your board. Measure the exact diagnostic image if capacity or deadlines matter.

Keep the final configuration, boot command line and exact symbols with each capture. Early allocations can lack accurate original allocation stacks even when later initialization marks them as allocated. Treat an early or missing provenance category as a limitation rather than assigning it to the first recognizable driver in the log.

Choose a repeatable checkpoint before taking snapshots

Define one operation cycle and its completion condition. For a hypothetical streaming device, the cycle might open a stream, allocate transfer buffers, run a fixed input, stop, close, and wait for owned callbacks to finish. Compare after close with after close, not after close with mid-transfer.

Separate warm-up from steady-state repetition. Loading a module, creating a network namespace or touching a file can allocate pages that legitimately remain in the system. Establish a baseline after the intended one-time setup. Keep unrelated workloads as stable as practical and record unavoidable background activity.

Capture timestamps and operation counters around each snapshot. A debugfs read occurs while the system can still change; it is not an atomic stop-the-world inventory. Where feasible, quiesce the owned workload at a documented safe point and take repeated observations to identify unstable totals.

Baseline and post-cleanup page-owner stack captures are compared in base-page units, then interpreted as plateau, per-cycle growth or slab-object lifetime questions.
Original allocation-stack delta workflow. No sample page totals or measured memory-growth curves are claimed.

Collect stack totals, then retain detailed evidence

In v6.12, /sys/kernel/debug/page_owner_stacks/show_stacks exposes allocation stacks and their current nr_base_pages totals. The tagged implementation applies count_threshold when printing stacks. Preserve the threshold with the capture so a path omitted by filtering is not interpreted as owning zero pages.

Use a readable record name tied to the checkpoint, such as baseline, after first complete cycle and after later complete cycles. Reading the interface and saving output is sufficient for collection; this article does not require modifying the threshold or other kernel state. Keep output in an approved diagnostic destination with enough space.

Retain /sys/kernel/debug/page_owner detail when an interesting path needs order, allocation metadata or per-page investigation. The matching tools/mm/page_owner_sort helper can aggregate the detailed dump; its default sort is by occurrences, while the documented memory-oriented option sorts by pages. Record helper revision and options rather than assuming the first row is always the largest memory consumer.

Calculate deltas in consistent units

For each normalized allocation stack, record baseline base pages, later base pages and their difference. A base-page count is not an allocation-call count. Higher-order allocations comprise multiple base pages, and the target’s base-page size must be verified before converting counts to bytes. Never silently assume every embedded Linux image uses the same page size.

Normalize stacks only enough to compare the same build. Preserve the meaningful driver and allocator frames, inline context where available, and unresolved categories. Do not merge two distinct paths merely because they share the final allocator function. If binaries changed between runs, treat the comparison as a new experiment rather than subtracting unlike symbolic signatures.

Maintain separate rows for stacks appearing, disappearing and remaining below a reporting threshold. A filtered capture cannot justify a precise grand total. Where totals disagree with another memory view, first reconcile scope and timing: page ownership, slab-object counts and process RSS describe different layers.

Use the trend shape to choose the next investigation

  • Growth after the first cycle, then a plateau: review bounded retained state and cache policy. Check whether the plateau fits the product budget and whether final teardown releases the intended resources.
  • Additional pages after every equivalent completed cycle: inspect the corresponding resource-release path and any references left by errors or cancellation.
  • Growth only during work, followed by return: investigate peak demand and concurrency rather than labeling the retained-state curve a leak.
  • Growth moves between stack groups: examine object reuse, different workload paths and stack aggregation before declaring a new owner.

These interpretations are hypotheses. The allocation stack records where pages entered the allocation lifecycle, not a complete history of every later logical owner. A worker thread shown in the stack may merely perform allocations for another subsystem. Follow the associated object and release mechanism before assigning the fix.

Resolve page-level growth into an object-level cause

If the growing path allocates slab pages, page_owner may lead only as far as the slab allocator and the caller that caused a slab to expand. Individual objects can have mixed lifetimes inside those pages. Add cache/object-level evidence before claiming that one object type leaked every retained page.

If the path allocates pages directly for a driver ring, build a ledger of ring generation, requested order, successful allocation, publication, stop condition and release. Check partially constructed rings as well as normal removal. An allocation can remain reachable and still violate the product’s intended lifetime, so a reachability-only leak detector answers a different question.

For the hypothetical stream, compare successful close with a start failure after some buffers were allocated. If only the failure case adds pages, minimize that branch and verify every acquired resource has a release owner. Do not “test” the explanation by globally dropping caches, which changes unrelated state and can hide the relevant retention pattern.

Define what closes the investigation

Deliver raw captures, threshold and unit settings, normalized stack deltas, operation counts and the resource ledger for the selected path. A repair should restore the intended post-cycle ownership state while preserving successful operation. Repeat cold setup, warm cycles and relevant failure exits under the same diagnostic conditions.

Then qualify the production-like image separately for memory headroom and timing. A clean page delta supports the tested lifecycle contract; it does not establish universal leak freedom, exact attribution of early allocations or complete ownership history. The value of page_owner is the source-backed next question it reveals.

Similar Posts