From owner-svn-src-all@FreeBSD.ORG Thu Mar 19 12:55:12 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 450D01065670; Thu, 19 Mar 2009 12:55:12 +0000 (UTC) (envelope-from marius@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 2F9F28FC08; Thu, 19 Mar 2009 12:55:12 +0000 (UTC) (envelope-from marius@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n2JCtC46061579; Thu, 19 Mar 2009 12:55:12 GMT (envelope-from marius@svn.freebsd.org) Received: (from marius@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n2JCtB7p061565; Thu, 19 Mar 2009 12:55:11 GMT (envelope-from marius@svn.freebsd.org) Message-Id: <200903191255.n2JCtB7p061565@svn.freebsd.org> From: Marius Strobl Date: Thu, 19 Mar 2009 12:55:11 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org X-SVN-Group: stable-7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r190033 - in stable/7/sys: . contrib/pf dev/ath/ath_hal dev/cxgb sparc64/include sparc64/sparc64 X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 19 Mar 2009 12:55:13 -0000 Author: marius Date: Thu Mar 19 12:55:11 2009 New Revision: 190033 URL: http://svn.freebsd.org/changeset/base/190033 Log: MFC: r182730, r182743, r183201 - USIII-based machines can consist of CPUs running at different frequencies (and having different cache sizes) so use the STICK (System TICK) timer, which was introduced due to this and is driven by the same frequency across all CPUs, instead of the TICK timer, whose frequency varies with the CPU clock, to drive hardclock. We use the STICK timers only when absolutely necessary though as the STICK timers are driven at frequencies as low as 5MHz, resulting in bad granularity compared to the TICK timers. However, don't employ the workaround for the BlackBird erratum #1 when using the TICK timer on machines with cheetah-class CPUs for performance reasons. Note that using the STICK counter also causes a hang with USIIIi MP machines for reasons unknown, but these can only consist of identical CPUs anyway. - Given that we only (try to) synchronize the (S)TICK timers of APs with the BSP during startup, we could end up spinning forever in DELAY(9) if that function is migrated to another CPU while we're spinning due to clock drift afterwards, so pin to the CPU in order to avoid migration. Unfortunately, pinning doesn't work at the point DELAY(9) is required by the low-level console drivers, yet, so switch to a function pointer, which is updated accordingly, for implementing DELAY(9). For USIII and beyond, this would also allow to easily use the STICK counter instead of the TICK one here, there's no benefit in doing so however. While at it, use cpu_spinwait(9) for spinning in the delay- functions. This currently is a NOP though. - Don't set the TICK timer of the BSP to 0 during startup as there's no need to do so. - Implement cpu_est_clockrate(). - Unfortunately, USIIIi-based machines don't provide a timecounter device besides the STICK and TICK counters (well, in theory the Tomatillo bridges have a performance counter that can be (ab)used as timecounter by configuring it to count bus cycles, though unlike the performance counter of Schizo bridges, the Tomatillo one is broken and counts Sun knows what in this mode). This means that we've to use a (S)TICK counter for timecounting, which has the old problem of not being in sync across CPUs, so provide an additional timecounter function which binds itself to the BSP but has an adequate low priority. Modified: stable/7/sys/ (props changed) stable/7/sys/contrib/pf/ (props changed) stable/7/sys/dev/ath/ath_hal/ (props changed) stable/7/sys/dev/cxgb/ (props changed) stable/7/sys/sparc64/include/clock.h stable/7/sys/sparc64/include/cpufunc.h stable/7/sys/sparc64/include/pcpu.h stable/7/sys/sparc64/include/smp.h stable/7/sys/sparc64/include/tick.h stable/7/sys/sparc64/include/ver.h stable/7/sys/sparc64/sparc64/clock.c stable/7/sys/sparc64/sparc64/exception.S stable/7/sys/sparc64/sparc64/genassym.c stable/7/sys/sparc64/sparc64/locore.S stable/7/sys/sparc64/sparc64/machdep.c stable/7/sys/sparc64/sparc64/mp_locore.S stable/7/sys/sparc64/sparc64/mp_machdep.c stable/7/sys/sparc64/sparc64/tick.c Modified: stable/7/sys/sparc64/include/clock.h ============================================================================== --- stable/7/sys/sparc64/include/clock.h Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/include/clock.h Thu Mar 19 12:55:11 2009 (r190033) @@ -29,10 +29,12 @@ #ifndef _MACHINE_CLOCK_H_ #define _MACHINE_CLOCK_H_ -extern u_long tick_increment; -extern u_long tick_freq; -extern u_long tick_MHz; +extern void (*delay_func)(int usec); +extern u_long clock_boot; -int sysbeep(int, int); +void delay_boot(int usec); +void delay_tick(int usec); + +int sysbeep(int pitch, int period); #endif /* !_MACHINE_CLOCK_H_ */ Modified: stable/7/sys/sparc64/include/cpufunc.h ============================================================================== --- stable/7/sys/sparc64/include/cpufunc.h Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/include/cpufunc.h Thu Mar 19 12:55:11 2009 (r190033) @@ -174,6 +174,15 @@ int fasword32(u_long asi, void *addr, ui } while (0) /* + * Trick GAS/GCC into compiling access to STICK/STICK_COMPARE independently + * of the selected instruction set. + */ +#define rdstick() rd(asr24) +#define rdstickcmpr() rd(asr25) +#define wrstick(val, xor) wr(asr24, (val), (xor)) +#define wrstickcmpr(val, xor) wr(asr25, (val), (xor)) + +/* * Macro intended to be used instead of wr(asr23, val, xor) for writing to * the TICK_COMPARE register in order to avoid a bug in BlackBird CPUs that * can cause these writes to fail under certain condidtions which in turn Modified: stable/7/sys/sparc64/include/pcpu.h ============================================================================== --- stable/7/sys/sparc64/include/pcpu.h Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/include/pcpu.h Thu Mar 19 12:55:11 2009 (r190033) @@ -53,6 +53,7 @@ struct pmap; vm_offset_t pc_addr; \ u_long pc_tickref; \ u_long pc_tickadj; \ + u_int pc_clock; \ u_int pc_mid; \ u_int pc_node; \ u_int pc_tlb_ctx; \ Modified: stable/7/sys/sparc64/include/smp.h ============================================================================== --- stable/7/sys/sparc64/include/smp.h Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/include/smp.h Thu Mar 19 12:55:11 2009 (r190033) @@ -29,9 +29,10 @@ #ifndef _MACHINE_SMP_H_ #define _MACHINE_SMP_H_ -#define CPU_CLKSYNC 1 -#define CPU_INIT 2 -#define CPU_BOOTSTRAP 3 +#define CPU_TICKSYNC 1 +#define CPU_STICKSYNC 2 +#define CPU_INIT 3 +#define CPU_BOOTSTRAP 4 #ifndef LOCORE @@ -62,6 +63,7 @@ struct cpu_start_args { u_int csa_state; vm_offset_t csa_pcpu; u_long csa_tick; + u_long csa_stick; u_long csa_ver; struct tte csa_ttes[PCPU_PAGES]; }; Modified: stable/7/sys/sparc64/include/tick.h ============================================================================== --- stable/7/sys/sparc64/include/tick.h Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/include/tick.h Thu Mar 19 12:55:11 2009 (r190033) @@ -29,7 +29,9 @@ #ifndef _MACHINE_TICK_H_ #define _MACHINE_TICK_H_ -void tick_init(u_long clock); +extern u_int hardclock_use_stick; + +void tick_clear(void); void tick_start(void); void tick_stop(void); Modified: stable/7/sys/sparc64/include/ver.h ============================================================================== --- stable/7/sys/sparc64/include/ver.h Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/include/ver.h Thu Mar 19 12:55:11 2009 (r190033) @@ -41,6 +41,8 @@ #define VER_MAXTL_SIZE (8) #define VER_MAXWIN_SIZE (5) +#ifndef LOCORE + #define VER_MANUF_MASK (((1L< #include +#include +#include +#include + #include +#include +#include + +void (*delay_func)(int usec); +u_long clock_boot; + +void +DELAY(int usec) +{ -u_long tick_increment; -u_long tick_freq; -u_long tick_MHz; + (*delay_func)(usec); +} void -DELAY(int n) +delay_boot(int usec) { - u_long start, end; + u_long end; - start = rd(tick); - if (n < 0) + if (usec < 0) return; - end = start + (u_long)n * tick_MHz; + + end = rd(tick) + (u_long)usec * clock_boot / 1000000; while (rd(tick) < end) - ; + cpu_spinwait(); +} + +void +delay_tick(int usec) +{ + u_long end; + + if (usec < 0) + return; + + /* + * We avoid being migrated to another CPU with a possibly + * unsynchronized TICK timer while spinning. + */ + sched_pin(); + + end = rd(tick) + (u_long)usec * PCPU_GET(clock) / 1000000; + while (rd(tick) < end) + cpu_spinwait(); + + sched_unpin(); } void Modified: stable/7/sys/sparc64/sparc64/exception.S ============================================================================== --- stable/7/sys/sparc64/sparc64/exception.S Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/sparc64/exception.S Thu Mar 19 12:55:11 2009 (r190033) @@ -550,7 +550,7 @@ END(tl0_sfsr_trap) tl ## traplvl ## _intr level, 1 << level #define TICK(traplvl) \ - tl ## traplvl ## _intr PIL_TICK, 1 + tl ## traplvl ## _intr PIL_TICK, 0x10001 #define INTR_LEVEL(tl) \ INTR(1, tl) ; \ Modified: stable/7/sys/sparc64/sparc64/genassym.c ============================================================================== --- stable/7/sys/sparc64/sparc64/genassym.c Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/sparc64/genassym.c Thu Mar 19 12:55:11 2009 (r190033) @@ -83,10 +83,11 @@ ASSYM(PAGE_SIZE_4M, PAGE_SIZE_4M); ASSYM(CSA_PCPU, offsetof(struct cpu_start_args, csa_pcpu)); ASSYM(CSA_STATE, offsetof(struct cpu_start_args, csa_state)); #ifdef SUN4U -ASSYM(CSA_TICK, offsetof(struct cpu_start_args, csa_tick)); -ASSYM(CSA_VER, offsetof(struct cpu_start_args, csa_ver)); ASSYM(CSA_MID, offsetof(struct cpu_start_args, csa_mid)); +ASSYM(CSA_STICK, offsetof(struct cpu_start_args, csa_stick)); +ASSYM(CSA_TICK, offsetof(struct cpu_start_args, csa_tick)); ASSYM(CSA_TTES, offsetof(struct cpu_start_args, csa_ttes)); +ASSYM(CSA_VER, offsetof(struct cpu_start_args, csa_ver)); #endif #ifdef SUN4V ASSYM(CSA_CPUID, offsetof(struct cpu_start_args, csa_cpuid)); Modified: stable/7/sys/sparc64/sparc64/locore.S ============================================================================== --- stable/7/sys/sparc64/sparc64/locore.S Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/sparc64/locore.S Thu Mar 19 12:55:11 2009 (r190033) @@ -54,7 +54,6 @@ ENTRY(_start) wrpr %g0, 0, %cleanwin wrpr %g0, 0, %pil wr %g0, 0, %fprs - wrpr %g0, 0, %tick /* * Get onto our per-CPU panic stack, which precedes the struct pcpu in Modified: stable/7/sys/sparc64/sparc64/machdep.c ============================================================================== --- stable/7/sys/sparc64/sparc64/machdep.c Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/sparc64/machdep.c Thu Mar 19 12:55:11 2009 (r190033) @@ -133,18 +133,6 @@ struct kva_md_info kmi; u_long ofw_vec; u_long ofw_tba; -/* - * Note: timer quality for CPU's is set low to try and prevent them from - * being chosen as the primary timecounter. The CPU counters are not - * synchronized among the CPU's so in MP machines this causes problems - * when calculating the time. With this value the CPU's should only be - * chosen as the primary timecounter as a last resort. - */ - -#define UP_TICK_QUALITY 1000 -#define MP_TICK_QUALITY -100 -static struct timecounter tick_tc; - char sparc64_model[32]; static int cpu_use_vis = 1; @@ -152,7 +140,6 @@ static int cpu_use_vis = 1; cpu_block_copy_t *cpu_block_copy; cpu_block_zero_t *cpu_block_zero; -static timecounter_get_t tick_get_timecount; void sparc64_init(caddr_t mdp, u_long o1, u_long o2, u_long o3, ofw_vec_t *vec); void sparc64_shutdown_final(void *dummy, int howto); @@ -180,22 +167,6 @@ cpu_startup(void *arg) vm_paddr_t physsz; int i; - tick_tc.tc_get_timecount = tick_get_timecount; - tick_tc.tc_poll_pps = NULL; - tick_tc.tc_counter_mask = ~0u; - tick_tc.tc_frequency = tick_freq; - tick_tc.tc_name = "tick"; - tick_tc.tc_quality = UP_TICK_QUALITY; -#ifdef SMP - /* - * We do not know if each CPU's tick counter is synchronized. - */ - if (cpu_mp_probe()) - tick_tc.tc_quality = MP_TICK_QUALITY; -#endif - - tc_init(&tick_tc); - physsz = 0; for (i = 0; i < sparc64_nmemreg; i++) physsz += sparc64_memreg[i].mr_size; @@ -217,7 +188,7 @@ cpu_startup(void *arg) if (bootverbose) printf("machine: %s\n", sparc64_model); - cpu_identify(rdpr(ver), tick_freq, PCPU_GET(cpuid)); + cpu_identify(rdpr(ver), PCPU_GET(clock), curcpu); } void @@ -262,12 +233,6 @@ spinlock_exit(void) wrpr(pil, td->td_md.md_saved_pil, 0); } -unsigned -tick_get_timecount(struct timecounter *tc) -{ - return ((unsigned)rd(tick)); -} - void sparc64_init(caddr_t mdp, u_long o1, u_long o2, u_long o3, ofw_vec_t *vec) { @@ -278,7 +243,6 @@ sparc64_init(caddr_t mdp, u_long o1, u_l caddr_t kmdp; phandle_t child; phandle_t root; - u_int clock; uint32_t portid; end = 0; @@ -291,6 +255,21 @@ sparc64_init(caddr_t mdp, u_long o1, u_l cpu_impl = VER_IMPL(rdpr(ver)); /* + * Clear (S)TICK timer (including NPT). + */ + tick_clear(); + + /* + * UltraSparc II[e,i] based systems come up with the tick interrupt + * enabled and a handler that resets the tick counter, causing DELAY() + * to not work properly when used early in boot. + * UltraSPARC III based systems come up with the system tick interrupt + * enabled, causing an interrupt storm on startup since they are not + * handled. + */ + tick_stop(); + + /* * Initialize Open Firmware (needed for console). */ OF_init(vec); @@ -329,7 +308,7 @@ sparc64_init(caddr_t mdp, u_long o1, u_l pc->pc_tlb_ctx_max = TLB_CTX_USER_MAX; /* - * Determine the OFW node (and ensure the + * Determine the OFW node and frequency of the BSP (and ensure the * BSP is in the device tree in the first place). */ pc->pc_node = 0; @@ -349,17 +328,23 @@ sparc64_init(caddr_t mdp, u_long o1, u_l } if (pc->pc_node == 0) OF_exit(); + if (OF_getprop(child, "clock-frequency", &pc->pc_clock, + sizeof(pc->pc_clock)) <= 0) + OF_exit(); /* - * Initialize the tick counter. Must be before the console is inited - * in order to provide the low-level console drivers with a working - * DELAY(). + * Provide a DELAY() that works before PCPU_REG is set. We can't + * set PCPU_REG without also taking over the trap table or the + * firmware will overwrite it. Unfortunately, it's way to early + * to also take over the trap table at this point. */ - OF_getprop(child, "clock-frequency", &clock, sizeof(clock)); - tick_init(clock); + clock_boot = pc->pc_clock; + delay_func = delay_boot; /* * Initialize the console before printing anything. + * NB: the low-level console drivers require a working DELAY() at + * this point. */ cninit(); @@ -445,6 +430,11 @@ sparc64_init(caddr_t mdp, u_long o1, u_l cpu_setregs(pc); /* + * It's now safe to use the real DELAY(). + */ + delay_func = delay_tick; + + /* * Initialize the message buffer (after setting trap table). */ msgbufinit(msgbufp, MSGBUF_SIZE); @@ -720,8 +710,13 @@ cpu_shutdown(void *args) int cpu_est_clockrate(int cpu_id, uint64_t *rate) { + struct pcpu *pc; - return (ENXIO); + pc = pcpu_find(cpu_id); + if (pc == NULL || rate == NULL) + return (EINVAL); + *rate = pc->pc_clock; + return (0); } /* Modified: stable/7/sys/sparc64/sparc64/mp_locore.S ============================================================================== --- stable/7/sys/sparc64/sparc64/mp_locore.S Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/sparc64/mp_locore.S Thu Mar 19 12:55:11 2009 (r190033) @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include "assym.s" @@ -92,7 +93,7 @@ ENTRY(mp_startup) SET(cpu_start_args, %l1, %l0) - mov CPU_CLKSYNC, %l1 + mov CPU_TICKSYNC, %l1 membar #StoreLoad stw %l1, [%l0 + CSA_STATE] @@ -101,7 +102,25 @@ ENTRY(mp_startup) nop wrpr %l1, 0, %tick - UPA_GET_MID(%o0) + rdpr %ver, %l1 + stx %l1, [%l0 + CSA_VER] + + srlx %l1, VER_IMPL_SHIFT, %l1 + sll %l1, VER_IMPL_SIZE, %l1 + srl %l1, VER_IMPL_SIZE, %l1 + cmp %l1, CPU_IMPL_ULTRASPARCIII + bl %icc, 3f + nop + mov CPU_STICKSYNC, %l1 + membar #StoreLoad + stw %l1, [%l0 + CSA_STATE] + +2: ldx [%l0 + CSA_STICK], %l1 + brz %l1, 2b + nop + wr %l1, 0, %asr24 + +3: UPA_GET_MID(%o0) #if KTR_COMPILE & KTR_SMP CATR(KTR_SMP, "mp_start: CPU %d entered kernel" @@ -110,9 +129,6 @@ ENTRY(mp_startup) 9: #endif - rdpr %ver, %l1 - stx %l1, [%l0 + CSA_VER] - /* * Inform the boot processor we have inited. */ @@ -123,9 +139,9 @@ ENTRY(mp_startup) /* * Wait till its our turn to bootstrap. */ -2: lduw [%l0 + CSA_MID], %l1 +4: lduw [%l0 + CSA_MID], %l1 cmp %l1, %o0 - bne %xcc, 2b + bne %xcc, 4b nop #if KTR_COMPILE & KTR_SMP @@ -141,7 +157,7 @@ ENTRY(mp_startup) /* * Map the per-CPU pages. */ -3: sllx %l2, TTE_SHIFT, %l3 +5: sllx %l2, TTE_SHIFT, %l3 add %l1, %l3, %l3 ldx [%l3 + TTE_VPN], %l4 @@ -156,7 +172,7 @@ ENTRY(mp_startup) add %l2, 1, %l2 cmp %l2, PCPU_PAGES - bne %xcc, 3b + bne %xcc, 5b nop /* Modified: stable/7/sys/sparc64/sparc64/mp_machdep.c ============================================================================== --- stable/7/sys/sparc64/sparc64/mp_machdep.c Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/sparc64/mp_machdep.c Thu Mar 19 12:55:11 2009 (r190033) @@ -81,6 +81,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -101,7 +102,7 @@ static ih_func_t cpu_ipi_stop; * since the other processors will use it before the boot CPU enters the * kernel. */ -struct cpu_start_args cpu_start_args = { 0, -1, -1, 0, 0 }; +struct cpu_start_args cpu_start_args = { 0, -1, -1, 0, 0, 0 }; struct ipi_cache_args ipi_cache_args; struct ipi_tlb_args ipi_tlb_args; struct pcb stoppcbs[MAXCPU]; @@ -270,17 +271,25 @@ cpu_mp_start(void) if (OF_getprop(child, "clock-frequency", &clock, sizeof(clock)) <= 0) panic("%s: can't get clock", __func__); + if (clock != PCPU_GET(clock)) + hardclock_use_stick = 1; csa->csa_state = 0; sun4u_startcpu(child, (void *)mp_tramp, 0); s = intr_disable(); - while (csa->csa_state != CPU_CLKSYNC) + while (csa->csa_state != CPU_TICKSYNC) ; membar(StoreLoad); csa->csa_tick = rd(tick); + if (cpu_impl >= CPU_IMPL_ULTRASPARCIII) { + while (csa->csa_state != CPU_STICKSYNC) + ; + membar(StoreLoad); + csa->csa_stick = rdstick(); + } while (csa->csa_state != CPU_INIT) ; - csa->csa_tick = 0; + csa->csa_tick = csa->csa_stick = 0; intr_restore(s); cpuid = mp_ncpus++; @@ -291,6 +300,7 @@ cpu_mp_start(void) pc = (struct pcpu *)(va + (PCPU_PAGES * PAGE_SIZE)) - 1; pcpu_init(pc, cpuid, sizeof(*pc)); pc->pc_addr = va; + pc->pc_clock = clock; pc->pc_mid = mid; pc->pc_node = child; Modified: stable/7/sys/sparc64/sparc64/tick.c ============================================================================== --- stable/7/sys/sparc64/sparc64/tick.c Thu Mar 19 12:52:19 2009 (r190032) +++ stable/7/sys/sparc64/sparc64/tick.c Thu Mar 19 12:55:11 2009 (r190033) @@ -1,5 +1,6 @@ /*- * Copyright (c) 2001 Jake Burkholder. + * Copyright (c) 2005, 2008 Marius Strobl * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,22 +29,30 @@ __FBSDID("$FreeBSD$"); #include -#include #include -#include -#include +#include +#include +#include #include +#include +#include +#include #include #include -#include +#include + #include #include #include #include #include -#define TICK_GRACE 10000 +/* 10000 ticks proved okay for 500MHz. */ +#define TICK_GRACE(clock) ((clock) / 1000000 * 2 * 10) + +#define TICK_QUALITY_MP 10 +#define TICK_QUALITY_UP 1000 SYSCTL_NODE(_machdep, OID_AUTO, tick, CTLFLAG_RD, 0, "tick statistics"); @@ -63,7 +72,24 @@ static int adjust_ticks = 0; SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_ticks, CTLFLAG_RD, &adjust_ticks, 0, "total number of tick interrupts with adjustment"); +u_int hardclock_use_stick = 0; +SYSCTL_INT(_machdep_tick, OID_AUTO, hardclock_use_stick, CTLFLAG_RD, + &hardclock_use_stick, 0, "hardclock uses STICK instead of TICK timer"); + +static struct timecounter tick_tc; +static u_long tick_increment; + +static uint64_t tick_cputicks(void); +static timecounter_get_t tick_get_timecount_up; +#ifdef SMP +static timecounter_get_t tick_get_timecount_mp; +#endif static void tick_hardclock(struct trapframe *tf); +static void tick_hardclock_bbwar(struct trapframe *tf); +static inline void tick_hardclock_common(struct trapframe *tf, u_long tick, + u_long adj); +static inline void tick_process(struct trapframe *tf); +static void stick_hardclock(struct trapframe *tf); static uint64_t tick_cputicks(void) @@ -75,9 +101,65 @@ tick_cputicks(void) void cpu_initclocks(void) { + uint32_t clock; stathz = hz; + + /* + * Given that the STICK timers typically are driven at rather low + * frequencies they shouldn't be used except when really necessary. + */ + if (hardclock_use_stick != 0) { + if (OF_getprop(OF_parent(PCPU_GET(node)), "stick-frequency", + &clock, sizeof(clock)) == -1) + panic("%s: could not determine STICK frequency", __func__); + intr_setup(PIL_TICK, stick_hardclock, -1, NULL, NULL); + /* + * We don't provide a CPU ticker as long as the frequency + * supplied isn't actually used per-CPU. + */ + } else { + clock = PCPU_GET(clock); + intr_setup(PIL_TICK, cpu_impl < CPU_IMPL_ULTRASPARCIII ? + tick_hardclock_bbwar : tick_hardclock, -1, NULL, NULL); + set_cputicker(tick_cputicks, clock, 0); + } + tick_increment = clock / hz; + /* + * Avoid stopping of hardclock in terms of a lost (S)TICK interrupt + * by ensuring that the (S)TICK period is at least TICK_GRACE ticks. + */ + if (tick_increment < TICK_GRACE(clock)) + panic("%s: HZ too high, decrease to at least %d", + __func__, clock / TICK_GRACE(clock)); tick_start(); + + /* + * Initialize the TICK-based timecounter. This must not happen + * before SI_SUB_INTRINSIC for tick_get_timecount_mp() to work. + */ + tick_tc.tc_get_timecount = tick_get_timecount_up; + tick_tc.tc_poll_pps = NULL; + tick_tc.tc_counter_mask = ~0u; + tick_tc.tc_frequency = PCPU_GET(clock); + tick_tc.tc_name = "tick"; + tick_tc.tc_quality = TICK_QUALITY_UP; + tick_tc.tc_priv = NULL; +#ifdef SMP + /* + * We (try to) sync the (S)TICK timers of APs with the BSP during + * their startup but not afterwards. The resulting drift can + * cause problems when the time is calculated based on (S)TICK + * values read on different CPUs. Thus we bind to the BSP for + * reading the register and use a low quality for the otherwise + * high quality (S)TICK timers in the MP case. + */ + if (cpu_mp_probe()) { + tick_tc.tc_get_timecount = tick_get_timecount_mp; + tick_tc.tc_quality = TICK_QUALITY_MP; + } +#endif + tc_init(&tick_tc); } static inline void @@ -93,27 +175,68 @@ tick_process(struct trapframe *tf) statclock(TRAPF_USERMODE(tf)); } +/* + * NB: the sequence of reading the (S)TICK register, calculating the value + * of the next tick and writing it to the (S)TICK_COMPARE register must not + * be interrupted, not even by an IPI, otherwise a value that is in the past + * could be written in the worst case, causing hardclock to stop. + */ + static void tick_hardclock(struct trapframe *tf) { - u_long adj, ref, tick; - long delta; + u_long adj, tick; + register_t s; + + critical_enter(); + adj = PCPU_GET(tickadj); + s = intr_disable(); + tick = rd(tick); + wr(tick_cmpr, tick + tick_increment - adj, 0); + intr_restore(s); + tick_hardclock_common(tf, tick, adj); + critical_exit(); +} + +static void +tick_hardclock_bbwar(struct trapframe *tf) +{ + u_long adj, tick; register_t s; - int count; - /* - * The sequence of reading the TICK register, calculating the value - * of the next tick and writing it to the TICK_CMPR register must not - * be interrupted, not even by an IPI, otherwise a value that is in - * the past could be written in the worst case, causing hardclock to - * stop. - */ critical_enter(); adj = PCPU_GET(tickadj); s = intr_disable(); tick = rd(tick); wrtickcmpr(tick + tick_increment - adj, 0); intr_restore(s); + tick_hardclock_common(tf, tick, adj); + critical_exit(); +} + +static void +stick_hardclock(struct trapframe *tf) +{ + u_long adj, stick; + register_t s; + + critical_enter(); + adj = PCPU_GET(tickadj); + s = intr_disable(); + stick = rdstick(); + wrstickcmpr(stick + tick_increment - adj, 0); + intr_restore(s); + tick_hardclock_common(tf, stick, adj); + critical_exit(); +} + +static inline void +tick_hardclock_common(struct trapframe *tf, u_long tick, u_long adj) +{ + u_long ref; + long delta; + int count; + ref = PCPU_GET(tickref); delta = tick - ref; count = 0; @@ -139,29 +262,36 @@ tick_hardclock(struct trapframe *tf) } PCPU_SET(tickref, ref); PCPU_SET(tickadj, adj); - critical_exit(); } -void -tick_init(u_long clock) +static u_int +tick_get_timecount_up(struct timecounter *tc) { - tick_freq = clock; - tick_MHz = clock / 1000000; - tick_increment = clock / hz; + return ((u_int)rd(tick)); +} - /* - * UltraSparc II[e,i] based systems come up with the tick interrupt - * enabled and a handler that resets the tick counter, causing DELAY() - * to not work properly when used early in boot. - * UltraSPARC III based systems come up with the system tick interrupt - * enabled, causing an interrupt storm on startup since they are not - * handled. - */ - tick_stop(); +#ifdef SMP +static u_int +tick_get_timecount_mp(struct timecounter *tc) +{ + struct thread *td; + u_int tick; + + td = curthread; + thread_lock(td); + sched_bind(td, 0); + thread_unlock(td); + + tick = tick_get_timecount_up(tc); + + thread_lock(td); + sched_unbind(td); + thread_unlock(td); - set_cputicker(tick_cputicks, tick_freq, 0); + return (tick); } +#endif void tick_start(void) @@ -170,32 +300,34 @@ tick_start(void) register_t s; /* - * Avoid stopping of hardclock in terms of a lost tick interrupt - * by ensuring that the tick period is at least TICK_GRACE ticks. - * This check would be better placed in tick_init(), however we - * have to call tick_init() before cninit() in order to provide - * the low-level console drivers with a working DELAY() which in - * turn means we cannot use panic() in tick_init(). - */ - if (tick_increment < TICK_GRACE) - panic("%s: HZ too high, decrease to at least %ld", __func__, - tick_freq / TICK_GRACE); - - if (curcpu == 0) - intr_setup(PIL_TICK, tick_hardclock, -1, NULL, NULL); - - /* - * Try to make the tick interrupts as synchronously as possible on - * all CPUs to avoid inaccuracies for migrating processes. Leave out - * one tick to make sure that it is not missed. + * Try to make the (S)TICK interrupts as synchronously as possible + * on all CPUs to avoid inaccuracies for migrating processes. Leave + * out one tick to make sure that it is not missed. */ + critical_enter(); PCPU_SET(tickadj, 0); s = intr_disable(); - base = rd(tick); + if (hardclock_use_stick != 0) + base = rdstick(); + else + base = rd(tick); base = roundup(base, tick_increment); PCPU_SET(tickref, base); - wrtickcmpr(base + tick_increment, 0); + if (hardclock_use_stick != 0) + wrstickcmpr(base + tick_increment, 0); + else + wrtickcmpr(base + tick_increment, 0); intr_restore(s); + critical_exit(); +} + +void +tick_clear(void) +{ + + if (cpu_impl >= CPU_IMPL_ULTRASPARCIII) + wrstick(0, 0); + wrpr(tick, 0, 0); } void @@ -203,6 +335,6 @@ tick_stop(void) { if (cpu_impl >= CPU_IMPL_ULTRASPARCIII) - wr(asr25, 1L << 63, 0); + wrstickcmpr(1L << 63, 0); wrtickcmpr(1L << 63, 0); }