add timecounter support (from branch simonb-timecounters)

This commit is contained in:
kardel 2006-06-07 22:41:09 +00:00
parent 09b51ec920
commit 9af3ab6763
3 changed files with 444 additions and 13 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: lapic.c,v 1.15 2006/01/04 00:15:50 rpaulo Exp $ */
/* $NetBSD: lapic.c,v 1.16 2006/06/07 22:41:09 kardel Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@ -39,11 +39,11 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: lapic.c,v 1.15 2006/01/04 00:15:50 rpaulo Exp $");
__KERNEL_RCSID(0, "$NetBSD: lapic.c,v 1.16 2006/06/07 22:41:09 kardel Exp $");
#include "opt_ddb.h"
#include "opt_multiprocessor.h"
#include "opt_mpbios.h" /* for MPDEBUG */
#include "opt_multiprocessor.h"
#include "opt_ntp.h"
#include <sys/param.h>
@ -51,12 +51,14 @@ __KERNEL_RCSID(0, "$NetBSD: lapic.c,v 1.15 2006/01/04 00:15:50 rpaulo Exp $");
#include <sys/user.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/timetc.h>
#include <uvm/uvm_extern.h>
#include <dev/ic/i8253reg.h>
#include <machine/cpu.h>
#include <machine/cpu_counter.h>
#include <machine/cpufunc.h>
#include <machine/cpuvar.h>
#include <machine/pmap.h>
@ -65,6 +67,9 @@ __KERNEL_RCSID(0, "$NetBSD: lapic.c,v 1.15 2006/01/04 00:15:50 rpaulo Exp $");
#include <machine/pcb.h>
#include <machine/specialreg.h>
#include <machine/segments.h>
#ifdef _HAVE_TIMECOUNTER
#include <x86/x86/tsc.h>
#endif
#include <machine/apicvar.h>
#include <machine/i82489reg.h>
@ -236,20 +241,87 @@ void
lapic_clockintr(void *arg, struct intrframe frame)
{
#if defined(I586_CPU) || defined(I686_CPU) || defined(__x86_64__)
#ifndef __HAVE_TIMECOUNTER
static int microset_iter; /* call cc_microset once/sec */
#endif /* __HAVE_TIMECOUNTER */
#if defined(TIMECOUNTER_DEBUG) && defined(__HAVE_TIMECOUNTER)
static u_int last_count[X86_MAXPROCS],
last_delta[X86_MAXPROCS],
last_tsc[X86_MAXPROCS],
last_tscdelta[X86_MAXPROCS],
last_factor[X86_MAXPROCS];
#endif /* TIMECOUNTER_DEBUG && __HAVE_TIMECOUNTER */
struct cpu_info *ci = curcpu();
ci->ci_isources[LIR_TIMER]->is_evcnt.ev_count++;
#if defined(TIMECOUNTER_DEBUG) && defined(__HAVE_TIMECOUNTER)
{
int cid = ci->ci_cpuid;
extern u_int i8254_get_timecount(struct timecounter *);
u_int c_count = i8254_get_timecount(NULL);
u_int c_tsc = cpu_counter32();
u_int delta, ddelta, tsc_delta, factor = 0;
int idelta;
if (c_count > last_count[cid])
delta = c_count - last_count[cid];
else
delta = 0x100000000ULL - last_count[cid] + c_count;
if (delta > last_delta[cid])
ddelta = delta - last_delta[cid];
else
ddelta = last_delta[cid] - delta;
if (c_tsc > last_tsc[cid])
tsc_delta = c_tsc - last_tsc[cid];
else
tsc_delta = 0x100000000ULL - last_tsc[cid] + c_tsc;
idelta = tsc_delta - last_tscdelta[cid];
if (idelta < 0)
idelta = -idelta;
if (delta) {
int fdelta = tsc_delta / delta - last_factor[cid];
if (fdelta < 0)
fdelta = -fdelta;
if (fdelta > last_factor[cid] / 10) {
printf("cpu%d: freq skew exceeds 10%%: delta %u, factor %u, last %u\n", cid, fdelta, tsc_delta / delta, last_factor[cid]);
}
factor = tsc_delta / delta;
}
if (ddelta > last_delta[cid] / 10) {
printf("cpu%d: tick delta exceeds 10%%: delta %u, last %u, tick %u, last %u, factor %u, last %u\n",
cid, ddelta, last_delta[cid], c_count, last_count[cid], factor, last_factor[cid]);
}
if (last_count[cid] > c_count) {
printf("cpu%d: tick wrapped/lost: delta %u, tick %u, last %u\n", cid, last_count[cid] - c_count, c_count, last_count[cid]);
}
if (idelta > last_tscdelta[cid] / 10) {
printf("cpu%d: TSC delta exceeds 10%%: delta %u, last %u, tsc %u, factor %u, last %u\n", cid, idelta, last_tscdelta[cid], last_tsc[cid],
factor, last_factor[cid]);
}
last_factor[cid] = factor;
last_delta[cid] = delta;
last_count[cid] = c_count;
last_tsc[cid] = c_tsc;
last_tscdelta[cid] = tsc_delta;
}
#endif /* TIMECOUNTER_DEBUG && __HAVE_TIMECOUNTER */
#ifndef __HAVE_TIMECOUNTER
/*
* If we have a cycle counter, do the microset thing.
*/
if (ci->ci_feature_flags & CPUID_TSC) {
if (
#if defined(MULTIPROCESSOR)
CPU_IS_PRIMARY(ci) &&
#endif
(microset_iter--) == 0) {
if (CPU_IS_PRIMARY(ci) && (microset_iter--) == 0) {
microset_iter = hz - 1;
cc_microset_time = time;
#if defined(MULTIPROCESSOR)
@ -258,26 +330,27 @@ lapic_clockintr(void *arg, struct intrframe frame)
cc_microset(ci);
}
}
#endif
#endif /* !__HAVE_TIMECOUNTER */
#endif /* I586_CPU || I686_CPU || __x86_64__ */
hardclock((struct clockframe *)&frame);
}
#ifdef NTP
#if !defined(__HAVE_TIMECOUNTER) && defined(NTP)
extern int fixtick;
#endif /* NTP */
#endif /* !__HAVE_TIMECOUNTER && NTP */
void
lapic_initclocks()
{
#ifdef NTP
#if !defined(__HAVE_TIMECOUNTER) && defined(NTP)
/*
* we'll actually get (lapic_per_second/lapic_tval) interrupts/sec.
*/
fixtick = 1000000 -
((int64_t)tick * lapic_per_second + lapic_tval / 2) / lapic_tval;
#endif /* NTP */
#endif /* !__HAVE_TIMECOUNTER && NTP */
/*
* Start local apic countdown timer running, in repeated mode.
@ -400,6 +473,11 @@ lapic_calibrate_timer(ci)
*/
delay_func = lapic_delay;
initclock_func = lapic_initclocks;
#ifdef __HAVE_TIMECOUNTER
initrtclock(0);
#else
initrtclock();
#endif
}
}

