On Sun, Nov 06, 2022 at 07:46:37PM +0000, Scott Cheloha wrote:
> This patch switches i386 to clockintr(9).
> 
> I have tested this on my Lenovo X1 Carbon 6th and Dell Optiplex 7070
> running in 32-bit compatibility mode.  It has survived ~20 parallel
> release builds and upgrades from the resulting bsd.rd.
> 
> mlarkin@ has tested this in an ESXi VM and reports that ACPI hibernate
> works.
> 
> This needs additional testing.  A couple things have not been
> confirmed to work yet:
> 
> - Accelerated graphics
> 
> - ACPI suspend and hibernate on real hardware
> 
> - APM suspend and hibernate
> 
> - Real 586- and 686-class hardware
> 
> - Real machines without a lapic
> 
> Notes:
> 
> - i386 machines with a lapic now have a randomized statclock().
> 
> - This includes a preliminary patch that disables/enables hpet_delay()
>   across suspend/resume.  It will be committed separately.
> 
> - In i8254-mode, profhz = 1024 does not divide evenly into one billion.
>   We could circumvent this problem by running the RTC at 512hz when
>   profiling is enabled.
> 
> - The same behavior change described in the amd64 patch:
> 
> https://marc.info/?l=openbsd-tech&m=166776339203279&w=2
> 
>   applies to i386:
> 
> > If we're using the i8254 as our interrupt clock, there is
> > a small behavior change.
> >
> > In i8254-mode, there are two interrupt clocks, the i8254 and mc146818.
> >
> > Currently, the i8254 runs hardclock() and the mc146818 runs statclock().
> > With this patch, neither interrupt handler has a monopoly on which events
> > are dispatched anymore.  So if hardclock() is due, the mc146818 handler
> > will dispatch it.  The i8254 might dispatch statclock(), too.
> >
> > Preserving the existing behavior would require per-intrclock event
> > queues.  That is, the i8254 would have its own work schedule
> > maintained separately from that of the mc146818.  I pitched this idea
> > to kettenis@ and he said we probably didn't need it.  I agree with
> > him.  Of course, if there is interest in this feature we could explore
> > it.

The HPET pieces have been committed separately.  Here is an updated
patch.

Index: sys/arch/i386/i386/acpi_machdep.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/acpi_machdep.c,v
retrieving revision 1.85
diff -u -p -r1.85 acpi_machdep.c
--- sys/arch/i386/i386/acpi_machdep.c   21 Feb 2022 10:24:28 -0000      1.85
+++ sys/arch/i386/i386/acpi_machdep.c   9 Nov 2022 19:07:10 -0000
@@ -421,8 +421,6 @@ acpi_resume_cpu(struct acpi_softc *sc, i
 #if NLAPIC > 0
        lapic_tpr = save_lapic_tpr;
        lapic_enable();
-       if (initclock_func == lapic_initclocks)
-               lapic_startclock();
        lapic_set_lvt();
 #endif
 
Index: sys/arch/i386/i386/apm.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/apm.c,v
retrieving revision 1.127
diff -u -p -r1.127 apm.c
--- sys/arch/i386/i386/apm.c    21 Feb 2022 10:24:28 -0000      1.127
+++ sys/arch/i386/i386/apm.c    9 Nov 2022 19:07:11 -0000
@@ -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>
@@ -271,6 +272,11 @@ apm_suspend(int state)
        if (initclock_func == i8254_initclocks)
                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;
Index: sys/arch/i386/i386/cpu.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/cpu.c,v
retrieving revision 1.109
diff -u -p -r1.109 cpu.c
--- sys/arch/i386/i386/cpu.c    15 Aug 2022 04:17:50 -0000      1.109
+++ sys/arch/i386/i386/cpu.c    9 Nov 2022 19:07:11 -0000
@@ -701,7 +701,6 @@ cpu_hatch(void *v)
 
        cpu_init_idt();
        lapic_enable();
-       lapic_startclock();
        lapic_set_lvt();
        gdt_init_cpu(ci);
 
@@ -727,6 +726,8 @@ cpu_hatch(void *v)
                    ci->ci_dev->dv_xname, ci->ci_cpuid);
        nanouptime(&ci->ci_schedstate.spc_runtime);
        splx(s);
+
+       lapic_startclock();
 
        SCHED_LOCK(s);
        cpu_switchto(NULL, sched_chooseproc());
Index: sys/arch/i386/i386/lapic.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/lapic.c,v
retrieving revision 1.52
diff -u -p -r1.52 lapic.c
--- sys/arch/i386/i386/lapic.c  10 Sep 2022 01:30:14 -0000      1.52
+++ sys/arch/i386/i386/lapic.c  9 Nov 2022 19:07:11 -0000
@@ -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, uint
 }
 
 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 *c
        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: sys/arch/i386/isa/clock.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/isa/clock.c,v
retrieving revision 1.61
diff -u -p -r1.61 clock.c
--- sys/arch/i386/isa/clock.c   1 Nov 2022 13:59:00 -0000       1.61
+++ sys/arch/i386/isa/clock.c   9 Nov 2022 19:07:11 -0000
@@ -89,7 +89,9 @@ WITH THE USE OR PERFORMANCE OF THIS SOFT
 #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
Index: sys/arch/i386/include/cpu.h
===================================================================
RCS file: /cvs/src/sys/arch/i386/include/cpu.h,v
retrieving revision 1.180
diff -u -p -r1.180 cpu.h
--- sys/arch/i386/include/cpu.h 8 Nov 2022 14:49:20 -0000       1.180
+++ sys/arch/i386/include/cpu.h 9 Nov 2022 19:07:11 -0000
@@ -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: sys/arch/i386/include/_types.h
===================================================================
RCS file: /cvs/src/sys/arch/i386/include/_types.h,v
retrieving revision 1.23
diff -u -p -r1.23 _types.h
--- sys/arch/i386/include/_types.h      5 Mar 2018 01:15:25 -0000       1.23
+++ sys/arch/i386/include/_types.h      9 Nov 2022 19:07:11 -0000
@@ -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

Reply via email to