git: 7c5caf70b6e9 - stable/15 - libpmc: Query hwpmc for caps
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 06 Oct 2026 18:43:32 UTC
The branch stable/15 has been updated by mhorne:
URL: https://cgit.FreeBSD.org/src/commit/?id=7c5caf70b6e9ec9de608480b841fb1d091f1d3ff
commit 7c5caf70b6e9ec9de608480b841fb1d091f1d3ff
Author: Ali Mashtizadeh <mashti@uwaterloo.ca>
AuthorDate: 2026-03-01 22:08:30 +0000
Commit: Mitchell Horne <mhorne@FreeBSD.org>
CommitDate: 2026-10-06 18:27:57 +0000
libpmc: Query hwpmc for caps
This change allows for fine-grained capabilities per counter index. This
is particularly useful for AMD where subclasses are not exposed to the
general PMC code, but other architectures also have asymmetric behaviors
when it comes to specific counter indices.
A new PMC_OP_GETCAPS op is added to the hwpmc(4) ioctl interface.
Reviewed by: mhorne
Sponsored by: Netflix
Pull Request: https://github.com/freebsd/freebsd-src/pull/2058
(cherry picked from commit 44a983d249d05d932b6cff333f130baf70febc22)
---
lib/libpmc/libpmc.c | 19 +++++++++----------
share/man/man4/hwpmc.4 | 4 ++++
sys/dev/hwpmc/hwpmc_amd.c | 15 +++++++++++++++
sys/dev/hwpmc/hwpmc_mod.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
sys/sys/pmc.h | 15 ++++++++++++++-
5 files changed, 87 insertions(+), 11 deletions(-)
diff --git a/lib/libpmc/libpmc.c b/lib/libpmc/libpmc.c
index 10e357f55935..aad9c2dff737 100644
--- a/lib/libpmc/libpmc.c
+++ b/lib/libpmc/libpmc.c
@@ -1168,17 +1168,16 @@ pmc_attach(pmc_id_t pmc, pid_t pid)
int
pmc_capabilities(pmc_id_t pmcid, uint32_t *caps)
{
- unsigned int i;
- enum pmc_class cl;
+ struct pmc_op_caps args;
+ int status;
- cl = PMC_ID_TO_CLASS(pmcid);
- for (i = 0; i < cpu_info.pm_nclass; i++)
- if (cpu_info.pm_classes[i].pm_class == cl) {
- *caps = cpu_info.pm_classes[i].pm_caps;
- return (0);
- }
- errno = EINVAL;
- return (-1);
+ args.pm_pmcid = pmcid;
+ args.pm_caps = 0;
+
+ status = PMC_CALL(PMC_OP_GETCAPS, &args);
+ *caps = args.pm_caps;
+
+ return (status);
}
int
diff --git a/share/man/man4/hwpmc.4 b/share/man/man4/hwpmc.4
index 8bb1cb96f694..5b3e9a195b8c 100644
--- a/share/man/man4/hwpmc.4
+++ b/share/man/man4/hwpmc.4
@@ -325,6 +325,10 @@ This operation returns to the caller after the write operation
has returned.
The returned error code reflects any pending error state inside
.Nm .
+.It Dv PMC_OP_GETCAPS
+Retrieve the capabilities associated with a specific PMC counter.
+Some capabilities may be limited to specific indices (i.e., not available
+across all counters within a class).
.It Dv PMC_OP_GETCPUINFO
Retrieve information about the highest possible CPU number for the system,
and the number of hardware performance monitoring counters available per CPU.
diff --git a/sys/dev/hwpmc/hwpmc_amd.c b/sys/dev/hwpmc/hwpmc_amd.c
index 82d1a4c6180c..1ecea9dd28d0 100644
--- a/sys/dev/hwpmc/hwpmc_amd.c
+++ b/sys/dev/hwpmc/hwpmc_amd.c
@@ -695,6 +695,20 @@ amd_get_msr(int ri, uint32_t *msr)
return (0);
}
+/*
+ * Return the capabilities of the given PMC.
+ */
+static int
+amd_get_caps(int ri, uint32_t *caps)
+{
+ KASSERT(ri >= 0 && ri < amd_npmcs,
+ ("[amd,%d] ri %d out of range", __LINE__, ri));
+
+ *caps = amd_pmcdesc[ri].pm_descr.pd_caps;
+
+ return (0);
+}
+
/*
* Processor-dependent initialization.
*/
@@ -1022,6 +1036,7 @@ pmc_amd_initialize(void)
pcd->pcd_start_pmc = amd_start_pmc;
pcd->pcd_stop_pmc = amd_stop_pmc;
pcd->pcd_write_pmc = amd_write_pmc;
+ pcd->pcd_get_caps = amd_get_caps;
pmc_mdep->pmd_cputype = cputype;
pmc_mdep->pmd_intr = amd_intr;
diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c
index 342b600276b0..de6beb165c66 100644
--- a/sys/dev/hwpmc/hwpmc_mod.c
+++ b/sys/dev/hwpmc/hwpmc_mod.c
@@ -4583,6 +4583,51 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
}
break;
+ /*
+ * Get the PMC capabilities
+ */
+
+ case PMC_OP_GETCAPS:
+ {
+ struct pmc_op_caps c;
+ struct pmc *pm;
+ struct pmc_classdep *pcd;
+ pmc_id_t pmcid;
+ int adjri, ri;
+
+ PMC_DOWNGRADE_SX();
+
+ if ((error = copyin(arg, &c, sizeof(c))) != 0)
+ break;
+
+ pmcid = c.pm_pmcid;
+
+ if ((error = pmc_find_pmc(pmcid, &pm)) != 0)
+ break;
+
+ KASSERT(pmcid == pm->pm_id,
+ ("[pmc,%d] pmc id %x != pmcid %x", __LINE__,
+ pm->pm_id, pmcid));
+
+ ri = PMC_TO_ROWINDEX(pm);
+ pcd = pmc_ri_to_classdep(md, ri, &adjri);
+
+ /*
+ * If PMC class has no GETCAPS return the class capabilities
+ * otherwise get the per counter capabilities.
+ */
+ if (pcd->pcd_get_caps == NULL) {
+ c.pm_caps = pcd->pcd_caps;
+ } else {
+ error = (*pcd->pcd_get_caps)(adjri, &c.pm_caps);
+ if (error < 0)
+ break;
+ }
+
+ if ((error = copyout(&c, arg, sizeof(c))) < 0)
+ break;
+ }
+ break;
default:
error = EINVAL;
diff --git a/sys/sys/pmc.h b/sys/sys/pmc.h
index 726a03c35502..7ac6eafdac4d 100644
--- a/sys/sys/pmc.h
+++ b/sys/sys/pmc.h
@@ -348,7 +348,8 @@ enum pmc_event {
__PMC_OP(PMCSTOP, "Stop a PMC") \
__PMC_OP(WRITELOG, "Write a cookie to the log file") \
__PMC_OP(CLOSELOG, "Close log file") \
- __PMC_OP(GETDYNEVENTINFO, "Get dynamic events list")
+ __PMC_OP(GETDYNEVENTINFO, "Get dynamic events list") \
+ __PMC_OP(GETCAPS, "Get capabilities")
enum pmc_ops {
#undef __PMC_OP
@@ -639,6 +640,17 @@ struct pmc_op_getdyneventinfo {
struct pmc_dyn_event_descr pm_events[PMC_EV_DYN_COUNT];
};
+/*
+ * OP GETCAPS
+ *
+ * Retrieve the PMC capabilties flags for this type of counter.
+ */
+
+struct pmc_op_caps {
+ pmc_id_t pm_pmcid; /* allocated pmc id */
+ uint32_t pm_caps; /* capabilities */
+};
+
#ifdef _KERNEL
#include <sys/malloc.h>
@@ -1031,6 +1043,7 @@ struct pmc_classdep {
/* description */
int (*pcd_describe)(int _cpu, int _ri, struct pmc_info *_pi,
struct pmc **_ppmc);
+ int (*pcd_get_caps)(int _ri, uint32_t *_caps);
/* class-dependent initialization & finalization */
int (*pcd_pcpu_init)(struct pmc_mdep *_md, int _cpu);