Index: bus/cam/cam_ccb.h
===================================================================
RCS file: /cvs/src/sys/bus/cam/cam_ccb.h,v
retrieving revision 1.6
diff -u -r1.6 cam_ccb.h
--- bus/cam/cam_ccb.h	29 Dec 2003 23:30:58 -0000	1.6
+++ bus/cam/cam_ccb.h	25 Jan 2004 19:52:29 -0000
@@ -276,7 +276,7 @@
 				 * tagged operations
 				 */
 	int	mintags;
-	struct	timeval last_reset;	/* Time of last bus reset/loop init */
+	struct	timeval last_reset;  /* Uptime of last bus reset/loop init */
 };
 
 typedef enum {
@@ -512,7 +512,7 @@
 /* Path Statistics CCB */
 struct ccb_pathstats {
 	struct	ccb_hdr	ccb_h;
-	struct	timeval last_reset;	/* Time of last bus reset/loop init */
+	struct	timeval last_reset;	/* Uptime of last bus reset/loop init */
 };
 
 typedef union {
Index: bus/cam/cam_periph.c
===================================================================
RCS file: /cvs/src/sys/bus/cam/cam_periph.c,v
retrieving revision 1.6
diff -u -r1.6 cam_periph.c
--- bus/cam/cam_periph.c	10 Nov 2003 06:12:00 -0000	1.6
+++ bus/cam/cam_periph.c	25 Jan 2004 20:02:07 -0000
@@ -1071,11 +1071,8 @@
 {
 	struct timeval delta;
 	struct timeval duration_tv;
-	int s;
 
-	s = splclock();
-	microtime(&delta);
-	splx(s);
+	microuptime(&delta);
 	timevalsub(&delta, event_time);
 	duration_tv.tv_sec = duration_ms / 1000;
 	duration_tv.tv_usec = (duration_ms % 1000) * 1000;
Index: bus/cam/cam_xpt.c
===================================================================
RCS file: /cvs/src/sys/bus/cam/cam_xpt.c,v
retrieving revision 1.8
diff -u -r1.8 cam_xpt.c
--- bus/cam/cam_xpt.c	29 Dec 2003 08:37:38 -0000	1.8
+++ bus/cam/cam_xpt.c	25 Jan 2004 19:54:53 -0000
@@ -162,7 +162,7 @@
 	target_id_t	target_id;
 	u_int32_t	refcount;	
 	u_int		generation;
-	struct		timeval last_reset;
+	struct		timeval last_reset;	/* uptime of last reset */
 };
 
 /*
@@ -175,7 +175,7 @@
 	TAILQ_ENTRY(cam_eb)  links;
 	path_id_t	     path_id;
 	struct cam_sim	     *sim;
-	struct timeval	     last_reset;
+	struct timeval	     last_reset;	/* uptime of last reset */
 	u_int32_t	     flags;
 #define	CAM_EB_RUNQ_SCHEDULED	0x01
 	u_int32_t	     refcount;
@@ -4233,12 +4233,8 @@
 	bus = path->bus;
 
 	if (async_code == AC_BUS_RESET) { 
-		int s;
-
-		s = splclock();
 		/* Update our notion of when the last reset occurred */
-		microtime(&bus->last_reset);
-		splx(s);
+		microuptime(&bus->last_reset);
 	}
 
 	for (target = TAILQ_FIRST(&bus->et_entries);
@@ -4253,12 +4249,8 @@
 			continue;
 
 		if (async_code == AC_SENT_BDR) {
-			int s;
-
 			/* Update our notion of when the last reset occurred */
-			s = splclock();
-			microtime(&path->target->last_reset);
-			splx(s);
+			microuptime(&path->target->last_reset);
 		}
 
 		for (device = TAILQ_FIRST(&target->ed_entries);
Index: bus/isa/syscons_isa.c
===================================================================
RCS file: /cvs/src/sys/bus/isa/syscons_isa.c,v
retrieving revision 1.3
diff -u -r1.3 syscons_isa.c
--- bus/isa/syscons_isa.c	7 Aug 2003 21:16:46 -0000	1.3
+++ bus/isa/syscons_isa.c	26 Jan 2004 06:32:29 -0000
@@ -33,6 +33,7 @@
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/module.h>
+#include <sys/systimer.h>
 #include <sys/bus.h>
 #include <sys/cons.h>
 
@@ -219,15 +220,15 @@
 		if (acquire_timer2(TIMER_16BIT | TIMER_SQWAVE))
 			return EBUSY;
 		/* set pitch */
-		pitch = timer_freq/herz;
+		pitch = cputimer_freq/herz;
 		outb(TIMER_CNTR2, pitch);
 		outb(TIMER_CNTR2, pitch >> 8);
 		/* enable counter 2 output to speaker */
 		outb(IO_PPI, inb(IO_PPI) | 3);
 	} else {
 		/* disable counter 2 output to speaker */
-		outb(IO_PPI, inb(IO_PPI) & 0xFC);
-		release_timer2();
+		if (release_timer2() == 0)
+			outb(IO_PPI, inb(IO_PPI) & 0xFC);
 	}
 #endif /* __i386__ */
 
Index: conf/files
===================================================================
RCS file: /cvs/src/sys/conf/files,v
retrieving revision 1.43
diff -u -r1.43 files
--- conf/files	20 Jan 2004 18:41:47 -0000	1.43
+++ conf/files	25 Jan 2004 07:50:40 -0000
@@ -633,6 +633,7 @@
 kern/kern_random.c	standard
 kern/kern_resource.c	standard
 kern/kern_slaballoc.c	standard
+kern/kern_systimer.c	standard
 kern/kern_mpipe.c	standard
 kern/kern_shutdown.c	standard
 kern/kern_sig.c		standard
Index: dev/acpica/acpi_timer.c
===================================================================
RCS file: /cvs/src/sys/dev/acpica/acpi_timer.c,v
retrieving revision 1.1
diff -u -r1.1 acpi_timer.c
--- dev/acpica/acpi_timer.c	24 Sep 2003 03:32:16 -0000	1.1
+++ dev/acpica/acpi_timer.c	25 Jan 2004 01:36:43 -0000
@@ -27,6 +27,9 @@
  *	$FreeBSD: src/sys/dev/acpica/acpi_timer.c,v 1.24.2.1 2003/08/22 20:49:20 jhb Exp $
  *      $DragonFly: src/sys/dev/acpica/acpi_timer.c,v 1.1 2003/09/24 03:32:16 drhodus Exp $ 
  */
+
+#ifdef NOTDEF	/* Disabled */
+
 #include "opt_acpi.h"
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -387,4 +390,6 @@
 
     return(ENXIO);		/* we never match anything */
 }
+#endif
+
 #endif
Index: dev/misc/dec/mcclock.c
===================================================================
RCS file: /cvs/src/sys/dev/misc/dec/mcclock.c,v
retrieving revision 1.3
diff -u -r1.3 mcclock.c
--- dev/misc/dec/mcclock.c	7 Aug 2003 21:16:56 -0000	1.3
+++ dev/misc/dec/mcclock.c	25 Jan 2004 19:58:43 -0000
@@ -72,11 +72,10 @@
 mcclock_get(device_t dev, time_t base, struct clocktime *ct)
 {
 	mc_todregs regs;
-	int s;
 
-	s = splclock();
+	crit_enter();
 	MC146818_GETTOD(dev, &regs)
-	splx(s);
+	crit_exit();
 
 	ct->sec = regs[MC_SEC];
 	ct->min = regs[MC_MIN];
@@ -94,11 +93,10 @@
 mcclock_set(device_t dev, struct clocktime *ct)
 {
 	mc_todregs regs;
-	int s;
 
-	s = splclock();
+	crit_enter();
 	MC146818_GETTOD(dev, &regs);
-	splx(s);
+	crit_exit();
 
 	regs[MC_SEC] = ct->sec;
 	regs[MC_MIN] = ct->min;
@@ -108,9 +106,9 @@
 	regs[MC_MONTH] = ct->mon;
 	regs[MC_YEAR] = ct->year;
 
-	s = splclock();
+	crit_enter();
 	MC146818_PUTTOD(dev, &regs);
-	splx(s);
+	crit_exit();
 }
 
 int
@@ -118,23 +116,22 @@
 {
 	int timeout = 100000000;
 	int sec;
-	int s;
 
-	s = splclock();
 	for (;;) {
+		crit_enter();
 		if (!(MCCLOCK_READ(dev, MC_REGA) & MC_REGA_UIP)) {
 			sec = MCCLOCK_READ(dev, MC_SEC);
+			crit_exit();
 			break;
 		}
-		if (--timeout == 0)
+		if (--timeout == 0) {
+			crit_exit();
 			goto fail;
+		}
 	}
-
-	splx(s);
 	*secp = sec;
 	return 0;
-
  fail:
-	splx(s);
 	return ETIMEDOUT;
 }
+
Index: dev/misc/pps/pps.c
===================================================================
RCS file: /cvs/src/sys/dev/misc/pps/pps.c,v
retrieving revision 1.5
diff -u -r1.5 pps.c
--- dev/misc/pps/pps.c	7 Aug 2003 21:16:58 -0000	1.5
+++ dev/misc/pps/pps.c	25 Jan 2004 01:32:48 -0000
@@ -183,16 +183,14 @@
 	device_t ppsdev = (device_t)arg;
 	device_t ppbus = device_get_parent(ppsdev);
 	struct pps_data *sc = DEVTOSOFTC(ppsdev);
-	struct timecounter *tc;
-	unsigned count;
+	sysclock_t count;
 
-	tc = timecounter;
-	count = timecounter->tc_get_timecount(tc);
+	count = cputimer_get_count();
 	if (!(ppb_rstr(ppbus) & nACK))
 		return;
 	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT) 
 		ppb_wctr(ppbus, IRQENABLE | AUTOFEED);
-	pps_event(&sc->pps, tc, count, PPS_CAPTUREASSERT);
+	pps_event(&sc->pps, count, PPS_CAPTUREASSERT);
 	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT) 
 		ppb_wctr(ppbus, IRQENABLE);
 }
Index: dev/misc/xrpu/xrpu.c
===================================================================
RCS file: /cvs/src/sys/dev/misc/xrpu/xrpu.c,v
retrieving revision 1.5
diff -u -r1.5 xrpu.c
--- dev/misc/xrpu/xrpu.c	7 Aug 2003 21:16:59 -0000	1.5
+++ dev/misc/xrpu/xrpu.c	25 Jan 2004 01:35:16 -0000
@@ -73,12 +73,16 @@
 	enum { NORMAL, TIMECOUNTER } mode;
 	vm_offset_t virbase, physbase;
 	u_int	*virbase62;
+#if 0
 	struct timecounter tc;
+#endif
 	u_int *trigger, *latch, dummy;
 	struct pps_state pps[XRPU_MAX_PPS];
 	u_int *assert[XRPU_MAX_PPS], *clear[XRPU_MAX_PPS];
 };
 
+#if 0
+
 static unsigned         
 xrpu_get_timecount(struct timecounter *tc)
 {               
@@ -117,6 +121,8 @@
 	}
 }
 
+#endif
+
 static int
 xrpu_open(dev_t dev, int flag, int mode, struct thread *td)
 {
@@ -157,6 +163,7 @@
 		return (error);
 	}
 		
+#if 0
 	if (cmd == XRPU_IOC_TIMECOUNTING) {
 		struct xrpu_timecounting *xt = (struct xrpu_timecounting *)arg;
 
@@ -194,6 +201,7 @@
 		init_timecounter(&sc->tc);
 		return (0);
 	}
+#endif
 	error = ENOTTY;
 	return (error);
 }
Index: dev/serial/sio/sio.c
===================================================================
RCS file: /cvs/src/sys/dev/serial/sio/sio.c,v
retrieving revision 1.12
diff -u -r1.12 sio.c
--- dev/serial/sio/sio.c	24 Jan 2004 08:00:45 -0000	1.12
+++ dev/serial/sio/sio.c	25 Jan 2004 08:49:44 -0000
@@ -1751,7 +1751,6 @@
 	u_char	recv_data;
 	u_char	int_ctl;
 	u_char	int_ctl_new;
-	struct	timecounter *tc;
 	u_int	count;
 
 	int_ctl = inb(com->intr_ctl_port);
