On Tue, Aug 8, 2017 at 10:12 PM, Mike Belopuhov <m...@belopuhov.com> wrote: > On Tue, Aug 08, 2017 at 08:18 +0800, Adam Steen wrote: >> On Mon, Jul 31, 2017 at 3:58 PM, Mike Belopuhov <m...@belopuhov.com> wrote: >> > On Mon, Jul 31, 2017 at 09:48 +0800, Adam Steen wrote: >> >> Ted Unangst wrote: >> >> > we don't currently export this info, but we could add some sysctls. >> >> > there's >> >> > some cpufeatures stuff there, but generally stuff isn't exported until >> >> > somebody finds a use for it... it shouldn't be too hard to add >> >> > something to >> >> > amd64/machdep.c sysctl if you're interested. >> >> >> >> I am interested, as i need the info, i will look into it and hopefully >> >> come back with a patch. >> > >> > This is a bad idea because TSC as the time source is only usable >> > by OpenBSD on Skylake and Kaby Lake CPUs since they encode the TSC >> > frequency in the CPUID. All older CPUs have their TSCs measured >> > against the PIT. Currently the measurement done by the kernel isn't >> > very precise and if TSC is selected as a timecounter, the machine >> > would be gaining time on a pace that cannot be corrected by our NTP >> > daemon. (IIRC, about an hour a day on my Haswell running with NTP). >> > >> > To be able to use TSC as a timecounter source on OpenBSD or Solo5 >> > you'd have to improve the in-kernel measurement of the TSC frequency >> > first. I've tried to perform 10 measurements and take an average and >> > it does improve accuracy, however I believe we need to poach another >> > bit from Linux and re-calibrate TSC via HPET: >> > >> > >> > http://elixir.free-electrons.com/linux/v4.12.4/source/arch/x86/kernel/tsc.c#L409 >> > >> > I think this is the most sane thing we can do. Here's a complete >> > procedure that Linux kernel undertakes: >> > >> > >> > http://elixir.free-electrons.com/linux/v4.12.4/source/arch/x86/kernel/tsc.c#L751 >> > >> > Regards, >> > Mike >> >> Hi Mike/All >> >> I would like to improve the accuracy of TSC frequency calibration as >> Mike B. describes above. >> >> I initially thought the calibration would take place at line 470 of >> amd64/identcpu.c >> (https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/arch/amd64/amd64/identcpu.c?annotate=1.87) >> > > Indeed, it cannot happen there simply because you don't know at > that point whether or not HPET actually exists. > >> But I looked into using the acpihpet directly but it is never exposed >> outside of acpihpet.c. >> > > And it shouldn't be. > >> Could someone point me to were if would be appropriate to complete >> this calibration and how to use the acpihpet? > > The way I envision this is a multi-step approach: > > 1) TSC frequency is approximated with the PIT (possibly performing > multiple measurements and averaging them out; also keep in mind that > doing it 8 times means you can shift the sum right by 3 instead of > using actual integer division). This is what should happen around > the line 470 of identcpu.c > > 2) A function can be provided by identcpu.c to further adjust the > TSC frequency once acpitimer(4) (this is a PM timer) and acpihpet(4) > (or any other timer for that matter) are attached. > > 3) Once acpitimer(4) or acpihpet(4) or any other timecounter source > are attached and are verified to be operating correctly, they can > perform TSC re-calibration and update the TSC frequency with their > measurements. The idea here is that the function (or functions) that > facilitate this must abstract enough logic so that you don't have to > duplicate it in the acpitimer or acpihpet themselves. > >> (Will it need to be >> exposed like i8254_delay/delay_func/delay in machdep.c and cpu.h) >> > > No it won't. > >> Lastly should the calibration be done using both delay(i8254 pit) and >> hpet timers similar to Linux described above or just using the hpet? >> > > Well, that's what I was arguing for. As I said in my initial mail > on misc (not quoted here), the TSC must be calibrated using separate > known clocks sources.
Hi Mike Please see the below diff to improve the accuracy of the TSC frequency. It is model after the linux calibration you linked to earlier. https://marc.info/?l=openbsd-misc&m=150148792804747&w=2 I feel like i don't know enough about the kernel internals, the consistency of the results across reboots are not as close as i would have liked, i feel the call to do the actual calibration should be later in the boot cycle, when things have calmed down a little, but couldn't figure out the best way of doing this. please bear with me i haven't been programming c for long, but the only way to get things done is to do it your self. Cheers Adam Index: sys/arch/amd64/amd64/acpi_machdep.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/acpi_machdep.c,v retrieving revision 1.78 diff -u -p -u -p -r1.78 acpi_machdep.c --- sys/arch/amd64/amd64/acpi_machdep.c 27 Mar 2017 18:32:53 -0000 1.78 +++ sys/arch/amd64/amd64/acpi_machdep.c 17 Aug 2017 04:07:25 -0000 @@ -69,6 +69,15 @@ extern int acpi_savecpu(void) __returns_ u_int8_t *acpi_scan(struct acpi_mem_map *, paddr_t, size_t); +#define RECALIBRATE_MAX_RETRIES 5 +#define RECALIBRATE_SMI_THRESHOLD 50000 +#define RECALIBRATE_DELAY_THRESHOLD 20 +struct timecounter *recalibrate_timecounter; +u_int64_t acpi_calculate_timecounter_delay(struct timecounter *, u_int64_t, u_int64_t); +u_int64_t acpi_calculate_tsc_frequency(u_int64_t, u_int64_t, int); +u_int64_t acpi_calibrate_tsc_frequency(); +int acpi_get_tsc_and_timecount(struct timecounter *, u_int64_t *, u_int64_t *); + int acpi_map(paddr_t pa, size_t len, struct acpi_mem_map *handle) { @@ -438,6 +447,105 @@ acpi_resume_cpu(struct acpi_softc *sc) /* Re-initialise memory range handling on BSP */ if (mem_range_softc.mr_op != NULL) mem_range_softc.mr_op->initAP(&mem_range_softc); +} + +int +acpi_get_tsc_and_timecount(struct timecounter *tc, u_int64_t *tsc, u_int64_t *count) +{ + u_int64_t n, tsc1, tsc2; + int i; + + for (i = 0; i < RECALIBRATE_MAX_RETRIES; i++) { + tsc1 = rdtsc(); + n = (tc->tc_get_timecount(tc) & tc->tc_counter_mask); + tsc2 = rdtsc(); + + if((tsc2-tsc1) < RECALIBRATE_SMI_THRESHOLD) { + *count = n; + *tsc = tsc2; + return 0; + } + } + return 1; +} + +u_int64_t +acpi_calculate_timecounter_delay(struct timecounter *tc, u_int64_t count1, u_int64_t count2) +{ + u_int64_t delta; + + if(count2 < count1) + count2 += tc->tc_counter_mask; + + delta = (count2 - count1); + return delta * 1000000 / tc->tc_frequency; +} + +u_int64_t +acpi_calculate_tsc_frequency(u_int64_t tsc1, u_int64_t tsc2, int usec) +{ + u_int64_t delta; + + delta = (tsc2 - tsc1); + return delta * 1000000 / usec; +} + +void +acpi_recalibrate_timecounter_init(struct timecounter *tc) +{ + recalibrate_timecounter = tc; + cpu_adjust_tsc_frequency(&acpi_calibrate_tsc_frequency); +} + +u_int64_t +acpi_calibrate_tsc_frequency() +{ + u_int64_t count1, count2, frequency, min_freq, tsc1, tsc2; + u_long ef; + int delay_usec, i, err1, err2, usec; + + struct timecounter *tc = recalibrate_timecounter; + + // warmup the timers + for(i =0; i < 3; i++) { + (void)tc->tc_get_timecount(tc); + (void)rdtsc(); + } + + min_freq = ULLONG_MAX; + + delay_usec = 100000; + for (i = 0; i < 3; i++) { + ef = read_rflags(); + disable_intr(); + + err1 = acpi_get_tsc_and_timecount(tc, &tsc1, &count1); + delay(delay_usec); + err2 = acpi_get_tsc_and_timecount(tc, &tsc2, &count2); + + write_rflags(ef); + + if(err1 || err2) + continue; + + usec = acpi_calculate_timecounter_delay(tc, count1, count2); + + if(usec < (delay_usec - RECALIBRATE_DELAY_THRESHOLD)) + continue; + + if(usec > (delay_usec + RECALIBRATE_DELAY_THRESHOLD)) + continue; + + frequency = acpi_calculate_tsc_frequency(tsc1, tsc2, usec); + + min_freq = ulmin(min_freq, frequency); + } + + if(min_freq == 0) { + printf("Failed to recalibrate timecounter \"tsc\" with timecounter \"%s\"\n", + tc->tc_name); + } + return min_freq; } #ifdef MULTIPROCESSOR Index: sys/arch/amd64/amd64/identcpu.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/identcpu.c,v retrieving revision 1.87 diff -u -p -u -p -r1.87 identcpu.c --- sys/arch/amd64/amd64/identcpu.c 20 Jun 2017 05:34:41 -0000 1.87 +++ sys/arch/amd64/amd64/identcpu.c 17 Aug 2017 04:07:25 -0000 @@ -63,6 +63,10 @@ struct timecounter tsc_timecounter = { tsc_get_timecount, NULL, ~0u, 0, "tsc", -1000, NULL }; +u_int64_t (*amd_tsc_get_frequency)(); +u_int64_t amd64_tsc_freq; +int amd64_tsc_requires_calibration; +int amd64_has_invariant_tsc; int amd64_has_xcrypt; #ifdef CRYPTO int amd64_has_pclmul; @@ -428,8 +432,9 @@ cpu_tsc_freq_ctr(struct cpu_info *ci) u_int64_t cpu_tsc_freq(struct cpu_info *ci) { - u_int64_t last_count, count; + u_int64_t last_count, count, sum; uint32_t eax, ebx, khz, dummy; + int i; if (!strcmp(cpu_vendor, "GenuineIntel") && cpuid_level >= 0x15) { @@ -468,11 +473,21 @@ cpu_tsc_freq(struct cpu_info *ci) if (count != 0) return (count); - last_count = rdtsc(); - delay(100000); - count = rdtsc(); + if(amd_tsc_get_frequency != NULL) + return (*amd_tsc_get_frequency)(); - return ((count - last_count) * 10); + if (ci->ci_flags & CPUF_PRIMARY) + amd64_tsc_requires_calibration = 1; + + sum = 0; + for(i=0; i < 8; i++) { + last_count = rdtsc(); + delay(100000); + count = rdtsc(); + sum += (count - last_count); + } + + return ((sum >> 3) * 10); } u_int @@ -482,6 +497,20 @@ tsc_get_timecount(struct timecounter *tc } void +cpu_adjust_tsc_frequency(u_int64_t (*get_frequency)()) +{ + if(amd64_tsc_requires_calibration) { + amd64_tsc_freq = (*get_frequency)(); + tsc_timecounter.tc_frequency = amd64_tsc_freq; + + printf("Timecounter \"%s\" recalibrated with frequency %llu Hz\n", + tsc_timecounter.tc_name, (unsigned long)tsc_timecounter.tc_frequency); + } else { + amd_tsc_get_frequency = get_frequency; + } +} + +void identifycpu(struct cpu_info *ci) { u_int32_t dummy, val; @@ -566,9 +595,13 @@ identifycpu(struct cpu_info *ci) /* Check if it's an invariant TSC */ if (cpu_apmi_edx & CPUIDEDX_ITSC) ci->ci_flags |= CPUF_INVAR_TSC; + + amd64_has_invariant_tsc = (ci->ci_flags & CPUF_INVAR_TSC) != 0; } ci->ci_tsc_freq = cpu_tsc_freq(ci); + if (ci->ci_flags & CPUF_PRIMARY) + amd64_tsc_freq = ci->ci_tsc_freq; amd_cpu_cacheinfo(ci); Index: sys/arch/amd64/amd64/machdep.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/machdep.c,v retrieving revision 1.231 diff -u -p -u -p -r1.231 machdep.c --- sys/arch/amd64/amd64/machdep.c 12 Jul 2017 06:26:32 -0000 1.231 +++ sys/arch/amd64/amd64/machdep.c 17 Aug 2017 04:07:25 -0000 @@ -425,6 +425,8 @@ int cpu_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen, struct proc *p) { + extern u_int64_t amd64_tsc_freq; + extern int amd64_has_invariant_tsc; extern int amd64_has_xcrypt; dev_t consdev; dev_t dev; @@ -496,6 +498,10 @@ cpu_sysctl(int *name, u_int namelen, voi pckbc_release_console(); return (error); #endif + case CPU_TSCFREQ: + return (sysctl_rdquad(oldp, oldlenp, newp, amd64_tsc_freq)); + case CPU_INVARIANTTSC: + return (sysctl_rdint(oldp, oldlenp, newp, amd64_has_invariant_tsc)); default: return (EOPNOTSUPP); } Index: sys/arch/amd64/include/cpu.h =================================================================== RCS file: /cvs/src/sys/arch/amd64/include/cpu.h,v retrieving revision 1.113 diff -u -p -u -p -r1.113 cpu.h --- sys/arch/amd64/include/cpu.h 12 Jul 2017 06:26:32 -0000 1.113 +++ sys/arch/amd64/include/cpu.h 17 Aug 2017 04:07:25 -0000 @@ -429,7 +429,9 @@ void mp_setperf_init(void); #define CPU_XCRYPT 12 /* supports VIA xcrypt in userland */ #define CPU_LIDACTION 14 /* action caused by lid close */ #define CPU_FORCEUKBD 15 /* Force ukbd(4) as console keyboard */ -#define CPU_MAXID 16 /* number of valid machdep ids */ +#define CPU_TSCFREQ 16 /* tsc frequency */ +#define CPU_INVARIANTTSC 17 /* has invariant tsc */ +#define CPU_MAXID 18 /* number of valid machdep ids */ #define CTL_MACHDEP_NAMES { \ { 0, 0 }, \ @@ -448,6 +450,8 @@ void mp_setperf_init(void); { 0, 0 }, \ { "lidaction", CTLTYPE_INT }, \ { "forceukbd", CTLTYPE_INT }, \ + { "tscfreq", CTLTYPE_QUAD }, \ + { "invarianttsc", CTLTYPE_INT }, \ } /* Index: sys/arch/amd64/include/cpuvar.h =================================================================== RCS file: /cvs/src/sys/arch/amd64/include/cpuvar.h,v retrieving revision 1.8 diff -u -p -u -p -r1.8 cpuvar.h --- sys/arch/amd64/include/cpuvar.h 28 Jul 2016 21:57:57 -0000 1.8 +++ sys/arch/amd64/include/cpuvar.h 17 Aug 2017 04:07:25 -0000 @@ -96,5 +96,6 @@ void x86_ipi_init(int); void identifycpu(struct cpu_info *); void cpu_init(struct cpu_info *); void cpu_init_first(void); +void cpu_adjust_tsc_frequency(u_int64_t (*)()); #endif Index: sys/dev/acpi/acpihpet.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpihpet.c,v retrieving revision 1.21 diff -u -p -u -p -r1.21 acpihpet.c --- sys/dev/acpi/acpihpet.c 6 Oct 2015 20:49:32 -0000 1.21 +++ sys/dev/acpi/acpihpet.c 17 Aug 2017 04:07:26 -0000 @@ -265,6 +265,7 @@ acpihpet_attach(struct device *parent, s hpet_timecounter.tc_name = sc->sc_dev.dv_xname; tc_init(&hpet_timecounter); acpihpet_attached++; + acpi_recalibrate_timecounter_init(&hpet_timecounter); } u_int Index: sys/dev/acpi/acpitimer.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpitimer.c,v retrieving revision 1.11 diff -u -p -u -p -r1.11 acpitimer.c --- sys/dev/acpi/acpitimer.c 14 Mar 2015 03:38:46 -0000 1.11 +++ sys/dev/acpi/acpitimer.c 17 Aug 2017 04:07:26 -0000 @@ -25,6 +25,8 @@ #include <dev/acpi/acpireg.h> #include <dev/acpi/acpivar.h> +#include "acpihpet.h" + int acpitimermatch(struct device *, void *, void *); void acpitimerattach(struct device *, struct device *, void *); @@ -96,6 +98,9 @@ acpitimerattach(struct device *parent, s acpi_timecounter.tc_priv = sc; acpi_timecounter.tc_name = sc->sc_dev.dv_xname; tc_init(&acpi_timecounter); +#if NACPIHPET == 0 + acpi_recalibrate_timecounter_init(&acpi_timecounter); +#endif } Index: sys/dev/acpi/acpivar.h =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpivar.h,v retrieving revision 1.87 diff -u -p -u -p -r1.87 acpivar.h --- sys/dev/acpi/acpivar.h 8 Apr 2017 01:20:10 -0000 1.87 +++ sys/dev/acpi/acpivar.h 17 Aug 2017 04:07:26 -0000 @@ -25,6 +25,7 @@ #include <sys/timeout.h> #include <sys/rwlock.h> +#include <sys/timetc.h> #include <machine/biosvar.h> #include "acpipwrres.h" @@ -324,6 +325,8 @@ void acpi_resume_cpu(struct acpi_softc void acpi_resume_mp(void); void acpi_sleep_walk(struct acpi_softc *, int); +void acpi_recalibrate_timecounter_init(struct timecounter *); +u_int64_t acpi_calibrate_tsc_frequency(); #define ACPI_IOREAD 0 #define ACPI_IOWRITE 1 Index: sys/dev/acpi/files.acpi =================================================================== RCS file: /cvs/src/sys/dev/acpi/files.acpi,v retrieving revision 1.37 diff -u -p -u -p -r1.37 files.acpi --- sys/dev/acpi/files.acpi 22 Feb 2017 16:39:56 -0000 1.37 +++ sys/dev/acpi/files.acpi 17 Aug 2017 04:07:26 -0000 @@ -39,7 +39,7 @@ file dev/acpi/acpicpu.c acpicpu needs-f # High Precision Event Timer device acpihpet attach acpihpet at acpi -file dev/acpi/acpihpet.c acpihpet +file dev/acpi/acpihpet.c acpihpet needs-flag # Embedded Controller device acpiec