Page MenuHomeFreeBSD

D58647.id183401.diff
No OneTemporary

D58647.id183401.diff

diff --git a/lib/libpmc/Makefile b/lib/libpmc/Makefile
--- a/lib/libpmc/Makefile
+++ b/lib/libpmc/Makefile
@@ -77,6 +77,7 @@
MAN+= pmc.ibs.3
MAN+= pmc.ivybridge.3
MAN+= pmc.ivybridgexeon.3
+MAN+= pmc.perf.3
MAN+= pmc.rapl.3
MAN+= pmc.sandybridge.3
MAN+= pmc.sandybridgeuc.3
diff --git a/lib/libpmc/libpmc.c b/lib/libpmc/libpmc.c
--- a/lib/libpmc/libpmc.c
+++ b/lib/libpmc/libpmc.c
@@ -60,6 +60,8 @@
struct pmc_op_pmcallocate *_pmc_config);
static int rapl_allocate_pmc(enum pmc_event _pe, char *_ctrspec,
struct pmc_op_pmcallocate *_pmc_config);
+static int perf_allocate_pmc(enum pmc_event _pe, char *_ctrspec,
+ struct pmc_op_pmcallocate *_pmc_config);
#endif
#if defined(__arm__)
static int armv7_allocate_pmc(enum pmc_event _pe, char *_ctrspec,
@@ -139,6 +141,7 @@
PMC_CLASSDEP_TABLE(iaf, IAF);
PMC_CLASSDEP_TABLE(k8, K8);
PMC_CLASSDEP_TABLE(ibs, IBS);
+PMC_CLASSDEP_TABLE(perf, PERF);
PMC_CLASSDEP_TABLE(armv7, ARMV7);
PMC_CLASSDEP_TABLE(armv8, ARMV8);
PMC_CLASSDEP_TABLE(cmn600_pmu, CMN600_PMU);
@@ -216,6 +219,7 @@
PMC_CLASS_TABLE_DESC(ibs, IBS, ibs, ibs);
PMC_CLASS_TABLE_DESC(tsc, TSC, tsc, tsc);
PMC_CLASS_TABLE_DESC(rapl, RAPL, rapl, rapl);
+PMC_CLASS_TABLE_DESC(perf, PERF, perf, perf);
#endif
#if defined(__arm__)
PMC_CLASS_TABLE_DESC(cortex_a8, ARMV7, cortex_a8, armv7);
@@ -903,6 +907,22 @@
return (0);
}
+
+static int
+perf_allocate_pmc(enum pmc_event pe, char *ctrspec,
+ struct pmc_op_pmcallocate *pmc_confg)
+{
+ if (pe < PMC_EV_PERF_FIRST || pe > PMC_EV_PERF_LAST)
+ return (-1);
+
+ /* PERF events must be unqualified. */
+ if (ctrspec != NULL && *ctrspec != '\0')
+ return (-1);
+
+ pmc_confg->pm_caps |= PMC_CAP_READ;
+
+ return (0);
+}
#endif
static struct pmc_event_alias generic_aliases[] = {
@@ -1463,6 +1483,10 @@
ev = rapl_event_table;
count = PMC_EVENT_TABLE_SIZE(rapl);
break;
+ case PMC_CLASS_PERF:
+ ev = perf_event_table;
+ count = PMC_EVENT_TABLE_SIZE(perf);
+ break;
case PMC_CLASS_K8:
ev = k8_event_table;
count = PMC_EVENT_TABLE_SIZE(k8);
@@ -1675,6 +1699,10 @@
pmc_class_table[n++] = &rapl_class_table_descr;
break;
+ case PMC_CLASS_PERF:
+ pmc_class_table[n++] = &perf_class_table_descr;
+ break;
+
case PMC_CLASS_K8:
pmc_class_table[n++] = &k8_class_table_descr;
break;
@@ -1950,6 +1978,9 @@
} else if (pe >= PMC_EV_RAPL_FIRST && pe <= PMC_EV_RAPL_LAST) {
ev = rapl_event_table;
evfence = rapl_event_table + PMC_EVENT_TABLE_SIZE(rapl);
+ } else if (pe >= PMC_EV_PERF_FIRST && pe <= PMC_EV_PERF_LAST) {
+ ev = perf_event_table;
+ evfence = perf_event_table + PMC_EVENT_TABLE_SIZE(perf);
} else if ((int)pe >= PMC_EV_SOFT_FIRST && (int)pe <= PMC_EV_SOFT_LAST) {
ev = soft_event_table;
evfence = soft_event_table + soft_event_info.pm_nevent;
diff --git a/lib/libpmc/pmc.3 b/lib/libpmc/pmc.3
--- a/lib/libpmc/pmc.3
+++ b/lib/libpmc/pmc.3
@@ -233,6 +233,10 @@
Programmable hardware counters present in
.Tn "AMD Athlon64"
CPUs.
+.It Li PMC_CLASS_PERF
+MPERF and APERF counters present in
+.Tn AMD
+CPUs.
.It Li PMC_CLASS_RAPL
RAPL energy counters present in
.Tn AMD
@@ -505,6 +509,7 @@
.It Li PMC_CLASS_IBS Ta Xr pmc.ibs 3
.It Li PMC_CLASS_K8 Ta Xr pmc.amd 3
.It Li PMC_CLASS_RAPL Ta Xr pmc.rapl 3
+.It Li PMC_CLASS_PERF Ta Xr pmc.perf 3
.It Li PMC_CLASS_TSC Ta Xr pmc.tsc 3
.El
.Ss Event Name Aliases
@@ -558,6 +563,7 @@
.Xr pmc.ibs 3 ,
.Xr pmc.ivybridge 3 ,
.Xr pmc.ivybridgexeon 3 ,
+.Xr pmc.perf 3 ,
.Xr pmc.rapl 3 ,
.Xr pmc.sandybridge 3 ,
.Xr pmc.sandybridgeuc 3 ,
diff --git a/lib/libpmc/pmc.perf.3 b/lib/libpmc/pmc.perf.3
new file mode 100644
--- /dev/null
+++ b/lib/libpmc/pmc.perf.3
@@ -0,0 +1,138 @@
+.\" Copyright (c) 2026 Advanced Micro Devices, Inc.
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd August 5, 2026
+.Dt PMC.PERF 3
+.Os.Sh NAME
+.Nm pmc.perf
+.Nd measurements using the MPERF/APERF effective frequency counters
+.Sh LIBRARY
+.Lb libpmc
+.Sh SYNOPSIS
+.In pmc.h
+.Sh DESCRIPTION
+AMD processors that implement the EffFreq
+.Pq Dq Effective Frequency Interface
+feature report two fixed-counters machine specific registers,
+.Va MPERF
+.Pq Dq Max Performance Frequency Clock Count
+and
+.Va APERF
+.Pq Dq Actual Performance Frequency Clock Count ,
+that together allow the average effective CPU frequency over an
+interval to be computed.
+.Va MPERF
+is incremented by hardware at the P0 frequency.
+The
+.Va APERF
+register increments in proportion to the actual number of core
+clock cycles.
+Both registers only increment while the core is in C0.
+The ratio of an
+.Va APERF
+delta to the corresponding
+.Va MPERF
+delta, scaled by the processor's nominal frequency, yields the
+average effective frequency over that interval.
+.Pp
+The
+.Dv PMC_CLASS_PERF
+class exposes these two MSRs as read-only 64-bit counters that may
+only be allocated in system-wide counting mode
+.Pq Dv PMC_MODE_SC .
+Counter values are the raw, unscaled contents of the underlying MSR.
+No conversion to frequency or any other unit is performed by the
+kernel; callers wanting an effective frequency must sample both
+counters at two points in time and compute the ratio of deltas
+themselves.
+.Pp
+A counter's value is not reset when a PMC is allocated or released;
+it reflects the free-running count of the underlying MSR since the
+processor was last reset, and continues to accumulate across
+allocate/release cycles.
+An individual reading is therefore only meaningful as a difference
+between two samples taken while a PMC of this class is allocated.
+.Ss PMC Features
+AMD PERF supports the following capabilities.
+.Bl -column "PMC_CAP_THRESHOLD" "Support"
+.It Sy Capability Ta Sy Support
+.It Dv PMC_CAP_CASCADE Ta \&No
+.It Dv PMC_CAP_EDGE Ta \&No
+.It Dv PMC_CAP_INTERRUPT Ta \&No
+.It Dv PMC_CAP_INVERT Ta \&No
+.It Dv PMC_CAP_PRECISE Ta \&No
+.It Dv PMC_CAP_READ Ta Yes
+.It Dv PMC_CAP_SYSTEM Ta \&No
+.It Dv PMC_CAP_TAGGING Ta \&No
+.It Dv PMC_CAP_THRESHOLD Ta \&No
+.It Dv PMC_CAP_USER Ta \&No
+.It Dv PMC_CAP_WRITE Ta \&No
+.El
+.Pp
+By default AMD PERF enables the read flag.
+.Pp
+PERF events do not support further event qualifiers.
+.Ss Event Specifiers
+The following event names are supported:
+.Bl -tag -width "perf-mperf / mperf"
+.It Cm perf-mperf , Cm mperf
+The Max Performance Frequency Clock Count
+MSR of the core the counter is bound to.
+.It Cm perf-aperf , Cm aperf
+The Actual Performance Frequency Clock Count
+MSR of the core the counter is bound to.
+.El
+.Ss Counter Scope
+.Va MPERF
+and
+.Va APERF
+are per-core registers: each core has its own independent pair of
+MSRs, and a PMC of this class always reads the value local to the
+CPU it is bound to.
+Readings from different cores must not be combined or averaged
+without accounting for each core's independent P-state history.
+.Ss Availability
+Availability of the
+.Dv PMC_CLASS_PERF
+class depends on both of the following being true:
+.Bl -enum
+.It
+The processor advertises EffFreq
+.Pq Dq Effective Frequency Interface
+support in CPUID 6 leaf 0000_0006H bit 0 ECX.
+.It
+The kernel verified, at boot time, that the MSRs actually increment.
+Some virtualized environments advertise the CPUID bit without
+functional MSR support, and the MSRs may also be read-only on such
+hosts.
+.El
+.Pp
+If either condition does not hold, the
+.Dv PMC_CLASS_PERF
+class is not registered and does not appear in the output of
+.Xr pmccontrol 8
+or
+.Xr pmcstat 8 .
+.Sh EXAMPLES
+Sample both counters on CPU 0 for five seconds:
+.Bd -literal -offset indent
+pmcstat -c 0 -s perf-mperf -s perf-aperf -w 5
+.Ed
+.Sh SEE ALSO
+.Xr pmc 3 ,
+.Xr pmc.tsc 3 ,
+.Xr pmc_allocate 3 ,
+.Xr pmc_read 3 ,
+.Xr pmclog 3 ,
+.Xr hwpmc 4 ,
+.Xr pmcstat 8
+.Sh HISTORY
+The
+.Dv PMC_CLASS_PERF
+class first appeared in
+.Fx 16.0 .
+.Sh AUTHORS
+AMD PERF support and this manual page were written by
+.An Anderson Nascimento Aq Mt anascime@amd.com
+and sponsored by AMD, Inc.
diff --git a/sys/amd64/include/pmc_mdep.h b/sys/amd64/include/pmc_mdep.h
--- a/sys/amd64/include/pmc_mdep.h
+++ b/sys/amd64/include/pmc_mdep.h
@@ -45,6 +45,7 @@
#include <dev/hwpmc/hwpmc_rapl.h>
#include <dev/hwpmc/hwpmc_tsc.h>
#include <dev/hwpmc/hwpmc_uncore.h>
+#include <dev/hwpmc/hwpmc_perf.h>
/*
* Intel processors implementing V2 and later of the Intel performance
@@ -66,6 +67,7 @@
* TSC The timestamp counter
* K8 AMD Athlon64 and Opteron PMCs in 64 bit mode.
* IBS AMD IBS
+ * PERF AMD MPERF / APERF
* IAP Intel Core/Core2/Atom CPUs in 64 bits mode.
* IAF Intel fixed-function PMCs in Core2 and later CPUs.
* UCP Intel Uncore programmable PMCs.
diff --git a/sys/conf/files.x86 b/sys/conf/files.x86
--- a/sys/conf/files.x86
+++ b/sys/conf/files.x86
@@ -118,6 +118,7 @@
dev/hptrr/hptrr_config.c optional hptrr
dev/hptrr/$M-elf.hptrr_lib.o optional hptrr
dev/hwpmc/hwpmc_amd.c optional hwpmc
+dev/hwpmc/hwpmc_perf.c optional hwpmc
dev/hwpmc/hwpmc_ibs.c optional hwpmc
dev/hwpmc/hwpmc_intel.c optional hwpmc
dev/hwpmc/hwpmc_core.c optional hwpmc
diff --git a/sys/dev/hwpmc/hwpmc_amd.c b/sys/dev/hwpmc/hwpmc_amd.c
--- a/sys/dev/hwpmc/hwpmc_amd.c
+++ b/sys/dev/hwpmc/hwpmc_amd.c
@@ -1090,6 +1090,17 @@
nclasses = 3;
}
+ /*
+ * Detect support for MPERF and APERF MSRs. tsc_perf_stat is set by the
+ * kernel's generic TSC initialization (start_TSC(), called at boot via
+ * cpu_startup() -> startrtclock()), not by hwpmc's TSC PMC class. It is
+ * set only after confirming both MSRs actually increment (some emulators
+ * expose the CPUID bit without real MSR support).
+ */
+ if ((cpu_power_ecx & CPUID_PERF_STAT) && (tsc_perf_stat == 1)) {
+ nclasses++;
+ }
+
pmc_mdep = pmc_mdep_alloc(nclasses);
ncpus = pmc_cpu_max();
@@ -1139,6 +1150,15 @@
goto error;
}
+ if ((cpu_power_ecx & CPUID_PERF_STAT) && (tsc_perf_stat == 1)) {
+ /* The PERF class is the one right after IBS. */
+ int perf_classindex = nclasses - 1;
+ /* Initialize PERF class. */
+ error = pmc_perf_initialize(pmc_mdep, ncpus, perf_classindex);
+ if (error != 0)
+ goto error;
+ }
+
/* RAPL takes the reserved last slot; drop it if the probe fails. */
error = pmc_rapl_initialize(pmc_mdep, ncpus, pmc_mdep->pmd_nclass - 1);
if (error != 0)
@@ -1164,6 +1184,10 @@
pmc_tsc_finalize(md);
+ if ((cpu_power_ecx & CPUID_PERF_STAT) && (tsc_perf_stat == 1)) {
+ pmc_perf_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_perf.h b/sys/dev/hwpmc/hwpmc_perf.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/hwpmc/hwpmc_perf.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * AMD APERF and MPERF MSRs counters exposed as an hwpmc(4) PMC class.
+ */
+
+#ifdef _KERNEL
+
+/*
+ * A row per MSR: MPERF and APERF.
+ */
+
+#define PERF_NPMCS 2
+#define PERF_MPERF 0
+#define PERF_APERF 1
+
+extern int tsc_perf_stat;
+
+/*
+ * Prototypes.
+ */
+
+int pmc_perf_initialize(struct pmc_mdep *_md, int maxcpu, int classindex);
+void pmc_perf_finalize(struct pmc_mdep *_md);
+#endif /* _KERNEL */
diff --git a/sys/dev/hwpmc/hwpmc_perf.c b/sys/dev/hwpmc/hwpmc_perf.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/hwpmc/hwpmc_perf.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * AMD MPERF and APERF MSRs counters exposed as an hwpmc(4) PMC class.
+ *
+ * Read-only, system-scope (PMC_MODE_SC), 64-bit counters reporting
+ * MPERF and APERF MSR values.
+ */
+
+#include <sys/param.h>
+#include <sys/pmc.h>
+#include <sys/pmckern.h>
+#include <sys/priv.h>
+
+#include <machine/specialreg.h>
+
+#define PERF_CAPS PMC_CAP_READ
+
+struct perf_descr {
+ struct pmc_descr pm_descr; /* "base class" */
+};
+
+static struct perf_descr perf_pmcdesc[PERF_NPMCS] = {
+ {
+ .pm_descr = {
+ .pd_name = "MPERF",
+ .pd_class = PMC_CLASS_PERF,
+ .pd_caps = PERF_CAPS,
+ .pd_width = 64
+ },
+ },
+ {
+ .pm_descr = {
+ .pd_name = "APERF",
+ .pd_class = PMC_CLASS_PERF,
+ .pd_caps = PERF_CAPS,
+ .pd_width = 64
+ }
+ }
+};
+
+struct perf_cpu {
+ struct pmc_hw tc_hw[PERF_NPMCS];
+};
+
+static struct perf_cpu **perf_pcpu;
+static int perf_classindex;
+
+static int
+perf_allocate_pmc(int cpu __diagused, int ri __diagused,
+ struct pmc *pm __unused, const struct pmc_op_pmcallocate *a)
+{
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal CPU value %d", __LINE__, cpu));
+ KASSERT(ri >= 0 && ri < PERF_NPMCS,
+ ("[perf,%d] illegal row index %d", __LINE__, ri));
+
+ if (a->pm_class != PMC_CLASS_PERF)
+ return (EINVAL);
+
+ if ((a->pm_ev < PMC_EV_PERF_FIRST || a->pm_ev > PMC_EV_PERF_LAST) ||
+ a->pm_mode != PMC_MODE_SC)
+ return (EINVAL);
+
+ if ((a->pm_caps & PERF_CAPS) == 0)
+ return (EINVAL);
+ if ((a->pm_caps & ~PERF_CAPS) != 0)
+ return (EPERM);
+
+ if (priv_check(curthread, PRIV_PMC_SYSTEM) != 0)
+ return (EPERM);
+
+ switch (ri) {
+ case PERF_MPERF:
+ if (a->pm_ev != PMC_EV_PERF_MPERF)
+ return (EINVAL);
+ break;
+ case PERF_APERF:
+ if (a->pm_ev != PMC_EV_PERF_APERF)
+ return (EINVAL);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+perf_config_pmc(int cpu, int ri, struct pmc *pm)
+{
+ struct pmc_hw *phw;
+
+ PMCDBG3(MDP, CFG, 1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
+
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal CPU value %d", __LINE__, cpu));
+ KASSERT(ri >= 0 && ri < PERF_NPMCS, ("[perf,%d] illegal row-index %d",
+ __LINE__, ri));
+
+ phw = &perf_pcpu[cpu]->tc_hw[ri];
+
+ KASSERT(pm == NULL || phw->phw_pmc == NULL,
+ ("[perf,%d] pm=%p phw->pm=%p hwpmc not unconfigured", __LINE__,
+ pm, phw->phw_pmc));
+
+ phw->phw_pmc = pm;
+
+ return (0);
+}
+
+static int
+perf_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
+{
+ const struct perf_descr *pd;
+ struct pmc_hw *phw;
+
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal CPU %d", __LINE__, cpu));
+ KASSERT(ri >= 0 && ri < PERF_NPMCS, ("[perf,%d] illegal row-index %d",
+ __LINE__, ri));
+
+ phw = &perf_pcpu[cpu]->tc_hw[ri];
+ pd = &perf_pmcdesc[ri];
+
+ strlcpy(pi->pm_name, pd->pm_descr.pd_name, sizeof(pi->pm_name));
+ pi->pm_class = pd->pm_descr.pd_class;
+
+ if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
+ pi->pm_enabled = TRUE;
+ *ppmc = phw->phw_pmc;
+ } else {
+ pi->pm_enabled = FALSE;
+ *ppmc = NULL;
+ }
+
+ return (0);
+}
+
+static int
+perf_get_config(int cpu, int ri __diagused, struct pmc **ppm)
+{
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal CPU %d", __LINE__, cpu));
+ KASSERT(ri >= 0 && ri < PERF_NPMCS, ("[perf,%d] illegal row-index %d",
+ __LINE__, ri));
+
+ *ppm = perf_pcpu[cpu]->tc_hw[ri].phw_pmc;
+
+ return (0);
+}
+
+static int
+perf_get_msr(int ri , uint32_t *msr)
+{
+ KASSERT(ri >= 0 && ri < PERF_NPMCS,
+ ("[perf,%d] ri %d out of range", __LINE__, ri));
+
+ switch (ri) {
+ case PERF_MPERF:
+ *msr = MSR_MPERF;
+ break;
+ case PERF_APERF:
+ *msr = MSR_APERF;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+perf_pcpu_fini(struct pmc_mdep *md, int cpu)
+{
+ int i, ri;
+ struct pmc_cpu *pc;
+
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal cpu %d", __LINE__, cpu));
+ KASSERT(perf_pcpu[cpu] != NULL, ("[perf,%d] null pcpu", __LINE__));
+
+ free(perf_pcpu[cpu], M_PMC);
+ perf_pcpu[cpu] = NULL;
+
+ ri = md->pmd_classdep[perf_classindex].pcd_ri;
+ pc = pmc_pcpu[cpu];
+
+ for (i = 0; i < PERF_NPMCS; i++) {
+ pc->pc_hwpmcs[i + ri] = NULL;
+ }
+
+ return (0);
+}
+
+static int
+perf_pcpu_init(struct pmc_mdep *md, int cpu)
+{
+ int i, ri;
+ struct pmc_cpu *pc;
+ struct perf_cpu *perf_pc;
+
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal cpu %d", __LINE__, cpu));
+ KASSERT(perf_pcpu, ("[perf,%d] null pcpu", __LINE__));
+ KASSERT(perf_pcpu[cpu] == NULL, ("[perf,%d] non-null per-cpu",
+ __LINE__));
+
+ perf_pc = malloc(sizeof(struct perf_cpu), M_PMC, M_WAITOK | M_ZERO);
+
+ perf_pcpu[cpu] = perf_pc;
+
+ ri = md->pmd_classdep[perf_classindex].pcd_ri;
+
+ KASSERT(pmc_pcpu, ("[perf,%d] null generic pcpu", __LINE__));
+
+ pc = pmc_pcpu[cpu];
+
+ KASSERT(pc, ("[perf,%d] null generic per-cpu", __LINE__));
+
+ for (i = 0; i < PERF_NPMCS; i++) {
+ perf_pc->tc_hw[i].phw_state = PMC_PHW_FLAG_IS_ENABLED |
+ PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i) |
+ PMC_PHW_FLAG_IS_SHAREABLE;
+ pc->pc_hwpmcs[i + ri] = &perf_pc->tc_hw[i];
+ }
+
+ return (0);
+}
+
+static int
+perf_read_pmc(int cpu __diagused, int ri, struct pmc *pm, pmc_value_t *v)
+{
+ enum pmc_mode mode __diagused;
+
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal CPU value %d", __LINE__, cpu));
+ KASSERT(ri >= 0 && ri < PERF_NPMCS, ("[perf,%d] illegal ri %d",
+ __LINE__, ri));
+
+ mode = PMC_TO_MODE(pm);
+
+ KASSERT(mode == PMC_MODE_SC,
+ ("[perf,%d] illegal pmc mode %d", __LINE__, mode));
+
+ PMCDBG1(MDP, REA, 1, "perf-read id=%d", ri);
+
+ switch (ri) {
+ case PERF_MPERF:
+ *v = rdmsr(MSR_MPERF);
+ break;
+ case PERF_APERF:
+ *v = rdmsr(MSR_APERF);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+perf_release_pmc(int cpu __diagused, int ri __diagused,
+ struct pmc *pmc __unused)
+{
+ struct pmc_hw *phw __diagused;
+
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal CPU value %d", __LINE__, cpu));
+ KASSERT(ri >= 0 && ri < PERF_NPMCS,
+ ("[perf,%d] illegal row-index %d", __LINE__, ri));
+
+ phw = &perf_pcpu[cpu]->tc_hw[ri];
+
+ KASSERT(phw->phw_pmc == NULL,
+ ("[perf,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
+
+ return (0);
+}
+
+static int
+perf_start_pmc(int cpu __diagused, int ri __diagused, struct pmc *pm __unused)
+{
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal CPU value %d", __LINE__, cpu));
+ KASSERT(ri >= 0 && ri < PERF_NPMCS, ("[perf,%d] illegal row-index %d",
+ __LINE__, ri));
+
+ return (0);
+}
+
+static int
+perf_stop_pmc(int cpu __diagused, int ri __diagused, struct pmc *pm __unused)
+{
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal CPU value %d", __LINE__, cpu));
+ KASSERT(ri >= 0 && ri < PERF_NPMCS, ("[perf,%d] illegal row-index %d",
+ __LINE__, ri));
+
+ return (0);
+}
+
+static int
+perf_write_pmc(int cpu __diagused, int ri __diagused, struct pmc *pm __unused,
+ pmc_value_t v __unused)
+{
+ KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
+ ("[perf,%d] illegal CPU value %d", __LINE__, cpu));
+ KASSERT(ri >= 0 && ri < PERF_NPMCS, ("[perf,%d] illegal row-index %d",
+ __LINE__, ri));
+
+ return (0);
+}
+
+int
+pmc_perf_initialize(struct pmc_mdep *md, int maxcpu, int classindex)
+{
+ struct pmc_classdep *pcd;
+
+ KASSERT(md != NULL, ("[perf,%d] md is NULL", __LINE__));
+ KASSERT(md->pmd_nclass >= 1, ("[perf,%d] dubious md->nclass %d",
+ __LINE__, md->pmd_nclass));
+
+ perf_pcpu = malloc(sizeof(struct perf_cpu *) * maxcpu, M_PMC,
+ M_ZERO | M_WAITOK);
+
+ perf_classindex = classindex;
+ pcd = &md->pmd_classdep[classindex];
+
+ pcd->pcd_caps = PMC_CAP_READ;
+ pcd->pcd_class = PMC_CLASS_PERF;
+ pcd->pcd_num = PERF_NPMCS;
+ pcd->pcd_ri = md->pmd_npmc;
+ pcd->pcd_width = 64;
+
+ pcd->pcd_allocate_pmc = perf_allocate_pmc;
+ pcd->pcd_config_pmc = perf_config_pmc;
+ pcd->pcd_describe = perf_describe;
+ pcd->pcd_get_config = perf_get_config;
+ pcd->pcd_get_msr = perf_get_msr;
+ pcd->pcd_pcpu_init = perf_pcpu_init;
+ pcd->pcd_pcpu_fini = perf_pcpu_fini;
+ pcd->pcd_read_pmc = perf_read_pmc;
+ pcd->pcd_release_pmc = perf_release_pmc;
+ pcd->pcd_start_pmc = perf_start_pmc;
+ pcd->pcd_stop_pmc = perf_stop_pmc;
+ pcd->pcd_write_pmc = perf_write_pmc;
+
+ md->pmd_npmc += PERF_NPMCS;
+
+ return (0);
+}
+
+void pmc_perf_finalize(struct pmc_mdep *md)
+{
+ PMCDBG0(MDP, INI, 1, "perf-finalize");
+
+ if (perf_pcpu != NULL) {
+ for (int i = 0; i < pmc_cpu_max(); i++)
+ KASSERT(perf_pcpu[i] == NULL,
+ ("[perf,%d] non-null pcpu cpu %d", __LINE__, i));
+
+ free(perf_pcpu, M_PMC);
+ perf_pcpu = NULL;
+ }
+}
diff --git a/sys/dev/hwpmc/pmc_events.h b/sys/dev/hwpmc/pmc_events.h
--- a/sys/dev/hwpmc/pmc_events.h
+++ b/sys/dev/hwpmc/pmc_events.h
@@ -62,6 +62,14 @@
#define PMC_EV_RAPL_FIRST PMC_EV_RAPL_ENERGY_PKG
#define PMC_EV_RAPL_LAST PMC_EV_RAPL_ENERGY_DRAM
+/* MPERF / APERF MSRs */
+#define __PMC_EV_PERF() \
+ __PMC_EV(PERF, MPERF) \
+ __PMC_EV(PERF, APERF) \
+
+#define PMC_EV_PERF_FIRST PMC_EV_PERF_MPERF
+#define PMC_EV_PERF_LAST PMC_EV_PERF_APERF
+
/*
* Software events are dynamically defined.
*/
@@ -2416,6 +2424,7 @@
* START #EVENTS DESCRIPTION
* 0 0x1000 Reserved
* 0x1000 0x0001 TSC
+ * 0x1001 0x0002 PERF MSRs events
* 0x2000 0x0080 AMD IBS (was AMD K7 events)
* 0x2080 0x0100 AMD K8 events
* 0x10000 0x0080 INTEL architectural fixed-function events
@@ -2443,6 +2452,8 @@
#define __PMC_EVENTS() \
__PMC_EV_BLOCK(TSC, 0x01000) \
__PMC_EV_TSC() \
+ __PMC_EV_BLOCK(PERF, 0x01001) \
+ __PMC_EV_PERF() \
__PMC_EV_BLOCK(IBS, 0x02000) \
__PMC_EV_IBS() \
__PMC_EV_BLOCK(K8, 0x02080) \
diff --git a/sys/i386/include/pmc_mdep.h b/sys/i386/include/pmc_mdep.h
--- a/sys/i386/include/pmc_mdep.h
+++ b/sys/i386/include/pmc_mdep.h
@@ -53,6 +53,7 @@
#include <dev/hwpmc/hwpmc_ibs.h>
#include <dev/hwpmc/hwpmc_core.h>
#include <dev/hwpmc/hwpmc_rapl.h>
+#include <dev/hwpmc/hwpmc_perf.h>
#include <dev/hwpmc/hwpmc_tsc.h>
#include <dev/hwpmc/hwpmc_uncore.h>
diff --git a/sys/modules/hwpmc/Makefile b/sys/modules/hwpmc/Makefile
--- a/sys/modules/hwpmc/Makefile
+++ b/sys/modules/hwpmc/Makefile
@@ -25,6 +25,7 @@
hwpmc_ibs.c \
hwpmc_intel.c \
hwpmc_rapl.c \
+ hwpmc_perf.c \
hwpmc_tsc.c \
hwpmc_uncore.c \
hwpmc_x86.c
@@ -38,6 +39,7 @@
hwpmc_core.c \
hwpmc_ibs.c \
hwpmc_intel.c \
+ hwpmc_perf.c \
hwpmc_tsc.c \
hwpmc_uncore.c \
hwpmc_x86.c
diff --git a/sys/sys/pmc.h b/sys/sys/pmc.h
--- a/sys/sys/pmc.h
+++ b/sys/sys/pmc.h
@@ -60,7 +60,7 @@
* The patch version is incremented for every bug fix.
*/
#define PMC_VERSION_MAJOR 0x0A
-#define PMC_VERSION_MINOR 0x02
+#define PMC_VERSION_MINOR 0x03
#define PMC_VERSION_PATCH 0x0000
#define PMC_VERSION (PMC_VERSION_MAJOR << 24 | \
@@ -141,6 +141,7 @@
*/
#define __PMC_CLASSES() \
__PMC_CLASS(TSC, 0x00, "CPU Timestamp counter") \
+ __PMC_CLASS(PERF, 0x01, "AMD PERF MSRs") \
__PMC_CLASS(K8, 0x02, "AMD K8 performance counters") \
__PMC_CLASS(IBS, 0x03, "AMD IBS performance counters") \
__PMC_CLASS(IAF, 0x06, "Intel Core2/Atom, fixed function") \

File Metadata

Mime Type
text/plain
Expires
Fri, Aug 28, 4:00 PM (6 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37466189
Default Alt Text
D58647.id183401.diff (22 KB)

Event Timeline