After I sent out my ksmn(4) diff to include cpu frequency sensors dlg@
told me that this is a generic way to find the cpu frequency on modern x86
cpus (both intel and amd support it).

So this diff cleans up the CPU frequency sensors and moves them to the
cpu(4). I had to split the sensor attachement up since sensordev_install()
calls into hotplug which does a selwakeup() and that call locks up (I
guess it is the KERNEL_LOCK()). Moving that part of the code to
cpu_attach() makes the problem go away.

Tested on a AMD Ryzen Pro 5850U and an Intel Core i7-7500U.
-- 
:wq Claudio

Index: arch/amd64/amd64/cpu.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/cpu.c,v
retrieving revision 1.155
diff -u -p -r1.155 cpu.c
--- arch/amd64/amd64/cpu.c      21 Feb 2022 11:03:39 -0000      1.155
+++ arch/amd64/amd64/cpu.c      25 Apr 2022 13:56:28 -0000
@@ -558,6 +558,11 @@ cpu_attach(struct device *parent, struct
        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
 #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: arch/amd64/amd64/identcpu.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/identcpu.c,v
retrieving revision 1.122
diff -u -p -r1.122 identcpu.c
--- arch/amd64/amd64/identcpu.c 20 Jan 2022 11:06:57 -0000      1.122
+++ arch/amd64/amd64/identcpu.c 25 Apr 2022 13:55:33 -0000
@@ -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: arch/amd64/include/cpu.h
===================================================================
RCS file: /cvs/src/sys/arch/amd64/include/cpu.h,v
retrieving revision 1.141
diff -u -p -r1.141 cpu.h
--- arch/amd64/include/cpu.h    31 Aug 2021 17:40:59 -0000      1.141
+++ arch/amd64/include/cpu.h    25 Apr 2022 11:11:47 -0000
@@ -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: arch/amd64/include/specialreg.h
===================================================================
RCS file: /cvs/src/sys/arch/amd64/include/specialreg.h,v
retrieving revision 1.91
diff -u -p -r1.91 specialreg.h
--- arch/amd64/include/specialreg.h     19 Nov 2021 04:00:53 -0000      1.91
+++ arch/amd64/include/specialreg.h     25 Apr 2022 11:16:43 -0000
@@ -235,6 +235,8 @@
  */
 #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):
@@ -369,6 +371,8 @@
 #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 */

Reply via email to