git: 8a0cab240f21 - main - hwpmc/amd: replace static pmcdesc[] and pc_amdpmcs[] with dynamic allocation
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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)