From a623d4ea00d31d6423d47da710b63aaf9f52512e Mon Sep 17 00:00:00 2001 From: cheloha Date: Tue, 6 Dec 2022 01:56:43 +0000 Subject: [PATCH] i386: switch to clockintr 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 | 4 +- sys/arch/i386/i386/apm.c | 8 +- sys/arch/i386/i386/cpu.c | 5 +- sys/arch/i386/i386/lapic.c | 128 +++++++++++------------------- sys/arch/i386/include/_types.h | 4 +- sys/arch/i386/include/cpu.h | 4 +- sys/arch/i386/isa/clock.c | 42 +++++----- 7 files changed, 86 insertions(+), 109 deletions(-) diff --git a/sys/arch/i386/i386/acpi_machdep.c b/sys/arch/i386/i386/acpi_machdep.c index 5f83b03de50..c368b213ec7 100644 --- a/sys/arch/i386/i386/acpi_machdep.c +++ b/sys/arch/i386/i386/acpi_machdep.c @@ -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 * @@ -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 diff --git a/sys/arch/i386/i386/apm.c b/sys/arch/i386/i386/apm.c index a62bcf15e8c..bf6087c7335 100644 --- a/sys/arch/i386/i386/apm.c +++ b/sys/arch/i386/i386/apm.c @@ -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 #include #include +#include #include #include #include @@ -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(); diff --git a/sys/arch/i386/i386/cpu.c b/sys/arch/i386/i386/cpu.c index 9916865ba00..5f6d94e22a6 100644 --- a/sys/arch/i386/i386/cpu.c +++ b/sys/arch/i386/i386/cpu.c @@ -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()); } diff --git a/sys/arch/i386/i386/lapic.c b/sys/arch/i386/i386/lapic.c index d3f316cb447..f7a6ef55721 100644 --- a/sys/arch/i386/i386/lapic.c +++ b/sys/arch/i386/i386/lapic.c @@ -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 #include +#include #include +#include #include @@ -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 /* 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; } /* diff --git a/sys/arch/i386/include/_types.h b/sys/arch/i386/include/_types.h index 7da9a3c6ea8..2ccf99a07c5 100644 --- a/sys/arch/i386/include/_types.h +++ b/sys/arch/i386/include/_types.h @@ -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 diff --git a/sys/arch/i386/include/cpu.h b/sys/arch/i386/include/cpu.h index 6d707fc2d77..10f5f3e086c 100644 --- a/sys/arch/i386/include/cpu.h +++ b/sys/arch/i386/include/cpu.h @@ -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 #include #include #include @@ -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]; }; diff --git a/sys/arch/i386/isa/clock.c b/sys/arch/i386/isa/clock.c index 1fba249f6ea..7b0b60d1140 100644 --- a/sys/arch/i386/isa/clock.c +++ b/sys/arch/i386/isa/clock.c @@ -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 #include #include +#include #include +#include #include #include #include @@ -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 -- 2.20.1