Hi,

Other BSDs use the TSC to implement delay(9) if the TSC is constant
and invariant.  Here's a patch to add something similar to our kernel.

This patch (or something equivalent) is a prerequisite to running the
lapic timer in oneshot or TSC deadline mode.  Using the lapic timer to
implement delay(9) when it isn't running in periodic mode is too
complicated.  However, using the i8254 for delay(9) is too slow.  We
need an alternative.

As for the patch, it works for me here, though I'd appreciate a few
tests.  I admit that comparing function pointers is ugly, but I think
this is as simple as it can be without implementing some sort of
framework for "registering" delay(9) implementations and comparing
them and selecting the "best" implementation.

I'm not sure I put the prototypes in the right headers.  We don't have
a tsc.h but cpuvar.h looks sorta-correct for tsc_delay().

FreeBSD's x86/delay.c may be of note:

https://github.com/freebsd/freebsd/blob/ed96335a07b688c39e16db8856232e5840bc22ac/sys/x86/x86/delay.c

Thoughts?

Index: amd64/tsc.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/tsc.c,v
retrieving revision 1.20
diff -u -p -r1.20 tsc.c
--- amd64/tsc.c 23 Aug 2020 21:38:47 -0000      1.20
+++ amd64/tsc.c 23 Aug 2020 22:59:25 -0000
@@ -26,6 +26,7 @@
 
 #include <machine/cpu.h>
 #include <machine/cpufunc.h>
+#include <machine/cpuvar.h>
 
 #define RECALIBRATE_MAX_RETRIES                5
 #define RECALIBRATE_SMI_THRESHOLD      50000
@@ -252,7 +253,8 @@ tsc_timecounter_init(struct cpu_info *ci
                tsc_timecounter.tc_quality = -1000;
                tsc_timecounter.tc_user = 0;
                tsc_is_invariant = 0;
-       }
+       } else
+               delay_func = tsc_delay;
 
        tc_init(&tsc_timecounter);
 }
@@ -342,4 +344,15 @@ tsc_sync_ap(struct cpu_info *ci)
 {
        tsc_post_ap(ci);
        tsc_post_ap(ci);
+}
+
+void
+tsc_delay(int usecs)
+{
+       uint64_t interval, start;
+
+       interval = (uint64_t)usecs * tsc_frequency / 1000000;
+       start = rdtsc_lfence();
+       while (rdtsc_lfence() - start < interval)
+               CPU_BUSY_CYCLE();
 }
Index: amd64/lapic.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/lapic.c,v
retrieving revision 1.55
diff -u -p -r1.55 lapic.c
--- amd64/lapic.c       3 Aug 2019 14:57:51 -0000       1.55
+++ amd64/lapic.c       23 Aug 2020 22:59:25 -0000
@@ -41,6 +41,7 @@
 #include <machine/codepatch.h>
 #include <machine/cpu.h>
 #include <machine/cpufunc.h>
+#include <machine/cpuvar.h>
 #include <machine/pmap.h>
 #include <machine/mpbiosvar.h>
 #include <machine/specialreg.h>
@@ -569,7 +570,8 @@ skip_calibration:
                 * Now that the timer's calibrated, use the apic timer routines
                 * for all our timing needs..
                 */
-               delay_func = lapic_delay;
+               if (delay_func != tsc_delay)
+                       delay_func = lapic_delay;
                initclock_func = lapic_initclocks;
        }
 }
Index: include/cpuvar.h
===================================================================
RCS file: /cvs/src/sys/arch/amd64/include/cpuvar.h,v
retrieving revision 1.10
diff -u -p -r1.10 cpuvar.h
--- include/cpuvar.h    9 Aug 2019 15:20:05 -0000       1.10
+++ include/cpuvar.h    23 Aug 2020 22:59:25 -0000
@@ -102,4 +102,6 @@ void tsc_sync_drift(int64_t);
 void tsc_sync_bp(struct cpu_info *);
 void tsc_sync_ap(struct cpu_info *);
 
+void tsc_delay(int);
+
 #endif
Index: include/i82489var.h
===================================================================
RCS file: /cvs/src/sys/arch/amd64/include/i82489var.h,v
retrieving revision 1.18
diff -u -p -r1.18 i82489var.h
--- include/i82489var.h 4 Oct 2018 05:00:40 -0000       1.18
+++ include/i82489var.h 23 Aug 2020 22:59:26 -0000
@@ -128,4 +128,6 @@ extern void lapic_calibrate_timer(struct
 extern void lapic_startclock(void);
 extern void lapic_initclocks(void);
 
+extern void lapic_delay(int);
+
 #endif

Reply via email to