The branch main has been updated by netchild:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=e42703f5c4b2d3a869b17e2cb8670f1b6359c8cf

commit e42703f5c4b2d3a869b17e2cb8670f1b6359c8cf
Author:     Alexander Leidinger <[email protected]>
AuthorDate: 2026-07-19 07:38:52 +0000
Commit:     Alexander Leidinger <[email protected]>
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;

Reply via email to