On Sun, Aug 14, 2022 at 11:24:37PM -0500, Scott Cheloha wrote:
> Hi,
>
> In the future when the LAPIC timer is run in oneshot mode there will
> be no lapic_delay().
>
> This is fine if you have a constant TSC, because we have tsc_delay().
>
> This is *very* bad for older amd64 machines, because you are left with
> i8254_delay().
>
> I would like to offer a less awful delay(9) implementation for this
> class of hardware. Otherwise we may trip over bizarre phantom bugs on
> MP kernels because only one CPU can read the i8254 at a time.
>
> I think patrick@ was struggling with some version of that problem last
> year, but in a VM.
>
> Real i386 hardware should be fine. Later models with an ACPI PM timer
> will be fine using acpitimer_delay() instead of i8254_delay().
>
> If this seems reasonable to people I will come back with a cleaned up
> patch for testing.
>
> Thoughts? Preferences?
>
Seems reasonable to me. Would be interested to see the revised diff once you're
done.
-ml
> -Scott
>
> Here are the sample measurements from my 2017 laptop (kaby lake
> refresh) running the attached patch. It takes longer than a
> microsecond to read either of the ACPI timers. The PM timer is better
> than the HPET. The HPET is a bit better than the i8254. I hope the
> numbers are a little better on older hardware.
>
> acpitimer_test_delay: expected 0.000001000 actual 0.000010638 error
> 0.000009638
> acpitimer_test_delay: expected 0.000010000 actual 0.000015464 error
> 0.000005464
> acpitimer_test_delay: expected 0.000100000 actual 0.000107619 error
> 0.000007619
> acpitimer_test_delay: expected 0.001000000 actual 0.001007275 error
> 0.000007275
> acpitimer_test_delay: expected 0.010000000 actual 0.010007891 error
> 0.000007891
>
> acpihpet_test_delay: expected 0.000001000 actual 0.000022208 error
> 0.000021208
> acpihpet_test_delay: expected 0.000010000 actual 0.000031690 error
> 0.000021690
> acpihpet_test_delay: expected 0.000100000 actual 0.000112647 error
> 0.000012647
> acpihpet_test_delay: expected 0.001000000 actual 0.001021480 error
> 0.000021480
> acpihpet_test_delay: expected 0.010000000 actual 0.010013736 error
> 0.000013736
>
> i8254_test_delay: expected 0.000001000 actual 0.000040110 error
> 0.000039110
> i8254_test_delay: expected 0.000010000 actual 0.000039471 error
> 0.000029471
> i8254_test_delay: expected 0.000100000 actual 0.000128031 error
> 0.000028031
> i8254_test_delay: expected 0.001000000 actual 0.001024586 error
> 0.000024586
> i8254_test_delay: expected 0.010000000 actual 0.010021859 error
> 0.000021859
>
> Index: dev/acpi/acpihpet.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/acpi/acpihpet.c,v
> retrieving revision 1.26
> diff -u -p -r1.26 acpihpet.c
> --- dev/acpi/acpihpet.c 6 Apr 2022 18:59:27 -0000 1.26
> +++ dev/acpi/acpihpet.c 15 Aug 2022 04:21:58 -0000
> @@ -18,6 +18,7 @@
> #include <sys/param.h>
> #include <sys/systm.h>
> #include <sys/device.h>
> +#include <sys/stdint.h>
> #include <sys/timetc.h>
>
> #include <machine/bus.h>
> @@ -31,8 +32,9 @@ int acpihpet_attached;
> int acpihpet_match(struct device *, void *, void *);
> void acpihpet_attach(struct device *, struct device *, void *);
> int acpihpet_activate(struct device *, int);
> -
> +void acpiphet_delay(u_int);
> u_int acpihpet_gettime(struct timecounter *tc);
> +void acpihpet_test_delay(u_int);
>
> uint64_t acpihpet_r(bus_space_tag_t _iot, bus_space_handle_t _ioh,
> bus_size_t _ioa);
> @@ -262,7 +264,7 @@ acpihpet_attach(struct device *parent, s
> freq = 1000000000000000ull / period;
> printf(": %lld Hz\n", freq);
>
> - hpet_timecounter.tc_frequency = (uint32_t)freq;
> + hpet_timecounter.tc_frequency = freq;
> hpet_timecounter.tc_priv = sc;
> hpet_timecounter.tc_name = sc->sc_dev.dv_xname;
> tc_init(&hpet_timecounter);
> @@ -273,10 +275,43 @@ acpihpet_attach(struct device *parent, s
> acpihpet_attached++;
> }
>
> +void
> +acpihpet_delay(u_int usecs)
> +{
> + uint64_t d, s;
> + struct acpihpet_softc *sc = hpet_timecounter.tc_priv;
> +
> + d = usecs * hpet_timecounter.tc_frequency / 1000000;
> + s = acpihpet_r(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER);
> + while (acpihpet_r(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER) - s < d)
> + CPU_BUSY_CYCLE();
> +}
> +
> u_int
> acpihpet_gettime(struct timecounter *tc)
> {
> struct acpihpet_softc *sc = tc->tc_priv;
>
> return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER));
> +}
> +
> +void
> +acpihpet_test_delay(u_int usecs)
> +{
> + struct timespec ac, er, ex, t0, t1;
> +
> + if (!acpihpet_attached) {
> + printf("%s: (no hpet attached)\n", __func__);
> + return;
> + }
> +
> + nanouptime(&t0);
> + acpihpet_delay(usecs);
> + nanouptime(&t1);
> + timespecsub(&t1, &t0, &ac);
> + NSEC_TO_TIMESPEC(usecs * 1000ULL, &ex);
> + timespecsub(&ac, &ex, &er);
> + printf("%s: expected %lld.%09ld actual %lld.%09ld error %lld.%09ld\n",
> + __func__, ex.tv_sec, ex.tv_nsec, ac.tv_sec, ac.tv_nsec,
> + er.tv_sec, er.tv_nsec);
> }
> Index: dev/acpi/acpitimer.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/acpi/acpitimer.c,v
> retrieving revision 1.15
> diff -u -p -r1.15 acpitimer.c
> --- dev/acpi/acpitimer.c 6 Apr 2022 18:59:27 -0000 1.15
> +++ dev/acpi/acpitimer.c 15 Aug 2022 04:21:58 -0000
> @@ -25,10 +25,14 @@
> #include <dev/acpi/acpireg.h>
> #include <dev/acpi/acpivar.h>
>
> +struct acpitimer_softc;
> +
> int acpitimermatch(struct device *, void *, void *);
> void acpitimerattach(struct device *, struct device *, void *);
> -
> +void acpitimer_delay(u_int);
> +void acpitimer_test_delay(u_int);
> u_int acpi_get_timecount(struct timecounter *tc);
> +uint32_t acpitimer_read(struct acpitimer_softc *);
>
> static struct timecounter acpi_timecounter = {
> .tc_get_timecount = acpi_get_timecount,
> @@ -104,12 +108,34 @@ acpitimerattach(struct device *parent, s
> #endif
> }
>
> +void
> +acpitimer_delay(u_int usecs)
> +{
> + uint64_t count = 0, cycles;
> + struct acpitimer_softc *sc = acpi_timecounter.tc_priv;
> + uint32_t mask = acpi_timecounter.tc_counter_mask;
> + uint32_t val1, val2;
> +
> + val2 = acpitimer_read(sc);
> + cycles = usecs * acpi_timecounter.tc_frequency / 1000000;
> + while (count < cycles) {
> + CPU_BUSY_CYCLE();
> + val1 = val2;
> + val2 = acpitimer_read(sc);
> + count += (val2 - val1) & mask;
> + }
> +}
>
> u_int
> acpi_get_timecount(struct timecounter *tc)
> {
> - struct acpitimer_softc *sc = tc->tc_priv;
> - u_int u1, u2, u3;
> + return acpitimer_read(tc->tc_priv);
> +}
> +
> +uint32_t
> +acpitimer_read(struct acpitimer_softc *sc)
> +{
> + uint32_t u1, u2, u3;
>
> u2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 0);
> u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 0);
> @@ -120,4 +146,20 @@ acpi_get_timecount(struct timecounter *t
> } while (u1 > u2 || u2 > u3);
>
> return (u2);
> +}
> +
> +void
> +acpitimer_test_delay(u_int usecs)
> +{
> + struct timespec ac, er, ex, t0, t1;
> +
> + nanouptime(&t0);
> + acpitimer_delay(usecs);
> + nanouptime(&t1);
> + timespecsub(&t1, &t0, &ac);
> + NSEC_TO_TIMESPEC(usecs * 1000ULL, &ex);
> + timespecsub(&ac, &ex, &er);
> + printf("%s: expected %lld.%09ld actual %lld.%09ld error %lld.%09ld\n",
> + __func__, ex.tv_sec, ex.tv_nsec, ac.tv_sec, ac.tv_nsec,
> + er.tv_sec, er.tv_nsec);
> }
> Index: arch/amd64/amd64/lapic.c
> ===================================================================
> RCS file: /cvs/src/sys/arch/amd64/amd64/lapic.c,v
> retrieving revision 1.59
> diff -u -p -r1.59 lapic.c
> --- arch/amd64/amd64/lapic.c 31 Aug 2021 15:53:36 -0000 1.59
> +++ arch/amd64/amd64/lapic.c 15 Aug 2022 04:21:58 -0000
> @@ -468,6 +468,19 @@ lapic_initclocks(void)
> lapic_startclock();
>
> i8254_inittimecounter_simple();
> +
> + extern void acpitimer_test_delay(u_int);
> + extern void acpihpet_test_delay(u_int);
> + extern void i8254_test_delay(u_int);
> + u_int usec[] = { 1, 10, 100, 1000, 10000 };
> + size_t i;
> + delay(20000); /* wait for real timecounter to activate */
> + for (i = 0; i < nitems(usec); i++)
> + acpitimer_test_delay(usec[i]);
> + for (i = 0; i < nitems(usec); i++)
> + acpihpet_test_delay(usec[i]);
> + for (i = 0; i < nitems(usec); i++)
> + i8254_test_delay(usec[i]);
> }
>
>
> Index: arch/amd64/isa/clock.c
> ===================================================================
> RCS file: /cvs/src/sys/arch/amd64/isa/clock.c,v
> retrieving revision 1.36
> diff -u -p -r1.36 clock.c
> --- arch/amd64/isa/clock.c 13 Feb 2022 19:15:09 -0000 1.36
> +++ arch/amd64/isa/clock.c 15 Aug 2022 04:21:59 -0000
> @@ -266,6 +266,22 @@ i8254_delay(int n)
> }
>
> void
> +i8254_test_delay(u_int usecs)
> +{
> + struct timespec ac, er, ex, t0, t1;
> +
> + nanouptime(&t0);
> + i8254_delay(usecs);
> + nanouptime(&t1);
> + timespecsub(&t1, &t0, &ac);
> + NSEC_TO_TIMESPEC(usecs * 1000ULL, &ex);
> + timespecsub(&ac, &ex, &er);
> + printf("%s: expected %lld.%09ld actual %lld.%09ld error %lld.%09ld\n",
> + __func__, ex.tv_sec, ex.tv_nsec, ac.tv_sec, ac.tv_nsec,
> + er.tv_sec, er.tv_nsec);
> +}
> +
> +void
> rtcdrain(void *v)
> {
> struct timeout *to = (struct timeout *)v;
>