@@ -1761,9 +1760,8 @@
 		if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) {
 			modem_status = inb(com->modem_status_port);
 		        if ((modem_status ^ com->last_modem_status) & MSR_DCD) {
-				tc = timecounter;
-				count = tc->tc_get_timecount(tc);
-				pps_event(&com->pps, tc, count, 
+				count = cputimer_count();
+				pps_event(&com->pps, count, 
 				    (modem_status & MSR_DCD) ? 
 				    PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
 			}
Index: dev/sound/isa/i386/pca/pcaudio.c
===================================================================
RCS file: /cvs/src/sys/dev/sound/isa/i386/pca/pcaudio.c,v
retrieving revision 1.7
diff -u -r1.7 pcaudio.c
--- dev/sound/isa/i386/pca/pcaudio.c	27 Aug 2003 06:48:15 -0000	1.7
+++ dev/sound/isa/i386/pca/pcaudio.c	24 Jan 2004 23:30:29 -0000
@@ -147,7 +147,7 @@
 static int pca_sleep = 0;
 static int pca_initialized = 0;
 
-static void pcaintr(struct clockframe *frame);
+static void pcaintr(struct intrframe *frame);
 
 static	d_open_t	pcaopen;
 static	d_close_t	pcaclose;
@@ -532,7 +532,7 @@
 
 
 static void
-pcaintr(struct clockframe *frame)
+pcaintr(struct intrframe *frame)
 {
 	if (pca_status.index < pca_status.in_use[pca_status.current]) {
 		cpu_disable_intr();
Index: dev/sound/isa/i386/spkr/spkr.c
===================================================================
RCS file: /cvs/src/sys/dev/sound/isa/i386/spkr/spkr.c,v
retrieving revision 1.8
diff -u -r1.8 spkr.c
--- dev/sound/isa/i386/spkr/spkr.c	27 Aug 2003 06:48:15 -0000	1.8
+++ dev/sound/isa/i386/spkr/spkr.c	26 Jan 2004 06:32:11 -0000
@@ -15,6 +15,7 @@
 #include <sys/uio.h>
 #include <sys/conf.h>
 #include <sys/ctype.h>
+#include <sys/systimer.h>
 #include <bus/isa/i386/isa.h>
 #include <i386/isa/timerreg.h>
 #include <machine/clock.h>
@@ -79,26 +80,22 @@
 	unsigned int thz, ticks;
 {
     unsigned int divisor;
-    int sps;
 
     if (thz <= 0)
 	return;
 
-    divisor = timer_freq / thz;
+    divisor = cputimer_freq / thz;
 
 #ifdef DEBUG
     (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
 #endif /* DEBUG */
 
     /* set timer to generate clicks at given frequency in Hertz */
-    sps = splclock();
 
     if (acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT)) {
 	/* enter list of waiting procs ??? */
-	splx(sps);
 	return;
     }
-    splx(sps);
     clock_lock();
     outb(TIMER_CNTR2, (divisor & 0xff));	/* send lo byte */
     outb(TIMER_CNTR2, (divisor >> 8));	/* send hi byte */
@@ -115,9 +112,7 @@
     if (ticks > 0)
 	tsleep((caddr_t)&endtone, PCATCH, "spkrtn", ticks);
     outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
-    sps = splclock();
     release_timer2();
-    splx(sps);
 }
 
 /* rest for given number of ticks */
Index: i386/i386/elan-mmcr.c
===================================================================
RCS file: /cvs/src/sys/i386/i386/elan-mmcr.c,v
retrieving revision 1.4
diff -u -r1.4 elan-mmcr.c
--- i386/i386/elan-mmcr.c	21 Jul 2003 07:57:43 -0000	1.4
+++ i386/i386/elan-mmcr.c	25 Jan 2004 01:38:19 -0000
@@ -34,6 +34,7 @@
 
 uint16_t *elan_mmcr;
 
+#if 0
 
 static unsigned
 elan_get_timecount(struct timecounter *tc)
@@ -49,6 +50,8 @@
 	"ELAN"
 };
 
+#endif
+
 void
 init_AMD_Elan_sc520(void)
 {
@@ -74,9 +77,11 @@
 	if (bootverbose)
 		printf("sysctl machdep.i8254_freq=%d returns %d\n", new, i);
 
+#if 0
 	/* Start GP timer #2 and use it as timecounter, hz permitting */
 	elan_mmcr[0xc82 / 2] = 0xc001;
 	init_timecounter(&elan_timecounter);
+#endif
 }
 
 
Index: i386/i386/mp_clock.c
===================================================================
RCS file: /cvs/src/sys/i386/i386/mp_clock.c,v
retrieving revision 1.3
diff -u -r1.3 mp_clock.c
--- i386/i386/mp_clock.c	7 Aug 2003 21:17:22 -0000	1.3
+++ i386/i386/mp_clock.c	27 Jan 2004 06:44:52 -0000
@@ -46,6 +46,8 @@
 
 #include <bus/pci/pcivar.h>
 
+#if 0
+
 static unsigned piix_get_timecount(struct timecounter *tc);
 
 static u_int32_t piix_timecounter_address;
@@ -140,3 +142,6 @@
 static devclass_t piix_devclass;
 
 DRIVER_MODULE(piix, pci, piix_driver, piix_devclass, 0, 0);
+
+#endif
+
Index: i386/i386/mp_machdep.c
===================================================================
RCS file: /cvs/src/sys/i386/i386/mp_machdep.c,v
retrieving revision 1.20
diff -u -r1.20 mp_machdep.c
--- i386/i386/mp_machdep.c	9 Jan 2004 20:49:39 -0000	1.20
+++ i386/i386/mp_machdep.c	25 Jan 2004 08:10:14 -0000
@@ -2452,10 +2452,8 @@
 	 */
 	kmem_cpu_init();
 
-	/*
-	 * Startup helper thread(s) one per cpu.
-	 */
-	sched_thread_init();
+	sched_thread_init();	/* startup helper thread(s) one per cpu */
+	initclocks_pcpu();	/* clock interrupts (via IPIs) */
 
 	/*
 	 * The idle loop doesn't expect the BGL to be held and while
@@ -2503,6 +2501,7 @@
 }
 #endif
 
+#if 0
 static void
 forwarded_statclock(int id, int pscnt, int *astmap)
 {
@@ -2595,7 +2594,9 @@
 	}
 #endif
 }
+#endif
 
+#if 0
 void
 forward_statclock(int pscnt)
 {
@@ -2655,7 +2656,9 @@
 	if (map != 0)
 		resched_cpus(map);
 }
+#endif
 
+#if 0
 void 
 forward_hardclock(int pscnt)
 {
@@ -2739,6 +2742,7 @@
 	if (map != 0) 
 		resched_cpus(map);
 }
+#endif
 
 #endif /* BETTER_CLOCK */
 
Index: i386/include/clock.h
===================================================================
RCS file: /cvs/src/sys/i386/include/clock.h,v
retrieving revision 1.4
diff -u -r1.4 clock.h
--- i386/include/clock.h	26 Aug 2003 21:42:18 -0000	1.4
+++ i386/include/clock.h	25 Jan 2004 08:46:06 -0000
@@ -30,19 +30,12 @@
 /*
  * Driver to clock driver interface.
  */
-struct clockframe;
 
-int	acquire_timer0 (int rate,
-			    void (*function)(struct clockframe *frame));
-int	acquire_timer2 (int mode);
-int	release_timer0 (void);
-int	release_timer2 (void);
 #ifndef PC98
 int	rtcin (int val);
-#else
-int	acquire_timer1 (int mode);
-int	release_timer1 (void);
 #endif
+int	acquire_timer2 (int mode);
+int	release_timer2 (void);
 int	sysbeep (int pitch, int period);
 void	timer_restore (void);
 
Index: i386/include/cpu.h
===================================================================
RCS file: /cvs/src/sys/i386/include/cpu.h,v
retrieving revision 1.12
diff -u -r1.12 cpu.h
--- i386/include/cpu.h	21 Nov 2003 05:29:08 -0000	1.12
+++ i386/include/cpu.h	24 Jan 2004 23:36:59 -0000
@@ -59,10 +59,10 @@
 #define cpu_setstack(p, ap)		((p)->p_md.md_regs[SP] = (ap))
 
 #define	CLKF_USERMODE(framep) \
-	((ISPL((framep)->cf_cs) == SEL_UPL) || (framep->cf_eflags & PSL_VM))
+	((ISPL((framep)->if_cs) == SEL_UPL) || (framep->if_eflags & PSL_VM))
 
 #define CLKF_INTR(framep)	(mycpu->gd_intr_nesting_level > 1 || (curthread->td_flags & TDF_INTTHREAD))
-#define	CLKF_PC(framep)		((framep)->cf_eip)
+#define	CLKF_PC(framep)		((framep)->if_eip)
 
 /*
  * Preempt the current process if in interrupt from user mode,
Index: i386/include/frame.h
===================================================================
RCS file: /cvs/src/sys/i386/include/frame.h,v
retrieving revision 1.3
diff -u -r1.3 frame.h
--- i386/include/frame.h	26 Aug 2003 21:42:18 -0000	1.3
+++ i386/include/frame.h	24 Jan 2004 23:29:31 -0000
@@ -129,33 +129,6 @@
 	int	if_ss;
 };
 
-/* frame of clock (same as interrupt frame) */
-
-struct clockframe {
-	int	cf_vec;
-	int	cf_ppl;
-	int	cf_fs;
-	int	cf_es;
-	int	cf_ds;
-	int	cf_edi;
-	int	cf_esi;
-	int	cf_ebp;
-	int	:32;
-	int	cf_ebx;
-	int	cf_edx;
-	int	cf_ecx;
-	int	cf_eax;
-	int	:32;		/* for compat with trap frame - trapno */
-	int	:32;		/* for compat with trap frame - err */
-	/* below portion defined in 386 hardware */
-	int	cf_eip;
-	int	cf_cs;
-	int	cf_eflags;
-	/* below only when crossing rings (e.g. user to kernel) */
-	int	cf_esp;
-	int	cf_ss;
-};
-
 int	kdb_trap (int, int, struct trapframe *);
 extern  int (*pmath_emulate) (struct trapframe *);
 
Index: i386/isa/apic_vector.s
===================================================================
RCS file: /cvs/src/sys/i386/isa/apic_vector.s,v
retrieving revision 1.14
diff -u -r1.14 apic_vector.s
--- i386/isa/apic_vector.s	25 Sep 2003 23:49:08 -0000	1.14
+++ i386/isa/apic_vector.s	25 Jan 2004 09:05:41 -0000
@@ -484,11 +484,13 @@
 	movl	PCPU(curthread),%ebx
 	cmpl	$TDPRI_CRIT,TD_PRI(%ebx)
 	jge	1f
+	subl	$8,%esp			/* make same as interrupt frame */
 	incl	PCPU(intr_nesting_level)
 	addl	$TDPRI_CRIT,TD_PRI(%ebx)
-	call	lwkt_process_ipiq
+	call	lwkt_process_ipiq_frame
 	subl	$TDPRI_CRIT,TD_PRI(%ebx)
 	decl	PCPU(intr_nesting_level)
+	addl	$8,%esp
 	pushl	TD_CPL(%ebx)
 	MEXITCOUNT
 	jmp	doreti
@@ -525,12 +527,8 @@
 	FAST_INTR(23,fastintr23)
 	
 	/* YYY what is this garbage? */
-#define	CLKINTR_PENDING							\
-	call	clock_lock ;						\
-	movl $1,CNAME(clkintr_pending) ;				\
-	call	clock_unlock ;						\
 
-	INTR(0,intr0, CLKINTR_PENDING)
+	INTR(0,intr0,)
 	INTR(1,intr1,)
 	INTR(2,intr2,)
 	INTR(3,intr3,)
Index: i386/isa/clock.c
===================================================================
RCS file: /cvs/src/sys/i386/isa/clock.c,v
retrieving revision 1.10
diff -u -r1.10 clock.c
--- i386/isa/clock.c	8 Jan 2004 08:11:12 -0000	1.10
+++ i386/isa/clock.c	27 Jan 2004 23:43:10 -0000
@@ -62,6 +62,10 @@
 #endif
 #include <sys/sysctl.h>
 #include <sys/cons.h>
+#include <sys/systimer.h>
+#include <sys/globaldata.h>
+#include <sys/thread2.h>
+#include <sys/systimer.h>
 
 #include <machine/clock.h>
 #ifdef CLK_CALIBRATION_LOOP
@@ -98,6 +102,7 @@
 static u_long read_intr_count (int vec);
 static void setup_8254_mixed_mode (void);
 #endif
+static void i8254_restore(void);
 
 /*
  * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we
@@ -106,201 +111,73 @@
 #define	LEAPYEAR(y) ((u_int)(y) % 4 == 0)
 #define DAYSPERYEAR   (31+28+31+30+31+30+31+31+30+31+30+31)
 
-#define	TIMER_DIV(x) (timer_freq / (x))
-#define FRAC_ADJUST(x) (timer_freq - ((timer_freq / (x)) * (x)))
-
-/*
- * Time in timer cycles that it takes for microtime() to disable interrupts
- * and latch the count.  microtime() currently uses "cli; outb ..." so it
- * normally takes less than 2 timer cycles.  Add a few for cache misses.
- * Add a few more to allow for latency in bogus calls to microtime() with
- * interrupts already disabled.
- */
-#define	TIMER0_LATCH_COUNT	20
+#ifndef TIMER_FREQ
+#define TIMER_FREQ   1193182
+#endif
 
-/*
- * Maximum frequency that we are willing to allow for timer0.  Must be
- * low enough to guarantee that the timer interrupt handler returns
- * before the next timer interrupt.
- */
-#define	TIMER0_MAX_FREQ		20000
+#define TIMER_SELX	TIMER_SEL2
+#define TIMER_CNTRX	TIMER_CNTR2
 
 int	adjkerntz;		/* local offset from GMT in seconds */
-int	clkintr_pending;
 int	disable_rtc_set;	/* disable resettodr() if != 0 */
 volatile u_int	idelayed;
-int	statclock_disable;
+int	statclock_disable = 1;	/* we don't use the statclock right now */
 u_int	stat_imask = SWI_CLOCK_MASK;
-#ifndef TIMER_FREQ
-#define TIMER_FREQ   1193182
+u_int	cputimer_freq = TIMER_FREQ;
+#if 0
+int64_t	cputimer_freq64_usec = ((int64_t)TIMER_FREQ << 32) / 1000000;
+int64_t	cputimer_freq64_nsec = ((int64_t)TIMER_FREQ << 32) / 1000000000LL;
 #endif
-u_int	timer_freq = TIMER_FREQ;
-int	timer0_max_count;
-u_int	timer0_frac_freq;
+int64_t	cputimer_freq64_usec = (1000000LL << 32) / TIMER_FREQ;
+int64_t	cputimer_freq64_nsec = (1000000000LL << 32) / TIMER_FREQ;
 u_int	tsc_freq;
 int	tsc_is_broken;
 int	wall_cmos_clock;	/* wall CMOS clock assumed if != 0 */
+int	timer0_running;
+enum tstate { RELEASED, ACQUIRED };
+enum tstate timer0_state;
+enum tstate timer1_state;
+enum tstate timer2_state;
 
 static	int	beeping = 0;
 static	u_int	clk_imask = HWI_MASK | SWI_MASK;
 static	const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
-static	u_int	hardclock_max_count;
-static	u_int32_t i8254_lastcount;
-static	u_int32_t i8254_offset;
-static	int	i8254_ticked;
-/*
- * XXX new_function and timer_func should not handle clockframes, but
- * timer_func currently needs to hold hardclock to handle the
- * timer0_state == 0 case.  We should use inthand_add()/inthand_remove()
- * to switch between clkintr() and a slightly different timerintr().
- */
-static	void	(*new_function) (struct clockframe *frame);
-static	u_int	new_rate;
 static	u_char	rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
 static	u_char	rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
-static	u_int	timer0_prescaler_count;
-
-/* Values for timerX_state: */
-#define	RELEASED	0
-#define	RELEASE_PENDING	1
-#define	ACQUIRED	2
-#define	ACQUIRE_PENDING	3
-
-static	u_char	timer0_state;
-static	u_char	timer2_state;
-static	void	(*timer_func) (struct clockframe *frame) = hardclock;
 static	u_int	tsc_present;
 
-static	unsigned i8254_get_timecount (struct timecounter *tc);
-static	unsigned tsc_get_timecount (struct timecounter *tc);
-static	void	set_timer_freq(u_int freq, int intr_freq);
-
-static struct timecounter tsc_timecounter = {
-	tsc_get_timecount,	/* get_timecount */
-	0,			/* no poll_pps */
- 	~0u,			/* counter_mask */
-	0,			/* frequency */
-	 "TSC"			/* name */
-};
-
-SYSCTL_OPAQUE(_debug, OID_AUTO, tsc_timecounter, CTLFLAG_RD, 
-	&tsc_timecounter, sizeof(tsc_timecounter), "S,timecounter", "");
-
-static struct timecounter i8254_timecounter = {
-	i8254_get_timecount,	/* get_timecount */
-	0,			/* no poll_pps */
-	~0u,			/* counter_mask */
-	0,			/* frequency */
-	"i8254"			/* name */
-};
-
-SYSCTL_OPAQUE(_debug, OID_AUTO, i8254_timecounter, CTLFLAG_RD, 
-	&i8254_timecounter, sizeof(i8254_timecounter), "S,timecounter", "");
-
+/*
+ * timer0 clock interrupt.  Timer0 is in one-shot mode and has stopped
+ * counting as of this interrupt.  We use timer1 in free-running mode (not
+ * generating any interrupts) as our main counter.  Each cpu has timeouts
+ * pending.
+ */
 static void
-clkintr(struct clockframe frame)
+clkintr(struct intrframe frame)
 {
-	int phase;
-	int delta;
-
-	if (timecounter->tc_get_timecount == i8254_get_timecount) {
-		clock_lock();
-		if (i8254_ticked) {
-			i8254_ticked = 0;
-		} else {
-			i8254_offset += timer0_max_count;
-			i8254_lastcount = 0;
-		}
-		/*
-		 * Lets say we are running at 100Hz.  Our counter load will
-		 * be 1193182 / 100 = 11931.82, which is really only 11931.
-		 * The fractional code accounts for the .82 count.  When it
-		 * exceeds 1.00 count we adjust the reload register by + 1
-		 * to compensate for the error.  We must also adjust 
-		 * i8254_offset.
-		 *
-		 * If we did not do this a high frequency would cause the
-		 * actual interrupt rate to seriously diverge from 'hz'.
-		 */
-		clkintr_pending = 0;
-		clock_unlock();
-	}
+	static sysclock_t timer1_count;
+	struct globaldata *gd = mycpu;
+	struct globaldata *gscan;
+	int n;
 
 	/*
-	 * Use the previously synchronized timecounter value to phase-sync
-	 * our hz clock interrupt.  We do this by reloading the initial count
-	 * register of timer0, which takes effect the next time it reloads.
+	 * SWSTROBE mode is a one-shot, the timer is no longer running
 	 */
-	phase = 1000000 / timer0_frac_freq;
-	delta = timecounter->tc_microtime.tv_usec % phase;
-#if 1
-	clock_lock();
-	if (delta < (phase >> 1)) {
-		/*
-		 * Current time is a bit past what we expect, speed up the
-		 * clock interrupt.
-		 */
-		outb(TIMER_CNTR0, timer0_max_count & 0xff);
-		outb(TIMER_CNTR0, timer0_max_count >> 8);
-	} else {
-		/*
-		 * Current time is a bit before what we expect, slow down
-		 * the clock interrupt.
-		 */
-		outb(TIMER_CNTR0, (timer0_max_count + 1) & 0xff);
-		outb(TIMER_CNTR0, (timer0_max_count + 1) >> 8);
-		++i8254_offset;	/* take into account extra count for tc==8254*/
-	}
-	clock_unlock();
-#endif
-
-	timer_func(&frame);
+	timer0_running = 0;
 
-	switch (timer0_state) {
-	case RELEASED:
-		setdelayed();
-		break;
-	case ACQUIRED:
-		timer0_prescaler_count += timer0_max_count;
-		if (timer0_prescaler_count >= hardclock_max_count) {
-			timer0_prescaler_count -= hardclock_max_count;
-			hardclock(&frame);
-			setdelayed();
-		}
-		break;
-	case ACQUIRE_PENDING:
-		clock_lock();
-		i8254_offset = i8254_get_timecount(NULL);
-		i8254_lastcount = 0;
-		timer0_max_count = TIMER_DIV(new_rate);
-		timer0_frac_freq = new_rate;
-		outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
-		outb(TIMER_CNTR0, timer0_max_count & 0xff);
-		outb(TIMER_CNTR0, timer0_max_count >> 8);
-		clock_unlock();
-		timer_func = new_function;
-		timer0_state = ACQUIRED;
-		setdelayed();
-		break;
-	case RELEASE_PENDING:
-		timer0_prescaler_count += timer0_max_count;
-		if (timer0_prescaler_count >= hardclock_max_count) {
-			clock_lock();
-			i8254_offset = i8254_get_timecount(NULL);
-			i8254_lastcount = 0;
-			timer0_max_count = hardclock_max_count;
-			outb(TIMER_MODE,
-			     TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
-			outb(TIMER_CNTR0, timer0_max_count & 0xff);
-			outb(TIMER_CNTR0, timer0_max_count >> 8);
-			clock_unlock();
-			timer0_prescaler_count = 0;
-			timer_func = hardclock;
-			timer0_state = RELEASED;
-			hardclock(&frame);
-			setdelayed();
-		}
-		break;
+	/*
+	 * XXX this could be done more efficiently by using a bitmask?
+	 */
+	timer1_count = cputimer_count();
+	for (n = 0; n < ncpus; ++n) {
+	    gscan = globaldata_find(n);
+	    if (gscan->gd_nextclock == 0)
+		continue;
+	    if (gscan != gd) {
+		lwkt_send_ipiq(gscan->gd_cpuid, (ipifunc_t)systimer_intr, &timer1_count);
+	    } else {
+		systimer_intr(&timer1_count, &frame);
+	    }
 	}
 #if NMCA > 0
 	/* Reset clock interrupt by asserting bit 7 of port 0x61 */
@@ -309,46 +186,16 @@
 #endif
 }
 
+
 /*
- * The acquire and release functions must be called at ipl >= splclock().
+ * NOTE! not MP safe.
  */
 int
-acquire_timer0(int rate, void (*function) (struct clockframe *frame))
-{
-	static int old_rate;
-
-	if (rate <= 0 || rate > TIMER0_MAX_FREQ)
-		return (-1);
-	switch (timer0_state) {
-
-	case RELEASED:
-		timer0_state = ACQUIRE_PENDING;
-		break;
-
-	case RELEASE_PENDING:
-		if (rate != old_rate)
-			return (-1);
-		/*
-		 * The timer has been released recently, but is being
-		 * re-acquired before the release completed.  In this
-		 * case, we simply reclaim it as if it had not been
-		 * released at all.
-		 */
-		timer0_state = ACQUIRED;
-		break;
-
-	default:
-		return (-1);	/* busy */
-	}
-	new_function = function;
-	old_rate = new_rate = rate;
-	return (0);
-}
-
-int
 acquire_timer2(int mode)
 {
-
+	/* Timer2 is being used for time count operation */
+	return(-1);
+#if 0
 	if (timer2_state != RELEASED)
 		return (-1);
 	timer2_state = ACQUIRED;
@@ -356,43 +203,20 @@
 	/*
 	 * This access to the timer registers is as atomic as possible
 	 * because it is a single instruction.  We could do better if we
-	 * knew the rate.  Use of splclock() limits glitches to 10-100us,
-	 * and this is probably good enough for timer2, so we aren't as
-	 * careful with it as with timer0.
+	 * knew the rate.
 	 */
 	outb(TIMER_MODE, TIMER_SEL2 | (mode & 0x3f));
-
-	return (0);
-}
-
-int
-release_timer0()
-{
-	switch (timer0_state) {
-
-	case ACQUIRED:
-		timer0_state = RELEASE_PENDING;
-		break;
-
-	case ACQUIRE_PENDING:
-		/* Nothing happened yet, release quickly. */
-		timer0_state = RELEASED;
-		break;
-
-	default:
-		return (-1);
-	}
 	return (0);
+#endif
 }
 
 int
 release_timer2()
 {
-
 	if (timer2_state != ACQUIRED)
 		return (-1);
-	timer2_state = RELEASED;
 	outb(TIMER_MODE, TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT);
+	timer2_state = RELEASED;
 	return (0);
 }
 
@@ -418,10 +242,11 @@
  * in the statistics, but the stat clock will no longer stop.
  */
 static void
-rtcintr(struct clockframe frame)
+rtcintr(struct intrframe frame)
 {
 	while (rtcin(RTC_INTR) & RTCIR_PERIOD)
-		statclock(&frame);
+		;
+		/* statclock(&frame); no longer used */
 }
 
 #include "opt_ddb.h"
@@ -437,26 +262,82 @@
 }
 #endif /* DDB */
 
-static int
-getit(void)
+/*
+ * Convert a frequency to a cpu timer count.
+ */
+sysclock_t
+cputimer_fromhz(int freq)
+{
+	return(cputimer_freq / freq + 1);
+}
+
+sysclock_t
+cputimer_fromus(int us)
 {
-	int high, low;
+	return((int64_t)cputimer_freq * us / 1000000);
+}
+
+/*
+ * Return the current cpu timer count as a 32 bit integer.
+ */
+sysclock_t
+cputimer_count(void)
+{
+	static sysclock_t cputimer_base;
+	static __uint16_t cputimer_last;
+	__uint16_t count;
+	sysclock_t ret;
 
 	clock_lock();
+	outb(TIMER_MODE, TIMER_SELX | TIMER_LATCH);
+	count = (__uint8_t)inb(TIMER_CNTRX);		/* get countdown */
+	count |= ((__uint8_t)inb(TIMER_CNTRX) << 8);
+	count = -count;					/* -> countup */
+	if (count < cputimer_last)			/* rollover */
+		cputimer_base += 0x00010000;
+	ret = cputimer_base | count;
+	cputimer_last = count;
+	clock_unlock();
+	return(ret);
+}
 
-	/* Select timer0 and latch counter value. */
-	outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
+/*
+ * Reload for the next timeout.  It is possible for the reload value
+ * to be 0 or negative, indicating that an immediate timer interrupt
+ * is desired.  For now make the minimum 2 ticks.
+ */
+void
+cputimer_intr_reload(sysclock_t reload)
+{
+    __uint16_t count;
 
-	low = inb(TIMER_CNTR0);
-	high = inb(TIMER_CNTR0);
+    if ((int)reload < 2)
+	reload = 2;
 
-	clock_unlock();
-	return ((high << 8) | low);
+    clock_lock();
+    if (timer0_running) {
+	outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);	/* count-down timer */
+	count = (__uint8_t)inb(TIMER_CNTR0);		/* lsb */
+	count |= ((__uint8_t)inb(TIMER_CNTR0) << 8);	/* msb */
+	if (reload < count) {
+	    outb(TIMER_MODE, TIMER_SEL0 | TIMER_SWSTROBE | TIMER_16BIT);
+	    outb(TIMER_CNTR0, (__uint8_t)reload); 	/* lsb */
+	    outb(TIMER_CNTR0, (__uint8_t)(reload >> 8)); /* msb */
+	}
+    } else {
+	timer0_running = 1;
+	if (reload > 0xFFFF)
+	    reload = 0;		/* full count */
+	outb(TIMER_MODE, TIMER_SEL0 | TIMER_SWSTROBE | TIMER_16BIT);
+	outb(TIMER_CNTR0, (__uint8_t)reload); 		/* lsb */
+	outb(TIMER_CNTR0, (__uint8_t)(reload >> 8));	/* msb */
+    }
+    clock_unlock();
 }
 
 /*
  * Wait "n" microseconds.
- * Relies on timer 1 counting down from (timer_freq / hz)
+ * Relies on timer 1 counting down from (cputimer_freq / hz)
  * Note: timer had better have been programmed before this is first used!
  */
 void
@@ -482,8 +363,8 @@
 	 * Guard against the timer being uninitialized if we are called
 	 * early for console i/o.
 	 */
-	if (timer0_max_count == 0)
-		set_timer_freq(timer_freq, hz);
+	if (timer0_state == RELEASED)
+		i8254_restore();
 
 	/*
 	 * Read the counter first, so that the rest of the setup overhead is
@@ -492,15 +373,15 @@
 	 * takes about 6 usec on a 486/33 and 13 usec on a 386/20.  The
 	 * multiplications and divisions to scale the count take a while).
 	 */
-	prev_tick = getit();
+	prev_tick = cputimer_count();
 	n -= 0;			/* XXX actually guess no initial overhead */
 	/*
-	 * Calculate (n * (timer_freq / 1e6)) without using floating point
+	 * Calculate (n * (cputimer_freq / 1e6)) without using floating point
 	 * and without any avoidable overflows.
 	 */
-	if (n <= 0)
+	if (n <= 0) {
 		ticks_left = 0;
-	else if (n < 256)
+	} else if (n < 256) {
 		/*
 		 * Use fixed point to avoid a slow division by 1000000.
 		 * 39099 = 1193182 * 2^15 / 10^6 rounded to nearest.
@@ -508,34 +389,26 @@
 		 * for n between 0 and 256.
 		 */
 		ticks_left = ((u_int)n * 39099 + (1 << 15) - 1) >> 15;
-	else
+	} else {
 		/*
 		 * Don't bother using fixed point, although gcc-2.7.2
 		 * generates particularly poor code for the long long
 		 * division, since even the slow way will complete long
 		 * before the delay is up (unless we're interrupted).
 		 */
-		ticks_left = ((u_int)n * (long long)timer_freq + 999999)
+		ticks_left = ((u_int)n * (long long)cputimer_freq + 999999)
 			     / 1000000;
+	}
 
 	while (ticks_left > 0) {
-		tick = getit();
+		tick = cputimer_count();
 #ifdef DELAYDEBUG
 		++getit_calls;
 #endif
-		delta = prev_tick - tick;
+		delta = tick - prev_tick;
 		prev_tick = tick;
-		if (delta < 0) {
-			delta += timer0_max_count;
-			/*
-			 * Guard against timer0_max_count being wrong.
-			 * This shouldn't happen in normal operation,
-			 * but it may happen if set_timer_freq() is
-			 * traced.
-			 */
-			if (delta < 0)
-				delta = 0;
-		}
+		if (delta < 0)
+			delta = 0;
 		ticks_left -= delta;
 	}
 #ifdef DELAYDEBUG
@@ -549,32 +422,26 @@
 sysbeepstop(void *chan)
 {
 	outb(IO_PPI, inb(IO_PPI)&0xFC);	/* disable counter2 output to speaker */
-	release_timer2();
 	beeping = 0;
+	release_timer2();
 }
 
 int
 sysbeep(int pitch, int period)
 {
-	int x = splclock();
-
 	if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
-		if (!beeping) {
-			/* Something else owns it. */
-			splx(x);
-			return (-1); /* XXX Should be EBUSY, but nobody cares anyway. */
-		}
-	clock_lock();
+		return(-1);
+	/*
+	 * Nobody else is using timer2, we do not need the clock lock
+	 */
 	outb(TIMER_CNTR2, pitch);
 	outb(TIMER_CNTR2, (pitch>>8));
-	clock_unlock();
 	if (!beeping) {
 		/* enable counter2 output to speaker */
 		outb(IO_PPI, inb(IO_PPI) | 3);
 		beeping = period;
 		timeout(sysbeepstop, (void *)NULL, period);
 	}
-	splx(x);
 	return (0);
 }
 
@@ -654,9 +521,7 @@
 	}
 
 	/* Start keeping track of the i8254 counter. */
-	prev_count = getit();
-	if (prev_count == 0 || prev_count > timer0_max_count)
-		goto fail;
+	prev_count = cputimer_count();
 	tot_count = 0;
 
 	if (tsc_present) 
@@ -678,13 +543,8 @@
 	for (;;) {
 		if (!(rtcin(RTC_STATUSA) & RTCSA_TUP))
 			sec = rtcin(RTC_SEC);
-		count = getit();
-		if (count == 0 || count > timer0_max_count)
-			goto fail;
-		if (count > prev_count)
-			tot_count += prev_count - (count - timer0_max_count);
-		else
-			tot_count += prev_count - count;
+		count = cputimer_count();
+		tot_count += (int)(count - prev_count);
 		prev_count = count;
 		if (sec != start_sec)
 			break;
@@ -709,42 +569,29 @@
 fail:
 	if (bootverbose)
 	        printf("failed, using default i8254 clock of %u Hz\n",
-		       timer_freq);
-	return (timer_freq);
-}
-
-static void
-set_timer_freq(u_int freq, int intr_freq)
-{
-	int new_timer0_max_count;
-
-	clock_lock();
-	timer_freq = freq;
-	new_timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
-	timer0_frac_freq = intr_freq;
-	if (new_timer0_max_count != timer0_max_count) {
-		timer0_max_count = new_timer0_max_count;
-		outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
-		outb(TIMER_CNTR0, timer0_max_count & 0xff);
-		outb(TIMER_CNTR0, timer0_max_count >> 8);
-	}
-	clock_unlock();
+		       cputimer_freq);
+	return (cputimer_freq);
 }
 
 static void
 i8254_restore(void)
 {
+	timer0_state = ACQUIRED;
+	timer1_state = ACQUIRED;
 	clock_lock();
-	outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
-	outb(TIMER_CNTR0, timer0_max_count & 0xff);
-	outb(TIMER_CNTR0, timer0_max_count >> 8);
+	outb(TIMER_MODE, TIMER_SEL0 | TIMER_SWSTROBE | TIMER_16BIT);
+	outb(TIMER_CNTR0, 2);	/* lsb */
+	outb(TIMER_CNTR0, 0);	/* msb */
+	outb(TIMER_MODE, TIMER_SELX | TIMER_RATEGEN | TIMER_16BIT);
+	outb(TIMER_CNTRX, 0);	/* lsb */
+	outb(TIMER_CNTRX, 0);	/* msb */
+	outb(IO_PPI, inb(IO_PPI) | 1);	/* bit 0: enable gate, bit 1: spkr */
 	clock_unlock();
 }
 
 static void
 rtc_restore(void)
 {
-
 	/* Restore all of the RTC's "status" (actually, control) registers. */
 	writertc(RTC_STATUSB, RTCSB_24HR);
 	writertc(RTC_STATUSA, rtc_statusa);
@@ -761,29 +608,41 @@
 void
 timer_restore(void)
 {
-
 	i8254_restore();		/* restore timer_freq and hz */
 	rtc_restore();			/* reenable RTC interrupts */
 }
 
 /*
  * Initialize 8254 timer 0 early so that it can be used in DELAY().
- * XXX initialization of other timers is unintentionally left blank.
  */
 void
 startrtclock()
 {
 	u_int delta, freq;
 
+	/* 
+	 * Can we use the TSC?
+	 */
 	if (cpu_feature & CPUID_TSC)
 		tsc_present = 1;
 	else
 		tsc_present = 0;
 
+	/*
+	 * Initial RTC state, don't do anything unexpected
+	 */
 	writertc(RTC_STATUSA, rtc_statusa);
 	writertc(RTC_STATUSB, RTCSB_24HR);
 
-	set_timer_freq(timer_freq, hz);
+	/*
+	 * Set the 8254 timer0 in TIMER_SWSTROBE mode and cause it to 
+	 * generate an interrupt, which we will ignore for now.
+	 *
+	 * Set the 8254 timer1 in TIMER_RATEGEN mode and load 0x0000
+	 * (so it counts a full 2^16 and repeats).  We will use this timer
+	 * for our counting.
+	 */
+	i8254_restore();
 	freq = calibrate_clocks();
 #ifdef CLK_CALIBRATION_LOOP
 	if (bootverbose) {
@@ -799,27 +658,26 @@
 	 * Otherwise use the default, and don't use the calibrated i586
 	 * frequency.
 	 */
-	delta = freq > timer_freq ? freq - timer_freq : timer_freq - freq;
-	if (delta < timer_freq / 100) {
+	delta = freq > cputimer_freq ? 
+			freq - cputimer_freq : cputimer_freq - freq;
+	if (delta < cputimer_freq / 100) {
 #ifndef CLK_USE_I8254_CALIBRATION
 		if (bootverbose)
 			printf(
 "CLK_USE_I8254_CALIBRATION not specified - using default frequency\n");
-		freq = timer_freq;
+		freq = cputimer_freq;
 #endif
-		timer_freq = freq;
+		cputimer_freq = freq;
+		cputimer_freq64_usec = (1000000LL << 32) / freq;
+		cputimer_freq64_nsec = (1000000000LL << 32) / freq;
 	} else {
 		if (bootverbose)
 			printf(
 		    "%d Hz differs from default of %d Hz by more than 1%%\n",
-			       freq, timer_freq);
+			       freq, cputimer_freq);
 		tsc_freq = 0;
 	}
 
-	set_timer_freq(timer_freq, hz);
-	i8254_timecounter.tc_frequency = timer_freq;
-	init_timecounter(&i8254_timecounter);
-
 #ifndef CLK_USE_TSC_CALIBRATION
 	if (tsc_freq != 0) {
 		if (bootverbose)
@@ -866,11 +724,6 @@
 	return;
 #endif /* NAPM > 0 */
 
-	if (tsc_present && tsc_freq != 0 && !tsc_is_broken) {
-		tsc_timecounter.tc_frequency = tsc_freq;
-		init_timecounter(&tsc_timecounter);
-	}
-
 #endif /* !defined(SMP) */
 }
 
@@ -884,15 +737,13 @@
 	unsigned long	sec, days;
 	int		yd;
 	int		year, month;
-	int		y, m, s;
+	int		y, m;
 	struct timespec ts;
 
 	if (base) {
-		s = splclock();
 		ts.tv_sec = base;
 		ts.tv_nsec = 0;
-		set_timecounter(&ts);
-		splx(s);
+		set_timeofday(&ts);
 	}
 
 	/* Look if we have a RTC present and the time is valid */
@@ -901,10 +752,10 @@
 
 	/* wait for time update to complete */
 	/* If RTCSA_TUP is zero, we have at least 244us before next update */
-	s = splhigh();
+	crit_enter();
 	while (rtcin(RTC_STATUSA) & RTCSA_TUP) {
-		splx(s);
-		s = splhigh();
+		crit_exit();
+		crit_enter();
 	}
 
 	days = 0;
@@ -916,7 +767,7 @@
 		year += 100;
 #endif
 	if (year < 1970) {
-		splx(s);
+		crit_exit();
 		goto wrong_time;
 	}
 	month = readrtc(RTC_MONTH);
@@ -942,9 +793,9 @@
 		/* badly off, adjust it */
 		ts.tv_sec = sec;
 		ts.tv_nsec = 0;
-		set_timecounter(&ts);
+		set_timeofday(&ts);
 	}
-	splx(s);
+	crit_exit();
 	return;
 
 wrong_time:
@@ -959,15 +810,14 @@
 resettodr()
 {
 	unsigned long	tm;
-	int		y, m, s;
+	int		y, m;
 
 	if (disable_rtc_set)
 		return;
 
-	s = splclock();
 	tm = time_second;
-	splx(s);
 
+	crit_enter();
 	/* Disable RTC updates and interrupts. */
 	writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR);
 
@@ -1007,11 +857,14 @@
 
 	/* Reenable RTC updates and interrupts. */
 	writertc(RTC_STATUSB, rtc_statusb);
+	crit_exit();
 }
 
 
 /*
- * Start both clocks running.
+ * Start both clocks running.  DragonFly note: the stat clock is no longer
+ * used.  Instead, 8254 based systimers are used for all major clock
+ * interrupts.  statclock_disable is set by default.
  */
 void
 cpu_initclocks()
@@ -1070,36 +923,44 @@
 	writertc(RTC_STATUSA, rtc_statusa);
 	writertc(RTC_STATUSB, RTCSB_24HR);
 
-	/* Don't bother enabling the statistics clock. */
-	if (statclock_disable)
-		return;
-	diag = rtcin(RTC_DIAG);
-	if (diag != 0)
-		printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
+	if (statclock_disable == 0) {
+		diag = rtcin(RTC_DIAG);
+		if (diag != 0)
+			printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
 
 #ifdef APIC_IO
-	if (isa_apic_irq(8) != 8)
-		panic("APIC RTC != 8");
+		if (isa_apic_irq(8) != 8)
+			panic("APIC RTC != 8");
 #endif /* APIC_IO */
 
-	inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL, &stat_imask,
-		    INTR_EXCL | INTR_FAST);
+		inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL, &stat_imask,
+			    INTR_EXCL | INTR_FAST);
 
 #ifdef APIC_IO
-	INTREN(APIC_IRQ8);
+		INTREN(APIC_IRQ8);
 #else
-	INTREN(IRQ8);
+		INTREN(IRQ8);
 #endif /* APIC_IO */
 
-	writertc(RTC_STATUSB, rtc_statusb);
+		writertc(RTC_STATUSB, rtc_statusb);
+	}
 
 #ifdef APIC_IO
 	if (apic_8254_trial) {
-		
+		sysclock_t base;
+		int lastcnt = read_intr_count(apic_8254_intr);
+
+		/*
+		 * XXX this assumes the 8254 is the cpu timer.  Force an
+		 * 8254 Timer0 interrupt and wait 1/100s for it to happen,
+		 * then see if we got it.
+		 */
 		printf("APIC_IO: Testing 8254 interrupt delivery\n");
-		while (read_intr_count(8) < 6)
+		cputimer_intr_reload(2);	/* XXX assumes 8254 */
+		base = cputimer_count();
+		while (cputimer_count() - base < cputimer_freq / 100)
 			;	/* nothing */
-		if (read_intr_count(apic_8254_intr) < 3) {
+		if (read_intr_count(apic_8254_intr) - lastcnt == 0) {
 			/* 
 			 * The MP table is broken.
 			 * The 8254 was not connected to the specified pin
@@ -1135,13 +996,14 @@
 	}
 	if (apic_int_type(0, 0) != 3 ||
 	    int_to_apicintpin[apic_8254_intr].ioapic != 0 ||
-	    int_to_apicintpin[apic_8254_intr].int_pin != 0)
+	    int_to_apicintpin[apic_8254_intr].int_pin != 0) {
 		printf("APIC_IO: routing 8254 via IOAPIC #%d intpin %d\n",
 		       int_to_apicintpin[apic_8254_intr].ioapic,
 		       int_to_apicintpin[apic_8254_intr].int_pin);
-	else
+	} else {
 		printf("APIC_IO: "
 		       "routing 8254 via 8259 and IOAPIC #0 intpin 0\n");
+	}
 #endif
 	
 }
@@ -1187,97 +1049,13 @@
 	writertc(RTC_STATUSA, rtc_statusa);
 }
 
-static int
-sysctl_machdep_i8254_freq(SYSCTL_HANDLER_ARGS)
-{
-	int error;
-	u_int freq;
-
-	/*
-	 * Use `i8254' instead of `timer' in external names because `timer'
-	 * is is too generic.  Should use it everywhere.
-	 */
-	freq = timer_freq;
-	error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
-	if (error == 0 && req->newptr != NULL) {
-		if (timer0_state != RELEASED)
-			return (EBUSY);	/* too much trouble to handle */
-		set_timer_freq(freq, hz);
-		i8254_timecounter.tc_frequency = freq;
-		update_timecounter(&i8254_timecounter);
-	}
-	return (error);
-}
-
-SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
-    0, sizeof(u_int), sysctl_machdep_i8254_freq, "IU", "");
-
-static int
-sysctl_machdep_tsc_freq(SYSCTL_HANDLER_ARGS)
-{
-	int error;
-	u_int freq;
-
-	if (tsc_timecounter.tc_frequency == 0)
-		return (EOPNOTSUPP);
-	freq = tsc_freq;
-	error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
-	if (error == 0 && req->newptr != NULL) {
-		tsc_freq = freq;
-		tsc_timecounter.tc_frequency = tsc_freq;
-		update_timecounter(&tsc_timecounter);
-	}
-	return (error);
-}
-
-SYSCTL_PROC(_machdep, OID_AUTO, tsc_freq, CTLTYPE_INT | CTLFLAG_RW,
-    0, sizeof(u_int), sysctl_machdep_tsc_freq, "IU", "");
-
-static unsigned
-i8254_get_timecount(struct timecounter *tc)
-{
-	u_int count;
-	u_long ef;
-	u_int high, low;
-
-	ef = read_eflags();
-	clock_lock();
-
-	/* 
-	 * Select timer0 and latch counter value.   Because we may reload
-	 * the counter with timer0_max_count + 1 to correct the frequency
-	 * our delta count calculation must use timer0_max_count + 1.
-	 */
-	outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
-
-	low = inb(TIMER_CNTR0);
-	high = inb(TIMER_CNTR0);
-	count = timer0_max_count + 1 - ((high << 8) | low);
-	if (count < i8254_lastcount ||
-	    (!i8254_ticked && (clkintr_pending ||
-	    ((count < 20 || (!(ef & PSL_I) && count < timer0_max_count / 2u)) &&
-#ifdef APIC_IO
-#define	lapic_irr1	((volatile u_int *)&lapic)[0x210 / 4]	/* XXX XXX */
-	    /* XXX this assumes that apic_8254_intr is < 24. */
-	    (lapic_irr1 & (1 << apic_8254_intr))))
-#else
-	    (inb(IO_ICU1) & 1)))
-#endif
-	    )) {
-		i8254_ticked = 1;
-		i8254_offset += timer0_max_count;
-	}
-	i8254_lastcount = count;
-	count += i8254_offset;
-	clock_unlock();
-	return (count);
-}
-
+#if 0
 static unsigned
 tsc_get_timecount(struct timecounter *tc)
 {
 	return (rdtsc());
 }
