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 <[email protected]>
Reviewed by:	mhorne
MFC after:	1 week
Sponsored by:	AMD
Pull Request:	https://github.com/freebsd/freebsd-src/pull/2415
This commit is contained in:
Osvaldo Janeri Filho authored and Mitchell Horne committed 2026-09-21 19:49:42 -03:00
1 parent c745fccd9b
commit 8a0cab240f
2 files changed
+43 -3

No files matched your search

+41 -2
View File
@@ -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;
}
+2 -1
View File
@@ -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)