aboutsummaryrefslogtreecommitdiff
path: root/lib/libpmc/libpmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libpmc/libpmc.c')
-rw-r--r--lib/libpmc/libpmc.c98
1 files changed, 88 insertions, 10 deletions
diff --git a/lib/libpmc/libpmc.c b/lib/libpmc/libpmc.c
index ceba40aa7b39..562000aef4e4 100644
--- a/lib/libpmc/libpmc.c
+++ b/lib/libpmc/libpmc.c
@@ -32,6 +32,10 @@
#include <sys/pmc.h>
#include <sys/syscall.h>
+#if defined(__amd64__) || defined(__i386__)
+#include <machine/cpufunc.h>
+#endif
+
#include <ctype.h>
#include <errno.h>
#include <err.h>
@@ -696,7 +700,9 @@ ibs_allocate_pmc(enum pmc_event pe, char *ctrspec,
struct pmc_op_pmcallocate *pmc_config)
{
char *e, *p, *q;
- uint64_t ctl;
+ uint64_t ctl, ldlat;
+ u_int ibs_features;
+ u_int regs[4];
pmc_config->pm_caps |=
(PMC_CAP_SYSTEM | PMC_CAP_EDGE | PMC_CAP_PRECISE);
@@ -714,23 +720,95 @@ ibs_allocate_pmc(enum pmc_event pe, char *ctrspec,
return (-1);
}
+ /* IBS only supports sampling mode */
+ if (!PMC_IS_SAMPLING_MODE(pmc_config->pm_mode)) {
+ return (-1);
+ }
+
+ /* Read the ibs feature flags */
+ ibs_features = 0;
+ do_cpuid(0x80000000, regs);
+ if (regs[0] >= CPUID_IBSID) {
+ do_cpuid(CPUID_IBSID, regs);
+ ibs_features = regs[0];
+ }
+
/* parse parameters */
- while ((p = strsep(&ctrspec, ",")) != NULL) {
- if (KWPREFIXMATCH(p, "ctl=")) {
- q = strchr(p, '=');
- if (*++q == '\0') /* skip '=' */
- return (-1);
+ ctl = 0;
+ if (pe == PMC_EV_IBS_FETCH) {
+ while ((p = strsep(&ctrspec, ",")) != NULL) {
+ if (KWMATCH(p, "l3miss")) {
+ if ((ibs_features & CPUID_IBSID_ZEN4IBSEXTENSIONS) == 0)
+ return (-1);
- ctl = strtoull(q, &e, 0);
- if (e == q || *e != '\0')
+ ctl |= IBS_FETCH_CTL_L3MISSONLY;
+ } else if (KWMATCH(p, "randomize")) {
+ ctl |= IBS_FETCH_CTL_RANDOMIZE;
+ } else {
return (-1);
+ }
+ }
- pmc_config->pm_md.pm_ibs.ibs_ctl |= ctl;
- } else {
+ if (pmc_config->pm_count < IBS_FETCH_MIN_RATE ||
+ pmc_config->pm_count > IBS_FETCH_MAX_RATE)
return (-1);
+
+ ctl |= IBS_FETCH_INTERVAL_TO_CTL(pmc_config->pm_count);
+ } else {
+ while ((p = strsep(&ctrspec, ",")) != NULL) {
+ if (KWMATCH(p, "l3miss")) {
+ ctl |= IBS_OP_CTL_L3MISSONLY;
+ } else if (KWPREFIXMATCH(p, "ldlat=")) {
+ if ((ibs_features & CPUID_IBSID_IBSLOADLATENCYFILT) == 0)
+ return (-1);
+
+ q = strchr(p, '=');
+ if (*++q == '\0') /* skip '=' */
+ return (-1);
+
+ ldlat = strtoull(q, &e, 0);
+ if (e == q || *e != '\0')
+ return (-1);
+
+ /*
+ * IBS load latency filtering requires the
+ * latency to be a multiple of 128 and between
+ * 128 and 2048. The latency is stored in the
+ * IbsOpLatThrsh field, which only contains
+ * four bits so the processor computes
+ * (IbsOpLatThrsh+1)*128 as the value.
+ *
+ * AMD PPR Vol 1 for AMD Family 1Ah Model 02h
+ * C1 (57238) 2026-03-06 Revision 0.49.
+ */
+ if (ldlat < 128 || ldlat > 2048)
+ return (-1);
+ ctl |= IBS_OP_CTL_LDLAT_TO_CTL(ldlat);
+ ctl |= IBS_OP_CTL_L3MISSONLY | IBS_OP_CTL_LATFLTEN;
+ } else if (KWMATCH(p, "opcount")) {
+ if ((ibs_features & CPUID_IBSID_OPCNT) == 0)
+ return (-1);
+
+ ctl |= IBS_OP_CTL_COUNTERCONTROL;
+ } else {
+ return (-1);
+ }
}
+
+ if (pmc_config->pm_count < IBS_OP_MIN_RATE ||
+ pmc_config->pm_count > IBS_OP_MAX_RATE)
+ return (-1);
+
+ if (((ibs_features & CPUID_IBSID_OPCNTEXT) == 0) &&
+ (pmc_config->pm_count > IBS_OP_MAX_RATE_PREEXT))
+ return (-1);
+
+ ctl |= IBS_OP_INTERVAL_TO_CTL(pmc_config->pm_count);
}
+
+ pmc_config->pm_md.pm_ibs.ibs_ctl |= ctl;
+
return (0);
}