- Add a workaround for a bug in BlackBird CPUs (said to be part of the
SpitFire erratum #54) which can cause writes to the TICK_CMPR register to fail. This seems to fix the dying clocks problem reported by jhb@ and kris@. [1] - In tick_start() don't reset the tick counter of the boot processor to zero. It's initially reset in _start() and afterwards but _before_ tick_start() is called on the BSP the APs synchronise with the tick counter of the BSP in mp_startup(). Resetting the tick counter of the BSP in tick_start() probably also was the cause of problems seen when using the CPU tick counter as timecounter on SMP machines. Not resetting the tick counter of the BSP in mp_startup() makes the tick counters and tick interrupts between the BSP and APs be pretty much in sync as it's supposed to be. This also means there's no longer a real reason to have separate tick_start() and tick_start_ap() so merge them and zap tick_start_ap(). This is also a first step in simplifying the interface to the tick counters in preparation to use alternate clock hardware where available. - Switch to the algorithm used on FreeBSD/ia64 for updating the tick interrupt register and which compensates the clock drift caused by varying delays between when the tick interrupts actually trigger and when they are serviced. Not compensating the clock drift mainly hurts interactive performance especially when using WITNESS. [2] For further information about the algorithm also see the commit log of sys/ia64/ia64/interrupt.c rev. 1.38. On sparc64 the sysctls for monitoring the behaviour of the tick interrupts are machdep.tick.adjust_edges, machdep.tick.adjust_excess, machdep.tick.adjust_missed and machdep.tick.adjust_ticks. - In tick_init() just use tick_stop() for stopping the tick interrupts until a proper handler is set up later. This also stops the system tick interrupt on USIII systems earlier. - In tick_start() check for a rough upper limit of HZ. - Some minor changes, e.g. use FBSDID, remove unused headers, etc. Info obtained from: Linux [1] Ok'ed by: marcel [2] Additional testing by: kris (earlier version of the workaround), jhb X-MFC after: 3 days [1]
This commit is contained in:
parent
c066bca62d
commit
7bed9b320b
@ -171,6 +171,23 @@ int fasword32(u_long asi, void *addr, uint32_t *val);
|
||||
: : "r" (val), "rI" (xor)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Macro intended to be used instead of wr(asr23, val, xor) for writing to
|
||||
* the TICK_CMPR register in order to avoid a bug in BlackBird CPUs that
|
||||
* can cause these writes to fail under certain condidtions which in turn
|
||||
* causes the hardclock to stop. The workaround is to perform the write
|
||||
* at the beginning of an I-Cache line directly followed by a dummy read.
|
||||
*/
|
||||
#define wrtickcmpr(val, xor) ({ \
|
||||
__asm __volatile( \
|
||||
" ba,pt %%xcc, 1f ; " \
|
||||
" nop ; " \
|
||||
" .align 64 ; " \
|
||||
"1: wr %0, %1, %%asr23 ; " \
|
||||
" rd %%asr23, %%g0 ; " \
|
||||
: : "r" (val), "rI" (xor)); \
|
||||
})
|
||||
|
||||
static __inline void
|
||||
breakpoint(void)
|
||||
{
|
||||
|
@ -51,6 +51,8 @@ struct pmap;
|
||||
struct intr_request *pc_irfree; \
|
||||
struct pmap *pc_pmap; \
|
||||
vm_offset_t pc_addr; \
|
||||
u_long pc_tickref; \
|
||||
u_long pc_tickadj; \
|
||||
u_int pc_mid; \
|
||||
u_int pc_node; \
|
||||
u_int pc_tlb_ctx; \
|
||||
|
@ -29,15 +29,8 @@
|
||||
#ifndef _MACHINE_TICK_H_
|
||||
#define _MACHINE_TICK_H_
|
||||
|
||||
typedef void tick_func_t(struct clockframe *);
|
||||
|
||||
void tick_init(u_long clock);
|
||||
void tick_start(tick_func_t *func);
|
||||
#ifdef SMP
|
||||
void tick_start_ap(void);
|
||||
#endif
|
||||
void tick_start(void);
|
||||
void tick_stop(void);
|
||||
|
||||
tick_func_t tick_hardclock;
|
||||
|
||||
#endif
|
||||
|
@ -51,10 +51,11 @@
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/lock.h>
|
||||
@ -342,7 +343,7 @@ cpu_mp_bootstrap(struct pcpu *pc)
|
||||
csa = &cpu_start_args;
|
||||
pmap_map_tsb();
|
||||
cpu_setregs(pc);
|
||||
tick_start_ap();
|
||||
tick_start();
|
||||
|
||||
smp_cpus++;
|
||||
KASSERT(curthread != NULL, ("cpu_mp_bootstrap: curthread"));
|
||||
|
@ -22,44 +22,54 @@
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/interrupt.h>
|
||||
#include <sys/pcpu.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/timetc.h>
|
||||
#ifdef SMP
|
||||
#include <sys/ktr.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/proc.h>
|
||||
#endif
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
|
||||
#include <machine/clock.h>
|
||||
#include <machine/frame.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/tick.h>
|
||||
#include <machine/ver.h>
|
||||
#ifdef SMP
|
||||
#include <machine/cpu.h>
|
||||
#endif
|
||||
|
||||
int tick_missed; /* statistics */
|
||||
|
||||
#define TICK_GRACE 10000
|
||||
|
||||
SYSCTL_NODE(_machdep, OID_AUTO, tick, CTLFLAG_RD, 0, "tick statistics");
|
||||
|
||||
static int adjust_edges = 0;
|
||||
SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_edges, CTLFLAG_RD, &adjust_edges,
|
||||
0, "total number of times tick interrupts got more than 12.5% behind");
|
||||
|
||||
static int adjust_excess = 0;
|
||||
SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_excess, CTLFLAG_RD, &adjust_excess,
|
||||
0, "total number of ignored tick interrupts");
|
||||
|
||||
static int adjust_missed = 0;
|
||||
SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_missed, CTLFLAG_RD, &adjust_missed,
|
||||
0, "total number of missed tick interrupts");
|
||||
|
||||
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");
|
||||
|
||||
static void tick_hardclock(struct clockframe *);
|
||||
|
||||
void
|
||||
cpu_initclocks(void)
|
||||
{
|
||||
|
||||
stathz = hz;
|
||||
tick_start(tick_hardclock);
|
||||
tick_start();
|
||||
}
|
||||
|
||||
static __inline void
|
||||
@ -75,72 +85,100 @@ tick_process(struct clockframe *cf)
|
||||
statclock(cf);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
tick_hardclock(struct clockframe *cf)
|
||||
{
|
||||
int missed;
|
||||
u_long next;
|
||||
register_t i;
|
||||
u_long adj, s, tick, ref;
|
||||
long delta;
|
||||
int count;
|
||||
|
||||
tick_process(cf);
|
||||
/*
|
||||
* Avoid stopping of hardclock in case we missed one tick period by
|
||||
* ensuring that the the value of the next tick is at least TICK_GRACE
|
||||
* ticks in the future.
|
||||
* Missed ticks need to be accounted for by repeatedly calling
|
||||
* hardclock.
|
||||
* 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.
|
||||
*/
|
||||
missed = 0;
|
||||
next = rd(asr23) + tick_increment;
|
||||
i = intr_disable();
|
||||
while (next < rd(tick) + TICK_GRACE) {
|
||||
next += tick_increment;
|
||||
missed++;
|
||||
}
|
||||
wr(asr23, next, 0);
|
||||
intr_restore(i);
|
||||
atomic_add_int(&tick_missed, missed);
|
||||
for (; missed > 0; missed--)
|
||||
adj = PCPU_GET(tickadj);
|
||||
s = intr_disable();
|
||||
tick = rd(tick);
|
||||
wrtickcmpr(tick + tick_increment - adj, 0);
|
||||
intr_restore(s);
|
||||
ref = PCPU_GET(tickref);
|
||||
delta = tick - ref;
|
||||
count = 0;
|
||||
while (delta >= tick_increment) {
|
||||
tick_process(cf);
|
||||
delta -= tick_increment;
|
||||
ref += tick_increment;
|
||||
if (adj != 0)
|
||||
adjust_ticks++;
|
||||
count++;
|
||||
}
|
||||
if (count > 0) {
|
||||
adjust_missed += count - 1;
|
||||
if (delta > (tick_increment >> 3)) {
|
||||
if (adj == 0)
|
||||
adjust_edges++;
|
||||
adj = tick_increment >> 4;
|
||||
} else
|
||||
adj = 0;
|
||||
} else {
|
||||
adj = 0;
|
||||
adjust_excess++;
|
||||
}
|
||||
PCPU_SET(tickref, ref);
|
||||
PCPU_SET(tickadj, adj);
|
||||
}
|
||||
|
||||
void
|
||||
tick_init(u_long clock)
|
||||
{
|
||||
|
||||
tick_freq = clock;
|
||||
tick_MHz = clock / 1000000;
|
||||
tick_increment = clock / hz;
|
||||
/*
|
||||
* Avoid stopping of hardclock in terms of a lost tick interrupt
|
||||
* by ensuring that the tick period is at least TICK_GRACE ticks.
|
||||
*/
|
||||
if (tick_increment < TICK_GRACE)
|
||||
panic("%s: HZ to high, decrease to at least %ld", __func__,
|
||||
clock / TICK_GRACE);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
wr(asr23, 1L << 63, 0);
|
||||
tick_stop();
|
||||
}
|
||||
|
||||
void
|
||||
tick_start(tick_func_t *func)
|
||||
tick_start(void)
|
||||
{
|
||||
intr_setup(PIL_TICK, (ih_func_t *)func, -1, NULL, NULL);
|
||||
wrpr(tick, 0, 0);
|
||||
wr(asr23, tick_increment, 0);
|
||||
}
|
||||
u_long base, s;
|
||||
|
||||
#ifdef SMP
|
||||
void
|
||||
tick_start_ap(void)
|
||||
{
|
||||
u_long base;
|
||||
if (PCPU_GET(cpuid) == 0)
|
||||
intr_setup(PIL_TICK, (ih_func_t *)tick_hardclock, -1, NULL,
|
||||
NULL);
|
||||
|
||||
/*
|
||||
* Try to make the ticks interrupt as synchronously as possible to
|
||||
* avoid inaccuracies for migrating processes. Leave out one tick to
|
||||
* make sure that it is not missed.
|
||||
* 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.
|
||||
*/
|
||||
PCPU_SET(tickadj, 0);
|
||||
s = intr_disable();
|
||||
base = rd(tick);
|
||||
wr(asr23, roundup(base, tick_increment) + tick_increment, 0);
|
||||
base = roundup(base, tick_increment);
|
||||
PCPU_SET(tickref, base);
|
||||
wrtickcmpr(base + tick_increment, 0);
|
||||
intr_restore(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
tick_stop(void)
|
||||
@ -148,5 +186,5 @@ tick_stop(void)
|
||||
|
||||
if (cpu_impl >= CPU_IMPL_ULTRASPARCIII)
|
||||
wr(asr24, 1L << 63, 0);
|
||||
wr(asr23, 1L << 63, 0);
|
||||
wrtickcmpr(1L << 63, 0);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user