aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAli Mashtizadeh <mashti@uwaterloo.ca>2026-03-01 22:08:30 +0000
committerMitchell Horne <mhorne@FreeBSD.org>2026-03-16 23:31:15 +0000
commit44a983d249d05d932b6cff333f130baf70febc22 (patch)
tree2d4f37205db9ad39f3bfd19c49e81c9d60480507
parentf0e0c4c52971f58d41a89690d402520500d286bd (diff)
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
-rw-r--r--lib/libpmc/libpmc.c19
-rw-r--r--share/man/man4/hwpmc.44
-rw-r--r--sys/dev/hwpmc/hwpmc_amd.c15
-rw-r--r--sys/dev/hwpmc/hwpmc_mod.c45
-rw-r--r--sys/sys/pmc.h15
5 files changed, 87 insertions, 11 deletions
diff --git a/lib/libpmc/libpmc.c b/lib/libpmc/libpmc.c
index 6be91a7501fe..ceba40aa7b39 100644
--- a/lib/libpmc/libpmc.c
+++ b/lib/libpmc/libpmc.c
@@ -1211,17 +1211,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 54a251dcca76..1ab690e5009c 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 c27d93995d59..51505bfcff37 100644
--- a/sys/dev/hwpmc/hwpmc_amd.c
+++ b/sys/dev/hwpmc/hwpmc_amd.c
@@ -703,6 +703,20 @@ amd_get_msr(int ri, uint32_t *msr)
}
/*
+ * 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.
*/
static int
@@ -958,6 +972,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 1fa021429c5a..fb1fdf832398 100644
--- a/sys/dev/hwpmc/hwpmc_mod.c
+++ b/sys/dev/hwpmc/hwpmc_mod.c
@@ -4538,6 +4538,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 7640a9b96c84..244a18e90c39 100644
--- a/sys/sys/pmc.h
+++ b/sys/sys/pmc.h
@@ -346,7 +346,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
@@ -638,6 +639,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>
@@ -1040,6 +1052,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);