Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F168331420
D58647.id183401.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
22 KB
Referenced Files
None
Subscribers
None
D58647.id183401.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D58647: hwpmc: add MPERF/APERF MSR support for AMD/Intel CPUs
Attached
Detach File
Event Timeline
Log In to Comment