git: 66118c3f1011 - main - hwpmc: fix false runcount assertion in user callchain capture

From: Mitchell Horne <mhorne_at_FreeBSD.org>
Date: Tue, 11 Aug 2026 16:22:42 UTC
The branch main has been updated by mhorne:

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

commit 66118c3f1011d7852bce8b659899179a83781897
Author:     Andre Silva <andasilv@amd.com>
AuthorDate: 2026-08-11 16:16:20 +0000
Commit:     Mitchell Horne <mhorne@FreeBSD.org>
CommitDate: 2026-08-11 16:22:01 +0000

    hwpmc: fix false runcount assertion in user callchain capture
    
    pmc_capture_user_callchain() checks a PMC's runcount before walking
    the user stack, but reads it without holding the spinlock that
    protects it. hardclock() can run on the same CPU during the capture
    and drop the runcount to zero in between, tripping the assertion and
    panicking INVARIANTS kernels under load.
    
    Move the check inside the existing spinlock, right where the code
    already confirms the sample is still valid. No functional change on
    kernels built without INVARIANTS.
    
    Signed-off-by:  Andre Silva <andasilv@amd.com>
    Reviewed by:    mhorne
    MFC after:      1 week
    Sponsored by:   AMD
    Differential Revision:  https://reviews.freebsd.org/D58571
---
 sys/dev/hwpmc/hwpmc_mod.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c
index 32bb00d671fe..e2d5a044e0b8 100644
--- a/sys/dev/hwpmc/hwpmc_mod.c
+++ b/sys/dev/hwpmc/hwpmc_mod.c
@@ -5016,9 +5016,6 @@ restart:
 		KASSERT(pm->pm_flags & PMC_F_CALLCHAIN,
 		    ("[pmc,%d] Retrieving callchain for PMC that doesn't "
 		    "want it", __LINE__));
-		KASSERT(counter_u64_fetch(pm->pm_runcount) > 0,
-		    ("[pmc,%d] runcount %ju", __LINE__,
-		    (uintmax_t)counter_u64_fetch(pm->pm_runcount)));
 
 		if (ring == PMC_UR) {
 			counter_u64_add(pmc_stats.pm_merges, 1);
@@ -5044,6 +5041,10 @@ restart:
 		 * Verify that the sample hasn't been dropped in the meantime.
 		 */
 		if (ps->ps_nsamples == PMC_USER_CALLCHAIN_PENDING) {
+			KASSERT(counter_u64_fetch(pm->pm_runcount) > 0,
+			    ("[pmc,%d] runcount %ju", __LINE__,
+			    (uintmax_t)counter_u64_fetch(pm->pm_runcount)));
+
 			ps->ps_nsamples = nsamples;
 			/*
 			 * If we couldn't get a sample, simply drop the