+#endif
 
 #ifdef KERN_TIMESTAMP
 #define KERN_TIMESTAMP_SIZE 16384
Index: i386/isa/icu_vector.s
===================================================================
RCS file: /cvs/src/sys/i386/isa/icu_vector.s,v
retrieving revision 1.15
diff -u -r1.15 icu_vector.s
--- i386/isa/icu_vector.s	7 Jan 2004 20:21:20 -0000	1.15
+++ i386/isa/icu_vector.s	25 Jan 2004 07:20:11 -0000
@@ -256,10 +256,8 @@
 	popl %ebp ;							\
 	ret ;								\
 
-#define	CLKINTR_PENDING	movl $1,CNAME(clkintr_pending)
-
 MCOUNT_LABEL(bintr)
-	FAST_INTR(0,fastintr0, IO_ICU1, ENABLE_ICU1, CLKINTR_PENDING)
+	FAST_INTR(0,fastintr0, IO_ICU1, ENABLE_ICU1,)
 	FAST_INTR(1,fastintr1, IO_ICU1, ENABLE_ICU1,)
 	FAST_INTR(2,fastintr2, IO_ICU1, ENABLE_ICU1,)
 	FAST_INTR(3,fastintr3, IO_ICU1, ENABLE_ICU1,)
@@ -276,7 +274,7 @@
 	FAST_INTR(14,fastintr14, IO_ICU2, ENABLE_ICU1_AND_2,)
 	FAST_INTR(15,fastintr15, IO_ICU2, ENABLE_ICU1_AND_2,)
 
-	INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al, CLKINTR_PENDING)
+	INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al,)
 	INTR(1,intr1, IO_ICU1, ENABLE_ICU1, al,)
 	INTR(2,intr2, IO_ICU1, ENABLE_ICU1, al,)
 	INTR(3,intr3, IO_ICU1, ENABLE_ICU1, al,)
Index: i386/isa/ipl.s
===================================================================
RCS file: /cvs/src/sys/i386/isa/ipl.s,v
retrieving revision 1.15
diff -u -r1.15 ipl.s
--- i386/isa/ipl.s	21 Nov 2003 05:29:08 -0000	1.15
+++ i386/isa/ipl.s	24 Jan 2004 23:51:55 -0000
@@ -273,7 +273,9 @@
 doreti_ipiq:
 	incl	PCPU(intr_nesting_level)
 	andl	$~RQF_IPIQ,PCPU(reqflags)
-	call	lwkt_process_ipiq
+	subl	$8,%esp			/* add dummy vec and ppl */
+	call	lwkt_process_ipiq_frame
+	addl	$8,%esp
 	decl	PCPU(intr_nesting_level)
 	movl	TD_CPL(%ebx),%eax	/* retrieve cpl again for loop */
 	jmp	doreti_next
Index: i386/isa/timerreg.h
===================================================================
RCS file: /cvs/src/sys/i386/isa/timerreg.h,v
retrieving revision 1.2
diff -u -r1.2 timerreg.h
--- i386/isa/timerreg.h	17 Jun 2003 04:28:37 -0000	1.2
+++ i386/isa/timerreg.h	25 Jan 2004 07:04:33 -0000
@@ -79,6 +79,45 @@
  *
  * Timer 0 is used to call hardclock.
  * Timer 1 is used to generate console beeps.
