> Date: Mon, 25 Apr 2022 16:19:45 +0200
> From: Claudio Jeker <[email protected]>
>
> 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.
Yes, that looks much better.
ok kettenis@
> 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 */
>
>