Date: Mon, 20 Jul 2026 10:25:13 +0000 From: Alexander Leidinger <netchild@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: e42703f5c4b2 - main - hwpmc: handle counter wraparound for process-mode counting PMCs Message-ID: <6a5df789.3127c.4e973f46@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by netchild: URL: https://cgit.FreeBSD.org/src/commit/?id=e42703f5c4b2d3a869b17e2cb8670f1b6359c8cf commit e42703f5c4b2d3a869b17e2cb8670f1b6359c8cf Author: Alexander Leidinger <netchild@FreeBSD.org> AuthorDate: 2026-07-19 07:38:52 +0000 Commit: Alexander Leidinger <netchild@FreeBSD.org> CommitDate: 2026-07-20 10:24:18 +0000 hwpmc: handle counter wraparound for process-mode counting PMCs The accumulated count of a process-mode counting PMC is kept in a 64-bit software counter and seeded into the hardware counter at every context switch in. Hardware counters are narrower than that - each PMC class discovers and records its own counter width, e.g. 48 bits on current x86 (queried from CPUID on Intel, architectural on AMD) - so once the accumulated count approaches the end of the hardware counter range, the counter wraps during a time slice and the value read back at switch out is smaller than the value seeded. The increment was computed assuming a full 64-bit counter: on INVARIANTS kernels a long enough counting run panics with "negative increment" the moment the accumulated count first crosses the hardware counter range, and on other kernels the totals silently lose a full counter range per wrap. Compute the increment modulo the per-class hardware counter width instead, in both places that accumulate switch-out deltas. Reviewed by: adrian MFC after: 2 weeks Assisted-by: Claude Code (Fable 5) Differential Revision: https://reviews.freebsd.org/D58340 --- sys/dev/hwpmc/hwpmc_mod.c | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c index 9533cb81b4a1..086a05657cfe 100644 --- a/sys/dev/hwpmc/hwpmc_mod.c +++ b/sys/dev/hwpmc/hwpmc_mod.c @@ -1594,6 +1594,24 @@ pmc_process_csw_in(struct thread *td) critical_exit(); } +/* + * Compute the change in a counter's value since it was last written. + * The hardware counter is only pcd_width bits wide and wraps around, + * while the value seeded into it may occupy the full 64-bit range, so + * take the difference modulo the counter width. + */ +static pmc_value_t +pmc_delta(const struct pmc_classdep *pcd, pmc_value_t newvalue, + pmc_value_t oldvalue) +{ + pmc_value_t delta; + + delta = newvalue - oldvalue; + if (pcd->pcd_width < 64) + delta &= ((pmc_value_t)1 << pcd->pcd_width) - 1; + return (delta); +} + /* * Thread context switch OUT. */ @@ -1606,8 +1624,7 @@ pmc_process_csw_out(struct thread *td) struct pmc_process *pp; struct pmc_thread *pt = NULL; struct proc *p; - pmc_value_t newvalue; - int64_t tmp; + pmc_value_t newvalue, tmp; enum pmc_mode mode; int cpu; u_int adjri, ri; @@ -1745,23 +1762,19 @@ pmc_process_csw_out(struct thread *td) } mtx_pool_unlock_spin(pmc_mtxpool, pm); } else { - tmp = newvalue - PMC_PCPU_SAVED(cpu, ri); + /* + * For counting process-virtual PMCs, the + * hardware counter's value increases + * monotonically modulo the counter width; + * pmc_delta() recovers the increment even + * when the counter wrapped during the run. + */ + tmp = pmc_delta(pcd, newvalue, + PMC_PCPU_SAVED(cpu, ri)); PMCDBG3(CSW,SWO,1,"cpu=%d ri=%d tmp=%jd (count)", cpu, ri, tmp); - /* - * For counting process-virtual PMCs, - * we expect the count to be - * increasing monotonically, modulo a 64 - * bit wraparound. - */ - KASSERT(tmp >= 0, - ("[pmc,%d] negative increment cpu=%d " - "ri=%d newvalue=%jx saved=%jx " - "incr=%jx", __LINE__, cpu, ri, - newvalue, PMC_PCPU_SAVED(cpu, ri), tmp)); - mtx_pool_lock_spin(pmc_mtxpool, pm); pm->pm_gv.pm_savedvalue += tmp; pp->pp_pmcs[ri].pp_pmcval += tmp; @@ -5177,7 +5190,8 @@ pmc_process_exit(void *arg __unused, struct proc *p) if (PMC_TO_MODE(pm) == PMC_MODE_TC) { pcd->pcd_read_pmc(cpu, adjri, pm, &newvalue); - tmp = newvalue - PMC_PCPU_SAVED(cpu, ri); + tmp = pmc_delta(pcd, newvalue, + PMC_PCPU_SAVED(cpu, ri)); mtx_pool_lock_spin(pmc_mtxpool, pm); pm->pm_gv.pm_savedvalue += tmp;home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a5df789.3127c.4e973f46>
