Now that we have resolved the problems with the UltraSPARC IIe, let's try the clockintr switch again. For reference, here is v1:
https://marc.info/?l=openbsd-tech&m=166776418803680&w=2 The major difference in v2 is that we send_softint() if we miss when setting the comparison register instead of spinning and retrying. I didn't do this in v1 because I didn't know it was an option. This is untested. I'm looking for tests on UltraSPARC I or UltraSPARC II (%TICK), UltraSPARC IIe (Hummingbird PCI bridge %STICK), and something newer (%STICK). kettenis, kn: could one of you try this on a newer system? miod@ only has the older stuff. mlarkin@ said he'd try it on the T4 but now he's out of office. Index: sys/arch/sparc64/include/cpu.h =================================================================== RCS file: /cvs/src/sys/arch/sparc64/include/cpu.h,v retrieving revision 1.100 diff -u -p -r1.100 cpu.h --- sys/arch/sparc64/include/cpu.h 22 Oct 2022 20:09:41 -0000 1.100 +++ sys/arch/sparc64/include/cpu.h 5 Jan 2023 00:10:15 -0000 @@ -78,6 +78,7 @@ #include <machine/psl.h> #include <machine/reg.h> +#include <sys/clockintr.h> #include <sys/sched.h> #include <sys/srp.h> @@ -129,7 +130,7 @@ struct cpu_info { int ci_want_resched; int ci_handled_intr_level; void *ci_intrpending[16][8]; - u_int64_t ci_tick; + struct clockintr_queue ci_queue; struct intrhand ci_tickintr; volatile int ci_ddb_paused; Index: sys/arch/sparc64/include/_types.h =================================================================== RCS file: /cvs/src/sys/arch/sparc64/include/_types.h,v retrieving revision 1.23 diff -u -p -r1.23 _types.h --- sys/arch/sparc64/include/_types.h 5 Mar 2018 01:15:25 -0000 1.23 +++ sys/arch/sparc64/include/_types.h 5 Jan 2023 00:10:15 -0000 @@ -35,6 +35,8 @@ #ifndef _MACHINE__TYPES_H_ #define _MACHINE__TYPES_H_ +#define __HAVE_CLOCKINTR + #if defined(_KERNEL) typedef struct label_t { long val[2]; Index: sys/arch/sparc64/sparc64/clock.c =================================================================== RCS file: /cvs/src/sys/arch/sparc64/sparc64/clock.c,v retrieving revision 1.74 diff -u -p -r1.74 clock.c --- sys/arch/sparc64/sparc64/clock.c 29 Dec 2022 22:44:23 -0000 1.74 +++ sys/arch/sparc64/sparc64/clock.c 5 Jan 2023 00:10:15 -0000 @@ -70,10 +70,12 @@ #include <sys/resourcevar.h> #include <sys/malloc.h> #include <sys/systm.h> +#include <sys/clockintr.h> #ifdef GPROF #include <sys/gmon.h> #endif #include <sys/sched.h> +#include <sys/stdint.h> #include <sys/timetc.h> #include <sys/atomic.h> @@ -132,37 +134,47 @@ struct timecounter sys_tick_timecounter .tc_user = TC_SYS_TICK, }; -/* - * Statistics clock interval and variance, in usec. Variance must be a - * power of two. Since this gives us an even number, not an odd number, - * we discard one case and compensate. That is, a variance of 1024 would - * give us offsets in [0..1023]. Instead, we take offsets in [1..1023]. - * This is symmetric about the point 512, or statvar/2, and thus averages - * to that value (assuming uniform random numbers). - */ -/* XXX fix comment to match value */ -int statvar = 8192; -int statmin; /* statclock interval - 1/2*variance */ - -static long tick_increment; - void tick_start(void); void sys_tick_start(void); void stick_start(void); -void tick_rearm(uint64_t); -void sys_tick_rearm(uint64_t); -void stick_rearm(uint64_t); - int tickintr(void *); int sys_tickintr(void *); int stickintr(void *); -int schedintr(void *); -static struct intrhand level10 = { clockintr }; +uint64_t tick_nsec_cycle_ratio; +uint64_t tick_nsec_max; + +void tick_rearm(void *, uint64_t); +void tick_trigger(void *); + +const struct intrclock tick_intrclock = { + .ic_rearm = tick_rearm, + .ic_trigger = tick_trigger +}; + +uint64_t sys_tick_nsec_cycle_ratio; +uint64_t sys_tick_nsec_max; + +void sys_tick_rearm(void *, uint64_t); +void sys_tick_trigger(void *); + +const struct intrclock sys_tick_intrclock = { + .ic_rearm = sys_tick_rearm, + .ic_trigger = sys_tick_trigger +}; + +void stick_rearm(void *, uint64_t); +void stick_trigger(void *); + +const struct intrclock stick_intrclock = { + .ic_rearm = stick_rearm, + .ic_trigger = stick_trigger +}; + +void sparc64_raise_clockintr(void); + static struct intrhand level0 = { tickintr }; -static struct intrhand level14 = { statintr }; -static struct intrhand schedint = { schedintr }; /* * clock (eeprom) attaches at the sbus or the ebus (PCI) @@ -466,7 +478,7 @@ timerattach(struct device *parent, struc { struct mainbus_attach_args *ma = aux; u_int *va = ma->ma_address; - +#if 0 /* * What we should have are 3 sets of registers that reside on * different parts of SYSIO or PSYCHO. We'll use the prom @@ -492,6 +504,8 @@ timerattach(struct device *parent, struc printf(" ivec 0x%llx, 0x%llx\n", INTVEC(level10.ih_number), INTVEC(level14.ih_number)); +#endif + printf(" ivec (none)\n"); } void @@ -534,15 +548,13 @@ myetheraddr(u_char *cp) } /* - * Set up the real-time and statistics clocks. Leave stathz 0 only if - * no alternative timer is available. + * Set up the real-time and statistics clocks. * * The frequencies of these clocks must be an even number of microseconds. */ void cpu_initclocks(void) { - int statint, minint; #ifdef DEBUG extern int intrdebug; #endif @@ -566,6 +578,10 @@ cpu_initclocks(void) tick_nsec = 1000000000 / hz; } + stathz = hz; + profhz = stathz * 10; + clockintr_init(CL_RNDSTAT); + /* Make sure we have a sane cpu_clockrate -- we'll need it */ if (!cpu_clockrate) /* Default to 200MHz clock XXXXX */ @@ -588,375 +604,183 @@ cpu_initclocks(void) tc_init(&sys_tick_timecounter); } - /* - * Now handle machines w/o counter-timers. - */ - - if (!timerreg_4u.t_timer || !timerreg_4u.t_clrintr) { - struct cpu_info *ci; - - /* We don't have a counter-timer -- use %tick */ - level0.ih_clr = 0; - - /* - * Establish a level 10 interrupt handler - * - * We will have a conflict with the softint handler, - * so we set the ih_number to 1. - */ - level0.ih_number = 1; - strlcpy(level0.ih_name, "clock", sizeof(level0.ih_name)); - intr_establish(10, &level0); - evcount_percpu(&level0.ih_count); - - /* We only have one timer so we have no statclock */ - stathz = 0; - - if (sys_tick_rate > 0) { - tick_increment = sys_tick_rate / hz; - if (impl == IMPL_HUMMINGBIRD) { - level0.ih_fun = stickintr; - cpu_start_clock = stick_start; - } else { - level0.ih_fun = sys_tickintr; - cpu_start_clock = sys_tick_start; - } - } else { - /* set the next interrupt time */ - tick_increment = cpu_clockrate / hz; - level0.ih_fun = tickintr; - cpu_start_clock = tick_start; - } + struct cpu_info *ci; - for (ci = cpus; ci != NULL; ci = ci->ci_next) - memcpy(&ci->ci_tickintr, &level0, sizeof(level0)); - - cpu_start_clock(); - - return; - } - - if (stathz == 0) - stathz = hz; - if (1000000 % stathz) { - printf("cannot get %d Hz statclock; using 100 Hz\n", stathz); - stathz = 100; - } - - profhz = stathz; /* always */ - - statint = 1000000 / stathz; - minint = statint / 2 + 100; - while (statvar > minint) - statvar >>= 1; + /* We don't have a counter-timer -- use %tick */ + level0.ih_clr = 0; /* - * Establish scheduler softint. - */ - schedint.ih_pil = PIL_SCHED; - schedint.ih_clr = NULL; - schedint.ih_arg = 0; - schedint.ih_pending = 0; - schedhz = stathz/4; - - /* - * Enable timers + * Establish a level 10 interrupt handler * - * Also need to map the interrupts cause we're not a child of the sbus. - * N.B. By default timer[0] is disabled and timer[1] is enabled. + * We will have a conflict with the softint handler, + * so we set the ih_number to 1. */ - stxa((vaddr_t)&timerreg_4u.t_timer[0].t_limit, ASI_NUCLEUS, - tmr_ustolim(tick)|TMR_LIM_IEN|TMR_LIM_PERIODIC|TMR_LIM_RELOAD); - stxa((vaddr_t)&timerreg_4u.t_mapintr[0], ASI_NUCLEUS, - timerreg_4u.t_mapintr[0]|INTMAP_V); - -#ifdef DEBUG - if (intrdebug) - /* Neglect to enable timer */ - stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS, - tmr_ustolim(statint)|TMR_LIM_RELOAD); - else -#endif - stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS, - tmr_ustolim(statint)|TMR_LIM_IEN|TMR_LIM_RELOAD); - stxa((vaddr_t)&timerreg_4u.t_mapintr[1], ASI_NUCLEUS, - timerreg_4u.t_mapintr[1]|INTMAP_V); + level0.ih_number = 1; + strlcpy(level0.ih_name, "clock", sizeof(level0.ih_name)); + intr_establish(10, &level0); + evcount_percpu(&level0.ih_count); + + if (sys_tick_rate > 0) { + sys_tick_nsec_cycle_ratio = + sys_tick_rate * (1ULL << 32) / 1000000000; + sys_tick_nsec_max = UINT64_MAX / sys_tick_nsec_cycle_ratio; + if (impl == IMPL_HUMMINGBIRD) { + level0.ih_func = stickintr; + cpu_start_clock = stick_start; + } else { + level0.ih_func = sys_tickintr; + cpu_start_clock = sys_tick_start; + } + } else { + tick_nsec_cycle_ratio = + cpu_clockrate * (1ULL << 32) / 1000000000; + tick_nsec_max = UINT64_MAX / tick_nsec_cycle_ratio; + level0.ih_func = tickintr; + cpu_start_clock = tick_start; + } - statmin = statint - (statvar >> 1); + for (ci = cpus; ci != NULL; ci = ci->ci_next) + memcpy(&ci->ci_tickintr, &level0, sizeof(level0)); - tick_enable(); + cpu_start_clock(); } -/* - * Dummy setstatclockrate(), since we know profhz==hz. - */ void setstatclockrate(int newhz) { - /* nothing */ + clockintr_setstatclockrate(newhz); } -/* - * Level 10 (clock) interrupts. If we are using the FORTH PROM for - * console input, we need to check for that here as well, and generate - * a software interrupt to read it. - */ -#ifdef DEBUG -static int clockcheck = 0; -#endif -int -clockintr(void *cap) -{ -#ifdef DEBUG - static int64_t tick_base = 0; - struct timeval ctime; - int64_t t; - - t = tick() & TICK_TICKS; - - microtime(&ctime); - if (!tick_base) { - tick_base = (ctime.tv_sec * 1000000LL + ctime.tv_usec) - * 1000000LL / cpu_clockrate; - tick_base -= t; - } else if (clockcheck) { - int64_t tk = t; - int64_t clk = (ctime.tv_sec * 1000000LL + ctime.tv_usec); - t -= tick_base; - t = t * 1000000LL / cpu_clockrate; - if (t - clk > hz) { - printf("Clock lost an interrupt!\n"); - printf("Actual: %llx Expected: %llx tick %llx " - "tick_base %llx\n", (long long)t, (long long)clk, - (long long)tk, (long long)tick_base); -#ifdef DDB - db_enter(); -#endif - tick_base = 0; - } - } -#endif - /* Let locore.s clear the interrupt for us. */ - hardclock((struct clockframe *)cap); - - return (1); -} - -/* - * Level 10 (clock) interrupts. If we are using the FORTH PROM for - * console input, we need to check for that here as well, and generate - * a software interrupt to read it. - * - * %tick is really a level-14 interrupt. We need to remap this in - * locore.s to a level 10. - */ int tickintr(void *cap) { - struct cpu_info *ci = curcpu(); - u_int64_t s; - - /* - * No need to worry about overflow; %tick is architecturally - * defined not to do that for at least 10 years. - */ - while (ci->ci_tick < tick()) { - ci->ci_tick += tick_increment; - hardclock((struct clockframe *)cap); - evcount_inc(&level0.ih_count); - } - - /* Reset the interrupt. */ - s = intr_disable(); - tick_rearm(ci->ci_tick); - intr_restore(s); - + clockintr_dispatch(cap); + evcount_inc(&level0.ih_count); return (1); } int sys_tickintr(void *cap) { - struct cpu_info *ci = curcpu(); - u_int64_t s; - - /* - * Do we need to worry about overflow here? - */ - while (ci->ci_tick < sys_tick()) { - ci->ci_tick += tick_increment; - hardclock((struct clockframe *)cap); - evcount_inc(&level0.ih_count); - } - - /* Reset the interrupt. */ - s = intr_disable(); - sys_tick_rearm(ci->ci_tick); - intr_restore(s); - + clockintr_dispatch(cap); + evcount_inc(&level0.ih_count); return (1); } int stickintr(void *cap) { - struct cpu_info *ci = curcpu(); - u_int64_t s; - - /* - * Do we need to worry about overflow here? - */ - while (ci->ci_tick < stick()) { - ci->ci_tick += tick_increment; - hardclock((struct clockframe *)cap); - evcount_inc(&level0.ih_count); - } - - /* Reset the interrupt. */ - s = intr_disable(); - stick_rearm(ci->ci_tick); - intr_restore(s); - + clockintr_dispatch(cap); + evcount_inc(&level0.ih_count); return (1); } -/* - * Level 14 (stat clock) interrupt handler. - */ -int -statintr(void *cap) +void +sparc64_raise_clockintr(void) { - u_long newint, r, var; - struct cpu_info *ci = curcpu(); - -#ifdef NOT_DEBUG - printf("statclock: count %x:%x, limit %x:%x\n", - timerreg_4u.t_timer[1].t_count, timerreg_4u.t_timer[1].t_limit); -#endif -#ifdef NOT_DEBUG - prom_printf("!"); -#endif - statclock((struct clockframe *)cap); -#ifdef NOTDEF_DEBUG - /* Don't re-schedule the IRQ */ - return 1; -#endif - /* - * Compute new randomized interval. The intervals are uniformly - * distributed on [statint - statvar / 2, statint + statvar / 2], - * and therefore have mean statint, giving a stathz frequency clock. - */ - var = statvar; - do { - r = random() & (var - 1); - } while (r == 0); - newint = statmin + r; - - if (schedhz) - if ((++ci->ci_schedstate.spc_schedticks & 3) == 0) - send_softint(-1, PIL_SCHED, &schedint); - stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS, - tmr_ustolim(newint)|TMR_LIM_IEN|TMR_LIM_RELOAD); - - return (1); + send_softint(-1, 14, &curcpu()->ci_tickintr); } -int -schedintr(void *arg) +void +tick_start(void) { - if (curproc) - schedclock(curproc); - return (1); + tick_enable(); + + clockintr_cpu_init(&tick_intrclock); + clockintr_trigger(); } void -tick_start(void) +tick_rearm(void *unused, uint64_t nsecs) { - struct cpu_info *ci = curcpu(); - u_int64_t s; + uint64_t cmp, s; + uint32_t cycles; - tick_enable(); + if (nsecs > tick_nsec_max) + nsecs = tick_nsec_max; + cycles = (nsecs * tick_nsec_cycle_ratio) >> 32; s = intr_disable(); - ci->ci_tick = tick(); - tick_rearm(ci->ci_tick); + cmp = tick() + cycles; + tickcmpr_set(cmp); + if (tick() <= cmp) + sparc64_raise_clockintr(); intr_restore(s); } void -tick_rearm(uint64_t cmp) +tick_trigger(void *unused) { - uint64_t now, off = 8; - - tickcmpr_set(cmp); - now = tick(); - while (cmp <= now) { - cmp += off; - tickcmpr_set(cmp); - now = tick(); - off *= 2; - } + sparc64_raise_clockintr(); } void sys_tick_start(void) { - struct cpu_info *ci = curcpu(); - u_int64_t s; - if (CPU_ISSUN4U || CPU_ISSUN4US) { tick_enable(); sys_tick_enable(); } - s = intr_disable(); - ci->ci_tick = sys_tick(); - sys_tick_rearm(ci->ci_tick); - intr_restore(s); + clockintr_cpu_init(&sys_tick_intrclock); + clockintr_trigger(); } void -sys_tick_rearm(uint64_t cmp) +sys_tick_rearm(void *unused, uint64_t nsecs) { - uint64_t now, off = 8; + uint64_t cmp, s; + uint32_t cycles; + + if (nsecs > sys_tick_nsec_max) + nsecs = sys_tick_nsec_max; + cycles = (nsecs * sys_tick_nsec_cycle_ratio) >> 32; + s = intr_disable(); + cmp = sys_tick() + cycles; sys_tickcmpr_set(cmp); - now = sys_tick(); - while (cmp <= now) { - cmp += off; - sys_tickcmpr_set(cmp); - now = sys_tick(); - off *= 2; - } + if (sys_tick() <= cmp) + sparc64_raise_clockintr(); + intr_restore(s); } void -stick_start(void) +sys_tick_trigger(void *unused) { - struct cpu_info *ci = curcpu(); - u_int64_t s; + sparc64_raise_clockintr(); +} +void +stick_start(void) +{ tick_enable(); - s = intr_disable(); - ci->ci_tick = stick(); - stick_rearm(ci->ci_tick); - intr_restore(s); + clockintr_cpu_init(&stick_intrclock); + clockintr_trigger(); } void -stick_rearm(uint64_t cmp) +stick_rearm(void *unused, uint64_t nsecs) { - uint64_t now, off = 8; + uint64_t cmp, s; + uint32_t cycles; + if (nsecs > sys_tick_nsec_max) + nsecs = sys_tick_nsec_max; + cycles = (nsecs * sys_tick_nsec_cycle_ratio) >> 32; + + s = intr_disable(); + cmp = stick() + cycles; stickcmpr_set(cmp); - now = stick(); - while (cmp <= now) { - cmp += off; - stickcmpr_set(cmp); - now = stick(); - off *= 2; - } + if (stick() <= cmp) + sparc64_raise_clockintr(); + intr_restore(s); +} + +void +stick_trigger(void *unused) +{ + sparc64_raise_clockintr(); } u_int