i386: switch to clockintr
authorcheloha <cheloha@openbsd.org>
Tue, 6 Dec 2022 01:56:43 +0000 (01:56 +0000)
committercheloha <cheloha@openbsd.org>
Tue, 6 Dec 2022 01:56:43 +0000 (01:56 +0000)
In lapic timer mode:

- Rip out lapic_delay().  We can't use the lapic timer to delay(9) when
  it's running in one-shot mode.
- Add a randomized statclock(), stathz = hz.
- Add profiling support, profhz = stathz * 10.
- Wire up lapic_intrclock.

In i8254-mode:

- i8254's clockintr() does not have a monopoly on hardclock(9).
- mc146818's rtcintr() does not have a monopoly on statclock().
- In profiling mode, the statclock() will drift very slightly
  because (profhz = 1024) does not divide into 1 billion.  Need to
  consider how best to fix this.

ACPI suspend/resume tested by mlarkin@ via ESXi.  Tons of testing by
Scott Bennett on a Pentium 4 machine; APM suspend/resume confirmed to
work there, too.

Link: https://marc.info/?l=openbsd-tech&m=166776370803446&w=2
ok mlarkin@

sys/arch/i386/i386/acpi_machdep.c
sys/arch/i386/i386/apm.c
sys/arch/i386/i386/cpu.c
sys/arch/i386/i386/lapic.c
sys/arch/i386/include/_types.h
sys/arch/i386/include/cpu.h
sys/arch/i386/isa/clock.c

index 5f83b03..c368b21 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: acpi_machdep.c,v 1.85 2022/02/21 10:24:28 mpi Exp $   */
+/*     $OpenBSD: acpi_machdep.c,v 1.86 2022/12/06 01:56:43 cheloha Exp $       */
 /*
  * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
  *
@@ -421,8 +421,6 @@ acpi_resume_cpu(struct acpi_softc *sc, int state)
 #if NLAPIC > 0
        lapic_tpr = save_lapic_tpr;
        lapic_enable();
-       if (initclock_func == lapic_initclocks)
-               lapic_startclock();
        lapic_set_lvt();
 #endif
 
index a62bcf1..bf6087c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: apm.c,v 1.127 2022/02/21 10:24:28 mpi Exp $   */
+/*     $OpenBSD: apm.c,v 1.128 2022/12/06 01:56:44 cheloha Exp $       */
 
 /*-
  * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved.
@@ -44,6 +44,7 @@
 #include <sys/rwlock.h>
 #include <sys/sysctl.h>
 #include <sys/malloc.h>
+#include <sys/clockintr.h>
 #include <sys/device.h>
 #include <sys/fcntl.h>
 #include <sys/ioctl.h>
@@ -272,6 +273,11 @@ apm_suspend(int state)
                rtcstart();             /* in i8254 mode, rtc is profclock */
        inittodr(gettime());
 
+#ifdef __HAVE_CLOCKINTR
+       clockintr_cpu_init(NULL);
+       clockintr_trigger();
+#endif
+
        config_suspend_all(DVACT_RESUME);
        cold = 0;
        intr_enable();
index 9916865..5f6d94e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cpu.c,v 1.109 2022/08/15 04:17:50 daniel Exp $        */
+/*     $OpenBSD: cpu.c,v 1.110 2022/12/06 01:56:44 cheloha Exp $       */
 /* $NetBSD: cpu.c,v 1.1.2.7 2000/06/26 02:04:05 sommerfeld Exp $ */
 
 /*-
@@ -701,7 +701,6 @@ cpu_hatch(void *v)
 
        cpu_init_idt();
        lapic_enable();
-       lapic_startclock();
        lapic_set_lvt();
        gdt_init_cpu(ci);
 
@@ -728,6 +727,8 @@ cpu_hatch(void *v)
        nanouptime(&ci->ci_schedstate.spc_runtime);
        splx(s);
 
+       lapic_startclock();
+
        SCHED_LOCK(s);
        cpu_switchto(NULL, sched_chooseproc());
 }
index d3f316c..f7a6ef5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: lapic.c,v 1.52 2022/09/10 01:30:14 cheloha Exp $      */
+/*     $OpenBSD: lapic.c,v 1.53 2022/12/06 01:56:44 cheloha Exp $      */
 /* $NetBSD: lapic.c,v 1.1.2.8 2000/02/23 06:10:50 sommerfeld Exp $ */
 
 /*-
@@ -34,7 +34,9 @@
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/clockintr.h>
 #include <sys/device.h>
+#include <sys/stdint.h>
 
 #include <uvm/uvm_extern.h>
 
@@ -68,7 +70,6 @@ struct evcount clk_count;
 struct evcount ipi_count;
 #endif
 
-void   lapic_delay(int);
 static u_int32_t lapic_gettick(void);
 void   lapic_clockintr(void *);
 void   lapic_initclocks(void);
@@ -239,19 +240,43 @@ lapic_gettick(void)
 
 #include <sys/kernel.h>                /* for hz */
 
