[PATCH 2/2] hwpmc_amd: fix DF counter MSR sharing in amd_pcpu_init()

From: Paulo Fragoso <paulo_at_nlink.com.br>
Date: Sun, 01 Mar 2026 13:55:43 UTC
AMD Data Fabric (DF) performance counter MSRs are shared across all
cores in a package. From AMD PPR 57930-A0 section 2.1.9 [Register
Sharing]:

   'The DF Performance Monitors are shared by all cores/threads in
    the socket. Software must coordinate writing to shared registers
    with other threads in the same sharing hierarchy level.'

Previously, amd_pcpu_init() marked DF counters as PMC_PHW_FLAG_IS_ENABLED
on all CPUs. This caused KASSERT failures in amd_start_pmc() and
amd_stop_pmc() when multiple CPUs attempted to start/stop the same
shared MSRs simultaneously, as AMD_PMC_IS_STOPPED() reads the physical
MSR state which is visible to all cores.

Fix by marking DF counters as enabled only on the package master CPU
(cpu_top->cg_first, the lowest CPU ID in the package). The MI layer
returns EPERM for allocation attempts on non-master CPUs, preventing
concurrent MSR access. DF counters remain functional via CPU 0 using
'pmcstat -c 0 -s <df_event>'.

Tested on: AMD Ryzen 5600X (Family 19h, Zen 3), FreeBSD 16.0-CURRENT
   - DF counters ENABLED on CPU 0 only
   - DF counters DISABLED on CPUs 1-11
   - dram_channel_data_controller_0 returns valid counts on CPU 0

Sponsored by: NLINK (https://nlink.com.br), Recife, Brazil
---
  sys/dev/hwpmc/hwpmc_amd.c | 19 +++++++++++++++++--
  1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/sys/dev/hwpmc/hwpmc_amd.c b/sys/dev/hwpmc/hwpmc_amd.c
index b7dbbca18da2..0013228da82d 100644
--- a/sys/dev/hwpmc/hwpmc_amd.c
+++ b/sys/dev/hwpmc/hwpmc_amd.c
@@ -705,8 +705,23 @@ amd_pcpu_init(struct pmc_mdep *md, int cpu)
  	KASSERT(pc != NULL, ("[amd,%d] NULL per-cpu pointer", __LINE__));

  	for (n = 0, phw = pac->pc_amdpmcs; n < amd_npmcs; n++, phw++) {
-		phw->phw_state = PMC_PHW_FLAG_IS_ENABLED |
-		    PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n);
+		/*
+		 * DF MSRs are shared across all cores in the package.
+		 * See AMD PPR section 2.1.9 [Register Sharing].
+		 * Only mark DF counters as enabled on the package
+		 * master CPU (lowest CPU ID in the package, cg_first
+		 * of cpu_top) to prevent multiple CPUs from writing
+		 * the shared MSRs simultaneously.
+		 */
+		if (amd_pmcdesc[n].pm_subclass ==
+		    PMC_AMD_SUB_CLASS_DATA_FABRIC &&
+		    cpu != cpu_top->cg_first) {
+			phw->phw_state = 0;
+		} else {
+			phw->phw_state = PMC_PHW_FLAG_IS_ENABLED |
+			    PMC_PHW_CPU_TO_STATE(cpu) |
+			    PMC_PHW_INDEX_TO_STATE(n);
+		}
  		phw->phw_pmc = NULL;
  		pc->pc_hwpmcs[n + first_ri] = phw;
  	}
--
2.53.0