+ *
+ * TIMER_INTTC:		Interrupt on Terminal Count.  OUT initially low,
+ *				goes high on terminal count and remains
+ *				high until a new count or a mode 0 control
+ *				word is written.
+ *
+ * TIMER_ONESHOT:	Hardware Retriggerable One Shot.  Out initially high,
+ *			out goes low following the trigger and remains low
+ *			until terminal count, then goes high and remains
+ *			high until the next trigger.
+ *
+ * TIMER_RATEGEN:	Rate Generator.  OUT is initially high.  When the
+ *			count has decremented to 1 OUT goes low for one CLK
+ *			pulse, then goes high again.  Counter reloads and
+ *			the sequence is repeated.
+ *
+ * TIMER_SQWAVE:	Square Wave Generator.  OUT is initially high.  When
+ *			half the count is expired, OUT goes low.  Counter
+ *			reloads, OUT goes high, and the sequence repepats.
+ *
+ * TIMER_SWSTROBE:	S/W Triggered Strobe.  OUT initially high.  On 
+ *			terminal count OUT goes low for one CLK pulse
+ *			and then goes high again.  Counting stops.
+ *			The counting sequence is 'triggered' by writing 
+ *			the initial count.  Writing a control word and
+ *			initial count resets and reloads the counter.
+ *
+ * TIMER_HWSTROBE:	H/W Triggered Strobe.  OUT initially high.  A rising
+ *			edge on GATE loads the counter and counting begins.
+ *			On terminal count OUT goes low for one CLK and then
+ *			high again.
+ *
+ * NOTE: the largest possible initial count is 0x0000.  This is equivalent
+ * to 2^16 binary and 10^4 BCD counts.  The counter does not stop when it
+ * reaches zero.  In Modes INTTC, ONESHOT, SWSTROBE, and HWSTROBE the 
+ * counter wraps aroudn to the highest count (0xFFFF or 9999bcd) and
+ * continues counting.  In MODES RATEGEN and SQWAVE (which are periodic)
+ * the counter reloads itself with the initial count and continues counting
+ * from there.
  */
 
 /*
Index: kern/init_main.c
===================================================================
RCS file: /cvs/src/sys/kern/init_main.c,v
retrieving revision 1.26
diff -u -r1.26 init_main.c
--- kern/init_main.c	10 Nov 2003 06:12:13 -0000	1.26
+++ kern/init_main.c	25 Jan 2004 07:54:29 -0000
@@ -610,6 +610,7 @@
 mi_gdinit(struct globaldata *gd, int cpuid)
 {
 	TAILQ_INIT(&gd->gd_tdfreeq);    /* for pmap_{new,dispose}_thread() */
+	TAILQ_INIT(&gd->gd_systimerq);
 	gd->gd_cpuid = cpuid;
 	lwkt_gdinit(gd);
 }
Index: kern/kern_clock.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_clock.c,v
retrieving revision 1.14
diff -u -r1.14 kern_clock.c
--- kern/kern_clock.c	7 Jan 2004 20:21:46 -0000	1.14
+++ kern/kern_clock.c	27 Jan 2004 23:01:05 -0000
@@ -1,4 +1,5 @@
 /*-
+ * Copyright (c) 2004, Matthew Dillon <dillon@backplane.com>
  * Copyright (c) 1997, 1998 Poul-Henning Kamp <phk@FreeBSD.org>
  * Copyright (c) 1982, 1986, 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -74,23 +75,9 @@
 extern void hardclock_device_poll(void);
 #endif /* DEVICE_POLLING */
 
-/*
- * Number of timecounters used to implement stable storage
- */
-#ifndef NTIMECOUNTER
-#define NTIMECOUNTER	5
-#endif
-
-static MALLOC_DEFINE(M_TIMECOUNTER, "timecounter", 
-	"Timecounter stable storage");
-
 static void initclocks (void *dummy);
 SYSINIT(clocks, SI_SUB_CLOCKS, SI_ORDER_FIRST, initclocks, NULL)
 
-static void tco_forward (int force);
-static void tco_setscales (struct timecounter *tc);
-static __inline unsigned tco_delta (struct timecounter *tc);
-
 /*
  * Some of these don't belong here, but it's easiest to concentrate them.
  * Note that cp_time[] counts in microseconds, but most userland programs
@@ -106,165 +93,372 @@
 long tk_nout;
 long tk_rawcc;
 
-time_t time_second;
+/*
+ * boottime is used to calculate the 'real' uptime.  Do not confuse this with
+ * microuptime().  microtime() is not drift compensated.  The real uptime
+ * with compensation is nanotime() - bootime.
+ *
+ * basetime is used to calculate the compensated real time of day.  Chunky
+ * changes to the time, aka settimeofday(), are made by modifying basetime.
+ *
+ * The gd_time_seconds and gd_cpuclock_base fields remain fairly monotonic.
+ * Slight adjustments to gd_cpuclock_base are made to phase-lock it to
+ * the real time.
+ */
+struct timespec boottime;	/* boot time (realtime) for reference only */
+struct timespec basetime;	/* base time adjusts uptime -> realtime */
+time_t time_second;		/* read-only 'passive' uptime in seconds */
 
-struct	timeval boottime;
 SYSCTL_STRUCT(_kern, KERN_BOOTTIME, boottime, CTLFLAG_RD,
     &boottime, timeval, "System boottime");
+SYSCTL_STRUCT(_kern, OID_AUTO, basetime, CTLFLAG_RD,
+    &basetime, timeval, "System basetime");
 
-/*
- * Which update policy to use.
- *   0 - every tick, bad hardware may fail with "calcru negative..."
- *   1 - more resistent to the above hardware, but less efficient.
- */
-static int tco_method;
+static void hardclock(systimer_t info, struct intrframe *frame);
+static void statclock(systimer_t info, struct intrframe *frame);
+static void schedclock(systimer_t info, struct intrframe *frame);
+
+int	ticks;			/* system master ticks at hz */
+int64_t	nsec_adj;		/* ntpd per-tick adjustment in nsec << 32 */
+int64_t	nsec_acc;		/* accumulator */
 
 /*
- * Implement a dummy timecounter which we can use until we get a real one
- * in the air.  This allows the console and other early stuff to use
- * timeservices.
+ * Finish initializing clock frequencies and start all clocks running.
  */
-
-static unsigned 
-dummy_get_timecount(struct timecounter *tc)
+/* ARGSUSED*/
+static void
+initclocks(void *dummy)
 {
-	static unsigned now;
-	return (++now);
+	cpu_initclocks();
+#ifdef DEVICE_POLLING
+	init_device_poll();
+#endif
+	/*psratio = profhz / stathz;*/
+	initclocks_pcpu();
 }
 
-static struct timecounter dummy_timecounter = {
-	dummy_get_timecount,
-	0,
-	~0u,
-	1000000,
-	"dummy"
-};
+/*
+ * Called on a per-cpu basis
+ */
+void
+initclocks_pcpu(void)
+{
+	struct globaldata *gd = mycpu;
 
-struct timecounter *timecounter = &dummy_timecounter;
+	crit_enter();
+	if (gd->gd_cpuid == 0) {
+	    gd->gd_time_seconds = 1;
+	    gd->gd_cpuclock_base = cputimer_count();
+	} else {
+	    /* XXX */
+	    gd->gd_time_seconds = globaldata_find(0)->gd_time_seconds;
+	    gd->gd_cpuclock_base = globaldata_find(0)->gd_cpuclock_base;
+	}
+	systimer_init_periodic(&gd->gd_hardclock, hardclock, NULL, hz);
+	systimer_init_periodic(&gd->gd_statclock, statclock, NULL, stathz);
+	/* XXX correct the frequency for scheduler / estcpu tests */
+	systimer_init_periodic(&gd->gd_schedclock, schedclock, NULL, 10); 
+	crit_exit();
+}
 
 /*
- * Clock handling routines.
- *
- * This code is written to operate with two timers that run independently of
- * each other.
- *
- * The main timer, running hz times per second, is used to trigger interval
- * timers, timeouts and rescheduling as needed.
- *
- * The second timer handles kernel and user profiling,
- * and does resource use estimation.  If the second timer is programmable,
- * it is randomized to avoid aliasing between the two clocks.  For example,
- * the randomization prevents an adversary from always giving up the cpu
- * just before its quantum expires.  Otherwise, it would never accumulate
- * cpu ticks.  The mean frequency of the second timer is stathz.
- *
- * If no second timer exists, stathz will be zero; in this case we drive
- * profiling and statistics off the main clock.  This WILL NOT be accurate;
- * do not do it unless absolutely necessary.
- *
- * The statistics clock may (or may not) be run at a higher rate while
- * profiling.  This profile clock runs at profhz.  We require that profhz
- * be an integral multiple of stathz.
- *
- * If the statistics clock is running fast, it must be divided by the ratio
- * profhz/stathz for statistics.  (For profiling, every tick counts.)
- *
- * Time-of-day is maintained using a "timecounter", which may or may
- * not be related to the hardware generating the above mentioned
- * interrupts.
+ * This sets the current real time of day.  Timespecs are in seconds and
+ * nanoseconds.  We do not mess with gd_time_seconds and gd_cpuclock_base,
+ * instead we adjust basetime so basetime + gd_* results in the current
+ * time of day.  This way the gd_* fields are guarenteed to represent
+ * a monotonically increasing 'uptime' value.
  */
+void
+set_timeofday(struct timespec *ts)
+{
+	struct timespec ts2;
 
-int	stathz;
-int	profhz;
-static int profprocs;
-int	ticks;
-static int psticks;			/* profiler ticks */
-static int psdiv;			/* prof / stat divider */
-int	psratio;			/* ratio: prof * 100 / stat */
-
+	/*
+	 * XXX SMP / non-atomic basetime updates
+	 */
+	crit_enter();
+	nanouptime(&ts2);
+	basetime.tv_sec = ts->tv_sec - ts2.tv_sec;
+	basetime.tv_nsec = ts->tv_nsec - ts2.tv_nsec;
+	if (basetime.tv_nsec < 0) {
+	    basetime.tv_nsec += 1000000000;
+	    --basetime.tv_sec;
+	}
+	if (boottime.tv_sec == 0)
+		boottime = basetime;
+	timedelta = 0;
+	crit_exit();
+}
+	
 /*
- * Initialize clock frequencies and start both clocks running.
+ * Each cpu has its own hardclock, but we only increments ticks and softticks
+ * on cpu #0.
+ *
+ * NOTE! systimer! the MP lock might not be held here.  We can only safely
+ * manipulate objects owned by the current cpu.
  */
-/* ARGSUSED*/
 static void