-u_int32_t lapic_tval;
-
 /*
  * this gets us up to a 4GHz busclock....
  */
 u_int32_t lapic_per_second = 0;
-u_int32_t lapic_frac_usec_per_cycle;
-u_int64_t lapic_frac_cycle_per_usec;
-u_int32_t lapic_delaytab[26];
+uint64_t lapic_timer_nsec_cycle_ratio;
+uint64_t lapic_timer_nsec_max;
+
+void lapic_timer_rearm(void *, uint64_t);
+void lapic_timer_trigger(void *);
+
+struct intrclock lapic_timer_intrclock = {
+       .ic_rearm = lapic_timer_rearm,
+       .ic_trigger = lapic_timer_trigger
+};
 
 void lapic_timer_oneshot(uint32_t, uint32_t);
 void lapic_timer_periodic(uint32_t, uint32_t);
 
+void
+lapic_timer_rearm(void *unused, uint64_t nsecs)
+{
+       uint32_t cycles;
+
+       if (nsecs > lapic_timer_nsec_max)
+               nsecs = lapic_timer_nsec_max;
+       cycles = (nsecs * lapic_timer_nsec_cycle_ratio) >> 32;
+       if (cycles == 0)
+               cycles = 1;
+       lapic_timer_oneshot(0, cycles);
+}
+
+void
+lapic_timer_trigger(void *unused)
+{
+       lapic_timer_oneshot(0, 1);
+}
+
 /*
  * Start the local apic countdown timer.
  *
@@ -280,25 +305,28 @@ lapic_timer_periodic(uint32_t mask, uint32_t cycles)
 }
 
 void
-lapic_clockintr(void *arg)
+lapic_clockintr(void *frame)
 {
-       struct clockframe *frame = arg;
-
-       hardclock(frame);
-
+       clockintr_dispatch(frame);
        clk_count.ec_count++;
 }
 
 void
 lapic_startclock(void)
 {
-       lapic_timer_periodic(0, lapic_tval);
+       clockintr_cpu_init(&lapic_timer_intrclock);
+       clockintr_trigger();
 }
 
 void
 lapic_initclocks(void)
 {
        i8254_inittimecounter_simple();
+
+       stathz = hz;
+       profhz = stathz * 10;
+       clockintr_init(CL_RNDSTAT);
+
        lapic_startclock();
 }
 
@@ -385,74 +413,14 @@ lapic_calibrate_timer(struct cpu_info *ci)
        printf("%s: apic clock running at %dMHz\n",
            ci->ci_dev->dv_xname, lapic_per_second / (1000 * 1000));
 
-       if (lapic_per_second != 0) {
-               /*
-                * reprogram the apic timer to run in periodic mode.
-                * XXX need to program timer on other cpu's, too.
-                */
-               lapic_tval = (lapic_per_second * 2) / hz;
-               lapic_tval = (lapic_tval / 2) + (lapic_tval & 0x1);
-
-               lapic_timer_periodic(LAPIC_LVTT_M, lapic_tval);
-
-               /*
-                * Compute fixed-point ratios between cycles and
-                * microseconds to avoid having to do any division
-                * in lapic_delay.
-                */
-
-               tmp = (1000000 * (u_int64_t)1 << 32) / lapic_per_second;
-               lapic_frac_usec_per_cycle = tmp;
-
-               tmp = (lapic_per_second * (u_int64_t)1 << 32) / 1000000;
-
-               lapic_frac_cycle_per_usec = tmp;
-
-               /*
-                * Compute delay in cycles for likely short delays in usec.
-                */
-               for (i = 0; i < 26; i++)
-                       lapic_delaytab[i] = (lapic_frac_cycle_per_usec * i) >>
-                           32;
-
-               /*
-                * Now that the timer's calibrated, use the apic timer routines
-                * for all our timing needs..
-                */
-               delay_init(lapic_delay, 3000);
-               initclock_func = lapic_initclocks;
-       }
-}
-
-/*
- * delay for N usec.
- */
-
-void
-lapic_delay(int usec)
-{
-       int32_t tick, otick;
-       int64_t deltat;         /* XXX may want to be 64bit */
-
-       otick = lapic_gettick();
-
-       if (usec <= 0)
+       /* XXX What should we do here if the timer frequency is zero? */
+       if (lapic_per_second == 0)
                return;
-       if (usec <= 25)
-               deltat = lapic_delaytab[usec];
-       else
-               deltat = (lapic_frac_cycle_per_usec * usec) >> 32;
-
-       while (deltat > 0) {
-               tick = lapic_gettick();
-               if (tick > otick)
-                       deltat -= lapic_tval - (tick - otick);
-               else
-                       deltat -= otick - tick;
-               otick = tick;
-
-               CPU_BUSY_CYCLE();
-       }
+
+       lapic_timer_nsec_cycle_ratio =
+           lapic_per_second * (1ULL << 32) / 1000000000;
+       lapic_timer_nsec_max = UINT64_MAX / lapic_timer_nsec_cycle_ratio;
+       initclock_func = lapic_initclocks;
 }
 
 /*
index 7da9a3c..2ccf99a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: _types.h,v 1.23 2018/03/05 01:15:25 deraadt Exp $     */
+/*     $OpenBSD: _types.h,v 1.24 2022/12/06 01:56:44 cheloha Exp $     */
 
 /*-
  * Copyright (c) 1990, 1993
@@ -35,6 +35,8 @@
 #ifndef _MACHINE__TYPES_H_
 #define _MACHINE__TYPES_H_
 
+#define        __HAVE_CLOCKINTR
+
 /*
  * _ALIGN(p) rounds p (pointer or byte index) up to a correctly-aligned
  * value for all data types (int, long, ...).   The result is an
index 6d707fc..10f5f3e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cpu.h,v 1.180 2022/11/08 14:49:20 cheloha Exp $       */
+/*     $OpenBSD: cpu.h,v 1.181 2022/12/06 01:56:44 cheloha Exp $       */
 /*     $NetBSD: cpu.h,v 1.35 1996/05/05 19:29:26 christos Exp $        */
 
 /*-
@@ -64,6 +64,7 @@
  */
 #define clockframe intrframe
 
