Subject: Re: separate statclock(9) with mips3 CP0 timer interrupt
To: Izumi Tsutsui <tsutsui@ceres.dti.ne.jp>
From: Garrett D'Amore <garrett_damore@tadpole.com>
List: port-cobalt
Date: 04/21/2006 23:52:31
Out of curiosity, why not use CPU INT5 (CP0 clock interrupt) for
hardclock?
I never even knew there was support for a separate statistics clock.
-- Garrett
Izumi Tsutsui wrote:
> Hi,
>
> Current several mips ports with MIP3 CPUs (evbmips, sgimips etc.)
> use CPU internal CP0 clock timer interrupts for hardclock(9),
> but some other ports (arc, cobalt etc) use their own external
> timer interrupts for hardclock(9) and CPU CP0 clock interrupt
> (i.e. CPU INT5) is not used.
>
> Is it worth to prepare a separate interrupt handler for
> statclock(9) with CP0 clock interrupt on such mips port?
> I guess it may provide more precise statistics on the system,
> but I'm afraid number of interrupts (and its overhead) is also
> increased ~two times.
>
> I've written some code for NetBSD/cobalt (derived from hp300 and
> arm/footbridge), but is there any good benchmark for this?
> ---
> Izumi Tsutsui
>
>
> Index: cobalt/autoconf.c
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/cobalt/autoconf.c,v
> retrieving revision 1.19
> diff -u -r1.19 autoconf.c
> --- cobalt/autoconf.c 21 Apr 2006 18:17:45 -0000 1.19
> +++ cobalt/autoconf.c 22 Apr 2006 01:21:48 -0000
> @@ -52,11 +52,13 @@
> (void)splhigh();
>
> evcnt_attach_static(&hardclock_ev);
> + evcnt_attach_static(&statclock_ev);
>
> if (config_rootfound("mainbus", NULL) == NULL)
> panic("no mainbus found");
>
> - _splnone();
> + /* turn on interrupts except cpu clock */
> + _spllower(MIPS_INT_MASK_5);
> }
>
> void
> Index: cobalt/clock.c
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/cobalt/clock.c,v
> retrieving revision 1.12
> diff -u -r1.12 clock.c
> --- cobalt/clock.c 21 Apr 2006 16:52:15 -0000 1.12
> +++ cobalt/clock.c 22 Apr 2006 01:21:48 -0000
> @@ -47,6 +47,20 @@
> void *timer_cookie;
>
> /*
> + * Statistics clock 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).
> + */
> +static const uint32_t statvar = 1024;
> +static uint32_t statint; /* number of clock ticks for stathz */
> +static uint32_t statmin; /* minimum stat clock count in ticks */
> +static uint32_t statprev;/* last value of we set statclock to */
> +static u_int statcountperusec; /* number of ticks per usec at current stathz */
> +
> +/*
> * Common parts of todclock autoconfiguration.
> */
> void
> @@ -63,12 +77,21 @@
> cpu_initclocks(void)
> {
>
> - /* start timer */
> + if (stathz == 0)
> + stathz = hz;
> +
> + if (profhz == 0)
> + profhz = hz * 5;
> +
> + setstatclockrate(stathz);
> +
> + /* start timer interrups for hardclock */
> if (timer_start == NULL)
> panic("cpu_initclocks(): no timer configured");
> (*timer_start)(timer_cookie);
>
> - return;
> + /* enable statclock intr (CPU INT5) */
> + _splnone();
> }
>
> /*
> @@ -139,11 +162,59 @@
> }
>
> void
> -setstatclockrate(int arg)
> +setstatclockrate(int newhz)
> {
> - /* XXX */
> + uint32_t countpersecond, statvarticks;
> +
> + statprev = mips3_cp0_count_read();
> +
> + statint = ((curcpu()->ci_cpu_freq + newhz / 2) / newhz) / 2;
> +
> + /* Get the total ticks a second */
> + countpersecond = statint * newhz;
> +
> + /* now work out how many ticks per usec */
> + statcountperusec = countpersecond / 1000000;
> +
> + /* calculate a variance range of statvar */
> + statvarticks = statcountperusec * statvar;
> +
> + /* minimum is statint - 50% of variant */
> + statmin = statint - (statvarticks / 2);
> +
> + mips3_cp0_compare_write(statprev + statint);
> +}
> +
> +void
> +statclockintr(struct clockframe *cfp)
> +{
> + uint32_t curcount, statnext, delta, r;
> + int lost;
> +
> + lost = 0;
> +
> + do {
> + r = (uint32_t)random() & (statvar - 1);
> + } while (r == 0);
> + statnext = statprev + statmin + (r * statcountperusec);
> +
> + mips3_cp0_compare_write(statnext);
> + curcount = mips3_cp0_count_read();
> + delta = statnext - curcount;
> +
> + while ((int32_t)delta < 0) {
> + lost++;
> + delta += statint;
> + }
> + if (lost > 0) {
> + statnext = curcount + delta;
> + mips3_cp0_compare_write(statnext);
> + for (; lost > 0; lost--)
> + statclock(cfp);
> + }
> + statclock(cfp);
>
> - return;
> + statprev = statnext;
> }
>
> void
> Index: cobalt/clockvar.h
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/cobalt/clockvar.h,v
> retrieving revision 1.3
> diff -u -r1.3 clockvar.h
> --- cobalt/clockvar.h 21 Apr 2006 18:17:45 -0000 1.3
> +++ cobalt/clockvar.h 22 Apr 2006 01:21:48 -0000
> @@ -25,7 +25,10 @@
> * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> */
>
> +void statclockintr(struct clockframe *);
> +
> extern struct evcnt hardclock_ev;
> +extern struct evcnt statclock_ev;
>
> extern void (*timer_start)(void *);
> extern long (*timer_read)(void *);
> Index: cobalt/machdep.c
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/cobalt/machdep.c,v
> retrieving revision 1.66
> diff -u -r1.66 machdep.c
> --- cobalt/machdep.c 21 Apr 2006 18:21:30 -0000 1.66
> +++ cobalt/machdep.c 22 Apr 2006 01:21:48 -0000
> @@ -110,6 +110,9 @@
>
> struct evcnt hardclock_ev =
> EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "cpu", "hardclock");
> +struct evcnt statclock_ev =
> + EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "cpu", "statclock");
> +
>
> u_int cobalt_id;
> static const char * const cobalt_model[] =
> @@ -451,11 +454,22 @@
> cpu_intr(uint32_t status, uint32_t cause, uint32_t pc, uint32_t ipending)
> {
> struct clockframe cf;
> - static uint32_t cycles;
> struct cobalt_intrhand *ih;
>
> uvmexp.intrs++;
>
> + if (ipending & MIPS_INT_MASK_5) {
> + /* statclock */
> + cf.pc = pc;
> + cf.sr = status;
> +
> + statclockintr(&cf);
> + statclock_ev.ev_count++;
> +
> + cause &= ~MIPS_INT_MASK_5;
> + }
> + _splset((status & MIPS_INT_MASK_5) | MIPS_SR_INT_IE);
> +
> if (ipending & MIPS_INT_MASK_0) {
> /* GT64x11 timer0 for hardclock */
> volatile uint32_t *irq_src =
> @@ -467,6 +481,29 @@
> cf.pc = pc;
> cf.sr = status;
>
> + if ((status & MIPS_INT_MASK) == MIPS_INT_MASK) {
> + if ((ipending & MIPS_INT_MASK &
> + ~MIPS_INT_MASK_0) == 0) {
> + /*
> + * If all interrupts were enabled and
> + * there is no pending interrupts,
> + * set MIPS_SR_INT_IE so that
> + * spllowerclock() in hardclock()
> + * works properly.
> + */
> +#if 0 /* MIPS_SR_INT_IE is enabled above */
> + _splset(MIPS_SR_INT_IE);
> +#endif
> + } else {
> + /*
> + * If there are any pending interrputs,
> + * clear MIPS_SR_INT_IE in cf.sr so that
> + * spllowerclock() in hardclock() will
> + * not happen.
> + */
> + cf.sr &= ~MIPS_SR_INT_IE;
> + }
> + }
> hardclock(&cf);
> hardclock_ev.ev_count++;
> }
> @@ -474,20 +511,6 @@
> }
> _splset((status & ~cause & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
>
> - if (ipending & MIPS_INT_MASK_5) {
> - cycles = mips3_cp0_count_read();
> - mips3_cp0_compare_write(cycles + 1250000); /* XXX */
> -
> -#if 0
> - cf.pc = pc;
> - cf.sr = status;
> -
> - statclock(&cf);
> -#endif
> - cause &= ~MIPS_INT_MASK_5;
> - }
> - _splset((status & ~cause & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
> -
> if (ipending & MIPS_INT_MASK_3) {
> /* 16650 serial */
> ih = &intrtab[3];
> Index: conf/std.cobalt
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/conf/std.cobalt,v
> retrieving revision 1.12
> diff -u -r1.12 std.cobalt
> --- conf/std.cobalt 11 Dec 2005 12:17:06 -0000 1.12
> +++ conf/std.cobalt 22 Apr 2006 01:21:48 -0000
> @@ -5,6 +5,7 @@
> makeoptions MACHINE_ARCH="mipsel"
>
> options MIPS3
> +options MIPS3_ENABLE_CLOCK_INTR
>
> options EXEC_ELF32 # exec ELF32 binaries
> options EXEC_SCRIPT # exec #! scripts
> Index: include/intr.h
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/include/intr.h,v
> retrieving revision 1.20
> diff -u -r1.20 intr.h
> --- include/intr.h 21 Apr 2006 18:17:45 -0000 1.20
> +++ include/intr.h 22 Apr 2006 01:21:48 -0000
> @@ -80,7 +80,8 @@
> #define SPLBIO (SPLSOFT | MIPS_INT_MASK_4)
> #define SPLNET (SPLBIO | MIPS_INT_MASK_1 | MIPS_INT_MASK_2)
> #define SPLTTY (SPLNET | MIPS_INT_MASK_3)
> -#define SPLCLOCK (SPLTTY | MIPS_INT_MASK_0 | MIPS_INT_MASK_5)
> +#define SPLCLOCK (SPLTTY | MIPS_INT_MASK_0)
> +#define SPLSTATCLOCK (SPLCLOCK | MIPS_INT_MASK_5)
> #define splbio() _splraise(SPLBIO)
> #define splnet() _splraise(SPLNET)
> #define spltty() _splraise(SPLTTY)
> @@ -88,7 +89,7 @@
> #define splserial() _splraise(SPLTTY)
> #define splclock() _splraise(SPLCLOCK)
> #define splvm() splclock()
> -#define splstatclock() splclock()
> +#define splstatclock() _splraise(SPLSTATCLOCK)
> #define spllowersoftclock() _spllower(MIPS_SOFT_INT_MASK_0)
>
> #define splsched() splhigh()
>
--
Garrett D'Amore, Principal Software Engineer
Tadpole Computer / Computing Technologies Division,
General Dynamics C4 Systems
http://www.tadpolecomputer.com/
Phone: 951 325-2134 Fax: 951 325-2191