[RFC PATCH 0/7] mm/damon/perf: observability framework for hardware-sampled access reports
Kunwu Chan <[email protected]>
| Newsgroups | dev.linux.lists.damon,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kernel.vger.linux-trace-kernel,org.kvack.linux-mm |
|---|---|
| Message-ID | <[email protected]> |
From: Kunwu Chan <[email protected]> This series adds a unified observability framework to DAMON's hardware-sampled access report pipeline. When a PMU backend delivers zero callbacks, the framework pinpoints the failing stage — was the event not created? not enabled? did the hardware produce no samples? did the ring drop them? did the drain fail to match? — without a single printk. Every PMU backend (ARM SPE, AMD IBS, Intel PEBS, …) funnels through the same damon_perf_observe_*() API, which fans out to per-CPU pipeline counters, debugfs statistics, and tracepoints. The framework does not contain any PMU driver. It is pure observability infrastructure that sits alongside the existing DAMON software statistics (scheme stats, kdamond stats) and complements them for the hardware-sampling path. Why this framework exists ------------------------- The framework was built to answer a concrete question during ARM SPE bring-up: what prevents SPE from delivering samples to DAMON? The answer could be anywhere in the pipeline — the event wasn't created, the PMU wasn't enabled, the hardware produced no samples, the ring dropped them, or the drain failed to match — and without per-stage visibility, the only way to find out was to insert printks across the entire pipeline. The framework instruments every stage with zero-overhead counters (when CONFIG_DAMON_PERF_OBSERVE=n) and optional tracepoints, so the failing stage is revealed at a glance. It immediately diagnosed the ARM SPE gap: Layers 1-3 (event create/bind/enable) passed, but the PMU was not producing data — a PMU-level gap, not a DAMON pipeline bug. This confirmed that SPE required an AUX buffer backend, which does not fit the perf_event_create_kernel_counter() API. During the subsequent ARM SPE AUX backend development [2] (which builds on the perf AUX kernel-consumer API [3]), the framework was used to verify each pipeline fix. When the AUX backend was complete, the same framework confirmed a fully functional pipeline: Layers 1-7 all pass, real SPE hardware samples flow through the ring, match, and update stages. The before/after results are shown in the Testing section. We are posting the framework now so that other PMU backends can reuse the same infrastructure instead of re-deriving it. How to use ---------- Compile with CONFIG_DAMON_PERF_OBSERVE=y. After starting a kdamond with a perf event, the pipeline is visible at a glance: # cat /sys/kernel/debug/damon/perf_stats -------------- ---------- Counter Value -------------- ---------- callback 233 valid 233 ... enqueue 233 dequeue 233 match 12 update 9 For automated diagnosis, run the selftest: # sudo ./damon_perf_obs_test.sh --pmu software --freq 1 --sample-freq 100 For structured diagnostics, enable the tracepoints under /sys/kernel/debug/tracing/events/damon/. What the series adds -------------------- Patch 1 defines the three tracepoints (damon_perf_sample with 8 fields including sample_flags, sample_type, and execution context, plus damon_perf_report_missed and damon_perf_drain) with the CONFIG switch, the observe API declarations, no-op stubs, and the Kconfig / Makefile wiring. (Reviewers: tracepoint folks — Steven, Masami, Mathieu.) Patch 2 implements the observe_*() core: per-CPU monotonic counters, event-lifecycle hooks with lazily-allocated per-event per-CPU state, NMI-safe sample classification with execution-context detection, ring enqueue/dequeue/peak, match/miss/update, and framework init. Patch 3 adds the debugfs perf_stats interface (debug-only, format unstable). Patch 4 wires the observe calls into the vaddr overflow handlers and the kdamond drain, integrating the framework into the hot path. Reads ring->tail once with READ_ONCE and reuses the cached value for the peak-occupancy estimate, avoiding a compiler reload and a torn read on the NMI producer side. Patch 5 adds the automated selftest with snapshot/delta accounting, per-CPU state-column checks, clean-session guard, and a PMU support verdict (FULLY INTEGRATED / PLUMBING-ONLY / UNUSABLE). (Reviewers: kselftest folks — Shuah.) Patch 6 documents the framework. Patch 7 adds CONFIG_DAMON_PERF_DEBUG as an optional Kconfig option for verbose pr_debug() output via damon_perf_dbg(), wired into every event-lifecycle hook, the drain summary, and the init paths. Adds a pipeline health check to the selftest that diagnoses which stage is broken when callbacks are zero. Testing ======= Tested on Kunpeng 920 (256 CPUs, ARM SPE, kernel 7.1.0-rc5-mm-new-damon+). KUnit: 35/35 PASSED (damon 29, damon-operations 5, damon-sysfs 1). The framework is PMU-agnostic. Three configurations demonstrate this, with the ARM SPE case being the framework's core diagnostic value: **Software PMU** (page-fault sampling, exercises the full pipeline, no hardware required): $ sudo ./damon_perf_obs_test.sh --pmu software --freq 1 --sample-freq 100 SUMMARY: 27 passed, 0 failed, 0 skipped (FULLY INTEGRATED) callback=170, valid=170, enqueue=170, dequeue=170, match=6, update=3 **CPU-clock** (address-less PMU, plumbing-only): $ sudo ./damon_perf_obs_test.sh --pmu software --config 0 --freq 1 --sample-freq 100 SUMMARY: 22 passed, 0 failed, 3 skipped (PLUMBING-ONLY) callback=1913, valid=0, addr_zero=1913, enqueue=0 Layers 1-4 pass; Ring/Match/Drain skipped as expected. **ARM SPE** (hardware trace). This is the scenario that motivated the framework: without an AUX buffer backend, SPE cannot deliver samples through perf_event_create_kernel_counter(), and the framework pinpoints the failure without a single printk: $ sudo ./damon_perf_obs_test.sh --pmu arm_spe_0 --freq 0 --period 256 --- Layer 2-3: Enable & Run (via per-CPU state) --- [PASS] Event Created (max per-CPU state >= CREATED) [PASS] Event Bound (max per-CPU state >= BOUND) [PASS] Event Enabled (max per-CPU state >= ENABLED) [PASS] Perf event creation (no errors in dmesg delta) --- Analysis --- Pipeline diagnosis: PMU not producing data or AUX pipeline broken [FAIL] Sampling — 0 callbacks — PMU is not delivering samples to DAMON [SKIP] Event Running — no callbacks — state cannot advance past ENABLED Ring: enqueue=0 dequeue=0 overflow=0 --- PMU support verdict --- UNUSABLE: the PMU never delivered a sample to DAMON SUMMARY: 19 passed, 1 failed, 5 skipped Layers 1-3 pass — the per-CPU state advances to ENABLED — so the kernel-side plumbing is intact. The health check confirms state >= ENABLED but enqueue=0, ruling out event-creation and enable bugs. The failure is isolated to Layer 4 (Sampling): the PMU itself is not producing data. This is the expected result for ARM SPE without an AUX buffer backend, and the framework diagnoses it as a PMU-level gap rather than a DAMON pipeline bug. After the AUX backend is added [2] (which itself depends on the perf AUX kernel-consumer API [3]), the same test script confirms a fully functional pipeline, including real SPE hardware samples: $ sudo ./damon_perf_obs_test.sh --pmu arm_spe_0 --freq 0 --period 256 SUMMARY: 27 passed, 0 failed, 0 skipped (FULLY INTEGRATED) callback=47248, valid=47248, enqueue=15161, dequeue=15161, overflow=32087, match=255, update=4 All 7 pipeline stages pass. The AUX drain path delivers samples in process context (context=0), and the ring overflow counter (32087) shows the ring is sized for software PMU rates — a real-world observation the framework provides without any additional instrumentation. Known limitations ================= - The debugfs perf_stats format is explicitly unstable and must not be used by scripts. Tracepoints and counters are the stable diagnostic interface. - The global per-CPU lifecycle state is monotonic and never reset, so a previous RUNNING event can mask a current event's failure. Per-event per-CPU state is available for accurate tracking. - This series depends on Ravi's hardware-sampled access reports branch [1] for the following facilities: * Per-CPU SPSC report ring (damon_report_rings, DAMON_REPORT_RING_MASK) * damon_report_access() and damon_report_page_fault() as the ring producer * kdamond_check_reported_accesses() as the drain path * damon_perf_event lifecycle (create/bind/enable/disable/free) Without [1] this series cannot compile, so [1] must be merged first. References ========== Link [1]: https://github.com/damonitor/linux.git branch: ravi_hw_sampled_access_reports_rfc_v1 commit: fd968106e5bf Link [2]: https://lore.kernel.org/all/[email protected]/ Link [3]: https://lore.kernel.org/all/[email protected]/ Base: fd968106e5bf ("mm/damon: add damos_node_eligible_mem_bp tracepoint") Kunwu Chan (4): mm/damon/perf: add observability framework with tracepoints and CONFIG switch mm/damon/perf: implement observe API and per-CPU statistics engine mm/damon: integrate observe API into vaddr overflow handlers and core drain selftests/damon: add automated layer-by-layer observability test Lian Wang (3): mm/damon/perf: add debugfs statistics interface Docs/mm/damon: document the perf observability framework mm/damon/perf: add CONFIG_DAMON_PERF_DEBUG and pipeline health check Documentation/admin-guide/mm/damon/index.rst | 1 + .../mm/damon/perf-observability.rst | 210 +++++++ include/linux/damon.h | 44 ++ include/trace/events/damon.h | 120 ++++ mm/damon/Kconfig | 33 + mm/damon/Makefile | 1 + mm/damon/core.c | 129 +++- mm/damon/perf/Makefile | 5 + mm/damon/perf/debugfs.c | 145 +++++ mm/damon/perf/perf.h | 235 +++++++ mm/damon/perf/stats.c | 308 ++++++++++ mm/damon/vaddr.c | 104 +++- tools/testing/selftests/damon/Makefile | 1 + .../selftests/damon/damon_perf_obs_test.sh | 578 ++++++++++++++++++ 14 files changed, 1880 insertions(+), 34 deletions(-) create mode 100644 Documentation/admin-guide/mm/damon/perf-observability.rst create mode 100644 mm/damon/perf/Makefile create mode 100644 mm/damon/perf/debugfs.c create mode 100644 mm/damon/perf/perf.h create mode 100644 mm/damon/perf/stats.c create mode 100755 tools/testing/selftests/damon/damon_perf_obs_test.sh -- 2.43.0