+#include <sys/clockintr.h>
 #include <sys/device.h>
 #include <sys/sched.h>
 #include <sys/sensors.h>
@@ -168,6 +169,7 @@ struct cpu_info {
 #if defined(GPROF) || defined(DDBPROF)
        struct gmonparam        *ci_gmon;
 #endif
+       struct clockintr_queue  ci_queue;
        char                    ci_panicbuf[512];
 };
 
index 1fba249..7b0b60d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: clock.c,v 1.61 2022/11/01 13:59:00 kettenis Exp $     */
+/*     $OpenBSD: clock.c,v 1.62 2022/12/06 01:56:44 cheloha Exp $      */
 /*     $NetBSD: clock.c,v 1.39 1996/05/12 23:11:54 mycroft Exp $       */
 
 /*-
@@ -89,7 +89,9 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <sys/systm.h>
 #include <sys/time.h>
 #include <sys/kernel.h>
+#include <sys/clockintr.h>
 #include <sys/device.h>
+#include <sys/stdint.h>
 #include <sys/timeout.h>
 #include <sys/timetc.h>
 #include <sys/mutex.h>
@@ -202,10 +204,8 @@ rtcdrain(void *v)
 }
 
 int
-clockintr(void *arg)
+clockintr(void *frame)
 {
-       struct clockframe *frame = arg;         /* not strictly necessary */
-
        if (timecounter->tc_get_timecount == i8254_get_timecount) {
                if (i8254_ticked) {
                        i8254_ticked = 0;
@@ -214,33 +214,26 @@ clockintr(void *arg)
                        i8254_lastcount = 0;
                }
        }
-
-       hardclock(frame);
+       clockintr_dispatch(frame);
        return (1);
 }
 
 int
-rtcintr(void *arg)
+rtcintr(void *frame)
 {
-       struct clockframe *frame = arg;         /* not strictly necessary */
        u_int stat = 0;
 
-       if (stathz == 0) {
-               extern int psratio;
-
-               stathz = 128;
-               profhz = 1024;
-               psratio = profhz / stathz;
-       }
-
        /*
         * If rtcintr is 'late', next intr may happen immediately.
         * Get them all. (Also, see comment in cpu_initclocks().)
         */
-       while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
-               statclock(frame);
+       while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
                stat = 1;
-       }
+
+       /* XXX Can rtcintr() run before i8254_initclocks() is complete? */
+       if (stathz != 0 && stat)
+               clockintr_dispatch(frame);
+
        return (stat);
 }
 
@@ -433,6 +426,14 @@ calibrate_cyclecounter(void)
 void
 i8254_initclocks(void)
 {
+       i8254_inittimecounter();        /* hook the interrupt-based i8254 tc */
+
+       stathz = 128;
+       profhz = 1024;          /* XXX does not divide into 1 billion */
+       clockintr_init(0);
+
+       clockintr_cpu_init(NULL);
+
        /* When using i8254 for clock, we also use the rtc for profclock */
        (void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK,
            clockintr, 0, "clock");
@@ -440,8 +441,6 @@ i8254_initclocks(void)
            rtcintr, 0, "rtc");
 
        rtcstart();                     /* start the mc146818 clock */
-
-       i8254_inittimecounter();        /* hook the interrupt-based i8254 tc */
 }
 
 void
@@ -668,6 +667,7 @@ setstatclockrate(int arg)
                        mc146818_write(NULL, MC_REGA,
                            MC_BASE_32_KHz | MC_RATE_1024_Hz);
        }
+       clockintr_setstatclockrate(arg);
 }
 
 void