aperf: Sensor for effective CPU frequency using APERF/MPERF MSRs.
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 11 Jul 2015 13:11:57 +0000 (21:11 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 11 Jul 2015 16:04:27 +0000 (00:04 +0800)
It was adapted by me to fit into cpu sensor device and utilize our
per-cpu sensor tasks.

Obtained-from: Imre Vadasz <imre@vdsz.com>

share/man/man4/Makefile
share/man/man4/aperf.4 [new file with mode: 0644]
sys/conf/files
sys/config/LINT64
sys/dev/misc/Makefile
sys/dev/misc/aperf/Makefile [new file with mode: 0644]
sys/dev/misc/aperf/aperf.c [new file with mode: 0644]

index 537628e..71baf79 100644 (file)
@@ -35,6 +35,7 @@ MAN=  aac.4 \
        amdsmb.4 \
        amr.4 \
        an.4 \
+       aperf.4 \
        aps.4 \
        arcmsr.4 \
        arp.4 \
diff --git a/share/man/man4/aperf.4 b/share/man/man4/aperf.4
new file mode 100644 (file)
index 0000000..8143f59
--- /dev/null
@@ -0,0 +1,93 @@
+.\"
+.\" Copyright (c) 2015 The DragonFly Project.  All rights reserved.
+.\" 
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\" 3. Neither the name of The DragonFly Project nor the names of its
+.\"    contributors may be used to endorse or promote products derived
+.\"    from this software without specific, prior written permission.
+.\" 
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd July 11, 2015
+.Dt APERF 4
+.Os
+.Sh NAME
+.Nm aperf
+.Nd driver measuring effective CPU frequency via APERF/MPERF MSRs
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device aperf"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+aperf_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for measuring the effective CPU frequency, using the
+APERF and MPERF MSRs.
+The values are exposed through the
+.Dv HW_SENSORS
+.Xr sysctl 3
+tree.
+For example:
+.Bd -literal -offset indent
+% sysctl hw.sensors
+hw.sensors.cpu0.freq0: 2684298000 Hz (cpu0 freq)
+hw.sensors.cpu1.freq0: 1418960000 Hz (cpu1 freq)
+hw.sensors.cpu2.freq0: 1234898000 Hz (cpu2 freq)
+hw.sensors.cpu3.freq0: 1242533000 Hz (cpu3 freq)
+.Ed
+.Sh HARDWARE
+The
+.Nm
+driver supports all AMD and Intel CPUs which provide the APERF and MPERF MSRs:
+.Pp
+.Bl -bullet -compact
+.It
+Most AMD CPUs starting at Family 10h
+.It
+Most Intel CPUs starting from Core series and Atom respectively.
+.El
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr sysctl 3 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Dx 4.3 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Imre Vadasz Aq Mt imre@vdsz.com .
index 22e9cf6..5b1f717 100644 (file)
@@ -432,6 +432,7 @@ dev/raid/hptmv/ioctl.c              optional hptmv
 dev/raid/hptrr/hptrr_os_bsd.c  optional hptrr
 dev/raid/hptrr/hptrr_osm_bsd.c optional hptrr
 dev/raid/hptrr/hptrr_config.c  optional hptrr
+dev/misc/aperf/aperf.c         optional aperf
 dev/powermng/clockmod/clockmod.c       optional clockmod
 dev/powermng/perfbias/perfbias.c       optional perfbias
 dev/powermng/coretemp/coretemp.c       optional coretemp
index 1a0996c..3ccbc85 100644 (file)
@@ -1740,6 +1740,9 @@ device            memtemp
 # microcode update feature.
 device         cpuctl
 
+# Effective CPU frequency interface via APERF/MPERF MSRs
+device         aperf
+
 # AMD Family 0Fh, 10h and 11h temperature sensors
 device         kate
 device         km
index fe7509c..0532f48 100644 (file)
@@ -1,5 +1,5 @@
 SUBDIR= amdsbwd cmx cpuctl dcons ecc ichwd ipmi joy kbdmux lpbb \
-       nmdm pcfclock putter snp syscons tbridge coremctl dimm
+       nmdm pcfclock putter snp syscons tbridge coremctl dimm aperf
 
 # Empty for concurrent build
 #
diff --git a/sys/dev/misc/aperf/Makefile b/sys/dev/misc/aperf/Makefile
new file mode 100644 (file)
index 0000000..1335a0c
--- /dev/null
@@ -0,0 +1,4 @@
+KMOD=  aperf
+SRCS=  aperf.c bus_if.h device_if.h cpu_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/misc/aperf/aperf.c b/sys/dev/misc/aperf/aperf.c
new file mode 100644 (file)
index 0000000..b411770
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2015 Imre Vadász <imre@vdsz.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* sensor driver for effective CPU frequency, using APERF/MPERF MSRs */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/sensors.h>
+#include <sys/systm.h>
+#include <sys/thread2.h>
+
+#include <machine/clock.h>
+#include <machine/cpufunc.h>
+
+#include "cpu_if.h"
+
+#define MSR_MPERF      0xE7
+#define MSR_APERF      0xE8
+
+struct aperf_softc {
+       struct ksensordev       *sc_sensdev;
+       struct ksensor          sc_sens;
+       struct sensor_task      *sc_senstask;
+
+       uint64_t                sc_aperf_prev;
+       uint64_t                sc_mperf_prev;
+       uint64_t                sc_tsc_prev;
+       uint32_t                sc_flags;       /* APERF_FLAG_ */
+};
+
+#define APERF_FLAG_PREV_VALID  0x1
+
+/*
+ * Device methods.
+ */
+static void    aperf_identify(driver_t *, device_t);
+static int     aperf_probe(device_t);
+static int     aperf_attach(device_t);
+static int     aperf_detach(device_t);
+
+static void    aperf_sensor_task(void *);
+
+static device_method_t aperf_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_identify,      aperf_identify),
+       DEVMETHOD(device_probe,         aperf_probe),
+       DEVMETHOD(device_attach,        aperf_attach),
+       DEVMETHOD(device_detach,        aperf_detach),
+
+       DEVMETHOD_END
+};
+
+static driver_t aperf_driver = {
+       "aperf",
+       aperf_methods,
+       sizeof(struct aperf_softc),
+};
+
+static devclass_t aperf_devclass;
+DRIVER_MODULE(aperf, cpu, aperf_driver, aperf_devclass, NULL, NULL);
+MODULE_VERSION(aperf, 1);
+
+static void
+aperf_identify(driver_t *driver, device_t parent)
+{
+       uint32_t regs[4];
+       device_t child;
+
+       /* Make sure we're not being doubly invoked. */
+       if (device_find_child(parent, "aperf", -1) != NULL)
+               return;
+
+       /* CPUID Fn0000_0006_ECX Effective Processor Frequency Interface */
+       do_cpuid(0x00000006, regs);
+       if ((regs[2] & 1) == 0)
+               return;
+
+       child = device_add_child(parent, "aperf", -1);
+       if (child == NULL)
+               device_printf(parent, "add aperf child failed\n");
+}
+
+static int
+aperf_probe(device_t dev)
+{
+       if (resource_disabled("aperf", 0))
+               return ENXIO;
+
+       device_set_desc(dev, "CPU Frequency Sensor");
+
+       return 0;
+}
+
+static int
+aperf_attach(device_t dev)
+{
+       struct aperf_softc *sc = device_get_softc(dev);
+       device_t parent;
+       int cpu;
+
+       parent = device_get_parent(dev);
+       cpu = device_get_unit(parent);
+
+       sc->sc_sensdev = CPU_GET_SENSDEV(parent);
+       if (sc->sc_sensdev == NULL)
+               return ENXIO;
+
+       /*
+        * Add hw.sensors.cpuN.raw0 MIB.
+        */
+       ksnprintf(sc->sc_sens.desc, sizeof(sc->sc_sens.desc),
+           "cpu%d freq", cpu);
+       sc->sc_sens.type = SENSOR_FREQ;
+       sensor_set_unknown(&sc->sc_sens);
+       sensor_attach(sc->sc_sensdev, &sc->sc_sens);
+
+       sc->sc_senstask = sensor_task_register2(sc, aperf_sensor_task, 1, cpu);
+
+       return 0;
+}
+
+static int
+aperf_detach(device_t dev)
+{
+       struct aperf_softc *sc = device_get_softc(dev);
+
+       if (sc->sc_senstask != NULL)
+               sensor_task_unregister2(sc->sc_senstask);
+
+       if (sc->sc_sensdev != NULL)
+               sensor_detach(sc->sc_sensdev, &sc->sc_sens);
+
+       return 0;
+}
+
+static void
+aperf_sensor_task(void *xsc)
+{
+       struct aperf_softc *sc = xsc;
+       uint64_t aperf, mperf, tsc, freq;
+       uint64_t aperf_diff, mperf_diff, tsc_diff;
+
+       crit_enter();
+       tsc = rdtsc_ordered();
+       aperf = rdmsr(MSR_APERF);
+       mperf = rdmsr(MSR_MPERF);
+       crit_exit();
+
+       if ((sc->sc_flags & APERF_FLAG_PREV_VALID) == 0) {
+               sc->sc_aperf_prev = aperf;
+               sc->sc_mperf_prev = mperf;
+               sc->sc_tsc_prev = tsc;
+               sc->sc_flags |= APERF_FLAG_PREV_VALID;
+               return;
+       }
+
+       aperf_diff = aperf - sc->sc_aperf_prev;
+       mperf_diff = mperf - sc->sc_mperf_prev;
+       if (tsc_invariant)
+               tsc_diff = tsc - sc->sc_tsc_prev;
+       else
+               tsc_diff = tsc_frequency;
+
+       sc->sc_aperf_prev = aperf;
+       sc->sc_mperf_prev = mperf;
+       sc->sc_tsc_prev = tsc;
+
+       /* Avoid division by zero */
+       if (mperf_diff == 0)
+               mperf_diff = 1;
+
+       /* Using tsc_diff/1000 to avoid overflowing */
+       freq = ((aperf_diff * (tsc_diff / 1000)) / mperf_diff) * 1000;
+       sensor_set(&sc->sc_sens, freq, SENSOR_S_UNSPEC);
+}