git: 8a0cab240f21 - main - hwpmc/amd: replace static pmcdesc[] and pc_amdpmcs[] with dynamic allocation

From: Mitchell Horne <mhorne_at_FreeBSD.org>
Date: Mon, 21 Sep 2026 22:50:43 UTC
The branch main has been updated by mhorne:

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

commit 8a0cab240f2146a000396c411df471df4b75bb9d
Author:     Osvaldo Janeri Filho <ojanerif@amd.com>
AuthorDate: 2026-09-09 15:13:52 +0000
Commit:     Mitchell Horne <mhorne@FreeBSD.org>
CommitDate: 2026-09-21 22:49:42 +0000

    hwpmc/amd: replace static pmcdesc[] and pc_amdpmcs[] with dynamic allocation
    
    AMD_NPMCS_MAX = 342 (16 core + 6 L3 + 64 DF + 256 UMC). On a Zen 4
    EPYC 9654 with 6 core, 6 L3, 16 DF, and 4 UMC counters, only 32
    descriptors are needed; the static arrays over-allocate by ~10x.
    
    Replace both amd_pmcdesc[AMD_NPMCS_MAX] and per-CPU pc_amdpmcs[AMD_NPMCS_MAX]
    with mallocarray() sized to the actual registered PMC count:
      - amd_pmcdesc: allocated in pmc_amd_initialize()
      - pc_amdpmcs:  allocated per-CPU in amd_pcpu_init(), freed in fini()
    
    Normalize amd_l3_npmcs and amd_df_npmcs against the AMDID2_PTSCEL2I
    and AMDID2_PNXC feature bits before computing npmcs_total, so that
    allocation, registration, and amd_get_msr() row offsets are all derived
    from the same values.  Previously the ternary in npmcs_total excluded
    L3/DF from the allocation while the globals retained their defaults,
    causing amd_get_msr() to miscompute DF row offsets when L3 is absent.
    
    amd_umc_npmcs comes from CPUID Fn8000_0022h EBX[23:16] (NumUMCCounters)
    and is zero when the leaf is absent, so no additional feature flag is
    needed.  See AMD64 APM Vol.3 Appendix E.
    
    Fix three error-path memory leaks: amd_hwcheck() failure, goto error,
    and finalize.  Reset amd_npmcs = 0 on the error path.
    
    Tested on AMD EPYC 9654 (Zen 4, Family 19h Model 11h, 192 threads).
    Full PMC test suite (IBS/UMCDF/PMC/L3/DF/TSC): 0 failures.
    
    Signed-off-by: Osvaldo Janeri Filho <ojanerif@amd.com>
    Reviewed by:    mhorne
    MFC after:      1 week
    Sponsored by:   AMD
    Pull Request:   https://github.com/freebsd/freebsd-src/pull/2415
---
 sys/dev/hwpmc/hwpmc_amd.c | 43 +++++++++++++++++++++++++++++++++++++++++--
 sys/dev/hwpmc/hwpmc_amd.h |  3 ++-
 2 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/sys/dev/hwpmc/hwpmc_amd.c b/sys/dev/hwpmc/hwpmc_amd.c
