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