On CPUs that have MPERF/APERF support use that information to install a
authorclaudio <claudio@openbsd.org>
Tue, 26 Apr 2022 08:35:30 +0000 (08:35 +0000)
committerclaudio <claudio@openbsd.org>
Tue, 26 Apr 2022 08:35:30 +0000 (08:35 +0000)
cpu frequency sensor for each core. This works on many "modern" Intel and
AMD cpus (probably anything that has some kind of turbo mode).
OK kettenis@

sys/arch/amd64/amd64/cpu.c
sys/arch/amd64/amd64/identcpu.c
sys/arch/amd64/include/cpu.h
sys/arch/amd64/include/specialreg.h

index 30e734e..4778347 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cpu.c,v 1.155 2022/02/21 11:03:39 mpi Exp $   */
+/*     $OpenBSD: cpu.c,v 1.156 2022/04/26 08:35:30 claudio Exp $       */
 /* $NetBSD: cpu.c,v 1.1 2003/04/26 18:39:26 fvdl Exp $ */
 
 /*-
@@ -558,6 +558,11 @@ cpu_attach(struct device *parent, struct device *self, void *aux)
        ci->ci_func = caa->cpu_func;
        ci->ci_handled_intr_level = IPL_NONE;
 
+#ifndef SMALL_KERNEL
+       strlcpy(ci->ci_sensordev.xname, ci->ci_dev->dv_xname,
+           sizeof(ci->ci_sensordev.xname));
+#endif
+
 #if defined(MULTIPROCESSOR)
        /*
         * Allocate UPAGES contiguous pages for the idle PCB and stack.
@@ -663,6 +668,11 @@ cpu_attach(struct device *parent, struct device *self, void *aux)
 #if NVMM > 0
        cpu_init_vmm(ci);
 #endif /* NVMM > 0 */
+
+#ifndef SMALL_KERNEL
+       if (ci->ci_sensordev.sensors_count > 0)
+               sensordev_install(&ci->ci_sensordev);
+#endif
 }
 
 static void
index b67e177..060e73e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: identcpu.c,v 1.122 2022/01/20 11:06:57 bluhm Exp $    */
+/*     $OpenBSD: identcpu.c,v 1.123 2022/04/26 08:35:30 claudio Exp $  */
 /*     $NetBSD: identcpu.c,v 1.1 2003/04/26 18:39:28 fvdl Exp $        */
 
 /*
@@ -38,6 +38,8 @@
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/atomic.h>
+#include <sys/proc.h>
 #include <sys/sysctl.h>
 
 #include "vmm.h"
@@ -246,7 +248,9 @@ cpu_amd64speed(int *freq)
 }
 
 #ifndef SMALL_KERNEL
-void   intelcore_update_sensor(void *args);
+void   intelcore_update_sensor(void *);
+void   cpu_hz_update_sensor(void *);
+
 /*
  * Temperature read on the CPU is relative to the maximum
  * temperature supported by the CPU, Tj(Max).
@@ -299,6 +303,44 @@ intelcore_update_sensor(void *args)
        }
 }
 
+/*
+ * Effective CPU frequency measurement
+ *
+ * Refer to:
+ *   64-ia-32-architectures-software-developer-vol-3b-part-2-manual.pdf
+ *   Section 14.2 and
+ *   OSRR for AMD Family 17h processors Section 2.1.2
+ * Round to 50Mhz which is the accuracy of this measurement.
+ */
+#define FREQ_50MHZ     (50ULL * 1000000ULL * 1000000ULL)
+void
+cpu_hz_update_sensor(void *args)
+{
+       extern uint64_t  tsc_frequency;
+       struct cpu_info *ci = args;
+       uint64_t         mperf, aperf, mdelta, adelta, val;
+       unsigned long    s;
+
+       sched_peg_curproc(ci);
+
+       s = intr_disable();
+       mperf = rdmsr(MSR_MPERF);
+       aperf = rdmsr(MSR_APERF);
+       intr_restore(s);
+
+       mdelta = mperf - ci->ci_hz_mperf;
+       adelta = aperf - ci->ci_hz_aperf;
+       ci->ci_hz_mperf = mperf;
+       ci->ci_hz_aperf = aperf;
+
+       if (mdelta > 0) {
+               val = (adelta * 1000000) / mdelta * tsc_frequency;
+               val = ((val + FREQ_50MHZ / 2) / FREQ_50MHZ) * FREQ_50MHZ; 
+               ci->ci_hz_sensor.value = val;
+       }
+
+       atomic_clearbits_int(&curproc->p_flag, P_CPUPEG);
+}
 #endif
 
 void (*setperf_setup)(struct cpu_info *);