index c75bb9d0d14c..232c59aea3bf 100644
--- a/sys/dev/hwpmc/hwpmc_amd.c
+++ b/sys/dev/hwpmc/hwpmc_amd.c
@@ -65,7 +65,7 @@ struct amd_descr {
 
 static int amd_npmcs;
 static int amd_core_npmcs, amd_l3_npmcs, amd_df_npmcs, amd_umc_npmcs;
-static struct amd_descr amd_pmcdesc[AMD_NPMCS_MAX];
+static struct amd_descr *amd_pmcdesc;
 struct amd_event_code_map {
 	enum pmc_event	pe_ev;	 /* enum value */
 	uint16_t	pe_code; /* encoded event mask */
@@ -178,7 +178,7 @@ const int amd_event_codes_size = nitems(amd_event_codes);
  * Per-processor information
  */
 struct amd_cpu {
-	struct pmc_hw	pc_amdpmcs[AMD_NPMCS_MAX];
+	struct pmc_hw	*pc_amdpmcs;
 };
 static struct amd_cpu **amd_pcpu;
 
@@ -840,6 +840,8 @@ amd_pcpu_init(struct pmc_mdep *md, int cpu)
 
 	amd_pcpu[cpu] = pac = malloc(sizeof(struct amd_cpu), M_PMC,
 	    M_WAITOK | M_ZERO);
+	pac->pc_amdpmcs = mallocarray(amd_npmcs, sizeof(*pac->pc_amdpmcs),
+	    M_PMC, M_WAITOK | M_ZERO);
 
 	/*
 	 * Set the content of the hardware descriptors to a known
@@ -903,6 +905,7 @@ amd_pcpu_fini(struct pmc_mdep *md, int cpu)
 	for (i = 0; i < amd_npmcs; i++)
 		pc->pc_hwpmcs[i + first_ri] = NULL;
 
+	free(pac->pc_amdpmcs, M_PMC);
 	free(pac, M_PMC);
 	return (0);
 }
@@ -988,6 +991,7 @@ pmc_amd_initialize(void)
 	enum pmc_cputype cputype;
 	int ncpus, nclasses, i;
 	int family, model, stepping;
+	int npmcs_total;
 	int error;
 	int pmcs_per_umc;
 
@@ -1046,6 +1050,24 @@ pmc_amd_initialize(void)
 		}
 	}
 
+	/*
+	 * Normalize per-class counts against feature bits so that allocation,
+	 * registration, and amd_get_msr() row offsets all use the same values.
+	 * UMC counters have no CPUID feature flag; amd_umc_npmcs is 0 when
+	 * the CPUID leaf is absent.
+	 */
+	if ((amd_feature2 & AMDID2_PTSCEL2I) == 0)
+		amd_l3_npmcs = 0;
+	if ((amd_feature2 & AMDID2_PNXC) == 0)
+		amd_df_npmcs = 0;
+	npmcs_total = amd_core_npmcs + amd_l3_npmcs + amd_df_npmcs +
+	    amd_umc_npmcs;
+	KASSERT(npmcs_total <= AMD_NPMCS_MAX,
+	    ("%s: npmcs_total %d exceeds AMD_NPMCS_MAX %d",
+	    __func__, npmcs_total, AMD_NPMCS_MAX));
+	amd_pmcdesc = mallocarray(npmcs_total, sizeof(*amd_pmcdesc),
+	    M_PMC, M_WAITOK | M_ZERO);
+
 	/* Enable the newer core counters */
 	for (i = 0; i < amd_core_npmcs; i++) {
 		d = &amd_pmcdesc[i];
@@ -1105,6 +1127,9 @@ pmc_amd_initialize(void)
 		amd_npmcs += amd_df_npmcs;
 	}
 
+	KASSERT(amd_npmcs == npmcs_total - amd_umc_npmcs,
+	    ("%s: UMC cursor wrong: got %d expected %d",
+	    __func__, amd_npmcs, npmcs_total - amd_umc_npmcs));
 	for (i = 0; i < amd_umc_npmcs; i++) {
 		d = &amd_pmcdesc[amd_npmcs + i];
 		snprintf(d->pm_descr.pd_name, PMC_NAME_MAX,
@@ -1118,11 +1143,17 @@ pmc_amd_initialize(void)
 	}
 	amd_npmcs += amd_umc_npmcs;
 
+	KASSERT(amd_npmcs == npmcs_total,
+	    ("%s: descriptor cursor %d != npmcs_total %d",
+	    __func__, amd_npmcs, npmcs_total));
+
 	/*
 	 * Sanity check that the hardware is safe to use.  Do not read or write
 	 * any of the PMC MSRs until after this check passes.
 	 */
 	if (amd_hwcheck() < 0) {
+		free(amd_pmcdesc, M_PMC);
+		amd_pmcdesc = NULL;
 		return (NULL);
 	}
 
@@ -1216,6 +1247,11 @@ pmc_amd_initialize(void)
 
 error:
 	free(pmc_mdep, M_PMC);
+	free(amd_pcpu, M_PMC);
+	amd_pcpu = NULL;
+	free(amd_pmcdesc, M_PMC);
+	amd_pmcdesc = NULL;
+	amd_npmcs = 0;
 	return (NULL);
 }
 
@@ -1240,4 +1276,7 @@ pmc_amd_finalize(struct pmc_mdep *md)
 
 	free(amd_pcpu, M_PMC);
 	amd_pcpu = NULL;
+
+	free(amd_pmcdesc, M_PMC);
+	amd_pmcdesc = NULL;
 }
diff --git a/sys/dev/hwpmc/hwpmc_amd.h b/sys/dev/hwpmc/hwpmc_amd.h
index 4a8a5f6b6433..4a77ca210f97 100644
--- a/sys/dev/hwpmc/hwpmc_amd.h
+++ b/sys/dev/hwpmc/hwpmc_amd.h
@@ -197,7 +197,8 @@
 #define	AMD_PMC_UMC_TO_RDWRMASK(x)	(((x) << 8) & AMD_PMC_UMC_RDWRMASK)
 
 #define	AMD_NPMCS_K8		4
-#define AMD_NPMCS_MAX		(AMD_PMC_CORE_MAX + AMD_PMC_L3_MAX + \
+/* Compile-time upper bound; npmcs_total must not exceed this. */
+#define	AMD_NPMCS_MAX		(AMD_PMC_CORE_MAX + AMD_PMC_L3_MAX + \
 				 AMD_PMC_DF_MAX + AMD_PMC_UMC_MAX)
 
 #define AMD_PMC_IS_STOPPED(evsel) ((rdmsr((evsel)) & AMD_PMC_ENABLE) == 0)