diff options
author | cheloha <cheloha@cvs.openbsd.org> | 2020-09-06 20:50:01 +0000 |
---|---|---|
committer | cheloha <cheloha@cvs.openbsd.org> | 2020-09-06 20:50:01 +0000 |
commit | 11ac1d3a27224b91ae62c6139bb5af3d573c0631 (patch) | |
tree | 0d39007c98be1d44f922523ee2a7f8f0622014ca /sys/arch | |
parent | b45bce942e9101f6e1748cbd470e9a0bca729457 (diff) |
amd64: add tsc_delay(), a delay(9) implementation based on the TSC
In preparation for running the lapic timer in oneshot mode on amd64 we
need a replacement for lapic_delay().
Using the lapic timer itself to implement delay(9) when the timer is
not running in periodic mode is complicated if not outright impossible.
Meanwhile, the i8254 provides our only other amd64 delay(9) implementation
and it is an extremely slow clock. On my 2GHz machine, gettick() takes
~20 microseconds to complete *without* mutex contention. On a VM it is
even slower, as you must exit the VM for each inb() and outb().
So, add tsc_delay() and use it when we have a constant/invariant TSC.
The TSC is a 64-bit "up-counter" so the implementation is simple.
Given how slow the i8254 is on modern machines, we may want to add an
HPET delay(9) implementation as a fallback for machines where the TSC
drifts. The HPET itself is pretty slow, but not as slow as the i8254.
Discussed with kettenis@, Mike Larkin, and naddy@.
Tweaked by kettenis@.
ok kettenis@
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/amd64/amd64/lapic.c | 7 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/tsc.c | 17 |
2 files changed, 20 insertions, 4 deletions
diff --git a/sys/arch/amd64/amd64/lapic.c b/sys/arch/amd64/amd64/lapic.c index 25f90b69508..6d82b6b2576 100644 --- a/sys/arch/amd64/amd64/lapic.c +++ b/sys/arch/amd64/amd64/lapic.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lapic.c,v 1.56 2020/09/03 21:38:46 cheloha Exp $ */ +/* $OpenBSD: lapic.c,v 1.57 2020/09/06 20:50:00 cheloha Exp $ */ /* $NetBSD: lapic.c,v 1.2 2003/05/08 01:04:35 fvdl Exp $ */ /*- @@ -489,6 +489,8 @@ wait_next_cycle(void) } } +extern void tsc_delay(int); + /* * Calibrate the local apic count-down timer (which is running at * bus-clock speed) vs. the i8254 counter/timer (which is running at @@ -584,7 +586,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; } } diff --git a/sys/arch/amd64/amd64/tsc.c b/sys/arch/amd64/amd64/tsc.c index 79730c607a7..238a5a068e1 100644 --- a/sys/arch/amd64/amd64/tsc.c +++ b/sys/arch/amd64/amd64/tsc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tsc.c,v 1.20 2020/08/23 21:38:47 cheloha Exp $ */ +/* $OpenBSD: tsc.c,v 1.21 2020/09/06 20:50:00 cheloha Exp $ */ /* * Copyright (c) 2008 The NetBSD Foundation, Inc. * Copyright (c) 2016,2017 Reyk Floeter <reyk@openbsd.org> @@ -44,6 +44,7 @@ volatile int64_t tsc_sync_val; volatile struct cpu_info *tsc_sync_cpu; u_int tsc_get_timecount(struct timecounter *tc); +void tsc_delay(int usecs); #include "lapic.h" #if NLAPIC > 0 @@ -252,7 +253,8 @@ tsc_timecounter_init(struct cpu_info *ci, uint64_t cpufreq) tsc_timecounter.tc_quality = -1000; tsc_timecounter.tc_user = 0; tsc_is_invariant = 0; - } + } else + delay_func = tsc_delay; tc_init(&tsc_timecounter); } @@ -343,3 +345,14 @@ 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(); +} |