Skip site navigation (1)Skip section navigation (2)
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>