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;
>

Reply via email to