git: 6d9f435739e5 - main - hwpmc: fix leak of IBS per-CPU array on unload

From: Mitchell Horne <mhorne_at_FreeBSD.org>
Date: Tue, 22 Sep 2026 14:01:27 UTC
The branch main has been updated by mhorne:

URL: https://cgit.FreeBSD.org/src/commit/?id=6d9f435739e5fb604f452d03ad7f2eb280bfc968

commit 6d9f435739e5fb604f452d03ad7f2eb280bfc968
Author:     Paulo Fragoso <paulo@nlink.com.br>
AuthorDate: 2026-09-22 14:00:31 +0000
Commit:     Mitchell Horne <mhorne@FreeBSD.org>
CommitDate: 2026-09-22 14:01:24 +0000

    hwpmc: fix leak of IBS per-CPU array on unload
    
    pmc_ibs_initialize() allocates the ibs_pcpu[] pointer array, and
    pmc_ibs_finalize() exists to free it, but pmc_ibs_finalize() is
    never called.  Every hwpmc unload on a CPU with IBS therefore leaks
    one pmc_cpu_max()-sized pointer array.
    
    Call pmc_ibs_finalize() from pmc_amd_finalize(), alongside the RAPL,
    TSC and perf classes.  IBS is only initialized on CPUs that support
    it, so make pmc_ibs_finalize() return early when ibs_pcpu is NULL,
    making it safe to call when the class was skipped at initialize
    time, as pmc_rapl_finalize() already is.
    
    Tested on an AMD Ryzen 5 5600X (Zen 3, 12 threads) with INVARIANTS.
    Before the change, each kldload/kldunload cycle leaked one 96-byte
    M_PMC allocation, and DTrace showed the ibs_pcpu[] allocation from
    pmc_ibs_initialize() as the only one never freed.  After the change,
    50 load/unload cycles leave M_PMC InUse and MemUse unchanged, and
    every allocation made at load is freed at unload.
    
    Reviewed by:    mhorne
    Fixes:          e51ef8ae490f ("hwpmc: Initial support for AMD IBS")
    Sponsored by:   NLINK (https://nlink.com.br), Recife, Brazil
    Differential Revision:  https://reviews.freebsd.org/D59881
---
 sys/dev/hwpmc/hwpmc_amd.c | 2 ++
 sys/dev/hwpmc/hwpmc_ibs.c | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/sys/dev/hwpmc/hwpmc_amd.c b/sys/dev/hwpmc/hwpmc_amd.c
index 232c59aea3bf..77d4a4230e49 100644
--- a/sys/dev/hwpmc/hwpmc_amd.c
+++ b/sys/dev/hwpmc/hwpmc_amd.c
@@ -1270,6 +1270,8 @@ pmc_amd_finalize(struct pmc_mdep *md)
 
 	pmc_perf_finalize(md);
 
+	pmc_ibs_finalize(md);
+
 	for (int i = 0; i < pmc_cpu_max(); i++)
 		KASSERT(amd_pcpu[i] == NULL,
 		    ("[amd,%d] non-null pcpu cpu %d", __LINE__, i));
diff --git a/sys/dev/hwpmc/hwpmc_ibs.c b/sys/dev/hwpmc/hwpmc_ibs.c
index ce9e038e5d0b..17c3495f6a79 100644
--- a/sys/dev/hwpmc/hwpmc_ibs.c
+++ b/sys/dev/hwpmc/hwpmc_ibs.c
@@ -918,6 +918,10 @@ pmc_ibs_finalize(struct pmc_mdep *md)
 {
 	PMCDBG0(MDP, INI, 1, "ibs-finalize");
 
+	/* Safe even if the IBS class was skipped at initialize time. */
+	if (ibs_pcpu == NULL)
+		return;
+
 	for (int i = 0; i < pmc_cpu_max(); i++)
 		KASSERT(ibs_pcpu[i] == NULL,
 		    ("[ibs,%d] non-null pcpu cpu %d", __LINE__, i));