-initclocks(dummy)
-	void *dummy;
+hardclock(systimer_t info, struct intrframe *frame)
 {
-	int i;
+	sysclock_t cputicks;
+	struct proc *p;
+	struct pstats *pstats;
+	struct globaldata *gd = mycpu;
 
 	/*
-	 * Set divisors to 1 (normal case) and let the machine-specific
-	 * code do its bit.
+	 * Realtime updates are per-cpu.  Note that timer corrections as
+	 * returned by microtime() and friends make an additional adjustment
+	 * using a system-wise 'basetime', but the running time is always
+	 * taken from the per-cpu globaldata area.  Since the same clock
+	 * is distributing (XXX SMP) to all cpus, the per-cpu timebases
+	 * stay in synch.
+	 *
+	 * Note that we never allow info->time (aka gd->gd_hardclock.time)
+	 * to reverse index gd_cpuclock_base.
 	 */
-	psdiv = 1;
-	cpu_initclocks();
-
-#ifdef DEVICE_POLLING
-	init_device_poll();
-#endif
+	cputicks = info->time - gd->gd_cpuclock_base;
+	if (cputicks > cputimer_freq) {
+		++gd->gd_time_seconds;
+		gd->gd_cpuclock_base += cputimer_freq;
+	}
 
 	/*
-	 * Compute profhz/stathz, and fix profhz if needed.
+	 * The system-wide ticks and softticks are only updated by cpu #0.
+	 * Callwheel actions are also (at the moment) only handled by cpu #0.
+	 * Finally, we also do NTP related timedelta/tickdelta adjustments
+	 * by adjusting basetime.
 	 */
-	i = stathz ? stathz : hz;
-	if (profhz == 0)
-		profhz = i;
-	psratio = profhz / i;
-}
+	if (gd->gd_cpuid == 0) {
+	    struct timespec nts;
+	    int leap;
 
-/*
- * The real-time timer, interrupting hz times per second.  This is implemented
- * as a FAST interrupt so it is in the context of the thread it interrupted,
- * and not in an interrupt thread.  YYY needs help.
- */
-void
-hardclock(frame)
-	struct clockframe *frame;
-{
-	struct proc *p;
+	    ++ticks;
 
-	p = curproc;
-	if (p) {
-		struct pstats *pstats;
+#ifdef DEVICE_POLLING
+	    hardclock_device_poll();	/* mpsafe, short and quick */
+#endif /* DEVICE_POLLING */
 
-		/*
-		 * Run current process's virtual and profile time, as needed.
-		 */
+	    if (TAILQ_FIRST(&callwheel[ticks & callwheelmask]) != NULL) {
+		setsoftclock();
+	    } else if (softticks + 1 == ticks) {
+		++softticks;
+	    }
+
+#if 0
+	    if (tco->tc_poll_pps) 
+		tco->tc_poll_pps(tco);
+#endif
+	    /*
+	     * Apply adjtime corrections.  At the moment only do this if 
+	     * we can get the MP lock to interlock with adjtime's modification
+	     * of these variables.  Note that basetime adjustments are not
+	     * MP safe either XXX.
+	     */
+	    if (timedelta != 0 && try_mplock()) {
+		basetime.tv_nsec += tickdelta * 1000;
+		if (basetime.tv_nsec >= 1000000000) {
+		    basetime.tv_nsec -= 1000000000;
+		    ++basetime.tv_sec;
+		} else if (basetime.tv_nsec < 0) {
+		    basetime.tv_nsec += 1000000000;
+		    --basetime.tv_sec;
+		}
+		timedelta -= tickdelta;
+		rel_mplock();
+	    }
+
+	    /*
+	     * Apply per-tick compensation.  ticks_adj adjusts for both
+	     * offset and frequency, and could be negative.
+	     */
+	    if (nsec_adj != 0 && try_mplock()) {
+		nsec_acc += nsec_adj;
+		if (nsec_acc >= 0x100000000LL) {
+		    basetime.tv_nsec += nsec_acc >> 32;
+		    nsec_acc = (nsec_acc & 0xFFFFFFFFLL);
+		} else if (nsec_acc <= -0x100000000LL) {
+		    basetime.tv_nsec -= -nsec_acc >> 32;
+		    nsec_acc = -(-nsec_acc & 0xFFFFFFFFLL);
+		}
+		if (basetime.tv_nsec >= 1000000000) {
+		    basetime.tv_nsec -= 1000000000;
+		    ++basetime.tv_sec;
+		} else if (basetime.tv_nsec < 0) {
+		    basetime.tv_nsec += 1000000000;
+		    --basetime.tv_sec;
+		}
+		rel_mplock();
+	    }
+
+	    /*
+	     * If the realtime-adjusted seconds hand rolls over then tell
+	     * ntp_update_second() what we did in the last second so it can
+	     * calculate what to do in the next second.  It may also add
+	     * or subtract a leap second.
+	     */
+	    getnanotime(&nts);
+	    if (time_second != nts.tv_sec) {
+		leap = ntp_update_second(time_second, &nsec_adj);
+		basetime.tv_sec += leap;
+		time_second = nts.tv_sec + leap;
+		nsec_adj /= hz;
+	    }
+	}
+
+	/*
+	 * ITimer handling is per-tick, per-cpu.  I don't think psignal()
+	 * is mpsafe on curproc, so XXX get the mplock.
+	 */
+	if ((p = curproc) != NULL && try_mplock()) {
 		pstats = p->p_stats;
-		if (CLKF_USERMODE(frame) &&
+		if (frame && CLKF_USERMODE(frame) &&
 		    timevalisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) &&
 		    itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0)
 			psignal(p, SIGVTALRM);
 		if (timevalisset(&pstats->p_timer[ITIMER_PROF].it_value) &&
 		    itimerdecr(&pstats->p_timer[ITIMER_PROF], tick) == 0)
 			psignal(p, SIGPROF);
+		rel_mplock();
 	}
+}
 
-#if 0 /* SMP and BETTER_CLOCK */
-	forward_hardclock(pscnt);
+/*
+ * The statistics clock typically runs at a 125Hz rate, and is intended
+ * to be frequency offset from the hardclock (typ 100Hz).  It is per-cpu.
+ *
+ * NOTE! systimer! the MP lock might not be held here.  We can only safely
+ * manipulate objects owned by the current cpu.
+ *
+ * The stats clock is responsible for grabbing a profiling sample.
+ * Most of the statistics are only used by user-level statistics programs.
+ * The main exceptions are p->p_uticks, p->p_sticks, p->p_iticks, and
+ * p->p_estcpu.
+ *
+ * Like the other clocks, the stat clock is called from what is effectively
+ * a fast interrupt, so the context should be the thread/process that got
+ * interrupted.
+ */
+static void
+statclock(systimer_t info, struct intrframe *frame)
+{
+#ifdef GPROF
+	struct gmonparam *g;
+	int i;
 #endif
+	thread_t td;
+	struct proc *p;
+	int bump;
+	struct timeval tv;
+	struct timeval *stv;
 
 	/*
-	 * If no separate statistics clock is available, run it from here.
+	 * How big was our timeslice relative to the last time?
 	 */
-	if (stathz == 0)
-		statclock(frame);
+	microuptime(&tv);	/* mpsafe */
+	stv = &mycpu->gd_stattv;
+	if (stv->tv_sec == 0) {
+	    bump = 1;
+	} else {
+	    bump = tv.tv_usec - stv->tv_usec +
+		(tv.tv_sec - stv->tv_sec) * 1000000;
+	    if (bump < 0)
+		bump = 0;
+	    if (bump > 1000000)
+		bump = 1000000;
+	}
+	*stv = tv;
 
-	tco_forward(0);
-	ticks++;
+	td = curthread;
+	p = td->td_proc;
 
-#ifdef DEVICE_POLLING
-	hardclock_device_poll();	/* this is very short and quick */
-#endif /* DEVICE_POLLING */
+	if (frame && CLKF_USERMODE(frame)) {
+		/*
+		 * Came from userland, handle user time and deal with
+		 * possible process.
+		 */
+		if (p && (p->p_flag & P_PROFIL))
+			addupc_intr(p, CLKF_PC(frame), 1);
+		td->td_uticks += bump;
 
-	/*
-	 * Process callouts at a very low cpu priority, so we don't keep the
-	 * relatively high clock interrupt priority any longer than necessary.
-	 */
-	if (TAILQ_FIRST(&callwheel[ticks & callwheelmask]) != NULL) {
-		setsoftclock();
-	} else if (softticks + 1 == ticks) {
-		++softticks;
+		/*
+		 * Charge the time as appropriate
+		 */
+		if (p && p->p_nice > NZERO)
+			cp_time[CP_NICE] += bump;
+		else
+			cp_time[CP_USER] += bump;
+	} else {
+#ifdef GPROF
+		/*
+		 * Kernel statistics are just like addupc_intr, only easier.
+		 */
+		g = &_gmonparam;
+		if (g->state == GMON_PROF_ON && frame) {
+			i = CLKF_PC(frame) - g->lowpc;
+			if (i < g->textsize) {
+				i /= HISTFRACTION * sizeof(*g->kcount);
+				g->kcount[i]++;
+			}
+		}
+#endif
+		/*
+		 * Came from kernel mode, so we were:
+		 * - handling an interrupt,
+		 * - doing syscall or trap work on behalf of the current
+		 *   user process, or
+		 * - spinning in the idle loop.
+		 * Whichever it is, charge the time as appropriate.
+		 * Note that we charge interrupts to the current process,
+		 * regardless of whether they are ``for'' that process,
+		 * so that we know how much of its real time was spent
+		 * in ``non-process'' (i.e., interrupt) work.
+		 *
+		 * XXX assume system if frame is NULL.  A NULL frame 
+		 * can occur if ipi processing is done from an splx().
+		 */
+		if (frame && CLKF_INTR(frame))
+			td->td_iticks += bump;
+		else
+			td->td_sticks += bump;
+
+		if (frame && CLKF_INTR(frame)) {
+			cp_time[CP_INTR] += bump;
+		} else {
+			if (td == &mycpu->gd_idlethread)
+				cp_time[CP_IDLE] += bump;
+			else
+				cp_time[CP_SYS] += bump;
+		}
+	}
+}
+
+/*
+ * The scheduler clock typically runs at a 10Hz rate.  NOTE! systimer,
+ * the MP lock might not be held.  We can safely manipulate parts of curproc
+ * but that's about it.
+ */
+static void
+schedclock(systimer_t info, struct intrframe *frame)
+{
+	struct proc *p;
+	struct pstats *pstats;
+	struct rusage *ru;
+	struct vmspace *vm;
+	long rss;
+
+	schedulerclock(NULL);	/* mpsafe */
+	if ((p = curproc) != NULL) {
+		/* Update resource usage integrals and maximums. */
+		if ((pstats = p->p_stats) != NULL &&
+		    (ru = &pstats->p_ru) != NULL &&
+		    (vm = p->p_vmspace) != NULL) {
+			ru->ru_ixrss += pgtok(vm->vm_tsize);
+			ru->ru_idrss += pgtok(vm->vm_dsize);
+			ru->ru_isrss += pgtok(vm->vm_ssize);
+			rss = pgtok(vmspace_resident_count(vm));
+			if (ru->ru_maxrss < rss)
+				ru->ru_maxrss = rss;
+		}
 	}
 }
 
@@ -350,19 +544,18 @@
  * keeps the profile clock running constantly.
  */
 void
-startprofclock(p)
-	struct proc *p;
+startprofclock(struct proc *p)
 {
-	int s;
-
 	if ((p->p_flag & P_PROFIL) == 0) {
 		p->p_flag |= P_PROFIL;
+#if 0	/* XXX */
 		if (++profprocs == 1 && stathz != 0) {
 			s = splstatclock();
 			psdiv = psratio;
 			setstatclockrate(profhz);
 			splx(s);
 		}
+#endif
 	}
 }
 
@@ -370,169 +563,18 @@
  * Stop profiling on a process.
  */
 void
-stopprofclock(p)
-	struct proc *p;
+stopprofclock(struct proc *p)
 {
-	int s;
-
 	if (p->p_flag & P_PROFIL) {
 		p->p_flag &= ~P_PROFIL;
+#if 0	/* XXX */
 		if (--profprocs == 0 && stathz != 0) {
 			s = splstatclock();
 			psdiv = 1;
 			setstatclockrate(stathz);
 			splx(s);
 		}
-	}
-}
-
-/*
- * Statistics clock.  Grab profile sample, and if divider reaches 0,
- * do process and kernel statistics.  Most of the statistics are only
- * used by user-level statistics programs.  The main exceptions are
- * p->p_uticks, p->p_sticks, p->p_iticks, and p->p_estcpu.
- *
- * The statclock should be called from an exclusive, fast interrupt,
- * so the context should be the thread/process that got interrupted and
- * not an interrupt thread.
- */
-void
-statclock(frame)
-	struct clockframe *frame;
-{
-#ifdef GPROF
-	struct gmonparam *g;
-	int i;
-#endif
-	thread_t td;
-	struct pstats *pstats;
-	long rss;
-	struct rusage *ru;
-	struct vmspace *vm;
-	struct proc *p;
-	int bump;
-	struct timeval tv;
-	struct timeval *stv;
-
-	/*
-	 * How big was our timeslice relative to the last time
-	 */
-	microuptime(&tv);
-	stv = &mycpu->gd_stattv;
-	if (stv->tv_sec == 0) {
-	    bump = 1;
-	} else {
-	    bump = tv.tv_usec - stv->tv_usec +
-		(tv.tv_sec - stv->tv_sec) * 1000000;
-	    if (bump < 0)
-		bump = 0;
-	    if (bump > 1000000)
-		bump = 1000000;
-	}
-	*stv = tv;
-
-	td = curthread;
-	p = td->td_proc;
-
-	if (CLKF_USERMODE(frame)) {
-		/*
-		 * Came from userland, handle user time and deal with
-		 * possible process.
-		 */
-		if (p && (p->p_flag & P_PROFIL))
-			addupc_intr(p, CLKF_PC(frame), 1);
-#if 0	/* SMP and BETTER_CLOCK */
-		if (stathz != 0)
-			forward_statclock(pscnt);
-#endif
-		td->td_uticks += bump;
-
-		/*
-		 * Charge the time as appropriate
-		 */
-		if (p && p->p_nice > NZERO)
-			cp_time[CP_NICE] += bump;
-		else
-			cp_time[CP_USER] += bump;
-	} else {
-#ifdef GPROF
-		/*
-		 * Kernel statistics are just like addupc_intr, only easier.
-		 */
-		g = &_gmonparam;
-		if (g->state == GMON_PROF_ON) {
-			i = CLKF_PC(frame) - g->lowpc;
-			if (i < g->textsize) {
-				i /= HISTFRACTION * sizeof(*g->kcount);
-				g->kcount[i]++;
-			}
-		}
-#endif
-#if 0	/* SMP and BETTER_CLOCK */
-		if (stathz != 0)
-			forward_statclock(pscnt);
-#endif
-		/*
-		 * Came from kernel mode, so we were:
-		 * - handling an interrupt,
-		 * - doing syscall or trap work on behalf of the current
-		 *   user process, or
-		 * - spinning in the idle loop.
-		 * Whichever it is, charge the time as appropriate.
-		 * Note that we charge interrupts to the current process,
-		 * regardless of whether they are ``for'' that process,
-		 * so that we know how much of its real time was spent
-		 * in ``non-process'' (i.e., interrupt) work.
-		 */
-		if (CLKF_INTR(frame))
-			td->td_iticks += bump;
-		else
-			td->td_sticks += bump;
-
-		if (CLKF_INTR(frame)) {
-			cp_time[CP_INTR] += bump;
-		} else {
-			if (td == &mycpu->gd_idlethread)
-				cp_time[CP_IDLE] += bump;
-			else
-				cp_time[CP_SYS] += bump;
-		}
-	}
-
-	/*
-	 * bump psticks and check against gd_psticks.  When we hit the
-	 * 1*hz mark (psdiv ticks) we do the more expensive stuff.  If
-	 * psdiv changes we reset everything to avoid confusion.
-	 */
-	++psticks;
-	if (psticks < mycpu->gd_psticks && psdiv == mycpu->gd_psdiv)
-		return;
-
-	mycpu->gd_psdiv = psdiv;
-	mycpu->gd_psticks = psticks + psdiv;
-
-	/*
-	 * XXX YYY DragonFly... need to rewrite all of this,
-	 * only schedclock is distributed at the moment
-	 */
-	schedclock(NULL);
-#ifdef SMP
-	if (smp_started && invltlb_ok && !cold && !panicstr) /* YYY */
-		lwkt_send_ipiq_mask(mycpu->gd_other_cpus, schedclock, NULL);
 #endif
-
-	if (p != NULL) {
-		/* Update resource usage integrals and maximums. */
-		if ((pstats = p->p_stats) != NULL &&
-		    (ru = &pstats->p_ru) != NULL &&
-		    (vm = p->p_vmspace) != NULL) {
-			ru->ru_ixrss += pgtok(vm->vm_tsize);
-			ru->ru_idrss += pgtok(vm->vm_dsize);
-			ru->ru_isrss += pgtok(vm->vm_ssize);
-			rss = pgtok(vmspace_resident_count(vm));
-			if (ru->ru_maxrss < rss)
-				ru->ru_maxrss = rss;
-		}
 	}
 }
 
@@ -557,14 +599,6 @@
 SYSCTL_PROC(_kern, KERN_CLOCKRATE, clockrate, CTLTYPE_STRUCT|CTLFLAG_RD,
 	0, 0, sysctl_kern_clockrate, "S,clockinfo","");
 
-static __inline unsigned
-tco_delta(struct timecounter *tc)
-{
-
-	return ((tc->tc_get_timecount(tc) - tc->tc_offset_count) & 
-	    tc->tc_counter_mask);
-}
-
 /*
  * We have eight functions for looking at the clock, four for
  * microseconds and four for nanoseconds.  For each there is fast
@@ -574,395 +608,163 @@
  * which is as precise as possible.  The "up" variants return the
  * time relative to system boot, these are well suited for time
  * interval measurements.
+ *
+ * Each cpu independantly maintains the current time of day, so all
+ * we need to do to protect ourselves from changes is to do a loop
+ * check on the seconds field changing out from under us.
  */
-
 void
-getmicrotime(struct timeval *tvp)
+getmicrouptime(struct timeval *tvp)
 {
-	struct timecounter *tc;
+	struct globaldata *gd = mycpu;
+	sysclock_t delta;
 
-	if (!tco_method) {
-		tc = timecounter;
-		*tvp = tc->tc_microtime;
-	} else {
-		microtime(tvp);
+	do {
+		tvp->tv_sec = gd->gd_time_seconds;
+		delta = gd->gd_hardclock.time - gd->gd_cpuclock_base;
+	} while (tvp->tv_sec != gd->gd_time_seconds);
+	tvp->tv_usec = (cputimer_freq64_usec * delta) >> 32;
+	if (tvp->tv_usec >= 1000000) {
+		tvp->tv_usec -= 1000000;
+		++tvp->tv_sec;
 	}
 }
 
 void
-getnanotime(struct timespec *tsp)
+getnanouptime(struct timespec *tsp)
 {
-	struct timecounter *tc;
+	struct globaldata *gd = mycpu;
+	sysclock_t delta;
 
-	if (!tco_method) {
-		tc = timecounter;
-		*tsp = tc->tc_nanotime;
-	} else {
-		nanotime(tsp);
+	do {
+		tsp->tv_sec = gd->gd_time_seconds;
+		delta = gd->gd_hardclock.time - gd->gd_cpuclock_base;
+	} while (tsp->tv_sec != gd->gd_time_seconds);
+	tsp->tv_nsec = (cputimer_freq64_nsec * delta) >> 32;
+	if (tsp->tv_nsec >= 1000000000) {
+		tsp->tv_nsec -= 1000000000;
+		++tsp->tv_sec;
 	}
 }
 
 void
-microtime(struct timeval *tv)
+microuptime(struct timeval *tvp)
 {
-	struct timecounter *tc;
-	int delta;
+	struct globaldata *gd = mycpu;
+	sysclock_t delta;
 
-	tc = timecounter;
-	crit_enter();
-	delta = tco_delta(tc);
-	tv->tv_sec = tc->tc_offset_sec;
-	tv->tv_usec = tc->tc_offset_micro;
-	tv->tv_usec += ((u_int64_t)delta * tc->tc_scale_micro) >> 32;
-	crit_exit();
-	tv->tv_usec += boottime.tv_usec;
-	tv->tv_sec += boottime.tv_sec;
-	while (tv->tv_usec < 0) {
-		tv->tv_usec += 1000000;
-		if (tv->tv_sec > 0)
-			tv->tv_sec--;
-	}
-	while (tv->tv_usec >= 1000000) {
-		tv->tv_usec -= 1000000;
-		tv->tv_sec++;
+	do {
+		tvp->tv_sec = gd->gd_time_seconds;
+		delta = cputimer_count() - gd->gd_cpuclock_base;
+	} while (tvp->tv_sec != gd->gd_time_seconds);
+	tvp->tv_usec = (cputimer_freq64_usec * delta) >> 32;
+	if (tvp->tv_usec >= 1000000) {
+		tvp->tv_usec -= 1000000;
+		++tvp->tv_sec;
 	}
 }
 
 void
-nanotime(struct timespec *ts)
+nanouptime(struct timespec *tsp)
 {
-	unsigned count;
-	u_int64_t delta;
-	struct timecounter *tc;
+	struct globaldata *gd = mycpu;
+	sysclock_t delta;
 
-	tc = timecounter;
-	crit_enter();
-	ts->tv_sec = tc->tc_offset_sec;
-	count = tco_delta(tc);
-	delta = tc->tc_offset_nano;
-	crit_exit();
-	delta += ((u_int64_t)count * tc->tc_scale_nano_f);
-	delta >>= 32;
-	delta += ((u_int64_t)count * tc->tc_scale_nano_i);
-	delta += boottime.tv_usec * 1000;
-	ts->tv_sec += boottime.tv_sec;
-	while (delta < 0) {
-		delta += 1000000000;
-		if (ts->tv_sec > 0)
-			ts->tv_sec--;
-	}
-	while (delta >= 1000000000) {
-		delta -= 1000000000;
-		ts->tv_sec++;
+	do {
+		tsp->tv_sec = gd->gd_time_seconds;
+		delta = cputimer_count() - gd->gd_cpuclock_base;
+	} while (tsp->tv_sec != gd->gd_time_seconds);
+	tsp->tv_nsec = (cputimer_freq64_nsec * delta) >> 32;
+	if (tsp->tv_nsec >= 1000000000) {
+		tsp->tv_nsec -= 1000000000;
+		++tsp->tv_sec;
 	}
-	ts->tv_nsec = delta;
 }
 
-void
-getmicrouptime(struct timeval *tvp)
-{
-	struct timecounter *tc;
-
-	if (!tco_method) {
-		tc = timecounter;
-		tvp->tv_sec = tc->tc_offset_sec;
-		tvp->tv_usec = tc->tc_offset_micro;
-	} else {
-		microuptime(tvp);
-	}
-}
+/*
+ * realtime routines
+ */
 
 void
-getnanouptime(struct timespec *tsp)
+getmicrotime(struct timeval *tvp)
 {
-	struct timecounter *tc;
+	struct globaldata *gd = mycpu;
+	sysclock_t delta;
 
-	if (!tco_method) {
-		tc = timecounter;
-		tsp->tv_sec = tc->tc_offset_sec;
-		tsp->tv_nsec = tc->tc_offset_nano >> 32;
-	} else {
-		nanouptime(tsp);
+	do {
+		tvp->tv_sec = gd->gd_time_seconds;
+		delta = gd->gd_hardclock.time - gd->gd_cpuclock_base;
+	} while (tvp->tv_sec != gd->gd_time_seconds);
+	tvp->tv_usec = (cputimer_freq64_usec * delta) >> 32;
+
+	tvp->tv_sec += basetime.tv_sec;
+	tvp->tv_usec += basetime.tv_nsec / 1000;
+	while (tvp->tv_usec >= 1000000) {
+		tvp->tv_usec -= 1000000;
+		++tvp->tv_sec;
 	}
 }
 
 void
-microuptime(struct timeval *tv)
+getnanotime(struct timespec *tsp)
 {
-	struct timecounter *tc;
+	struct globaldata *gd = mycpu;
+	sysclock_t delta;
 
-	tc = timecounter;
-	tv->tv_sec = tc->tc_offset_sec;
-	tv->tv_usec = tc->tc_offset_micro;
-	tv->tv_usec += ((u_int64_t)tco_delta(tc) * tc->tc_scale_micro) >> 32;
-	while (tv->tv_usec < 0) {
-		tv->tv_usec += 1000000;
-		if (tv->tv_sec > 0)
-			tv->tv_sec--;
-	}
-	while (tv->tv_usec >= 1000000) {
-		tv->tv_usec -= 1000000;
-		tv->tv_sec++;
-	}
-}
+	do {
+		tsp->tv_sec = gd->gd_time_seconds;
+		delta = gd->gd_hardclock.time - gd->gd_cpuclock_base;
+	} while (tsp->tv_sec != gd->gd_time_seconds);
+	tsp->tv_nsec = (cputimer_freq64_nsec * delta) >> 32;
 
-void
-nanouptime(struct timespec *ts)
-{
-	unsigned count;
-	u_int64_t delta;
-	struct timecounter *tc;
-
-	tc = timecounter;
-	ts->tv_sec = tc->tc_offset_sec;
-	count = tco_delta(tc);
-	delta = tc->tc_offset_nano;
-	delta += ((u_int64_t)count * tc->tc_scale_nano_f);
-	delta >>= 32;
-	delta += ((u_int64_t)count * tc->tc_scale_nano_i);
-	while (delta < 0) {
-		delta += 1000000000;
-		if (ts->tv_sec > 0)
-			ts->tv_sec--;
-	}
-	while (delta >= 1000000000) {
-		delta -= 1000000000;
-		ts->tv_sec++;
+	tsp->tv_sec += basetime.tv_sec;
+	tsp->tv_nsec += basetime.tv_nsec;
+	while (tsp->tv_nsec >= 1000000000) {
+		tsp->tv_nsec -= 1000000000;
+		++tsp->tv_sec;
 	}
-	ts->tv_nsec = delta;
-}
-
-static void
-tco_setscales(struct timecounter *tc)
-{
-	u_int64_t scale;
-
-	scale = 1000000000LL << 32;
-	scale += tc->tc_adjustment;
-	scale /= tc->tc_tweak->tc_frequency;
-	tc->tc_scale_micro = scale / 1000;
-	tc->tc_scale_nano_f = scale & 0xffffffff;
-	tc->tc_scale_nano_i = scale >> 32;
 }
 
 void
-update_timecounter(struct timecounter *tc)
+microtime(struct timeval *tvp)
 {
-	tco_setscales(tc);
-}
+	struct globaldata *gd = mycpu;
+	sysclock_t delta;
 
-void
-init_timecounter(struct timecounter *tc)
-{
-	struct timespec ts1;
-	struct timecounter *t1, *t2, *t3;
-	unsigned u;
-	int i;
+	do {
+		tvp->tv_sec = gd->gd_time_seconds;
+		delta = cputimer_count() - gd->gd_cpuclock_base;
+	} while (tvp->tv_sec != gd->gd_time_seconds);
+	tvp->tv_usec = (cputimer_freq64_usec * delta) >> 32;
 
-	u = tc->tc_frequency / tc->tc_counter_mask;
-	if (u > hz) {
-		printf("Timecounter \"%s\" frequency %lu Hz"
-		       " -- Insufficient hz, needs at least %u\n",
-		       tc->tc_name, (u_long) tc->tc_frequency, u);
-		return;
+	tvp->tv_sec += basetime.tv_sec;
+	tvp->tv_usec += basetime.tv_nsec / 1000;
+	while (tvp->tv_usec >= 1000000) {
+		tvp->tv_usec -= 1000000;
+		++tvp->tv_sec;
 	}
-
-	tc->tc_adjustment = 0;
-	tc->tc_tweak = tc;
-	tco_setscales(tc);
-	tc->tc_offset_count = tc->tc_get_timecount(tc);
-	if (timecounter == &dummy_timecounter)
-		tc->tc_avail = tc;
-	else {
-		tc->tc_avail = timecounter->tc_tweak->tc_avail;
-		timecounter->tc_tweak->tc_avail = tc;
-	}
-	MALLOC(t1, struct timecounter *, sizeof *t1, M_TIMECOUNTER, M_WAITOK);
-	tc->tc_other = t1;
-	*t1 = *tc;
-	t2 = t1;
-	for (i = 1; i < NTIMECOUNTER; i++) {
-		MALLOC(t3, struct timecounter *, sizeof *t3,
-		    M_TIMECOUNTER, M_WAITOK);
-		*t3 = *tc;
-		t3->tc_other = t2;
-		t2 = t3;
-	}
-	t1->tc_other = t3;
-	tc = t1;
-
-	printf("Timecounter \"%s\"  frequency %lu Hz\n", 
-	    tc->tc_name, (u_long)tc->tc_frequency);
-
-	/* XXX: For now always start using the counter. */
-	tc->tc_offset_count = tc->tc_get_timecount(tc);
-	nanouptime(&ts1);
-	tc->tc_offset_nano = (u_int64_t)ts1.tv_nsec << 32;
-	tc->tc_offset_micro = ts1.tv_nsec / 1000;
-	tc->tc_offset_sec = ts1.tv_sec;
-	timecounter = tc;
 }
 
 void
-set_timecounter(struct timespec *ts)
+nanotime(struct timespec *tsp)
 {
-	struct timespec ts2;
-
-	nanouptime(&ts2);
-	boottime.tv_sec = ts->tv_sec - ts2.tv_sec;
-	boottime.tv_usec = (ts->tv_nsec - ts2.tv_nsec) / 1000;
-	if (boottime.tv_usec < 0) {
-		boottime.tv_usec += 1000000;
-		boottime.tv_sec--;
-	}
-	/* fiddle all the little crinkly bits around the fiords... */
-	tco_forward(1);
-}
-
-static void
-switch_timecounter(struct timecounter *newtc)
-{
-	int s;
-	struct timecounter *tc;
-	struct timespec ts;
-
-	s = splclock();
-	tc = timecounter;
-	if (newtc->tc_tweak == tc->tc_tweak) {
-		splx(s);
-		return;
-	}
-	newtc = newtc->tc_tweak->tc_other;
-	nanouptime(&ts);
-	newtc->tc_offset_sec = ts.tv_sec;
-	newtc->tc_offset_nano = (u_int64_t)ts.tv_nsec << 32;
-	newtc->tc_offset_micro = ts.tv_nsec / 1000;
-	newtc->tc_offset_count = newtc->tc_get_timecount(newtc);
-	tco_setscales(newtc);
-	timecounter = newtc;
-	splx(s);
-}
-
-static struct timecounter *
-sync_other_counter(void)
-{
-	struct timecounter *tc, *tcn, *tco;
-	unsigned delta;
-
-	tco = timecounter;
-	tc = tco->tc_other;
-	tcn = tc->tc_other;
-	*tc = *tco;
-	tc->tc_other = tcn;
-	delta = tco_delta(tc);
-	tc->tc_offset_count += delta;
-	tc->tc_offset_count &= tc->tc_counter_mask;
-	tc->tc_offset_nano += (u_int64_t)delta * tc->tc_scale_nano_f;
-	tc->tc_offset_nano += (u_int64_t)delta * tc->tc_scale_nano_i << 32;
-	return (tc);
-}
-
-static void
-tco_forward(int force)
-{
-	struct timecounter *tc, *tco;
-	struct timeval tvt;
-
-	tco = timecounter;
-	tc = sync_other_counter();
-	/*
-	 * We may be inducing a tiny error here, the tc_poll_pps() may
-	 * process a latched count which happens after the tco_delta()
-	 * in sync_other_counter(), which would extend the previous
-	 * counters parameters into the domain of this new one.
-	 * Since the timewindow is very small for this, the error is
-	 * going to be only a few weenieseconds (as Dave Mills would
-	 * say), so lets just not talk more about it, OK ?
-	 */
-	if (tco->tc_poll_pps) 
-		tco->tc_poll_pps(tco);
-	if (timedelta != 0) {
-		tvt = boottime;
-		tvt.tv_usec += tickdelta;
-		if (tvt.tv_usec >= 1000000) {
-			tvt.tv_sec++;
-			tvt.tv_usec -= 1000000;
-		} else if (tvt.tv_usec < 0) {
-			tvt.tv_sec--;
-			tvt.tv_usec += 1000000;
-		}
-		boottime = tvt;
-		timedelta -= tickdelta;
-	}
-
-	while (tc->tc_offset_nano >= 1000000000ULL << 32) {
-		tc->tc_offset_nano -= 1000000000ULL << 32;
-		tc->tc_offset_sec++;
-		ntp_update_second(tc);	/* XXX only needed if xntpd runs */
-		tco_setscales(tc);
-		force++;
-	}
-
-	if (tco_method && !force)
-		return;
+	struct globaldata *gd = mycpu;
+	sysclock_t delta;
 
-	tc->tc_offset_micro = (tc->tc_offset_nano / 1000) >> 32;
+	do {
+		tsp->tv_sec = gd->gd_time_seconds;
+		delta = cputimer_count() - gd->gd_cpuclock_base;
+	} while (tsp->tv_sec != gd->gd_time_seconds);
+	tsp->tv_nsec = (cputimer_freq64_nsec * delta) >> 32;
 
-	/* Figure out the wall-clock time */
-	tc->tc_nanotime.tv_sec = tc->tc_offset_sec + boottime.tv_sec;
-	tc->tc_nanotime.tv_nsec = 
-	    (tc->tc_offset_nano >> 32) + boottime.tv_usec * 1000;
-	tc->tc_microtime.tv_usec = tc->tc_offset_micro + boottime.tv_usec;
-	while (tc->tc_nanotime.tv_nsec >= 1000000000) {
-		tc->tc_nanotime.tv_nsec -= 1000000000;
-		tc->tc_microtime.tv_usec -= 1000000;
-		tc->tc_nanotime.tv_sec++;
+	tsp->tv_sec += basetime.tv_sec;
+	tsp->tv_nsec += basetime.tv_nsec;
+	while (tsp->tv_nsec >= 1000000000) {
+		tsp->tv_nsec -= 1000000000;
+		++tsp->tv_sec;
 	}
-	time_second = tc->tc_microtime.tv_sec = tc->tc_nanotime.tv_sec;
-
-	timecounter = tc;
 }
 
-SYSCTL_NODE(_kern, OID_AUTO, timecounter, CTLFLAG_RW, 0, "");
-
-SYSCTL_INT(_kern_timecounter, OID_AUTO, method, CTLFLAG_RW, &tco_method, 0,
-    "This variable determines the method used for updating timecounters. "
-    "If the default algorithm (0) fails with \"calcru negative...\" messages "
-    "try the alternate algorithm (1) which handles bad hardware better."
-
-);
-
-static int
-sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS)
-{
-	char newname[32];
-	struct timecounter *newtc, *tc;
-	int error;
-
-	tc = timecounter->tc_tweak;
-	strncpy(newname, tc->tc_name, sizeof(newname));
-	error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req);
-	if (error == 0 && req->newptr != NULL &&
-	    strcmp(newname, tc->tc_name) != 0) {
-		for (newtc = tc->tc_avail; newtc != tc;
-		    newtc = newtc->tc_avail) {
-			if (strcmp(newname, newtc->tc_name) == 0) {
-				/* Warm up new timecounter. */
-				(void)newtc->tc_get_timecount(newtc);
-
-				switch_timecounter(newtc);
-				return (0);
-			}
-		}
-		return (EINVAL);
-	}
-	return (error);
-}
-
-SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING | CTLFLAG_RW,
-    0, 0, sysctl_kern_timecounter_hardware, "A", "");
-
-
 int
 pps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps)
 {
@@ -1031,13 +833,22 @@
 }
 
 void
-pps_event(struct pps_state *pps, struct timecounter *tc, unsigned count, int event)
+pps_event(struct pps_state *pps, sysclock_t count, int event)
 {
-	struct timespec ts, *tsp, *osp;
-	u_int64_t delta;
-	unsigned tcount, *pcount;
-	int foff, fhard;
-	pps_seq_t	*pseq;
+	struct globaldata *gd;
+	struct timespec *tsp;
+	struct timespec *osp;
+	struct timespec ts;
+	sysclock_t *pcount;
+#ifdef PPS_SYNC
+	sysclock_t tcount;
+#endif
+	sysclock_t delta;
+	pps_seq_t *pseq;
+	int foff;
+	int fhard;
+
+	gd = mycpu;
 
 	/* Things would be easier with arrays... */
 	if (event == PPS_CAPTUREASSERT) {
@@ -1056,36 +867,27 @@
 		pseq = &pps->ppsinfo.clear_sequence;
 	}
 
-	/* The timecounter changed: bail */
-	if (!pps->ppstc || 
-	    pps->ppstc->tc_name != tc->tc_name || 
-	    tc->tc_name != timecounter->tc_name) {
-		pps->ppstc = tc;
-		*pcount = count;
-		return;
-	}
-
 	/* Nothing really happened */
 	if (*pcount == count)
 		return;
 
 	*pcount = count;
 
-	/* Convert the count to timespec */
-	ts.tv_sec = tc->tc_offset_sec;
-	tcount = count - tc->tc_offset_count;
-	tcount &= tc->tc_counter_mask;
-	delta = tc->tc_offset_nano;
-	delta += ((u_int64_t)tcount * tc->tc_scale_nano_f);
-	delta >>= 32;
-	delta += ((u_int64_t)tcount * tc->tc_scale_nano_i);
-	delta += boottime.tv_usec * 1000;
-	ts.tv_sec += boottime.tv_sec;
-	while (delta >= 1000000000) {
-		delta -= 1000000000;
-		ts.tv_sec++;
+	do {
+		ts.tv_sec = gd->gd_time_seconds;
+		delta = count - gd->gd_cpuclock_base;
+	} while (ts.tv_sec != gd->gd_time_seconds);
+	if (delta > cputimer_freq) {
+		ts.tv_sec += delta / cputimer_freq;
+		delta %= cputimer_freq;
+	}
+	ts.tv_nsec = (cputimer_freq64_nsec * delta) >> 32;
+	ts.tv_sec += basetime.tv_sec;
+	ts.tv_nsec += basetime.tv_nsec;
+	while (ts.tv_nsec >= 1000000000) {
+		ts.tv_nsec -= 1000000000;
+		++ts.tv_sec;
 	}
-	ts.tv_nsec = delta;
 
 	(*pseq)++;
 	*tsp = ts;
@@ -1102,11 +904,9 @@
 		/* magic, at its best... */
 		tcount = count - pps->ppscount[2];
 		pps->ppscount[2] = count;
-		tcount &= tc->tc_counter_mask;
-		delta = ((u_int64_t)tcount * tc->tc_tweak->tc_scale_nano_f);
-		delta >>= 32;
-		delta += ((u_int64_t)tcount * tc->tc_tweak->tc_scale_nano_i);
+		delta = (cputimer_freq64_nsec * tcount) >> 32;
 		hardpps(tsp, delta);
 	}
 #endif
 }
+
Index: kern/kern_ntptime.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_ntptime.c,v
retrieving revision 1.7
diff -u -r1.7 kern_ntptime.c
--- kern/kern_ntptime.c	30 Jul 2003 00:19:14 -0000	1.7
+++ kern/kern_ntptime.c	27 Jan 2004 01:28:49 -0000
@@ -43,6 +43,7 @@
 #include <sys/timex.h>
 #include <sys/timepps.h>
 #include <sys/sysctl.h>
+#include <sys/thread2.h>
 
 /*
  * Single-precision macros for 64-bit machines
@@ -271,7 +272,6 @@
 	struct timex ntv;	/* temporary structure */
 	long freq;		/* frequency ns/s) */
 	int modes;		/* mode bits from structure */
-	int s;			/* caller priority */
 	int error;
 
 	error = copyin((caddr_t)uap->tp, (caddr_t)&ntv, sizeof(ntv));
@@ -292,7 +292,7 @@
 		error = suser(td);
 	if (error)
 		return (error);
-	s = splclock();
+	crit_enter();
 	if (modes & MOD_MAXERROR)
 		time_maxerror = ntv.maxerror;
 	if (modes & MOD_ESTERROR)
@@ -344,6 +344,11 @@
 		else
 			hardupdate(ntv.offset * 1000);
 	}
+	/*
+	 * Note: the userland specified frequency is in seconds per second
+	 * times 65536e+6.  Multiply by a thousand and divide by 65336 to
+	 * get nanoseconds.
+	 */
 	if (modes & MOD_FREQUENCY) {
 		freq = (ntv.freq * 1000LL) >> 16;
 		if (freq > MAXFREQ)
@@ -388,7 +393,7 @@
 	ntv.jitcnt = pps_jitcnt;
 	ntv.stbcnt = pps_stbcnt;
 #endif /* PPS_SYNC */
-	splx(s);
+	crit_exit();
 
 	error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv));
 	if (error)
@@ -415,18 +420,16 @@
 /*
  * second_overflow() - called after ntp_tick_adjust()
  *
- * This routine is ordinarily called immediately following the above
- * routine ntp_tick_adjust(). While these two routines are normally
- * combined, they are separated here only for the purposes of
- * simulation.
+ * This routine is ordinarily called from hardclock() whenever the seconds
+ * hand rolls over.  It returns leap seconds to add or drop, and sets nsec_adj
+ * to the total adjustment to make over the next second in (ns << 32).
  */
-void
-ntp_update_second(struct timecounter *tcp)
+int
+ntp_update_second(time_t newsec, int64_t *nsec_adj)
 {
-	u_int32_t *newsec;
 	l_fp ftemp;		/* 32/64-bit temporary */
+	int  adjsec = 0;
 
-	newsec = &tcp->tc_offset_sec;
 	/*
 	 * On rollover of the second both the nanosecond and microsecond
 	 * clocks are updated and the state machine cranked as
@@ -463,8 +466,8 @@
 		case TIME_INS:
 		if (!(time_status & STA_INS))
 			time_state = TIME_OK;
-		else if ((*newsec) % 86400 == 0) {
-			(*newsec)--;
+		else if ((newsec) % 86400 == 0) {
+			--adjsec;
 			time_state = TIME_OOP;
 		}
 		break;
@@ -475,8 +478,8 @@
 		case TIME_DEL:
 		if (!(time_status & STA_DEL))
 			time_state = TIME_OK;
-		else if (((*newsec) + 1) % 86400 == 0) {
-			(*newsec)++;
+		else if (((newsec) + 1) % 86400 == 0) {
+			++adjsec;
 			time_tai--;
 			time_state = TIME_WAIT;
 		}
@@ -499,33 +502,35 @@
 	}
 
 	/*
-	 * Compute the total time adjustment for the next second
-	 * in ns. The offset is reduced by a factor depending on
-	 * whether the PPS signal is operating. Note that the
-	 * value is in effect scaled by the clock frequency,
-	 * since the adjustment is added at each tick interrupt.
+	 * time_offset represents the total time adjustment we wish to
+	 * make (over no particular period of time).  time_freq represents
+	 * the frequency compensation we wish to apply.
+	 *
+	 * time_adj represents the total adjustment we wish to make over
+	 * one full second.  hardclock usually applies this adjustment in
+	 * time_adj / hz jumps, hz times a second.
 	 */
 	ftemp = time_offset;
 #ifdef PPS_SYNC
 	/* XXX even if PPS signal dies we should finish adjustment ? */
-	if (time_status & STA_PPSTIME && time_status &
-	    STA_PPSSIGNAL)
+	if ((time_status & STA_PPSTIME( && (time_status & STA_PPSSIGNAL))
 		L_RSHIFT(ftemp, pps_shift);
 	else
 		L_RSHIFT(ftemp, SHIFT_PLL + time_constant);
 #else
 		L_RSHIFT(ftemp, SHIFT_PLL + time_constant);
 #endif /* PPS_SYNC */
-	time_adj = ftemp;
+	time_adj = ftemp;		/* adjustment for part of the offset */
 	L_SUB(time_offset, ftemp);
-	L_ADD(time_adj, time_freq);
-	tcp->tc_adjustment = time_adj;
+	L_ADD(time_adj, time_freq);	/* add frequency correction */
+	*nsec_adj = time_adj;
 #ifdef PPS_SYNC
 	if (pps_valid > 0)
 		pps_valid--;
 	else
 		time_status &= ~STA_PPSSIGNAL;
 #endif /* PPS_SYNC */
+	return(adjsec);
 }
 
 /*
@@ -594,6 +599,9 @@
 {
 	long mtemp;
 	l_fp ftemp;
+	globaldata_t gd;
+
+	gd = mycpu;
 
 	/*
 	 * Select how the phase is to be controlled and from which
@@ -603,8 +611,7 @@
 	 */
 	if (!(time_status & STA_PLL))
 		return;
-	if (!(time_status & STA_PPSTIME && time_status &
-	    STA_PPSSIGNAL)) {
+	if (!((time_status & STA_PPSTIME) && (time_status & STA_PPSSIGNAL))) {
 		if (offset > MAXPHASE)
 			time_monitor = MAXPHASE;
 		else if (offset < -MAXPHASE)
@@ -619,6 +626,9 @@
 	 * mode (PLL or FLL). If the PPS signal is present and enabled
 	 * to discipline the frequency, the PPS frequency is used;
 	 * otherwise, the argument offset is used to compute it.
+	 *
+	 * gd_time_seconds is basically an uncompensated uptime.  We use
+	 * this for consistency.
 	 */
 	if (time_status & STA_PPSFREQ && time_status & STA_PPSSIGNAL) {
 		time_reftime = time_second;
@@ -632,8 +642,7 @@
 	L_MPY(ftemp, mtemp);
 	L_ADD(time_freq, ftemp);
 	time_status &= ~STA_MODE;
-	if (mtemp >= MINSEC && (time_status & STA_FLL || mtemp >
-	    MAXSEC)) {
+	if (mtemp >= MINSEC && (time_status & STA_FLL || mtemp > MAXSEC)) {
 		L_LINT(ftemp, (time_monitor << 4) / mtemp);
 		L_RSHIFT(ftemp, SHIFT_FLL + 4);
 		L_ADD(time_freq, ftemp);
Index: kern/kern_poll.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_poll.c,v
retrieving revision 1.6
diff -u -r1.6 kern_poll.c
--- kern/kern_poll.c	8 Nov 2003 07:57:41 -0000	1.6
+++ kern/kern_poll.c	25 Jan 2004 08:08:07 -0000
@@ -201,6 +201,8 @@
  * The first part of the code is just for debugging purposes, and tries
  * to count how often hardclock ticks are shorter than they should,
  * meaning either stray interrupts or delayed events.
+ *
+ * WARNING! called from fastint or IPI, the MP lock might not be held.
  */
 void
 hardclock_device_poll(void)
Index: kern/kern_random.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_random.c,v
retrieving revision 1.5
diff -u -r1.5 kern_random.c
--- kern/kern_random.c	29 Jul 2003 21:30:02 -0000	1.5
+++ kern/kern_random.c	25 Jan 2004 08:27:46 -0000
@@ -47,6 +47,7 @@
 #include <sys/random.h>
 #include <sys/select.h>
 #include <sys/systm.h>
+#include <sys/systimer.h>
 
 #ifdef __i386__
 #include <i386/isa/icu.h>
@@ -191,10 +192,8 @@
 	int		delta, delta2;
 	u_int		nbits;
 	u_int32_t	time;
-	struct timecounter *tc;
 
-	tc = timecounter;
-	num ^= tc->tc_get_timecount(tc) << 16;
+	num ^= cputimer_count() << 16;
 	r->entropy_count += 2;
 		
 	time = ticks;
Index: kern/kern_synch.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_synch.c,v
retrieving revision 1.26
diff -u -r1.26 kern_synch.c
--- kern/kern_synch.c	7 Jan 2004 11:04:18 -0000	1.26
+++ kern/kern_synch.c	25 Jan 2004 08:03:39 -0000
@@ -161,7 +161,7 @@
  *          Note that, as ps(1) mentions, this can let percentages
  *          total over 100% (I've seen 137.9% for 3 processes).
  *
- * Note that schedclock() updates p_estcpu and p_cpticks asynchronously.
+ * Note that schedulerclock() updates p_estcpu and p_cpticks asynchronously.
  *
  * We wish to decay away 90% of p_estcpu in (5 * loadavg) seconds.
  * That is, the system wants to compute a value of decay such
@@ -871,10 +871,11 @@
  * time in 5 * loadav seconds.  This causes the system to favor processes
  * which haven't run much recently, and to round-robin among other processes.
  *
- * WARNING!
+ * WARNING! called from a fast-int or an IPI, the MP lock MIGHT NOT BE HELD
+ * and we cannot block.
  */
 void
-schedclock(void *dummy)
+schedulerclock(void *dummy)
 {
 	struct thread *td;
 	struct proc *p;
Index: kern/kern_systimer.c
===================================================================
RCS file: kern/kern_systimer.c
diff -N kern/kern_systimer.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ kern/kern_systimer.c	27 Jan 2004 23:54:04 -0000
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
+ * 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.
+ *
+ * $DragonFly$
+ */
+
+/*
+ * This code implements a fine-grained per-cpu system timer which is
+ * ultimately based on a hardware timer.  The hardware timer abstraction
+ * is sufficiently disconnected from this code to support both per-cpu
+ * hardware timers or a single system-wide hardware timer.
+ *
+ * Notes on machine-dependant code (in arch/arch/systimer.c)
+ *
+ * cputimer_intr_reload()	Reload the one-shot (per-cpu basis)
+ *
+ * cputimer_count()		Get the current absolute sysclock_t value.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/globaldata.h>
+#include <sys/systimer.h>
+#include <sys/thread2.h>
+
+/*
+ * Execute ready systimers.  Called directly from the platform-specific
+ * one-shot timer clock interrupt (e.g. clkintr()).  Systimer functions are
+ * responsible for calling hardclock, statclock, and other finely-timed
+ * routines.
+ */
+void
+systimer_intr(sysclock_t *timep, struct intrframe *frame)
+{
+    globaldata_t gd = mycpu;
+    sysclock_t time = *timep;
+    systimer_t info;
+
+    if (gd->gd_syst_nest)
+	return;
+
+    crit_enter();
+    ++gd->gd_syst_nest;
+    while ((info = TAILQ_FIRST(&gd->gd_systimerq)) != NULL) {
+	/*
+	 * If we haven't reached the requested time, tell the cputimer
+	 * how much is left and break out.
+	 */
+	if ((int)(info->time - time) > 0) {
+	    cputimer_intr_reload(info->time - time);
+	    break;
+	}
+
+	/*
+	 * Dequeue and execute
+	 */
+	info->flags &= ~SYSTF_ONQUEUE;
+	TAILQ_REMOVE(info->queue, info, node);
+	crit_exit();
+	info->func(info, frame);
+	crit_enter();
+
+	/*
+	 * Reinstall if periodic
+	 */
+	if (info->periodic) {
+	    info->time += info->periodic;
+	    systimer_add(info);
+	}
+    }
+    if (info)
+	gd->gd_nextclock = info->time;
+    else
+	gd->gd_nextclock = 0;
+    --gd->gd_syst_nest;
+    crit_exit();
+}
+
+void
+systimer_add(systimer_t info)
+{
+    struct globaldata *gd = mycpu;
+
+    KKASSERT((info->flags & (SYSTF_ONQUEUE|SYSTF_IPIRUNNING)) == 0);
+    crit_enter();
+    if (info->gd == gd) {
+	systimer_t scan1;
+	systimer_t scan2;
+	scan1 = TAILQ_FIRST(&gd->gd_systimerq);
+	if (scan1 == NULL || (int)(scan1->time - info->time) > 0) {
+	    gd->gd_nextclock = info->time;
+	    cputimer_intr_reload(info->time - cputimer_count());
+	    TAILQ_INSERT_HEAD(&gd->gd_systimerq, info, node);
+	} else {
+	    scan2 = TAILQ_LAST(&gd->gd_systimerq, systimerq);
+	    for (;;) {
+		if (scan1 == NULL) {
+		    TAILQ_INSERT_TAIL(&gd->gd_systimerq, info, node);
+		    break;
+		}
+		if ((int)(scan1->time - info->time) > 0) {
+		    TAILQ_INSERT_BEFORE(scan1, info, node);
+		    break;
+		}
+		if ((int)(scan2->time - info->time) <= 0) {
+		    TAILQ_INSERT_AFTER(&gd->gd_systimerq, scan2, info, node);
+		    break;
+		}
+		scan1 = TAILQ_NEXT(scan1, node);
+		scan2 = TAILQ_PREV(scan2, systimerq, node);
+	    }
+	}
+	info->flags = (info->flags | SYSTF_ONQUEUE) & ~SYSTF_IPIRUNNING;
+	info->queue = &gd->gd_systimerq;
+    } else {
+	info->flags |= SYSTF_IPIRUNNING;
+	lwkt_send_ipiq(info->gd->gd_cpuid, (ipifunc_t)systimer_add, info);
+    }
+    crit_exit();
+}
+
+/*
+ * systimer_del()
+ *
+ *	Delete a system timer.  Only the owning cpu can delete a timer.
+ */
+void
+systimer_del(systimer_t info)
+{
+    KKASSERT(info->gd == mycpu && (info->flags & SYSTF_IPIRUNNING) == 0);
+    crit_enter();
+    if (info->flags & SYSTF_ONQUEUE) {
+	TAILQ_REMOVE(info->queue, info, node);
+	info->flags &= ~SYSTF_ONQUEUE;
+    }
+    crit_exit();
+}
+
+/*
+ * systimer_init_periodic()
+ *
+ *	Initialize a periodic timer at the specified frequency and add
+ *	it to the system.  The frequency is uncompensated and approximate.
+ *
+ *	Try to synchronize multi registrations of the same or similar
+ *	frequencies so the hardware interrupt is able to dispatch several
+ *	at together by adjusting the phase of the initial interrupt.  This
+ *	helps SMP.  Note that we are not attempting to synchronize to 
+ *	the realtime clock.
+ */
+void
+systimer_init_periodic(systimer_t info, void *func, void *data, int hz)
+{
+    sysclock_t base_count;
+
+    bzero(info, sizeof(struct systimer));
+    info->periodic = cputimer_fromhz(hz);
+    base_count = cputimer_count();
+    base_count = base_count - (base_count % info->periodic);
+    info->time = base_count + info->periodic;
+    info->func = func;
+    info->data = data;
+    info->gd = mycpu;
+    systimer_add(info);
+}
+
+/*
+ * systimer_init_oneshot()
+ *
+ *	Initialize a periodic timer at the specified frequency and add
+ *	it to the system.  The frequency is uncompensated and approximate.
+ */
+void
+systimer_init_oneshot(systimer_t info, void *func, void *data, int us)
+{
+    bzero(info, sizeof(struct systimer));
+    info->time = cputimer_count() + cputimer_fromus(us);
+    info->func = func;
+    info->data = data;
+    info->gd = mycpu;
+    systimer_add(info);
+}
+
Index: kern/kern_time.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_time.c,v
retrieving revision 1.13
diff -u -r1.13 kern_time.c
--- kern/kern_time.c	7 Jan 2004 11:08:06 -0000	1.13
+++ kern/kern_time.c	27 Jan 2004 02:40:54 -0000
@@ -52,6 +52,7 @@
 #include <vm/vm.h>
 #include <vm/vm_extern.h>
 #include <sys/msgport2.h>
+#include <sys/thread2.h>
 
 struct timezone tz;
 
@@ -71,8 +72,8 @@
 static void	timevalfix (struct timeval *);
 static void	no_lease_updatetime (int);
 
-static int	sleep_hardloop = 0;
-SYSCTL_INT(_kern, OID_AUTO, sleep_hardloop, CTLFLAG_RW, &sleep_hardloop, 0, "");
+static int     sleep_hard_us = 100;
+SYSCTL_INT(_kern, OID_AUTO, sleep_hard_us, CTLFLAG_RW, &sleep_hard_us, 0, "")
 
 static void 
 no_lease_updatetime(deltat)
@@ -89,9 +90,8 @@
 	struct timeval delta, tv1, tv2;
 	static struct timeval maxtime, laststep;
 	struct timespec ts;
-	int s;
 
-	s = splclock();
+	crit_enter();
 	microtime(&tv1);
 	delta = *tv;
 	timevalsub(&delta, &tv1);
@@ -122,7 +122,7 @@
 			}
 		} else {
 			if (tv1.tv_sec == laststep.tv_sec) {
-				splx(s);
+				crit_exit();
 				return (EPERM);
 			}
 			if (delta.tv_sec > 1) {
@@ -135,10 +135,9 @@
 
 	ts.tv_sec = tv->tv_sec;
 	ts.tv_nsec = tv->tv_usec * 1000;
-	set_timecounter(&ts);
-	(void) splsoftclock();
+	set_timeofday(&ts);
 	lease_updatetime(delta.tv_sec);
-	splx(s);
+	crit_exit();
 	resettodr();
 	return (0);
 }
@@ -195,20 +194,45 @@
 		 * Rounding up is especially important if rounding down
 		 * would give 0.  Perfect rounding is unimportant.
 		 */
-		ts.tv_nsec = 1000000000 / timecounter->tc_frequency + 1;
+		ts.tv_nsec = 1000000000 / cputimer_freq + 1;
 		error = copyout(&ts, SCARG(uap, tp), sizeof(ts));
 	}
 	return (error);
 }
 
-static int nanowait;
+/*
+ * nanosleep1()
+ *
+ *	This is a general helper function for nanosleep() (aka sleep() aka
+ *	usleep()).
+ *
+ *	If there is less then one tick's worth of time left and
+ *	we haven't done a yield, or the remaining microseconds is
+ *	ridiculously low, do a yield.  This avoids having
+ *	to deal with systimer overheads when the system is under
+ *	heavy loads.  If we have done a yield already then use
+ *	a systimer and an uninterruptable thread wait.
+ *
+ *	If there is more then a tick's worth of time left,
+ *	calculate the baseline ticks and use an interruptable
+ *	tsleep, then handle the fine-grained delay on the next
+ *	loop.  This usually results in two sleeps occuring, a long one
+ *	and a short one.
+ */
+static void
+ns1_systimer(systimer_t info)
+{
+	lwkt_schedule(info->data);
+}
 
 static int
 nanosleep1(struct timespec *rqt, struct timespec *rmt)
 {
+	static int nanowait;
 	struct timespec ts, ts2, ts3;
 	struct timeval tv;
 	int error;
+	int tried_yield;
 
 	if (rqt->tv_nsec < 0 || rqt->tv_nsec >= 1000000000)
 		return (EINVAL);
@@ -217,34 +241,36 @@
 	nanouptime(&ts);
 	timespecadd(&ts, rqt);		/* ts = target timestamp compare */
 	TIMESPEC_TO_TIMEVAL(&tv, rqt);	/* tv = sleep interval */
+	tried_yield = 0;
+
 	for (;;) {
-		/*
-		 * If hard looping is allowed and the interval is too short,
-		 * hard loop with a yield, otherwise sleep with a conservative
-		 * tick count.  In normal mode sleep with one extra tick count
-		 * which will be sufficient for most sleep values.  If it
-		 * isn't sufficient in normal mode we will wind up doing an
-		 * extra loop.
-		 *
-		 * sleep_hardloop = 0	Normal mode
-		 * sleep_hardloop = 1	Strict hard loop
-		 * sleep_hardloop = 2	Hard loop on < 1 tick requests only
-		 */
-		int ticks = tvtohz_low(&tv);
+		int ticks;
+		struct systimer info;
+
+		ticks = tv.tv_usec / tick;	/* approximate */
 
-		if (sleep_hardloop) {
-			if (ticks == 0) {
+		if (tv.tv_sec == 0 && ticks == 0) {
+			if (tried_yield || tv.tv_usec < sleep_hard_us) {
+				tried_yield = 0;
 				uio_yield();
-				error = iscaught(curproc);
 			} else {
-				error = tsleep(&nanowait, PCATCH, "nanslp", 
-						ticks + sleep_hardloop - 1);
+				crit_enter();
+				systimer_init_oneshot(&info, ns1_systimer,
+						curthread, tv.tv_usec);
+				lwkt_deschedule_self();
+				crit_exit();
+				lwkt_switch();
+				systimer_del(&info); /* make sure it's gone */
 			}
+			error = iscaught(curproc);
+		} else if (tv.tv_sec == 0) {
+			error = tsleep(&nanowait, PCATCH, "nanslp", ticks);
 		} else {
-			error = tsleep(&nanowait, PCATCH, "nanslp", ticks + 1);
+			ticks = tvtohz_low(&tv); /* also handles overflow */
+			error = tsleep(&nanowait, PCATCH, "nanslp", ticks);
 		}
 		nanouptime(&ts2);
-		if (error != EWOULDBLOCK) {
+		if (error && error != EWOULDBLOCK) {
 			if (error == ERESTART)
 				error = EINTR;
 			if (rmt != NULL) {
@@ -403,7 +429,7 @@
 	struct thread *td = curthread;
 	struct timeval atv;
 	long ndelta, ntickdelta, odelta;
-	int s, error;
+	int error;
 
 	if ((error = suser(td)))
 		return (error);
@@ -433,11 +459,14 @@
 	 */
 	if (ndelta < 0)
 		ntickdelta = -ntickdelta;
-	s = splclock();
+	/* 
+	 * XXX not MP safe , but will probably work anyway.
+	 */
+	crit_enter();
 	odelta = timedelta;
 	timedelta = ndelta;
 	tickdelta = ntickdelta;
-	splx(s);
+	crit_exit();
 
 	if (uap->olddelta) {
 		atv.tv_sec = odelta / 1000000;
@@ -476,11 +505,10 @@
 	struct proc *p = curproc;
 	struct timeval ctv;
 	struct itimerval aitv;
-	int s;
 
 	if (uap->which > ITIMER_PROF)
 		return (EINVAL);
-	s = splclock(); /* XXX still needed ? */
+	crit_enter();
 	if (uap->which == ITIMER_REAL) {
 		/*
 		 * Convert from absolute to relative time in .it_value
@@ -496,9 +524,10 @@
 			else
 				timevalsub(&aitv.it_value, &ctv);
 		}
-	} else
+	} else {
 		aitv = p->p_stats->p_timer[uap->which];
-	splx(s);
+	}
+	crit_exit();
 	return (copyout((caddr_t)&aitv, (caddr_t)uap->itv,
 	    sizeof (struct itimerval)));
 }
@@ -511,7 +540,7 @@
 	struct timeval ctv;
 	struct itimerval *itvp;
 	struct proc *p = curproc;
-	int s, error;
+	int error;
 
 	if (uap->which > ITIMER_PROF)
 		return (EINVAL);
@@ -530,7 +559,7 @@
 		timevalclear(&aitv.it_interval);
 	else if (itimerfix(&aitv.it_interval))
 		return (EINVAL);
-	s = splclock(); /* XXX: still needed ? */
+	crit_enter();
 	if (uap->which == ITIMER_REAL) {
 		if (timevalisset(&p->p_realtimer.it_value))
 			untimeout(realitexpire, (caddr_t)p, p->p_ithandle);
@@ -540,9 +569,10 @@
 		getmicrouptime(&ctv);
 		timevaladd(&aitv.it_value, &ctv);
 		p->p_realtimer = aitv;
-	} else
+	} else {
 		p->p_stats->p_timer[uap->which] = aitv;
-	splx(s);
+	}
+	crit_exit();
 	return (0);
 }
 
@@ -564,7 +594,6 @@
 {
 	struct proc *p;
 	struct timeval ctv, ntv;
-	int s;
 
 	p = (struct proc *)arg;
 	psignal(p, SIGALRM);
@@ -573,7 +602,7 @@
 		return;
 	}
 	for (;;) {
-		s = splclock(); /* XXX: still neeeded ? */
+		crit_enter();
 		timevaladd(&p->p_realtimer.it_value,
 		    &p->p_realtimer.it_interval);
 		getmicrouptime(&ctv);
@@ -582,10 +611,10 @@
 			timevalsub(&ntv, &ctv);
 			p->p_ithandle = timeout(realitexpire, (caddr_t)p,
 			    tvtohz_low(&ntv));
-			splx(s);
+			crit_exit();
 			return;
 		}
-		splx(s);
+		crit_exit();
 	}
 }
 
Index: kern/kern_timeout.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_timeout.c,v
retrieving revision 1.6
diff -u -r1.6 kern_timeout.c
--- kern/kern_timeout.c	26 Aug 2003 21:09:02 -0000	1.6
+++ kern/kern_timeout.c	26 Jan 2004 05:39:24 -0000
@@ -46,6 +46,7 @@
 #include <sys/kernel.h>
 #include <sys/interrupt.h>
 #include <sys/thread.h>
+#include <sys/thread2.h>
 #include <machine/ipl.h>
 
 /*
@@ -83,7 +84,6 @@
 {
 	struct callout *c;
 	struct callout_tailq *bucket;
-	int s;
 	int curticks;
 	int steps;	/* #steps since we last allowed interrupts */
 
@@ -92,7 +92,7 @@
 #endif /* MAX_SOFTCLOCK_STEPS */
 
 	steps = 0;
-	s = splhigh();
+	crit_enter();
 	while (softticks != ticks) {
 		softticks++;
 		/*
@@ -109,8 +109,8 @@
 				if (steps >= MAX_SOFTCLOCK_STEPS) {
 					nextsoftcheck = c;
 					/* Give interrupts a chance. */
-					splx(s);
-					s = splhigh();
+					crit_exit();
+					crit_enter();
 					c = nextsoftcheck;
 					steps = 0;
 				}
@@ -131,16 +131,16 @@
 					c->c_flags =
 					    (c->c_flags & ~CALLOUT_PENDING);
 				}
-				splx(s);
+				crit_exit();
 				c_func(c_arg);
-				s = splhigh();
+				crit_enter();
 				steps = 0;
 				c = nextsoftcheck;
 			}
 		}
 	}
 	nextsoftcheck = NULL;
-	splx(s);
+	crit_exit();
 }
 
 /*
Index: kern/lwkt_thread.c
===================================================================
RCS file: /cvs/src/sys/kern/lwkt_thread.c,v
retrieving revision 1.48
diff -u -r1.48 lwkt_thread.c
--- kern/lwkt_thread.c	18 Jan 2004 12:29:49 -0000	1.48
+++ kern/lwkt_thread.c	24 Jan 2004 23:56:43 -0000
@@ -1502,6 +1502,10 @@
  * us in a critical section.  The MP lock may or may not be held.
  * May also be called from doreti or splz, or be reentrantly called
  * indirectly through the ip_func[] we run.
+ *
+ * There are two versions, one where no interrupt frame is available (when
+ * called from the send code and from splz, and one where an interrupt
+ * frame is available.
  */
 void
 lwkt_process_ipiq(void)
@@ -1528,7 +1532,39 @@
 	while (ip->ip_rindex != ip->ip_windex) {
 	    ri = ip->ip_rindex & MAXCPUFIFO_MASK;
 	    ++ip->ip_rindex;
-	    ip->ip_func[ri](ip->ip_arg[ri]);
+	    ip->ip_func[ri](ip->ip_arg[ri], NULL);
+	    /* YYY memory barrier */
+	    ip->ip_xindex = ip->ip_rindex;
+	}
+    }
+}
+
+void
+lwkt_process_ipiq_frame(struct intrframe frame)
+{
+    int n;
+    int cpuid = mycpu->gd_cpuid;
+
+    for (n = 0; n < ncpus; ++n) {
+	lwkt_ipiq_t ip;
+	int ri;
+
+	if (n == cpuid)
+	    continue;
+	ip = globaldata_find(n)->gd_ipiq;
+	if (ip == NULL)
+	    continue;
+	ip = &ip[cpuid];
+
+	/*
+	 * Note: xindex is only updated after we are sure the function has
+	 * finished execution.  Beware lwkt_process_ipiq() reentrancy!  The
+	 * function may send an IPI which may block/drain.
+	 */
+	while (ip->ip_rindex != ip->ip_windex) {
+	    ri = ip->ip_rindex & MAXCPUFIFO_MASK;
+	    ++ip->ip_rindex;
+	    ip->ip_func[ri](ip->ip_arg[ri], &frame);
 	    /* YYY memory barrier */
 	    ip->ip_xindex = ip->ip_rindex;
 	}
Index: kern/subr_param.c
===================================================================
RCS file: /cvs/src/sys/kern/subr_param.c,v
retrieving revision 1.2
diff -u -r1.2 subr_param.c
--- kern/subr_param.c	17 Jun 2003 04:28:41 -0000	1.2
+++ kern/subr_param.c	25 Jan 2004 02:46:28 -0000
@@ -68,6 +68,8 @@
 #endif
 
 int	hz;
+int	stathz;
+int	profhz;
 int	tick;
 int	tickadj;			 /* can adjust 30ms in 60s */
 int	maxusers;			/* base tunable */
@@ -106,6 +108,8 @@
 {
 	hz = HZ;
 	TUNABLE_INT_FETCH("kern.hz", &hz);
+	stathz = hz * 128 / 100;
+	profhz = stathz;
 	tick = 1000000 / hz;
 	tickadj = howmany(30000, 60 * hz);	/* can adjust 30ms in 60s */
 
Index: netinet6/icmp6.c
===================================================================
RCS file: /cvs/src/sys/netinet6/icmp6.c,v
retrieving revision 1.6
diff -u -r1.6 icmp6.c
--- netinet6/icmp6.c	23 Aug 2003 11:02:45 -0000	1.6
+++ netinet6/icmp6.c	25 Jan 2004 20:26:45 -0000
@@ -2809,11 +2809,9 @@
 	int maxpps;	/* maximum pps allowed */
 {
 	struct timeval tv, delta;
-	int s, rv;
+	int rv;
 
-	s = splclock(); 
 	microtime(&tv);
-	splx(s);
 
 	timersub(&tv, lasttime, &delta);
 
Index: netproto/ns/ns_error.c
===================================================================
RCS file: /cvs/src/sys/netproto/ns/ns_error.c,v
retrieving revision 1.5
diff -u -r1.5 ns_error.c
--- netproto/ns/ns_error.c	6 Sep 2003 21:51:12 -0000	1.5
+++ netproto/ns/ns_error.c	25 Jan 2004 20:27:02 -0000
@@ -294,19 +294,6 @@
 	m_freem(m);
 }
 
-#ifdef notdef
-u_long
-nstime()
-{
-	int s = splclock();
-	u_long t;
-
-	t = (time.tv_sec % (24*60*60)) * 1000 + time.tv_usec / 1000;
-	splx(s);
-	return (htonl(t));
-}
-#endif
-
 int
 ns_echo(m)
 struct mbuf *m;
Index: pc98/pc98/clock.c
===================================================================
RCS file: /cvs/src/sys/pc98/pc98/clock.c,v
retrieving revision 1.4
diff -u -r1.4 clock.c
--- pc98/pc98/clock.c	20 Aug 2003 10:07:50 -0000	1.4
+++ pc98/pc98/clock.c	24 Jan 2004 23:31:17 -0000
@@ -166,12 +166,12 @@
 static	u_int32_t i8254_offset;
 static	int	i8254_ticked;
 /*
- * XXX new_function and timer_func should not handle clockframes, but
+ * XXX new_function and timer_func should not handle intrframes, but
  * timer_func currently needs to hold hardclock to handle the
  * timer0_state == 0 case.  We should use inthand_add()/inthand_remove()
  * to switch between clkintr() and a slightly different timerintr().
  */
-static	void	(*new_function) (struct clockframe *frame);
+static	void	(*new_function) (struct intrframe *frame);
 static	u_int	new_rate;
 #ifndef PC98
 static	u_char	rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
@@ -190,7 +190,7 @@
 static 	u_char	timer1_state;
 #endif
 static	u_char	timer2_state;
-static	void	(*timer_func) (struct clockframe *frame) = hardclock;
+static	void	(*timer_func) (struct intrframe *frame) = hardclock;
 #ifdef PC98
 static void rtc_serialcombit (int);
 static void rtc_serialcom (int);
@@ -226,7 +226,7 @@
 	&i8254_timecounter, sizeof(i8254_timecounter), "S,timecounter", "");
 
 static void
-clkintr(struct clockframe frame)
+clkintr(struct intrframe frame)
 {
 	if (timecounter->tc_get_timecount == i8254_get_timecount) {
 		disable_intr();
@@ -300,7 +300,7 @@
  * The acquire and release functions must be called at ipl >= splclock().
  */
 int
-acquire_timer0(int rate, void (*function) (struct clockframe *frame))
+acquire_timer0(int rate, void (*function) (struct intrframe *frame))
 {
 	static int old_rate;
 
@@ -441,7 +441,7 @@
  * in the statistics, but the stat clock will no longer stop.
  */
 static void
-rtcintr(struct clockframe frame)
+rtcintr(struct intrframe frame)
 {
 	while (rtcin(RTC_INTR) & RTCIR_PERIOD)
 		statclock(&frame);
Index: pc98/pc98/pcaudio.c
===================================================================
RCS file: /cvs/src/sys/pc98/pc98/pcaudio.c,v
retrieving revision 1.6
diff -u -r1.6 pcaudio.c
--- pc98/pc98/pcaudio.c	20 Aug 2003 10:07:50 -0000	1.6
+++ pc98/pc98/pcaudio.c	24 Jan 2004 23:31:24 -0000
@@ -151,7 +151,7 @@
 static int pca_sleep = 0;
 static int pca_initialized = 0;
 
-static void pcaintr(struct clockframe *frame);
+static void pcaintr(struct intrframe *frame);
 
 static	d_open_t	pcaopen;
 static	d_close_t	pcaclose;
@@ -561,7 +561,7 @@
 
 
 static void
-pcaintr(struct clockframe *frame)
+pcaintr(struct intrframe *frame)
 {
 	if (pca_status.index < pca_status.in_use[pca_status.current]) {
 		disable_intr();
Index: sys/globaldata.h
===================================================================
RCS file: /cvs/src/sys/sys/globaldata.h,v
retrieving revision 1.22
diff -u -r1.22 globaldata.h
--- sys/globaldata.h	30 Dec 2003 03:19:04 -0000	1.22
+++ sys/globaldata.h	25 Jan 2004 09:02:24 -0000
@@ -47,6 +47,9 @@
 #ifndef _SYS_SLABALLOC_H_
 #include <sys/slaballoc.h> /* SLGlobalData */
 #endif
+#ifndef _SYS_SYSTIMER_H_
+#include <sys/systimer.h> /* fine-grained system timers */
+#endif
 
 /*
  * This structure maps out the global data that needs to be kept on a
@@ -93,8 +96,6 @@
 	__uint32_t	gd_other_cpus;		/* mask of 'other' cpus */
 	struct timeval	gd_stattv;
 	int		gd_intr_nesting_level;	/* (for interrupts) */
-	int		gd_psticks;		/* profile kern/kern_clock.c */
-	int		gd_psdiv;		/* profile kern/kern_clock.c */
 	struct vmmeter	gd_cnt;
 	struct lwkt_ipiq *gd_ipiq;
 	short		gd_upri;		/* userland scheduler helper */
@@ -105,6 +106,14 @@
 	int		gd_vme_kdeficit;	/* vm_map_entry reservation */
 	int		gd_vme_avail;		/* vm_map_entry reservation */
 	struct vm_map_entry *gd_vme_base;	/* vm_map_entry reservation */
+	struct systimerq gd_systimerq;		/* per-cpu system timers */
+	int		gd_syst_nest;
+	sysclock_t	gd_nextclock;		/* next best-case clock req */
+	struct systimer gd_hardclock;		/* scheduler periodic */
+	struct systimer gd_statclock;		/* statistics periodic */
+	struct systimer gd_schedclock;		/* scheduler periodic */
+	volatile __uint32_t gd_time_seconds;	/* uptime in seconds */
+	volatile sysclock_t gd_cpuclock_base;	/* cpuclock relative base */
 	/* extended by <machine/pcpu.h> */
 };
 
Index: sys/kernel.h
===================================================================
RCS file: /cvs/src/sys/sys/kernel.h,v
retrieving revision 1.7
diff -u -r1.7 kernel.h
--- sys/kernel.h	22 Nov 2003 19:30:57 -0000	1.7
+++ sys/kernel.h	25 Jan 2004 22:43:39 -0000
@@ -64,7 +64,8 @@
 extern char kernelname[MAXPATHLEN];
 
 /* 1.2 */
-extern struct timeval boottime;
+extern struct timespec boottime;
+extern struct timespec basetime;
 
 extern struct timezone tz;			/* XXX */
 
@@ -76,8 +77,9 @@
 extern int profhz;			/* profiling clock's frequency */
 extern int ticks;
 extern int lbolt;			/* once a second sleep address */
-extern int tickdelta;
-extern long timedelta;
+extern int tickdelta;			/* make this correction */
+extern long timedelta;			/* until this aggregate is exhausted */
+extern int tickpll;			/* scaled tick adjustment */
 
 #endif /* _KERNEL */
 
Index: sys/proc.h
===================================================================
RCS file: /cvs/src/sys/sys/proc.h,v
retrieving revision 1.41
diff -u -r1.41 proc.h
--- sys/proc.h	4 Jan 2004 22:14:39 -0000	1.41
+++ sys/proc.h	25 Jan 2004 04:23:04 -0000
@@ -420,7 +420,7 @@
 void	resetpriority (struct proc *);
 int	roundrobin_interval (void);
 void	resched_cpus(u_int32_t mask);
-void	schedclock (void *dummy);
+void	schedulerclock (void *dummy);
 void	setrunnable (struct proc *);
 void	clrrunnable (struct proc *, int stat);
 void	setrunqueue (struct proc *);
Index: sys/systimer.h
===================================================================
RCS file: sys/systimer.h
diff -N sys/systimer.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/systimer.h	27 Jan 2004 23:52:35 -0000
@@ -0,0 +1,58 @@
+/*
+ * SYS/SYSTIMER.H
+ */
+
+#ifndef _SYS_SYSTIMER_H_
+#define _SYS_SYSTIMER_H_
+
+struct intrframe;
+
+typedef __uint32_t	sysclock_t;
+typedef TAILQ_HEAD(systimerq, systimer) *systimerq_t;
+typedef void (*systimer_func_t)(struct systimer *info);
+typedef void (*systimer_func2_t)(struct systimer *info, struct intrframe *frame);
+
+typedef struct systimer {
+    TAILQ_ENTRY(systimer)	node;
+    systimerq_t			queue;
+    sysclock_t			time;		/* absolute time next intr */
+    sysclock_t			periodic;	/* if non-zero */
+    systimer_func2_t		func;
+    void			*data;
+    int				flags;
+    struct globaldata		*gd;		/* cpu owning structure */
+} *systimer_t;
+
+#define SYSTF_ONQUEUE		0x0001
+#define SYSTF_IPIRUNNING	0x0002
+
+void systimer_intr(sysclock_t *time, struct intrframe *frame);
+void systimer_add(systimer_t info);
+void systimer_del(systimer_t info);
+void systimer_init_periodic(systimer_t info, void *func, void *data, int hz);
+void systimer_init_oneshot(systimer_t info, void *func, void *data, int us);
+
+
+/*
+ * note that cputimer_count() always returns a full-width wrapping counter.
+ */
+sysclock_t cputimer_count(void);
+sysclock_t cputimer_fromhz(int freq);
+sysclock_t cputimer_fromus(int us);
+void cputimer_intr_reload(sysclock_t clock);
+
+/*
+ * These variables hold the fixed cputimer frequency, determining the
+ * granularity of cputimer_count().  
+ *
+ * The 64 bit versions are used for converting count values into uS or nS
+ * as follows:
+ *
+ *	usec = (cputimer_freq64_usec * count) >> 32
+ */
+extern sysclock_t cputimer_freq;	/* in Hz */
+extern int64_t cputimer_freq64_usec;	/* in (1e6 << 32) / timer_freq */
+extern int64_t cputimer_freq64_nsec;	/* in (1e9 << 32) / timer_freq */
+
+#endif
+
Index: sys/systm.h
===================================================================
RCS file: /cvs/src/sys/sys/systm.h,v
retrieving revision 1.16
diff -u -r1.16 systm.h
--- sys/systm.h	15 Nov 2003 21:05:43 -0000	1.16
+++ sys/systm.h	25 Jan 2004 08:18:22 -0000
@@ -104,7 +104,7 @@
  * General function declarations.
  */
 
-struct clockframe;
+struct intrframe;
 struct malloc_type;
 struct proc;
 struct xwait;
@@ -186,8 +186,6 @@
 
 void	realitexpire (void *);
 void	DELAY(int usec);
-void	hardclock (struct clockframe *frame);
-void	statclock (struct clockframe *frame);
 
 void	startprofclock (struct proc *);
 void	stopprofclock (struct proc *);
Index: sys/thread.h
===================================================================
RCS file: /cvs/src/sys/sys/thread.h,v
retrieving revision 1.39
diff -u -r1.39 thread.h
--- sys/thread.h	18 Jan 2004 12:29:50 -0000	1.39
+++ sys/thread.h	24 Jan 2004 23:54:42 -0000
@@ -62,6 +62,11 @@
 #ifndef _MACHINE_THREAD_H_
 #include <machine/thread.h>		/* md_thread */
 #endif
+#ifndef _MACHINE_FRAME_H_
+#include <machine/frame.h>
+#endif
+#else
+struct intrframe;
 #endif
 
 /*
@@ -90,13 +95,19 @@
 #define MAXCPUFIFO      16	/* power of 2 */
 #define MAXCPUFIFO_MASK	(MAXCPUFIFO - 1)
 
+/*
+ * Always cast to ipifunc_t when registering an ipi.  The actual ipi function
+ * is called with both the data and an interrupt frame, but the ipi function
+ * that is registered might only declare a data argument.
+ */
 typedef void (*ipifunc_t)(void *arg);
+typedef void (*ipifunc2_t)(void *arg, struct intrframe *frame);
 
 typedef struct lwkt_ipiq {
     int		ip_rindex;      /* only written by target cpu */
     int		ip_xindex;      /* writte by target, indicates completion */
     int		ip_windex;      /* only written by source cpu */
-    ipifunc_t	ip_func[MAXCPUFIFO];
+    ipifunc2_t	ip_func[MAXCPUFIFO];
     void	*ip_arg[MAXCPUFIFO];
 } lwkt_ipiq;
 
@@ -284,6 +295,9 @@
 extern void lwkt_send_ipiq_mask(u_int32_t mask, ipifunc_t func, void *arg);
 extern void lwkt_wait_ipiq(int dcpu, int seq);
 extern void lwkt_process_ipiq(void);
+#ifdef _KERNEL
+extern void lwkt_process_ipiq_frame(struct intrframe frame);
+#endif
 extern void crit_panic(void);
 extern struct proc *lwkt_preempted_proc(void);
 
Index: sys/time.h
===================================================================
RCS file: /cvs/src/sys/sys/time.h,v
retrieving revision 1.7
diff -u -r1.7 time.h
--- sys/time.h	7 Jan 2004 11:04:20 -0000	1.7
+++ sys/time.h	27 Jan 2004 06:46:22 -0000
@@ -82,86 +82,6 @@
 #define	DST_EET		5	/* Eastern European dst */
 #define	DST_CAN		6	/* Canada */
 
-/*
- * Structure used to interface to the machine dependent hardware support
- * for timekeeping.
- *
- * A timecounter is a (hard or soft) binary counter which has two properties:
- *    * it runs at a fixed, known frequency.
- *    * it must not roll over in less than (1 + delta)/HZ seconds.  "delta"
- *	is expected to be less than 20 msec, but no hard data has been 
- *      collected on this.  16 bit at 5 MHz (31 msec) is known to work.
- *
- * get_timecount() reads the counter.
- *
- * counter_mask removes unimplemented bits from the count value.
- *
- * frequency is the counter frequency in hz.
- *
- * name is a short mnemonic name for this counter.
- *
- * cost is a measure of how long time it takes to read the counter.
- *
- * adjustment [PPM << 16] which means that the smallest unit of correction
- *     you can apply amounts to 481.5 usec/year.
- *
- * scale_micro [2^32 * usec/tick].
- * scale_nano_i [ns/tick].
- * scale_nano_f [(ns/2^32)/tick].
- *
- * offset_count is the contents of the counter which corresponds to the
- *     rest of the offset_* values.
- *
- * offset_sec [s].
- * offset_micro [usec].
- * offset_nano [ns/2^32] is misnamed, the real unit is .23283064365...
- *     attoseconds (10E-18) and before you ask: yes, they are in fact 
- *     called attoseconds, it comes from "atten" for 18 in Danish/Swedish.
- *
- * Each timecounter must supply an array of three timecounters, this is needed
- * to guarantee atomicity in the code.  Index zero is used to transport 
- * modifications, for instance done with sysctl, into the timecounter being 
- * used in a safe way.  Such changes may be adopted with a delay of up to 1/HZ,
- * index one & two are used alternately for the actual timekeeping.
- *
- * 'tc_avail' points to the next available (external) timecounter in a
- *      circular queue.  This is only valid for index 0.
- *
- * `tc_other' points to the next "work" timecounter in a circular queue,
- *      i.e., for index i > 0 it points to index 1 + (i - 1) % NTIMECOUNTER.
- *      We also use it to point from index 0 to index 1.
- *
- * `tc_tweak' points to index 0.
- */
-
-struct timecounter;
-typedef unsigned timecounter_get_t (struct timecounter *);
-typedef void timecounter_pps_t (struct timecounter *);
-
-struct timecounter {
-	/* These fields must be initialized by the driver. */
-	timecounter_get_t	*tc_get_timecount;
-	timecounter_pps_t	*tc_poll_pps;
-	unsigned 		tc_counter_mask;
-	u_int32_t		tc_frequency;
-	char			*tc_name;
-	void			*tc_priv;
-	/* These fields will be managed by the generic code. */
-	int64_t			tc_adjustment;
-	u_int32_t		tc_scale_micro;
-	u_int32_t		tc_scale_nano_i;
-	u_int32_t		tc_scale_nano_f;
-	unsigned 		tc_offset_count;
-	u_int32_t		tc_offset_sec;
-	u_int32_t		tc_offset_micro;
-	u_int64_t		tc_offset_nano;
-	struct timeval		tc_microtime;
-	struct timespec		tc_nanotime;
-	struct timecounter	*tc_avail;
-	struct timecounter	*tc_other;
-	struct timecounter	*tc_tweak;
-};
-
 #ifdef _KERNEL
 
 /* Operations on timespecs */
@@ -269,14 +189,13 @@
 #endif
 
 #ifdef _KERNEL
-extern struct timecounter *timecounter;
 extern time_t	time_second;
 
+void	initclocks_pcpu(void);
 void	getmicrouptime (struct timeval *tv);
 void	getmicrotime (struct timeval *tv);
 void	getnanouptime (struct timespec *tv);
 void	getnanotime (struct timespec *tv);
-void	init_timecounter (struct timecounter *tc);
 int	itimerdecr (struct itimerval *itp, int usec);
 int	itimerfix (struct timeval *tv);
 int 	ppsratecheck (struct timeval *, int *, int usec);
@@ -285,12 +204,11 @@
 void	microtime (struct timeval *tv);
 void	nanouptime (struct timespec *ts);
 void	nanotime (struct timespec *ts);
-void	set_timecounter (struct timespec *ts);
+void	set_timeofday(struct timespec *ts);
 void	timevaladd (struct timeval *, struct timeval *);
 void	timevalsub (struct timeval *, struct timeval *);
 int	tvtohz_high (struct timeval *);
 int	tvtohz_low (struct timeval *);
-void	update_timecounter (struct timecounter *tc);
 #else /* !_KERNEL */
 #include <time.h>
 
Index: sys/timepps.h
===================================================================
RCS file: /cvs/src/sys/sys/timepps.h,v
retrieving revision 1.3
diff -u -r1.3 timepps.h
--- sys/timepps.h	20 Aug 2003 07:31:21 -0000	1.3
+++ sys/timepps.h	25 Jan 2004 01:32:44 -0000
@@ -17,6 +17,7 @@
 #define _SYS_TIMEPPS_H_
 
 #include <sys/ioccom.h>
+#include <sys/systimer.h>
 
 #define PPS_API_VERS_1	1
 
@@ -109,11 +110,10 @@
 	pps_info_t	ppsinfo;
 	int		kcmode;
 	int		ppscap;
-	struct timecounter *ppstc;
-	unsigned	ppscount[3];
+	sysclock_t	ppscount[3];
 };
 
-void pps_event (struct pps_state *pps, struct timecounter *tc, unsigned count, int event);
+void pps_event (struct pps_state *pps, sysclock_t count, int event);
 void pps_init (struct pps_state *pps);
 int pps_ioctl (u_long cmd, caddr_t data, struct pps_state *pps);
 void hardpps (struct timespec *tsp, long nsec);
Index: sys/timex.h
===================================================================
RCS file: /cvs/src/sys/sys/timex.h,v
retrieving revision 1.3
diff -u -r1.3 timex.h
--- sys/timex.h	20 Aug 2003 07:31:21 -0000	1.3
+++ sys/timex.h	26 Jan 2004 04:34:27 -0000
@@ -221,9 +221,11 @@
 #ifdef __FreeBSD__
 
 #ifdef _KERNEL
-struct timecounter;
-void	ntp_update_second (struct timecounter *tc);
-#else /* !_KERNEL */
+
+int   ntp_update_second (time_t, int64_t *);
+
+#else
+
 #include <sys/cdefs.h>
 
 __BEGIN_DECLS
Index: vfs/nfs/nfs_serv.c
===================================================================
RCS file: /cvs/src/sys/vfs/nfs/nfs_serv.c,v
retrieving revision 1.11
diff -u -r1.11 nfs_serv.c
--- vfs/nfs/nfs_serv.c	9 Oct 2003 22:27:26 -0000	1.11
+++ vfs/nfs/nfs_serv.c	25 Jan 2004 08:39:16 -0000
@@ -136,7 +136,7 @@
 int nfsrvw_procrastinate = NFS_GATHERDELAY * 1000;
 int nfsrvw_procrastinate_v3 = 0;
 
-static struct timeval	nfsver = { 0 };
+static struct timespec	nfsver;
 
 SYSCTL_DECL(_vfs_nfs);
 
@@ -1196,7 +1196,7 @@
 		if (nfsver.tv_sec == 0)
 			nfsver = boottime;
 		*tl++ = txdr_unsigned(nfsver.tv_sec);
-		*tl = txdr_unsigned(nfsver.tv_usec);
+		*tl = txdr_unsigned(nfsver.tv_nsec / 1000);
 	} else {
 		nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
 		nfsm_srvfillattr(vap, fp);
@@ -1484,7 +1484,7 @@
 			    if (nfsver.tv_sec == 0)
 				    nfsver = boottime;
 			    *tl++ = txdr_unsigned(nfsver.tv_sec);
-			    *tl = txdr_unsigned(nfsver.tv_usec);
+			    *tl = txdr_unsigned(nfsver.tv_nsec / 1000);
 			} else {
 			    nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
 			    nfsm_srvfillattr(&va, fp);
@@ -3694,7 +3694,7 @@
 		if (nfsver.tv_sec == 0)
 			nfsver = boottime;
 		*tl++ = txdr_unsigned(nfsver.tv_sec);
-		*tl = txdr_unsigned(nfsver.tv_usec);
+		*tl = txdr_unsigned(nfsver.tv_nsec / 1000);
 	} else {
 		error = 0;
 	}
Index: vfs/nfs/nfs_vfsops.c
===================================================================
RCS file: /cvs/src/sys/vfs/nfs/nfs_vfsops.c,v
retrieving revision 1.10
diff -u -r1.10 nfs_vfsops.c
--- vfs/nfs/nfs_vfsops.c	15 Nov 2003 21:05:44 -0000	1.10
+++ vfs/nfs/nfs_vfsops.c	25 Jan 2004 08:41:23 -0000
@@ -409,8 +409,8 @@
 	 * XXX time must be non-zero when we init the interface or else
 	 * the arp code will wedge...
 	 */
-	while (time_second == 0)
-		tsleep(&time_second, 0, "arpkludge", 10);
+	while (mycpu->gd_time_seconds == 0)
+		tsleep(mycpu, 0, "arpkludge", 10);
 
 	if (nfs_diskless_valid==1) 
 	  nfs_convert_diskless();
Index: vfs/nfs/nfs_vnops.c
===================================================================
RCS file: /cvs/src/sys/vfs/nfs/nfs_vnops.c,v
retrieving revision 1.16
diff -u -r1.16 nfs_vnops.c
--- vfs/nfs/nfs_vnops.c	23 Jan 2004 23:00:52 -0000	1.16
+++ vfs/nfs/nfs_vnops.c	25 Jan 2004 08:41:41 -0000
@@ -301,7 +301,7 @@
 		rmode = fxdr_unsigned(u_int32_t, *tl);
 		np->n_mode = rmode;
 		np->n_modeuid = cred->cr_uid;
-		np->n_modestamp = time_second;
+		np->n_modestamp = mycpu->gd_time_seconds;
 	}
 	m_freem(mrep);
 nfsmout:
@@ -382,7 +382,7 @@
 		 * Does our cached result allow us to give a definite yes to
 		 * this request?
 		 */
-		if ((time_second < (np->n_modestamp + nfsaccess_cache_timeout)) &&
+		if ((mycpu->gd_time_seconds < (np->n_modestamp + nfsaccess_cache_timeout)) &&
 		    (ap->a_cred->cr_uid == np->n_modeuid) &&
 		    ((np->n_mode & mode) == mode)) {
 			nfsstats.accesscache_hits++;