@@ -469,7 +511,7 @@ void
 identifycpu(struct cpu_info *ci)
 {
        uint64_t freq = 0;
-       u_int32_t dummy, val;
+       u_int32_t dummy, val, cpu_tpm_ecxflags = 0;
        char mycpu_model[48];
        int i;
        char *brandstr_from, *brandstr_to;
@@ -619,12 +661,15 @@ identifycpu(struct cpu_info *ci)
        }
 
        if (!strcmp(cpu_vendor, "GenuineIntel") && cpuid_level >= 0x06) {
-               CPUID(0x06, ci->ci_feature_tpmflags, dummy, dummy, dummy);
+               CPUID(0x06, ci->ci_feature_tpmflags, dummy, cpu_tpm_ecxflags,
+                   dummy);
                for (i = 0; i < nitems(cpu_tpm_eaxfeatures); i++)
                        if (ci->ci_feature_tpmflags &
                            cpu_tpm_eaxfeatures[i].bit)
                                printf(",%s", cpu_tpm_eaxfeatures[i].str);
        } else if (!strcmp(cpu_vendor, "AuthenticAMD")) {
+               CPUID(0x06, ci->ci_feature_tpmflags, dummy, cpu_tpm_ecxflags,
+                   dummy);
                if (ci->ci_family >= 0x12)
                        ci->ci_feature_tpmflags |= TPM_ARAT;
        }
@@ -737,12 +782,9 @@ identifycpu(struct cpu_info *ci)
 
 #ifndef SMALL_KERNEL
        if (CPU_IS_PRIMARY(ci) && (ci->ci_feature_tpmflags & TPM_SENSOR)) {
-               strlcpy(ci->ci_sensordev.xname, ci->ci_dev->dv_xname,
-                   sizeof(ci->ci_sensordev.xname));
                ci->ci_sensor.type = SENSOR_TEMP;
                sensor_task_register(ci, intelcore_update_sensor, 5);
                sensor_attach(&ci->ci_sensordev, &ci->ci_sensor);
-               sensordev_install(&ci->ci_sensordev);
        }
 #endif
 
@@ -762,12 +804,9 @@ identifycpu(struct cpu_info *ci)
        if (CPU_IS_PRIMARY(ci) && !strcmp(cpu_vendor, "CentaurHauls")) {
                ci->cpu_setup = via_nano_setup;
 #ifndef SMALL_KERNEL
-               strlcpy(ci->ci_sensordev.xname, ci->ci_dev->dv_xname,
-                   sizeof(ci->ci_sensordev.xname));
                ci->ci_sensor.type = SENSOR_TEMP;
                sensor_task_register(ci, via_update_sensor, 5);
                sensor_attach(&ci->ci_sensordev, &ci->ci_sensor);
-               sensordev_install(&ci->ci_sensordev);
 #endif
        }
 
@@ -777,6 +816,16 @@ identifycpu(struct cpu_info *ci)
 #if NVMM > 0
        cpu_check_vmm_cap(ci);
 #endif /* NVMM > 0 */
+
+       /* Check for effective frequency via MPERF, APERF */
+       if ((cpu_tpm_ecxflags & TPM_EFFFREQ) &&
+           ci->ci_smt_id == 0) {
+#ifndef SMALL_KERNEL
+               ci->ci_hz_sensor.type = SENSOR_FREQ;
+               sensor_task_register(ci, cpu_hz_update_sensor, 1);
+               sensor_attach(&ci->ci_sensordev, &ci->ci_hz_sensor);
+#endif
+       }
 }
 
 #ifndef SMALL_KERNEL
index 4bc8627..0bf6cc4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cpu.h,v 1.141 2021/08/31 17:40:59 dv Exp $    */
+/*     $OpenBSD: cpu.h,v 1.142 2022/04/26 08:35:30 claudio Exp $       */
 /*     $NetBSD: cpu.h,v 1.1 2003/04/26 18:39:39 fvdl Exp $     */
 
 /*-
@@ -196,6 +196,9 @@ struct cpu_info {
 
        struct ksensordev       ci_sensordev;
        struct ksensor          ci_sensor;
+       struct ksensor          ci_hz_sensor;
+       u_int64_t               ci_hz_mperf;
+       u_int64_t               ci_hz_aperf;
 #if defined(GPROF) || defined(DDBPROF)
        struct gmonparam        *ci_gmon;
 #endif
index 6b32f07..34701ff 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: specialreg.h,v 1.91 2021/11/19 04:00:53 guenther Exp $        */
+/*     $OpenBSD: specialreg.h,v 1.92 2022/04/26 08:35:30 claudio Exp $ */
 /*     $NetBSD: specialreg.h,v 1.1 2003/04/26 18:39:48 fvdl Exp $      */
 /*     $NetBSD: x86/specialreg.h,v 1.2 2003/04/25 21:54:30 fvdl Exp $  */
 
  */
 #define        TPM_SENSOR      0x00000001       /* Digital temp sensor */
 #define        TPM_ARAT        0x00000004       /* APIC Timer Always Running */
+/* Thermal and Power Management (CPUID function 0x6) ECX bits */
+#define        TPM_EFFFREQ     0x00000001       /* APERF & MPERF MSR present */
 
  /*
   * "Architectural Performance Monitoring" bits (CPUID function 0x0a):
 #define MSR_PERFCTR0           0x0c1
 #define MSR_PERFCTR1           0x0c2
 #define MSR_FSB_FREQ           0x0cd   /* Core Duo/Solo only */
+#define MSR_MPERF              0x0e7
+#define MSR_APERF              0x0e8
 #define MSR_MTRRcap            0x0fe
 #define MTRRcap_FIXED          0x100   /* bit 8 - fixed MTRRs supported */
 #define MTRRcap_WC             0x400   /* bit 10 - WC type supported */