313
sys/arch/x86/x86/tsc.c Normal file
View File

@ -0,0 +1,313 @@
/* $NetBSD: tsc.c,v 1.2 2006/06/07 22:41:09 kardel Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
* All rights reserved.
*
* re-implementation of TSC for MP systems merging cc_microtime and
* TSC for timecounters by Frank Kardel
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT 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.
*/
/* basic calibration ideas are (kern_microtime.c): */
/******************************************************************************
* *
* Copyright (c) David L. Mills 1993, 1994 *
* *
* Permission to use, copy, modify, and distribute this software and its *
* documentation for any purpose and without fee is hereby granted, provided *
* that the above copyright notice appears in all copies and that both the *
* copyright notice and this permission notice appear in supporting *
* documentation, and that the name University of Delaware not be used in *
* advertising or publicity pertaining to distribution of the software *
* without specific, written prior permission. The University of Delaware *
* makes no representations about the suitability this software for any *
* purpose. It is provided "as is" without express or implied warranty. *
* *
******************************************************************************/
/* reminiscents from older version of this file are: */
/*-
* Copyright (c) 1998-2003 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* 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.
*/
#include <sys/cdefs.h>
/* __FBSDID("$FreeBSD: src/sys/i386/i386/tsc.c,v 1.204 2003/10/21 18:28:34 silby Exp $"); */
__KERNEL_RCSID(0, "$NetBSD: tsc.c,v 1.2 2006/06/07 22:41:09 kardel Exp $");
#include "opt_multiprocessor.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/timetc.h>
#include <sys/kernel.h>
#include <sys/power.h>
#include <sys/reboot.h> /* XXX for bootverbose */
#include <machine/cpu.h>
#include <machine/cpu_counter.h>
#include <x86/x86/tsc.h>
#include <machine/specialreg.h>
uint64_t tsc_freq;
u_int tsc_present;
int tsc_is_broken = 0;
static int64_t tsc_cal_val; /* last calibrate time stamp */
static timecounter_get_t tsc_get_timecount;
static timecounter_pps_t tsc_calibrate;
void tsc_calibrate_cpu(struct cpu_info *);
static struct timecounter tsc_timecounter = {
tsc_get_timecount, /* get_timecount */
tsc_calibrate, /* once per second - used to calibrate cpu TSC */
~0u, /* counter_mask */
0, /* frequency */
"TSC", /* name */
800, /* quality (adjusted in code) */
};
void
init_TSC(void)
{
u_int64_t tscval[2];
if (cpu_feature & CPUID_TSC)
tsc_present = 1;
else
tsc_present = 0;
if (!tsc_present)
return;
if (bootverbose)
printf("Calibrating TSC clock ... ");
do {
tscval[0] = rdtsc();
DELAY(100000);
tscval[1] = rdtsc();
} while (tscval[1] < tscval[0]);
tsc_freq = 10 * (tscval[1] - tscval[0]);
if (bootverbose)
printf("TSC clock: %" PRId64 " Hz\n", tsc_freq);
}
void
init_TSC_tc(void)
{
if (tsc_present && tsc_freq != 0 && !tsc_is_broken) {
tsc_timecounter.tc_frequency = tsc_freq;
tc_init(&tsc_timecounter);
}
}
/* XXX make tsc_timecounter.tc_frequency settable by sysctl() */
/*
* pick up tick count scaled to reference tick count
*/
static u_int
tsc_get_timecount(struct timecounter *tc)
{
struct cpu_info *ci = curcpu();
int64_t rcc, cc;
u_int gen;
if (ci->ci_cc.cc_denom == 0) {
/*
* This is our first time here on this CPU. Just
* start with reasonable initial values.
*/
ci->ci_cc.cc_cc = cpu_counter32();
ci->ci_cc.cc_val = 0;
if (ci->ci_cc.cc_gen == 0)
ci->ci_cc.cc_gen++;
ci->ci_cc.cc_denom = cpu_frequency(ci);
if (ci->ci_cc.cc_denom == 0)
ci->ci_cc.cc_denom = tsc_freq;
ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
}
/* read counter and re-read when the re-calibration
strikes inbetween */
do {
/* pick up current generation number */
gen = ci->ci_cc.cc_gen;
/* determine local delta ticks */
cc = cpu_counter32() - ci->ci_cc.cc_cc;
if (cc < 0)
cc += 0x100000000LL;
/* scale to primary */
rcc = (cc * ci->ci_cc.cc_delta) / ci->ci_cc.cc_denom
+ ci->ci_cc.cc_val;
} while (gen == 0 || gen != ci->ci_cc.cc_gen);
return rcc;
}
/*
* called once per second via the pps callback
* for the calibration of the TSC counters.
* it is called only for the PRIMARY cpu. all
* other cpus are called via a broadcast IPI
*/
static void
tsc_calibrate(struct timecounter *tc)
{
struct cpu_info *ci = curcpu();
/* pick up reference ticks */
tsc_cal_val = cpu_counter32();
#if defined(MULTIPROCESSOR)
x86_broadcast_ipi(X86_IPI_MICROSET);
#endif
tsc_calibrate_cpu(ci);
}
/*
* This routine is called about once per second directly by the master
* processor and via an interprocessor interrupt for other processors.
* It determines the CC frequency of each processor relative to the
* master clock and the time this determination is made. These values
* are used by tsc_get_timecount() to interpolate the ticks between
* timer interrupts. Note that we assume the kernel variables have
* been zeroed early in life.
*/
void
tsc_calibrate_cpu(struct cpu_info *ci)
{
u_int gen;
int64_t val;
int64_t delta, denom;
int s;
#ifdef TIMECOUNTER_DEBUG
int64_t factor, old_factor;
#endif
val = tsc_cal_val;
s = splhigh();
/* create next generation number */
gen = ci->ci_cc.cc_gen;
gen++;
if (gen == 0)
gen++;
/* update in progress */
ci->ci_cc.cc_gen = 0;
denom = ci->ci_cc.cc_cc;
ci->ci_cc.cc_cc = cpu_counter32();
if (ci->ci_cc.cc_denom == 0) {
/*
* This is our first time here on this CPU. Just
* start with reasonable initial values.
*/
ci->ci_cc.cc_val = val;
ci->ci_cc.cc_denom = cpu_frequency(ci);
if (ci->ci_cc.cc_denom == 0)
ci->ci_cc.cc_denom = tsc_freq;
ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
ci->ci_cc.cc_gen = gen;
splx(s);
return;
}
#ifdef TIMECOUNTER_DEBUG
old_factor = (ci->ci_cc.cc_delta * 1000 ) / ci->ci_cc.cc_denom;
#endif
/* local ticks per period */
denom = ci->ci_cc.cc_cc - denom;
if (denom < 0)
denom += 0x100000000LL;
ci->ci_cc.cc_denom = denom;
/* reference ticks per period */
delta = val - ci->ci_cc.cc_val;
if (delta < 0)
delta += 0x100000000LL;
ci->ci_cc.cc_val = val;
ci->ci_cc.cc_delta = delta;
/* publish new generation number */
ci->ci_cc.cc_gen = gen;
splx(s);
#ifdef TIMECOUNTER_DEBUG
factor = (delta * 1000) / denom - old_factor;
if (factor < 0)
factor = -factor;
if (factor > old_factor / 10)
printf("tsc_calibrate_cpu[%lu]: 10%% exceeded - delta %"
PRId64 ", denom %" PRId64 ", factor %" PRId64
", old factor %" PRId64"\n", ci->ci_cpuid,
delta, denom, (delta * 1000) / denom, old_factor);
#if 0
printf("tsc_calibrate_cpu[%lu]: delta %" PRId64
", denom %" PRId64 ", factor %" PRId64 "\n", ci->ci_cpuid, delta, denom, (delta * 1000) / denom);
#endif
#endif /* TIMECOUNTER_DEBUG */
}

40
sys/arch/x86/x86/tsc.h Normal file
View File

@ -0,0 +1,40 @@
/* $NetBSD: tsc.h,v 1.2 2006/06/07 22:41:09 kardel Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT 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.
*/
/*
* initialization of timecounter interface
*/
void init_TSC(void);
void init_TSC_tc(void);