On Mon, Nov 07, 2022 at 04:12:06PM +0000, Miod Vallat wrote:
> > This patch switches sparc64 to clockintr(9).
>
> [...]
>
> > Testing on the UltraSPARC IIe ("Hummingbird") would also be helpful.
> > Apparently it has %SYS_TICK and %SYS_TICK_COMPARE, but in an unusual
> > hardware configuration. I imagine this machine is a bit rare, though.
>
> All Sun Blade 100 and 150 are IIe, so they're actually quite common.
Oh good.
> Fixed diff which compiles below.
Whoops, thanks.
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 7 Nov 2022 16:45:24 -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 7 Nov 2022 16:45:25 -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.71
diff -u -p -r1.71 clock.c
--- sys/arch/sparc64/sparc64/clock.c 24 Oct 2021 17:05:04 -0000 1.71
+++ sys/arch/sparc64/sparc64/clock.c 7 Nov 2022 16:45:25 -0000
@@ -65,6 +65,7 @@
#include <sys/param.h>
#include <sys/kernel.h>
+#include <sys/clockintr.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
@@ -74,6 +75,7 @@
#include <sys/gmon.h>
#endif
#include <sys/sched.h>
+#include <sys/stdint.h>
#include <sys/timetc.h>
#include <sys/atomic.h>
@@ -132,19 +134,35 @@ 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 */
+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;
-static long tick_increment;
+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 tick_start(void);
void sys_tick_start(void);
@@ -153,12 +171,8 @@ void stick_start(void);
int tickintr(void *);
int sys_tickintr(void *);
int stickintr(void *);
-int schedintr(void *);
-static struct intrhand level10 = { clockintr };
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)
@@ -473,21 +487,7 @@ timerattach(struct device *parent, struc
timerreg_4u.t_clrintr = (int64_t *)(u_long)va[1];
timerreg_4u.t_mapintr = (int64_t *)(u_long)va[2];
- /* Install the appropriate interrupt vector here */
- level10.ih_number = INTVEC(ma->ma_interrupts[0]);
- level10.ih_clr = (void *)&timerreg_4u.t_clrintr[0];
- level10.ih_map = (void *)&timerreg_4u.t_mapintr[0];
- strlcpy(level10.ih_name, "clock", sizeof(level10.ih_name));
- intr_establish(10, &level10);
-
- level14.ih_number = INTVEC(ma->ma_interrupts[1]);
- level14.ih_clr = (void *)&timerreg_4u.t_clrintr[1];
- level14.ih_map = (void *)&timerreg_4u.t_mapintr[1];
- strlcpy(level14.ih_name, "prof", sizeof(level14.ih_name));
- intr_establish(14, &level14);
-
- printf(" ivec 0x%llx, 0x%llx\n", INTVEC(level10.ih_number),
- INTVEC(level14.ih_number));
+ printf(" ivec (none)\n");
}
void
@@ -538,7 +538,7 @@ myetheraddr(u_char *cp)
void
cpu_initclocks(void)
{
- int statint, minint;
+ struct cpu_info *ci;
#ifdef DEBUG
extern int intrdebug;
#endif
@@ -562,6 +562,14 @@ cpu_initclocks(void)
tick_nsec = 1000000000 / hz;
}
+ if (stathz == 0)
+ stathz = hz;
+ if (1000000 % stathz) {
+ printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
+ stathz = 100;
+ }
+ profhz = stathz * 10;
+
/* Make sure we have a sane cpu_clockrate -- we'll need it */
if (!cpu_clockrate)
/* Default to 200MHz clock XXXXX */
@@ -570,6 +578,9 @@ cpu_initclocks(void)
tick_timecounter.tc_frequency = cpu_clockrate;
tc_init(&tick_timecounter);
+ tick_nsec_cycle_ratio = cpu_clockrate * (1ULL << 32) / 1000000000;
+ tick_nsec_max = UINT64_MAX / tick_nsec_cycle_ratio;
+
/*
* UltraSPARC IIe processors do have a STICK register, but it
* lives on the PCI host bridge and isn't accessible through
@@ -582,159 +593,49 @@ cpu_initclocks(void)
if (sys_tick_rate > 0 && impl != IMPL_HUMMINGBIRD) {
sys_tick_timecounter.tc_frequency = sys_tick_rate;
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);
-
- /* 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;
- }
-
- for (ci = cpus; ci != NULL; ci = ci->ci_next)
- memcpy(&ci->ci_tickintr, &level0, sizeof(level0));
-
- cpu_start_clock();
-
- return;
+ sys_tick_nsec_cycle_ratio =
+ sys_tick_rate * (1ULL << 32) / 1000000000;
+ sys_tick_nsec_max = UINT64_MAX / sys_tick_nsec_cycle_ratio;
}
- if (stathz == 0)
- stathz = hz;
- if (1000000 % stathz) {
- printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
- stathz = 100;
- }
+ clockintr_init(CL_RNDSTAT);
- profhz = stathz; /* always */
-
- statint = 1000000 / stathz;
- minint = statint / 2 + 100;
- while (statvar > minint)
- statvar >>= 1;
+ 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);
+
+ if (sys_tick_rate > 0) {
+ 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 {
+ level0.ih_fun = 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 */
-}
-
-/*
- * 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);
+ clockintr_setstatclockrate(newhz);
}
/*
@@ -748,180 +649,58 @@ clockintr(void *cap)
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);
- atomic_add_long((unsigned long *)&level0.ih_count.ec_count, 1);
- }
-
- /* Reset the interrupt. */
- s = intr_disable();
- tickcmpr_set(ci->ci_tick);
- intr_restore(s);
-
+ clockintr_dispatch(cap);
+ atomic_inc_long((unsigned long *)&level0.ih_count.ec_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);
- atomic_add_long((unsigned long *)&level0.ih_count.ec_count, 1);
- }
-
- /* Reset the interrupt. */
- s = intr_disable();
- sys_tickcmpr_set(ci->ci_tick);
- intr_restore(s);
-
+ clockintr_dispatch(cap);
+ atomic_inc_long((unsigned long *)&level0.ih_count.ec_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);
- atomic_add_long((unsigned long *)&level0.ih_count.ec_count, 1);
- }
-
- /* Reset the interrupt. */
- s = intr_disable();
- stickcmpr_set(ci->ci_tick);
- intr_restore(s);
-
- return (1);
-}
-
-/*
- * Level 14 (stat clock) interrupt handler.
- */
-int
-statintr(void *cap)
-{
- 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);
-}
-
-int
-schedintr(void *arg)
-{
- if (curproc)
- schedclock(curproc);
+ clockintr_dispatch(cap);
+ atomic_inc_long((unsigned long *)&level0.ih_count.ec_count);
return (1);
}
void
tick_start(void)
{
- struct cpu_info *ci = curcpu();
- u_int64_t s;
+ clockintr_cpu_init(&tick_intrclock);
tick_enable();
- /*
- * Try to make the tick interrupts as synchronously as possible on
- * all CPUs to avoid inaccuracies for migrating processes.
- */
-
- s = intr_disable();
- ci->ci_tick = roundup(tick(), tick_increment);
- tickcmpr_set(ci->ci_tick);
- intr_restore(s);
+ clockintr_trigger();
}
void
sys_tick_start(void)
{
- struct cpu_info *ci = curcpu();
- u_int64_t s;
+ clockintr_cpu_init(&sys_tick_intrclock);
if (CPU_ISSUN4U || CPU_ISSUN4US) {
tick_enable();
sys_tick_enable();
}
- /*
- * Try to make the tick interrupts as synchronously as possible on
- * all CPUs to avoid inaccuracies for migrating processes.
- */
-
- s = intr_disable();
- ci->ci_tick = roundup(sys_tick(), tick_increment);
- sys_tickcmpr_set(ci->ci_tick);
- intr_restore(s);
+ clockintr_trigger();
}
void
stick_start(void)
{
- struct cpu_info *ci = curcpu();
- u_int64_t s;
+ clockintr_cpu_init(&stick_intrclock);
tick_enable();
- /*
- * Try to make the tick interrupts as synchronously as possible on
- * all CPUs to avoid inaccuracies for migrating processes.
- */
-
- s = intr_disable();
- ci->ci_tick = roundup(stick(), tick_increment);
- stickcmpr_set(ci->ci_tick);
- intr_restore(s);
+ clockintr_trigger();
}
u_int
@@ -942,4 +721,76 @@ sys_tick_get_timecount(struct timecounte
__asm volatile("rd %%sys_tick, %0" : "=r" (tick));
return (tick & ~0u);
+}
+
+void
+tick_rearm(void *unused, uint64_t nsecs)
+{
+ uint64_t s;
+ uint32_t cycles;
+
+ if (nsecs > tick_nsec_max)
+ nsecs = tick_nsec_max;
+ cycles = (nsecs * tick_nsec_cycle_ratio) >> 32;
+ s = intr_disable();
+ tickcmpr_set(tick() + cycles);
+ intr_restore(s);
+}
+
+void
+tick_trigger(void *unused)
+{
+ uint64_t s;
+
+ s = intr_disable();
+ tickcmpr_set(tick());
+ intr_restore(s);
+}
+
+void
+sys_tick_rearm(void *unused, uint64_t nsecs)
+{
+ uint64_t 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();
+ sys_tickcmpr_set(sys_tick() + cycles);
+ intr_restore(s);
+}
+
+void
+sys_tick_trigger(void *unused)
+{
+ uint64_t s;
+
+ s = intr_disable();
+ sys_tickcmpr_set(sys_tick());
+ intr_restore(s);
+}
+
+void
+stick_rearm(void *unused, uint64_t nsecs)
+{
+ uint64_t 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();
+ stickcmpr_set(stick() + cycles);
+ intr_restore(s);
+}
+
+void
+stick_trigger(void *unused)
+{
+ uint64_t s;
+
+ s = intr_disable();
+ stickcmpr_set(stick());
+ intr_restore(s);
}