Subject: port-i386/36839: x86/x86/tsc.c should be switched to using MI kern_cctr.c
To: None <port-i386-maintainer@netbsd.org, gnats-admin@netbsd.org,>
From: None <tsutsui@ceres.dti.ne.jp>
List: netbsd-bugs
Date: 08/26/2007 05:45:00
>Number: 36839
>Category: port-i386
>Synopsis: x86/x86/tsc.c should be switched to using MI kern_cctr.c
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: port-i386-maintainer
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Sun Aug 26 05:45:00 +0000 2007
>Originator: Izumi Tsutsui
>Release: NetBSD 4.99.25
>Organization:
>Environment:
NetBSD-current around 20070822
Architecture: i386
Machine: i386
>Description:
When I was working on MI todr(9) and timecounter(9) support for alpha,
Frank Kardel (kardel@) suggested that we should split x86/x86/tsc.c
int an MD part (x86/x86/tsc.c) and an MI part (kern/kern_cctr.c):
http://mail-index.netbsd.org/port-alpha/2007/02/24/0004.html
and then I adapted the patch for alpha's PCC counters:
http://mail-index.netbsd.org/port-alpha/2007/07/08/0001.html
I've commit MI files (sys/kern/kern_cctr.c and sys/sys/cctr.h)
before commiting timecounter(9) support for alpha:
http://mail-index.netbsd.org/source-changes/2007/07/21/0012.html
http://mail-index.netbsd.org/source-changes/2007/07/21/0013.html
but changes for x86/x86/tsc.c (and amd64 and i386 MD part) have not
been committed due to lack of testers, so I'd file a PR about
these changes for a record.
>How-To-Repeat:
By code inspection.
>Fix:
Here is a diff based on the above Frank's patch (with some tweaks by me):
Index: arch/amd64/amd64/ipifuncs.c
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/amd64/ipifuncs.c,v
retrieving revision 1.9
diff -u -r1.9 ipifuncs.c
--- arch/amd64/amd64/ipifuncs.c 21 Mar 2007 06:36:43 -0000 1.9
+++ arch/amd64/amd64/ipifuncs.c 26 Aug 2007 04:59:12 -0000
@@ -83,7 +83,7 @@
void (*ipifunc[X86_NIPI])(struct cpu_info *) =
{
x86_64_ipi_halt,
- tsc_calibrate_cpu,
+ cc_calibrate_cpu,
x86_64_ipi_flush_fpu,
x86_64_ipi_synch_fpu,
pmap_do_tlb_shootdown,
Index: arch/amd64/conf/files.amd64
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/conf/files.amd64,v
retrieving revision 1.41
diff -u -r1.41 files.amd64
--- arch/amd64/conf/files.amd64 6 Aug 2007 06:40:41 -0000 1.41
+++ arch/amd64/conf/files.amd64 26 Aug 2007 04:59:12 -0000
@@ -141,6 +141,7 @@
file arch/x86/isa/clock.c isa
# TSC timecounter support
+file kern/kern_cctr.c
file arch/x86/x86/tsc.c
# attribute used to represent the "keyboard controller"
Index: arch/amd64/include/cpu.h
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/include/cpu.h,v
retrieving revision 1.24
diff -u -r1.24 cpu.h
--- arch/amd64/include/cpu.h 21 May 2007 08:10:39 -0000 1.24
+++ arch/amd64/include/cpu.h 26 Aug 2007 04:59:12 -0000
@@ -55,14 +55,14 @@
#include <sys/device.h>
#include <sys/simplelock.h>
#include <sys/cpu_data.h>
-#include <sys/cc_microtime.h>
+#include <sys/cctr.h>
struct cpu_info {
struct device *ci_dev;
struct cpu_info *ci_self;
void *ci_self200; /* self + 0x200, see lock_stubs.S */
struct cpu_data ci_data; /* MI per-cpu data */
- struct cc_microtime_state ci_cc;/* cc_microtime state */
+ struct cctr_state ci_cc; /* cycle counter state */
struct cpu_info *ci_next;
struct lwp *ci_curlwp;
Index: arch/i386/conf/files.i386
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/conf/files.i386,v
retrieving revision 1.312
diff -u -r1.312 files.i386
--- arch/i386/conf/files.i386 8 Jul 2007 01:13:26 -0000 1.312
+++ arch/i386/conf/files.i386 26 Aug 2007 04:59:12 -0000
@@ -260,6 +260,7 @@
file arch/x86/isa/clock.c isa
# TSC support
+file kern/kern_cctr.c i586_cpu | i686_cpu
file arch/x86/x86/tsc.c i586_cpu | i686_cpu
# Numeric Processing Extension; Math Co-processor
Index: arch/i386/i386/ipifuncs.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/i386/ipifuncs.c,v
retrieving revision 1.17
diff -u -r1.17 ipifuncs.c
--- arch/i386/i386/ipifuncs.c 17 May 2007 14:51:20 -0000 1.17
+++ arch/i386/i386/ipifuncs.c 26 Aug 2007 04:59:12 -0000
@@ -88,7 +88,7 @@
{
i386_ipi_halt,
#if defined(I586_CPU) || defined(I686_CPU)
- tsc_calibrate_cpu, /* keep cycle counters synchronized */
+ cc_calibrate_cpu, /* keep cycle counters synchronized */
#else
0,
#endif
Index: arch/i386/include/cpu.h
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/include/cpu.h,v
retrieving revision 1.143
diff -u -r1.143 cpu.h
--- arch/i386/include/cpu.h 9 Jul 2007 20:52:17 -0000 1.143
+++ arch/i386/include/cpu.h 26 Aug 2007 04:59:12 -0000
@@ -57,7 +57,7 @@
#include <sys/device.h>
#include <sys/cpu_data.h>
-#include <sys/cc_microtime.h>
+#include <sys/cctr.h>
#include <lib/libkern/libkern.h> /* offsetof */
@@ -83,7 +83,7 @@
cpuid_t ci_cpuid; /* our CPU ID */
u_int ci_apicid; /* our APIC ID */
struct cpu_data ci_data; /* MI per-cpu data */
- struct cc_microtime_state ci_cc;/* cc_microtime state */
+ struct cctr_state ci_cc; /* cycle counter state */
/*
* Private members.
Index: arch/x86/include/cpu_counter.h
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/include/cpu_counter.h,v
retrieving revision 1.1
diff -u -r1.1 cpu_counter.h
--- arch/x86/include/cpu_counter.h 7 Jul 2007 17:38:27 -0000 1.1
+++ arch/x86/include/cpu_counter.h 26 Aug 2007 04:59:12 -0000
@@ -43,6 +43,8 @@
* x86 common functions for CPU counter.
*/
+#define cc_calibrate_mp(ci) x86_broadcast_ipi(X86_IPI_MICROSET)
+
static __inline uint64_t
cpu_counter(void)
{
Index: arch/x86/x86/tsc.c
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/x86/tsc.c,v
retrieving revision 1.10
diff -u -r1.10 tsc.c
--- arch/x86/x86/tsc.c 16 Nov 2006 01:32:39 -0000 1.10
+++ arch/x86/x86/tsc.c 26 Aug 2007 04:59:12 -0000
@@ -88,8 +88,6 @@
#include "opt_multiprocessor.h"
#ifdef i386
#include "opt_enhanced_speedstep.h"
-#endif
-#ifdef i386
#include "opt_powernow_k7.h"
#endif
#include "opt_powernow_k8.h"
@@ -100,44 +98,21 @@
#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>
+#include <x86/x86/tsc.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 */
-#if (defined(ENHANCED_SPEEDSTEP) || defined(POWERNOW_K7) || defined(POWERNOW_K8))
- -100, /* don't pick TSC automatically */
- /* if frequency changes might affect TSC */
-#else
- 800, /* quality (adjusted in code) */
-#endif
- NULL,
- NULL,
-};
+int tsc_qual = -100000;
void
init_TSC(void)
{
- u_int64_t tscval[2];
+ uint64_t tscval[2];
if (cpu_feature & CPUID_TSC)
tsc_present = 1;
@@ -157,6 +132,12 @@
} while (tscval[1] < tscval[0]);
tsc_freq = 10 * (tscval[1] - tscval[0]);
+#if defined(ENHANCED_SPEEDSTEP) || defined(POWERNOW_K7) || defined(POWERNOW_K8)
+ tsc_qual = -100; /* don't pick TSC automatically */
+ /* if frequency changes might affect TSC */
+#else
+ tsc_qual = 800; /* quality (adjusted in code) */
+#endif
if (bootverbose)
printf("TSC clock: %" PRId64 " Hz\n", tsc_freq);
}
@@ -164,164 +145,8 @@
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;
+ if (tsc_present && tsc_freq != 0 && !tsc_is_broken) {
+ (void)cc_init(tsc_freq, "TSC", tsc_qual);
}
-
-#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 */
}
---
Note <sys/cc_microtime.h> can be removed once the diff is applied.
---
Izumi Tsutsui