From 88c4d2f6b86f6969f185bb4d4bdf1e4d59e08932 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Fri, 30 Jan 2004 05:42:18 +0000 Subject: [PATCH] This commit represents a major revamping of the clock interrupt and timebase infrastructure in DragonFly. * Rip out the existing 8254 timer 0 code, and also disable the use of Timer 2 (which means that the PC speaker will no longer go beep). Timer 0 used to represent a periodic interrupt and a great deal of code was in place to attempt to obtain a timebase off of that periodic interrupt. Timer 0 is now used in software retriggerable one-shot mode to produce variable-delay interrupts. A new hardware interrupt clock abstraction called SYSTIMERS has been introduced which allows threads to register periodic or one-shot interrupt/IPI callbacks at approximately 1uS granularity. Timer 2 is now set in continuous periodic mode with a period of 65536 and provides the timebase for the system, abstracted to 32 bits. All the old platform-integrated hardclock() and statclock() code has been rewritten. The old IPI forwarding code has been #if 0'd out and will soon be entirely removed (the systimer abstraction takes care of multi-cpu registrations now). The architecture-specific clkintr() now simply calls an entry point into the systimer and provides a Timer 0 reload and Timer 2 timebase function API. * On both UP and SMP systems, cpus register systimer interrupts for the Hz interrupt, the stat interrupt, and the scheduler round-robin interrupt. The abstraction is carefully designed to allow multiple interrupts occuring at the same time to be processed in a single hardware interrupt. While we currently use IPI's to distribute requested interrupts from other cpu's, the intent is to use the abstraction to take advantage of per-cpu timers when available (e.g. on the LAPIC) in the future. systimer interrupts run OUTSIDE THE MP LOCK. Entry points may be called from the hard interrupt or via an IPI message (IPI messages have always run outside the MP lock). * Rip out timecounters and disable alternative timecounter code for other time sources. This is temporary. Eventually other time sources, such as the TSC, will be reintegrated as independant, parallel-running entities. There will be no 'time switching' per-say, subsystems will be able to select which timebase they wish to use. It is desireable to reintegrate at least the TSC to improve [get]{micro,nano}[up]time() performance. WARNING: PPS events may not work properly. They were not removed, but they have not been retested with the new code either. * Remove spl protection around [get]{micro,nano}[up]time() calls, they are now internally protected. * Use uptime instead of realtime in certain CAM timeout tests * Remove struct clockframe. Use struct intrframe everywhere where clockframe used to be used. * Replace most splstatclock() protections with crit_*() protections, because such protections must now also protect against IPI messaging interrupts. * Add fields to the per-cpu globaldata structure to access timebase related information using only a critical section rather then a mutex. However, the 8254 Timer 2 access code still uses spin locks. More work needs to be done here, the 'realtime' correction is still done in a single global 'struct timespec basetime' structure. * Remove the CLKINTR_PENDING icu and apic interrupt hacks. * Augment the IPI Messaging code to make an intrframe available to callbacks. * Document 8254 timing modes in i386/sai/timerreg.h. Note that at the moment we assume an 8254 instead of an 8253 as we are using TIMER_SWSTROBE mode. This may or may not have to be changed to an 8253 mode. * Integrate the NTP correction code into the new timebase subsystem. * Separate boottime from basettime. Once boottime is believed to be stable it is no longer effected by NTP or other time corrections. CAVETS: * PC speaker no longer works * Profiling interrupt rate not increased (it needs work to be made operational on a per-cpu basis rather then system-wide). * The native timebase API is function-based, but currently hardwired. * There might or might not be issues with 486 systems due to the timer mode I am using. --- sys/bus/cam/cam_ccb.h | 6 +- sys/bus/cam/cam_periph.c | 7 +- sys/bus/cam/cam_xpt.c | 18 +- sys/bus/isa/syscons_isa.c | 9 +- sys/cpu/i386/include/cpu.h | 6 +- sys/cpu/i386/include/frame.h | 29 +- sys/dev/acpica/acpi_timer.c | 7 +- sys/dev/misc/dec/mcclock.c | 29 +- sys/dev/misc/pps/pps.c | 10 +- sys/dev/misc/xrpu/xrpu.c | 10 +- sys/dev/serial/sio/sio.c | 8 +- sys/dev/sound/isa/i386/pca/pcaudio.c | 6 +- sys/dev/sound/isa/i386/spkr/spkr.c | 11 +- sys/i386/apic/apic_vector.s | 12 +- sys/i386/i386/elan-mmcr.c | 7 +- sys/i386/i386/mp_clock.c | 7 +- sys/i386/i386/mp_machdep.c | 14 +- sys/i386/icu/icu_vector.s | 8 +- sys/i386/include/clock.h | 13 +- sys/i386/include/cpu.h | 6 +- sys/i386/include/frame.h | 29 +- sys/i386/isa/apic_vector.s | 12 +- sys/i386/isa/clock.c | 672 +++++---------- sys/i386/isa/icu_vector.s | 8 +- sys/i386/isa/ipl.s | 6 +- sys/i386/isa/timerreg.h | 41 +- sys/kern/init_main.c | 3 +- sys/kern/kern_clock.c | 1136 +++++++++++--------------- sys/kern/kern_ntptime.c | 69 +- sys/kern/kern_poll.c | 4 +- sys/kern/kern_random.c | 7 +- sys/kern/kern_synch.c | 9 +- sys/kern/kern_systimer.c | 207 +++++ sys/kern/kern_time.c | 121 +-- sys/kern/kern_timeout.c | 16 +- sys/kern/lwkt_thread.c | 40 +- sys/kern/subr_param.c | 6 +- sys/netinet6/icmp6.c | 6 +- sys/netproto/ns/ns_error.c | 15 +- sys/platform/pc32/apic/apic_vector.s | 12 +- sys/platform/pc32/i386/elan-mmcr.c | 7 +- sys/platform/pc32/i386/mp_clock.c | 7 +- sys/platform/pc32/i386/mp_machdep.c | 14 +- sys/platform/pc32/icu/icu_vector.s | 8 +- sys/platform/pc32/include/clock.h | 13 +- sys/platform/pc32/isa/apic_vector.s | 12 +- sys/platform/pc32/isa/clock.c | 672 +++++---------- sys/platform/pc32/isa/icu_vector.s | 8 +- sys/platform/pc32/isa/ipl.s | 6 +- sys/platform/pc32/isa/timerreg.h | 41 +- sys/sys/globaldata.h | 15 +- sys/sys/kernel.h | 10 +- sys/sys/proc.h | 4 +- sys/sys/systimer.h | 84 ++ sys/sys/systm.h | 6 +- sys/sys/thread.h | 18 +- sys/sys/time.h | 88 +- sys/sys/timepps.h | 8 +- sys/sys/timex.h | 10 +- 59 files changed, 1673 insertions(+), 2000 deletions(-) create mode 100644 sys/kern/kern_systimer.c create mode 100644 sys/sys/systimer.h diff --git a/sys/bus/cam/cam_ccb.h b/sys/bus/cam/cam_ccb.h index 108f6f9d17..364dc095e5 100644 --- a/sys/bus/cam/cam_ccb.h +++ b/sys/bus/cam/cam_ccb.h @@ -26,7 +26,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/cam/cam_ccb.h,v 1.15.2.3 2003/07/29 04:00:34 njl Exp $ - * $DragonFly: src/sys/bus/cam/cam_ccb.h,v 1.6 2003/12/29 23:30:58 dillon Exp $ + * $DragonFly: src/sys/bus/cam/cam_ccb.h,v 1.7 2004/01/30 05:42:09 dillon Exp $ */ #ifndef _CAM_CAM_CCB_H @@ -276,7 +276,7 @@ struct ccb_getdevstats { * 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 @@ struct ccb_pathinq { /* 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 { diff --git a/sys/bus/cam/cam_periph.c b/sys/bus/cam/cam_periph.c index 68d86d3389..c37ee718f1 100644 --- a/sys/bus/cam/cam_periph.c +++ b/sys/bus/cam/cam_periph.c @@ -27,7 +27,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/cam/cam_periph.c,v 1.24.2.3 2003/01/25 19:04:40 dillon Exp $ - * $DragonFly: src/sys/bus/cam/cam_periph.c,v 1.6 2003/11/10 06:12:00 dillon Exp $ + * $DragonFly: src/sys/bus/cam/cam_periph.c,v 1.7 2004/01/30 05:42:09 dillon Exp $ */ #include @@ -1071,11 +1071,8 @@ cam_periph_freeze_after_event(struct cam_periph *periph, { 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; diff --git a/sys/bus/cam/cam_xpt.c b/sys/bus/cam/cam_xpt.c index 72aa2e1694..b1d9d252f0 100644 --- a/sys/bus/cam/cam_xpt.c +++ b/sys/bus/cam/cam_xpt.c @@ -27,7 +27,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/cam/cam_xpt.c,v 1.80.2.18 2002/12/09 17:31:55 gibbs Exp $ - * $DragonFly: src/sys/bus/cam/cam_xpt.c,v 1.8 2003/12/29 08:37:38 dillon Exp $ + * $DragonFly: src/sys/bus/cam/cam_xpt.c,v 1.9 2004/01/30 05:42:09 dillon Exp $ */ #include #include @@ -162,7 +162,7 @@ struct cam_et { 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 @@ struct cam_eb { 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 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg) 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 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg) 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); diff --git a/sys/bus/isa/syscons_isa.c b/sys/bus/isa/syscons_isa.c index fd8ec69186..bbae98423b 100644 --- a/sys/bus/isa/syscons_isa.c +++ b/sys/bus/isa/syscons_isa.c @@ -24,7 +24,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/isa/syscons_isa.c,v 1.11.2.2 2001/08/01 10:42:28 yokota Exp $ - * $DragonFly: src/sys/bus/isa/syscons_isa.c,v 1.3 2003/08/07 21:16:46 dillon Exp $ + * $DragonFly: src/sys/bus/isa/syscons_isa.c,v 1.4 2004/01/30 05:42:12 dillon Exp $ */ #include "opt_syscons.h" @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -219,15 +220,15 @@ sc_tone(int herz) 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__ */ diff --git a/sys/cpu/i386/include/cpu.h b/sys/cpu/i386/include/cpu.h index b299ed8b83..82a0ee87c0 100644 --- a/sys/cpu/i386/include/cpu.h +++ b/sys/cpu/i386/include/cpu.h @@ -35,7 +35,7 @@ * * from: @(#)cpu.h 5.4 (Berkeley) 5/9/91 * $FreeBSD: src/sys/i386/include/cpu.h,v 1.43.2.2 2001/06/15 09:37:57 scottl Exp $ - * $DragonFly: src/sys/cpu/i386/include/cpu.h,v 1.12 2003/11/21 05:29:08 dillon Exp $ + * $DragonFly: src/sys/cpu/i386/include/cpu.h,v 1.13 2004/01/30 05:42:16 dillon Exp $ */ #ifndef _MACHINE_CPU_H_ @@ -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, diff --git a/sys/cpu/i386/include/frame.h b/sys/cpu/i386/include/frame.h index 60ae2e0c1e..9c6c597898 100644 --- a/sys/cpu/i386/include/frame.h +++ b/sys/cpu/i386/include/frame.h @@ -35,7 +35,7 @@ * * from: @(#)frame.h 5.2 (Berkeley) 1/18/91 * $FreeBSD: src/sys/i386/include/frame.h,v 1.20 1999/09/29 15:06:22 marcel Exp $ - * $DragonFly: src/sys/cpu/i386/include/frame.h,v 1.3 2003/08/26 21:42:18 rob Exp $ + * $DragonFly: src/sys/cpu/i386/include/frame.h,v 1.4 2004/01/30 05:42:16 dillon Exp $ */ #ifndef _MACHINE_FRAME_H_ @@ -129,33 +129,6 @@ struct intrframe { 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 *); diff --git a/sys/dev/acpica/acpi_timer.c b/sys/dev/acpica/acpi_timer.c index 5ff2dd60aa..88e7c0096c 100644 --- a/sys/dev/acpica/acpi_timer.c +++ b/sys/dev/acpica/acpi_timer.c @@ -25,8 +25,11 @@ * SUCH DAMAGE. * * $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/Attic/acpi_timer.c,v 1.1 2003/09/24 03:32:16 drhodus Exp $ + * $DragonFly: src/sys/dev/acpica/Attic/acpi_timer.c,v 1.2 2004/01/30 05:42:15 dillon Exp $ */ + +#ifdef NOTDEF /* Disabled */ + #include "opt_acpi.h" #include #include @@ -388,3 +391,5 @@ acpi_timer_pci_probe(device_t dev) return(ENXIO); /* we never match anything */ } #endif + +#endif diff --git a/sys/dev/misc/dec/mcclock.c b/sys/dev/misc/dec/mcclock.c index a7dc065396..127b6b1788 100644 --- a/sys/dev/misc/dec/mcclock.c +++ b/sys/dev/misc/dec/mcclock.c @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/dev/dec/mcclock.c,v 1.5.2.2 2001/12/17 14:03:15 gallatin Exp $ */ -/* $DragonFly: src/sys/dev/misc/dec/Attic/mcclock.c,v 1.3 2003/08/07 21:16:56 dillon Exp $ */ +/* $DragonFly: src/sys/dev/misc/dec/Attic/mcclock.c,v 1.4 2004/01/30 05:42:15 dillon Exp $ */ /* $NetBSD: mcclock.c,v 1.11 1998/04/19 07:50:25 jonathan Exp $ */ /* @@ -72,11 +72,10 @@ void mcclock_get(device_t dev, time_t base, struct clocktime *ct) { mc_todregs regs; - int s; - s = splclock(); + crit_enter(); MC146818_GETTOD(dev, ®s) - splx(s); + crit_exit(); ct->sec = regs[MC_SEC]; ct->min = regs[MC_MIN]; @@ -94,11 +93,10 @@ void mcclock_set(device_t dev, struct clocktime *ct) { mc_todregs regs; - int s; - s = splclock(); + crit_enter(); MC146818_GETTOD(dev, ®s); - splx(s); + crit_exit(); regs[MC_SEC] = ct->sec; regs[MC_MIN] = ct->min; @@ -108,9 +106,9 @@ mcclock_set(device_t dev, struct clocktime *ct) regs[MC_MONTH] = ct->mon; regs[MC_YEAR] = ct->year; - s = splclock(); + crit_enter(); MC146818_PUTTOD(dev, ®s); - splx(s); + crit_exit(); } int @@ -118,23 +116,22 @@ mcclock_getsecs(device_t dev, int *secp) { 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; } + diff --git a/sys/dev/misc/pps/pps.c b/sys/dev/misc/pps/pps.c index 75a98b0e4f..65535cc7f9 100644 --- a/sys/dev/misc/pps/pps.c +++ b/sys/dev/misc/pps/pps.c @@ -7,7 +7,7 @@ * ---------------------------------------------------------------------------- * * $FreeBSD: src/sys/dev/ppbus/pps.c,v 1.24.2.1 2000/05/24 00:20:57 n_hibma Exp $ - * $DragonFly: src/sys/dev/misc/pps/pps.c,v 1.5 2003/08/07 21:16:58 dillon Exp $ + * $DragonFly: src/sys/dev/misc/pps/pps.c,v 1.6 2004/01/30 05:42:15 dillon Exp $ * * This driver implements a draft-mogul-pps-api-02.txt PPS source. * @@ -183,16 +183,14 @@ ppsintr(void *arg) 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); } diff --git a/sys/dev/misc/xrpu/xrpu.c b/sys/dev/misc/xrpu/xrpu.c index 518a85e6ab..9eb8efe9b5 100644 --- a/sys/dev/misc/xrpu/xrpu.c +++ b/sys/dev/misc/xrpu/xrpu.c @@ -7,7 +7,7 @@ * ---------------------------------------------------------------------------- * * $FreeBSD: src/sys/pci/xrpu.c,v 1.19.2.1 2000/08/02 22:19:57 peter Exp $ - * $DragonFly: src/sys/dev/misc/xrpu/Attic/xrpu.c,v 1.5 2003/08/07 21:16:59 dillon Exp $ + * $DragonFly: src/sys/dev/misc/xrpu/Attic/xrpu.c,v 1.6 2004/01/30 05:42:15 dillon Exp $ * * A very simple device driver for PCI cards based on Xilinx 6200 series * FPGA/RPU devices. Current Functionality is to allow you to open and @@ -73,12 +73,16 @@ struct softc { 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 @@ xrpu_poll_pps(struct timecounter *tc) } } +#endif + static int xrpu_open(dev_t dev, int flag, int mode, struct thread *td) { @@ -157,6 +163,7 @@ xrpu_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *td) return (error); } +#if 0 if (cmd == XRPU_IOC_TIMECOUNTING) { struct xrpu_timecounting *xt = (struct xrpu_timecounting *)arg; @@ -194,6 +201,7 @@ xrpu_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *td) init_timecounter(&sc->tc); return (0); } +#endif error = ENOTTY; return (error); } diff --git a/sys/dev/serial/sio/sio.c b/sys/dev/serial/sio/sio.c index 2e1bf8cfa9..bef9e06468 100644 --- a/sys/dev/serial/sio/sio.c +++ b/sys/dev/serial/sio/sio.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/isa/sio.c,v 1.291.2.35 2003/05/18 08:51:15 murray Exp $ - * $DragonFly: src/sys/dev/serial/sio/sio.c,v 1.12 2004/01/24 08:00:45 dillon Exp $ + * $DragonFly: src/sys/dev/serial/sio/sio.c,v 1.13 2004/01/30 05:42:15 dillon Exp $ * from: @(#)com.c 7.5 (Berkeley) 5/16/91 * from: i386/isa sio.c,v 1.234 */ @@ -1751,7 +1751,6 @@ siointr1(com) 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 @@ siointr1(com) 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); } diff --git a/sys/dev/sound/isa/i386/pca/pcaudio.c b/sys/dev/sound/isa/i386/pca/pcaudio.c index 36aaec2ffe..eefe3553d6 100644 --- a/sys/dev/sound/isa/i386/pca/pcaudio.c +++ b/sys/dev/sound/isa/i386/pca/pcaudio.c @@ -26,7 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/i386/isa/pcaudio.c,v 1.58 2000/01/25 21:58:43 dfr Exp $ - * $DragonFly: src/sys/dev/sound/isa/i386/pca/Attic/pcaudio.c,v 1.7 2003/08/27 06:48:15 rob Exp $ + * $DragonFly: src/sys/dev/sound/isa/i386/pca/Attic/pcaudio.c,v 1.8 2004/01/30 05:42:16 dillon Exp $ */ #include @@ -147,7 +147,7 @@ static unsigned char alaw_linear[] = { 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 @@ pcaioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) static void -pcaintr(struct clockframe *frame) +pcaintr(struct intrframe *frame) { if (pca_status.index < pca_status.in_use[pca_status.current]) { cpu_disable_intr(); diff --git a/sys/dev/sound/isa/i386/spkr/spkr.c b/sys/dev/sound/isa/i386/spkr/spkr.c index 1553a618b5..972106796c 100644 --- a/sys/dev/sound/isa/i386/spkr/spkr.c +++ b/sys/dev/sound/isa/i386/spkr/spkr.c @@ -5,7 +5,7 @@ * modified for FreeBSD by Andrew A. Chernov * * $FreeBSD: src/sys/i386/isa/spkr.c,v 1.45 2000/01/29 16:00:32 peter Exp $ - * $DragonFly: src/sys/dev/sound/isa/i386/spkr/Attic/spkr.c,v 1.8 2003/08/27 06:48:15 rob Exp $ + * $DragonFly: src/sys/dev/sound/isa/i386/spkr/Attic/spkr.c,v 1.9 2004/01/30 05:42:16 dillon Exp $ */ #include @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -79,26 +80,22 @@ tone(thz, ticks) 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 @@ tone(thz, ticks) 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 */ diff --git a/sys/i386/apic/apic_vector.s b/sys/i386/apic/apic_vector.s index 306dbaa1cf..9e42b5d026 100644 --- a/sys/i386/apic/apic_vector.s +++ b/sys/i386/apic/apic_vector.s @@ -1,7 +1,7 @@ /* * from: vector.s, 386BSD 0.1 unknown origin * $FreeBSD: src/sys/i386/isa/apic_vector.s,v 1.47.2.5 2001/09/01 22:33:38 tegge Exp $ - * $DragonFly: src/sys/i386/apic/Attic/apic_vector.s,v 1.14 2003/09/25 23:49:08 dillon Exp $ + * $DragonFly: src/sys/i386/apic/Attic/apic_vector.s,v 1.15 2004/01/30 05:42:16 dillon Exp $ */ @@ -484,11 +484,13 @@ Xipiq: 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 @@ MCOUNT_LABEL(bintr) 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,) diff --git a/sys/i386/i386/elan-mmcr.c b/sys/i386/i386/elan-mmcr.c index a9dc5d169e..fe72f8b83d 100644 --- a/sys/i386/i386/elan-mmcr.c +++ b/sys/i386/i386/elan-mmcr.c @@ -7,7 +7,7 @@ * ---------------------------------------------------------------------------- * * $FreeBSD: src/sys/i386/i386/elan-mmcr.c,v 1.6.2.1 2002/09/17 22:39:53 sam Exp $ - * $DragonFly: src/sys/i386/i386/Attic/elan-mmcr.c,v 1.4 2003/07/21 07:57:43 dillon Exp $ + * $DragonFly: src/sys/i386/i386/Attic/elan-mmcr.c,v 1.5 2004/01/30 05:42:16 dillon Exp $ * The AMD Elan sc520 is a system-on-chip gadget which is used in embedded * kind of things, see www.soekris.com for instance, and it has a few quirks * we need to deal with. @@ -34,6 +34,7 @@ uint16_t *elan_mmcr; +#if 0 static unsigned elan_get_timecount(struct timecounter *tc) @@ -49,6 +50,8 @@ static struct timecounter elan_timecounter = { "ELAN" }; +#endif + void init_AMD_Elan_sc520(void) { @@ -74,9 +77,11 @@ init_AMD_Elan_sc520(void) 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 } diff --git a/sys/i386/i386/mp_clock.c b/sys/i386/i386/mp_clock.c index 1b310d26bb..c8be35aead 100644 --- a/sys/i386/i386/mp_clock.c +++ b/sys/i386/i386/mp_clock.c @@ -30,7 +30,7 @@ * directions. If we only cared about monosity two reads would be enough. * * $FreeBSD: src/sys/i386/i386/mp_clock.c,v 1.4.2.2 2000/09/30 02:49:32 ps Exp $ - * $DragonFly: src/sys/i386/i386/Attic/mp_clock.c,v 1.3 2003/08/07 21:17:22 dillon Exp $ + * $DragonFly: src/sys/i386/i386/Attic/mp_clock.c,v 1.4 2004/01/30 05:42:16 dillon Exp $ * */ @@ -46,6 +46,8 @@ #include +#if 0 + static unsigned piix_get_timecount(struct timecounter *tc); static u_int32_t piix_timecounter_address; @@ -140,3 +142,6 @@ static driver_t piix_driver = { static devclass_t piix_devclass; DRIVER_MODULE(piix, pci, piix_driver, piix_devclass, 0, 0); + +#endif + diff --git a/sys/i386/i386/mp_machdep.c b/sys/i386/i386/mp_machdep.c index 21a361593d..134bb68243 100644 --- a/sys/i386/i386/mp_machdep.c +++ b/sys/i386/i386/mp_machdep.c @@ -23,7 +23,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/i386/i386/mp_machdep.c,v 1.115.2.15 2003/03/14 21:22:35 jhb Exp $ - * $DragonFly: src/sys/i386/i386/Attic/mp_machdep.c,v 1.20 2004/01/09 20:49:39 drhodus Exp $ + * $DragonFly: src/sys/i386/i386/Attic/mp_machdep.c,v 1.21 2004/01/30 05:42:16 dillon Exp $ */ #include "opt_cpu.h" @@ -2452,10 +2452,8 @@ ap_init(void) */ 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 @@ addupc_intr_forwarded(struct proc *p, int id, int *astmap) } #endif +#if 0 static void forwarded_statclock(int id, int pscnt, int *astmap) { @@ -2595,7 +2594,9 @@ forwarded_statclock(int id, int pscnt, int *astmap) } #endif } +#endif +#if 0 void forward_statclock(int pscnt) { @@ -2655,7 +2656,9 @@ forward_statclock(int pscnt) if (map != 0) resched_cpus(map); } +#endif +#if 0 void forward_hardclock(int pscnt) { @@ -2739,6 +2742,7 @@ forward_hardclock(int pscnt) if (map != 0) resched_cpus(map); } +#endif #endif /* BETTER_CLOCK */ diff --git a/sys/i386/icu/icu_vector.s b/sys/i386/icu/icu_vector.s index 866c63a521..4e7f264c54 100644 --- a/sys/i386/icu/icu_vector.s +++ b/sys/i386/icu/icu_vector.s @@ -1,7 +1,7 @@ /* * from: vector.s, 386BSD 0.1 unknown origin * $FreeBSD: src/sys/i386/isa/icu_vector.s,v 1.14.2.2 2000/07/18 21:12:42 dfr Exp $ - * $DragonFly: src/sys/i386/icu/Attic/icu_vector.s,v 1.15 2004/01/07 20:21:20 dillon Exp $ + * $DragonFly: src/sys/i386/icu/Attic/icu_vector.s,v 1.16 2004/01/30 05:42:16 dillon Exp $ */ /* @@ -256,10 +256,8 @@ IDTVEC(vec_name) ; \ 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 @@ MCOUNT_LABEL(bintr) 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,) diff --git a/sys/i386/include/clock.h b/sys/i386/include/clock.h index e61ca19246..411751d4ee 100644 --- a/sys/i386/include/clock.h +++ b/sys/i386/include/clock.h @@ -4,7 +4,7 @@ * This file is in the public domain. * * $FreeBSD: src/sys/i386/include/clock.h,v 1.38.2.1 2002/11/02 04:41:50 iwasaki Exp $ - * $DragonFly: src/sys/i386/include/Attic/clock.h,v 1.4 2003/08/26 21:42:18 rob Exp $ + * $DragonFly: src/sys/i386/include/Attic/clock.h,v 1.5 2004/01/30 05:42:16 dillon Exp $ */ #ifndef _MACHINE_CLOCK_H_ @@ -30,19 +30,12 @@ extern int apic_8254_intr; /* * 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); diff --git a/sys/i386/include/cpu.h b/sys/i386/include/cpu.h index 8e183c8b8a..159b9d4bcf 100644 --- a/sys/i386/include/cpu.h +++ b/sys/i386/include/cpu.h @@ -35,7 +35,7 @@ * * from: @(#)cpu.h 5.4 (Berkeley) 5/9/91 * $FreeBSD: src/sys/i386/include/cpu.h,v 1.43.2.2 2001/06/15 09:37:57 scottl Exp $ - * $DragonFly: src/sys/i386/include/Attic/cpu.h,v 1.12 2003/11/21 05:29:08 dillon Exp $ + * $DragonFly: src/sys/i386/include/Attic/cpu.h,v 1.13 2004/01/30 05:42:16 dillon Exp $ */ #ifndef _MACHINE_CPU_H_ @@ -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, diff --git a/sys/i386/include/frame.h b/sys/i386/include/frame.h index 77f24b12f9..3f71f2f431 100644 --- a/sys/i386/include/frame.h +++ b/sys/i386/include/frame.h @@ -35,7 +35,7 @@ * * from: @(#)frame.h 5.2 (Berkeley) 1/18/91 * $FreeBSD: src/sys/i386/include/frame.h,v 1.20 1999/09/29 15:06:22 marcel Exp $ - * $DragonFly: src/sys/i386/include/Attic/frame.h,v 1.3 2003/08/26 21:42:18 rob Exp $ + * $DragonFly: src/sys/i386/include/Attic/frame.h,v 1.4 2004/01/30 05:42:16 dillon Exp $ */ #ifndef _MACHINE_FRAME_H_ @@ -129,33 +129,6 @@ struct intrframe { 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 *); diff --git a/sys/i386/isa/apic_vector.s b/sys/i386/isa/apic_vector.s index d5f11088b9..e9ff512067 100644 --- a/sys/i386/isa/apic_vector.s +++ b/sys/i386/isa/apic_vector.s @@ -1,7 +1,7 @@ /* * from: vector.s, 386BSD 0.1 unknown origin * $FreeBSD: src/sys/i386/isa/apic_vector.s,v 1.47.2.5 2001/09/01 22:33:38 tegge Exp $ - * $DragonFly: src/sys/i386/isa/Attic/apic_vector.s,v 1.14 2003/09/25 23:49:08 dillon Exp $ + * $DragonFly: src/sys/i386/isa/Attic/apic_vector.s,v 1.15 2004/01/30 05:42:16 dillon Exp $ */ @@ -484,11 +484,13 @@ Xipiq: 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 @@ MCOUNT_LABEL(bintr) 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,) diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c index d68f7b2022..dc56164c67 100644 --- a/sys/i386/isa/clock.c +++ b/sys/i386/isa/clock.c @@ -35,7 +35,7 @@ * * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 * $FreeBSD: src/sys/i386/isa/clock.c,v 1.149.2.6 2002/11/02 04:41:50 iwasaki Exp $ - * $DragonFly: src/sys/i386/isa/Attic/clock.c,v 1.10 2004/01/08 08:11:12 dillon Exp $ + * $DragonFly: src/sys/i386/isa/Attic/clock.c,v 1.11 2004/01/30 05:42:16 dillon Exp $ */ /* @@ -62,6 +62,10 @@ #endif #include #include +#include +#include +#include +#include #include #ifdef CLK_CALIBRATION_LOOP @@ -98,6 +102,7 @@ int apic_8254_intr; 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 @@ static void setup_8254_mixed_mode (void); #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))) +#ifndef TIMER_FREQ +#define TIMER_FREQ 1193182 +#endif -/* - * 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 - -/* - * 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 + timer0_running = 0; - timer_func(&frame); - - 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 @@ clkintr(struct clockframe frame) #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 @@ acquire_timer2(int mode) /* * 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 @@ release_timer2() * 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 @@ DB_SHOW_COMMAND(rtc, rtc) } #endif /* DDB */ -static int -getit(void) +/* + * Convert a frequency to a cpu timer count. + */ +sysclock_t +cputimer_fromhz(int freq) { - int high, low; - - clock_lock(); + return(cputimer_freq / freq + 1); +} - /* Select timer0 and latch counter value. */ - outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); +sysclock_t +cputimer_fromus(int us) +{ + return((int64_t)cputimer_freq * us / 1000000); +} - low = inb(TIMER_CNTR0); - high = inb(TIMER_CNTR0); +/* + * 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 ((high << 8) | low); + return(ret); +} + +/* + * 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; + + if ((int)reload < 2) + reload = 2; + + 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 @@ DELAY(int n) * 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 @@ DELAY(int n) * 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 @@ DELAY(int n) * 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 @@ static void 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 @@ calibrate_clocks(void) } /* 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 @@ calibrate_clocks(void) 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 @@ calibrate_clocks(void) 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 @@ rtc_restore(void) 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 @@ startrtclock() * 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 @@ startrtclock() 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 @@ inittodr(time_t base) 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 @@ inittodr(time_t base) /* 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 @@ inittodr(time_t base) year += 100; #endif if (year < 1970) { - splx(s); + crit_exit(); goto wrong_time; } month = readrtc(RTC_MONTH); @@ -942,9 +793,9 @@ inittodr(time_t base) /* 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 @@ void 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 @@ resettodr() /* 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 @@ cpu_initclocks() 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 @@ cpu_initclocks() } 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 @@ setstatclockrate(int newhz) 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 diff --git a/sys/i386/isa/icu_vector.s b/sys/i386/isa/icu_vector.s index ad4a2d8d62..83c0981d79 100644 --- a/sys/i386/isa/icu_vector.s +++ b/sys/i386/isa/icu_vector.s @@ -1,7 +1,7 @@ /* * from: vector.s, 386BSD 0.1 unknown origin * $FreeBSD: src/sys/i386/isa/icu_vector.s,v 1.14.2.2 2000/07/18 21:12:42 dfr Exp $ - * $DragonFly: src/sys/i386/isa/Attic/icu_vector.s,v 1.15 2004/01/07 20:21:20 dillon Exp $ + * $DragonFly: src/sys/i386/isa/Attic/icu_vector.s,v 1.16 2004/01/30 05:42:16 dillon Exp $ */ /* @@ -256,10 +256,8 @@ IDTVEC(vec_name) ; \ 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 @@ MCOUNT_LABEL(bintr) 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,) diff --git a/sys/i386/isa/ipl.s b/sys/i386/isa/ipl.s index d4a252bd91..7299ce1cb4 100644 --- a/sys/i386/isa/ipl.s +++ b/sys/i386/isa/ipl.s @@ -37,7 +37,7 @@ * @(#)ipl.s * * $FreeBSD: src/sys/i386/isa/ipl.s,v 1.32.2.3 2002/05/16 16:03:56 bde Exp $ - * $DragonFly: src/sys/i386/isa/Attic/ipl.s,v 1.15 2003/11/21 05:29:08 dillon Exp $ + * $DragonFly: src/sys/i386/isa/Attic/ipl.s,v 1.16 2004/01/30 05:42:16 dillon Exp $ */ @@ -273,7 +273,9 @@ doreti_ast: 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 diff --git a/sys/i386/isa/timerreg.h b/sys/i386/isa/timerreg.h index 0357369b7e..7cb4547c85 100644 --- a/sys/i386/isa/timerreg.h +++ b/sys/i386/isa/timerreg.h @@ -32,7 +32,7 @@ * * from: Header: timerreg.h,v 1.2 93/02/28 15:08:58 mccanne Exp * $FreeBSD: src/sys/i386/isa/timerreg.h,v 1.6 1999/08/28 00:45:04 peter Exp $ - * $DragonFly: src/sys/i386/isa/Attic/timerreg.h,v 1.2 2003/06/17 04:28:37 dillon Exp $ + * $DragonFly: src/sys/i386/isa/Attic/timerreg.h,v 1.3 2004/01/30 05:42:16 dillon Exp $ */ /* @@ -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. */ /* diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index c01fea203e..c1f884c305 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -40,7 +40,7 @@ * * @(#)init_main.c 8.9 (Berkeley) 1/21/94 * $FreeBSD: src/sys/kern/init_main.c,v 1.134.2.8 2003/06/06 20:21:32 tegge Exp $ - * $DragonFly: src/sys/kern/init_main.c,v 1.26 2003/11/10 06:12:13 dillon Exp $ + * $DragonFly: src/sys/kern/init_main.c,v 1.27 2004/01/30 05:42:17 dillon Exp $ */ #include "opt_init_path.h" @@ -610,6 +610,7 @@ void 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); } diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c index 3e31ab6d39..46588ad15e 100644 --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2004, Matthew Dillon * Copyright (c) 1997, 1998 Poul-Henning Kamp * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -38,7 +39,7 @@ * * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94 * $FreeBSD: src/sys/kern/kern_clock.c,v 1.105.2.10 2002/10/17 13:19:40 maxim Exp $ - * $DragonFly: src/sys/kern/kern_clock.c,v 1.14 2004/01/07 20:21:46 dillon Exp $ + * $DragonFly: src/sys/kern/kern_clock.c,v 1.15 2004/01/30 05:42:17 dillon Exp $ */ #include "opt_ntp.h" @@ -74,23 +75,9 @@ extern void init_device_poll(void); 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_nin; 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 @@ tvtohz_low(struct timeval *tv) * 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 @@ startprofclock(p) * 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_kern_clockrate(SYSCTL_HANDLER_ARGS) 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 @@ tco_delta(struct timecounter *tc) * 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) -{ - struct timecounter *tc; - - if (!tco_method) { - tc = timecounter; - *tvp = tc->tc_microtime; - } else { - microtime(tvp); - } -} - -void -getnanotime(struct timespec *tsp) -{ - struct timecounter *tc; - - if (!tco_method) { - tc = timecounter; - *tsp = tc->tc_nanotime; - } else { - nanotime(tsp); - } -} - -void -microtime(struct timeval *tv) -{ - struct timecounter *tc; - int 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++; - } -} - -void -nanotime(struct timespec *ts) -{ - unsigned count; - u_int64_t delta; - struct timecounter *tc; - - 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++; - } - 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); + struct globaldata *gd = mycpu; + sysclock_t delta; + + 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 getnanouptime(struct timespec *tsp) { - struct timecounter *tc; - - if (!tco_method) { - tc = timecounter; - tsp->tv_sec = tc->tc_offset_sec; - tsp->tv_nsec = tc->tc_offset_nano >> 32; - } else { - nanouptime(tsp); + struct globaldata *gd = mycpu; + sysclock_t delta; + + 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 -microuptime(struct timeval *tv) +microuptime(struct timeval *tvp) { - struct timecounter *tc; - - 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++; + struct globaldata *gd = mycpu; + sysclock_t delta; + + 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 -nanouptime(struct timespec *ts) +nanouptime(struct timespec *tsp) { - 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++; + struct globaldata *gd = mycpu; + sysclock_t delta; + + 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; } -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) -{ - tco_setscales(tc); -} +/* + * realtime routines + */ void -init_timecounter(struct timecounter *tc) +getmicrotime(struct timeval *tvp) { - struct timespec ts1; - struct timecounter *t1, *t2, *t3; - unsigned u; - int i; + struct globaldata *gd = mycpu; + sysclock_t delta; - 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; - } + 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; - 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; + 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; } - 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) +getnanotime(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); -} + struct globaldata *gd = mycpu; + sysclock_t delta; -static void -switch_timecounter(struct timecounter *newtc) -{ - int s; - struct timecounter *tc; - struct timespec ts; + 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; - s = splclock(); - tc = timecounter; - if (newtc->tc_tweak == tc->tc_tweak) { - splx(s); - return; + tsp->tv_sec += basetime.tv_sec; + tsp->tv_nsec += basetime.tv_nsec; + while (tsp->tv_nsec >= 1000000000) { + tsp->tv_nsec -= 1000000000; + ++tsp->tv_sec; } - 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) +void +microtime(struct timeval *tvp) { - 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); -} + struct globaldata *gd = mycpu; + sysclock_t delta; -static void -tco_forward(int force) -{ - struct timecounter *tc, *tco; - struct timeval tvt; + 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; - 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; + 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; } - - 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; - - tc->tc_offset_micro = (tc->tc_offset_nano / 1000) >> 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++; - } - 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." +void +nanotime(struct timespec *tsp) +{ + struct globaldata *gd = mycpu; + sysclock_t delta; -); + 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; -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); + tsp->tv_sec += basetime.tv_sec; + tsp->tv_nsec += basetime.tv_nsec; + while (tsp->tv_nsec >= 1000000000) { + tsp->tv_nsec -= 1000000000; + ++tsp->tv_sec; } - 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 @@ pps_init(struct pps_state *pps) } 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 @@ pps_event(struct pps_state *pps, struct timecounter *tc, unsigned count, int eve 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 @@ pps_event(struct pps_state *pps, struct timecounter *tc, unsigned count, int eve /* 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 } + diff --git a/sys/kern/kern_ntptime.c b/sys/kern/kern_ntptime.c index 3cc00b66d6..9fab220e25 100644 --- a/sys/kern/kern_ntptime.c +++ b/sys/kern/kern_ntptime.c @@ -29,7 +29,7 @@ * confusing and/or plain wrong in that context. * * $FreeBSD: src/sys/kern/kern_ntptime.c,v 1.32.2.2 2001/04/22 11:19:46 jhay Exp $ - * $DragonFly: src/sys/kern/kern_ntptime.c,v 1.7 2003/07/30 00:19:14 dillon Exp $ + * $DragonFly: src/sys/kern/kern_ntptime.c,v 1.8 2004/01/30 05:42:17 dillon Exp $ */ #include "opt_ntp.h" @@ -43,6 +43,7 @@ #include #include #include +#include /* * Single-precision macros for 64-bit machines @@ -271,7 +272,6 @@ ntp_adjtime(struct ntp_adjtime_args *uap) 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 @@ ntp_adjtime(struct ntp_adjtime_args *uap) 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 @@ ntp_adjtime(struct ntp_adjtime_args *uap) 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 @@ ntp_adjtime(struct ntp_adjtime_args *uap) 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 @@ ntp_adjtime(struct ntp_adjtime_args *uap) /* * 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 @@ ntp_update_second(struct timecounter *tcp) 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 @@ ntp_update_second(struct timecounter *tcp) 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 @@ ntp_update_second(struct timecounter *tcp) } /* - * 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 @@ hardupdate(offset) { 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 @@ hardupdate(offset) */ 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 @@ hardupdate(offset) * 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 @@ hardupdate(offset) 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); diff --git a/sys/kern/kern_poll.c b/sys/kern/kern_poll.c index e2b26fdc7f..aaad339a84 100644 --- a/sys/kern/kern_poll.c +++ b/sys/kern/kern_poll.c @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/kern/kern_poll.c,v 1.2.2.4 2002/06/27 23:26:33 luigi Exp $ - * $DragonFly: src/sys/kern/kern_poll.c,v 1.6 2003/11/08 07:57:41 dillon Exp $ + * $DragonFly: src/sys/kern/kern_poll.c,v 1.7 2004/01/30 05:42:17 dillon Exp $ */ #include @@ -201,6 +201,8 @@ init_device_poll(void) * 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) diff --git a/sys/kern/kern_random.c b/sys/kern/kern_random.c index e7dba9f469..7aa59c2fcb 100644 --- a/sys/kern/kern_random.c +++ b/sys/kern/kern_random.c @@ -2,7 +2,7 @@ * kern_random.c -- A strong random number generator * * $FreeBSD: src/sys/kern/kern_random.c,v 1.36.2.4 2002/09/17 17:11:57 sam Exp $ - * $DragonFly: src/sys/kern/Attic/kern_random.c,v 1.5 2003/07/29 21:30:02 hmp Exp $ + * $DragonFly: src/sys/kern/Attic/kern_random.c,v 1.6 2004/01/30 05:42:17 dillon Exp $ * * Version 0.95, last modified 18-Oct-95 * @@ -47,6 +47,7 @@ #include #include #include +#include #ifdef __i386__ #include @@ -191,10 +192,8 @@ add_timer_randomness(struct random_bucket *r, struct timer_rand_state *state, 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; diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c index e0fb35f360..ee48b3def3 100644 --- a/sys/kern/kern_synch.c +++ b/sys/kern/kern_synch.c @@ -37,7 +37,7 @@ * * @(#)kern_synch.c 8.9 (Berkeley) 5/19/95 * $FreeBSD: src/sys/kern/kern_synch.c,v 1.87.2.6 2002/10/13 07:29:53 kbyanc Exp $ - * $DragonFly: src/sys/kern/kern_synch.c,v 1.26 2004/01/07 11:04:18 dillon Exp $ + * $DragonFly: src/sys/kern/kern_synch.c,v 1.27 2004/01/30 05:42:17 dillon Exp $ */ #include "opt_ktrace.h" @@ -161,7 +161,7 @@ resched_cpus(u_int32_t mask) * 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 @@ sched_setup(dummy) * 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; diff --git a/sys/kern/kern_systimer.c b/sys/kern/kern_systimer.c new file mode 100644 index 0000000000..47c00956ec --- /dev/null +++ b/sys/kern/kern_systimer.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2003 Matthew Dillon + * 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: src/sys/kern/kern_systimer.c,v 1.1 2004/01/30 05:42:17 dillon Exp $ + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include + +/* + * 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); +} + diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c index 7565a99f31..0c19e376d7 100644 --- a/sys/kern/kern_time.c +++ b/sys/kern/kern_time.c @@ -32,7 +32,7 @@ * * @(#)kern_time.c 8.1 (Berkeley) 6/10/93 * $FreeBSD: src/sys/kern/kern_time.c,v 1.68.2.1 2002/10/01 08:00:41 bde Exp $ - * $DragonFly: src/sys/kern/kern_time.c,v 1.13 2004/01/07 11:08:06 dillon Exp $ + * $DragonFly: src/sys/kern/kern_time.c,v 1.14 2004/01/30 05:42:17 dillon Exp $ */ #include @@ -52,6 +52,7 @@ #include #include #include +#include struct timezone tz; @@ -71,8 +72,8 @@ static int settime (struct timeval *); 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 @@ settime(tv) 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 @@ settime(tv) } } else { if (tv1.tv_sec == laststep.tv_sec) { - splx(s); + crit_exit(); return (EPERM); } if (delta.tv_sec > 1) { @@ -135,10 +135,9 @@ settime(tv) 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 @@ clock_getres(struct clock_getres_args *uap) * 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 @@ nanosleep1(struct timespec *rqt, struct timespec *rmt) 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; - if (sleep_hardloop) { - if (ticks == 0) { + ticks = tv.tv_usec / tick; /* approximate */ + + 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 @@ adjtime(struct adjtime_args *uap) 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 @@ adjtime(struct adjtime_args *uap) */ 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 @@ getitimer(struct getitimer_args *uap) 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 @@ getitimer(struct getitimer_args *uap) 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 @@ setitimer(struct setitimer_args *uap) 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 @@ setitimer(struct setitimer_args *uap) 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 @@ setitimer(struct setitimer_args *uap) 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 @@ realitexpire(arg) { struct proc *p; struct timeval ctv, ntv; - int s; p = (struct proc *)arg; psignal(p, SIGALRM); @@ -573,7 +602,7 @@ realitexpire(arg) 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 @@ realitexpire(arg) timevalsub(&ntv, &ctv); p->p_ithandle = timeout(realitexpire, (caddr_t)p, tvtohz_low(&ntv)); - splx(s); + crit_exit(); return; } - splx(s); + crit_exit(); } } diff --git a/sys/kern/kern_timeout.c b/sys/kern/kern_timeout.c index 30b9da1f68..aeaf23910b 100644 --- a/sys/kern/kern_timeout.c +++ b/sys/kern/kern_timeout.c @@ -37,7 +37,7 @@ * * From: @(#)kern_clock.c 8.5 (Berkeley) 1/21/94 * $FreeBSD: src/sys/kern/kern_timeout.c,v 1.59.2.1 2001/11/13 18:24:52 archie Exp $ - * $DragonFly: src/sys/kern/kern_timeout.c,v 1.6 2003/08/26 21:09:02 rob Exp $ + * $DragonFly: src/sys/kern/kern_timeout.c,v 1.7 2004/01/30 05:42:17 dillon Exp $ */ #include @@ -46,6 +46,7 @@ #include #include #include +#include #include /* @@ -83,7 +84,6 @@ swi_softclock(void *dummy) { struct callout *c; struct callout_tailq *bucket; - int s; int curticks; int steps; /* #steps since we last allowed interrupts */ @@ -92,7 +92,7 @@ swi_softclock(void *dummy) #endif /* MAX_SOFTCLOCK_STEPS */ steps = 0; - s = splhigh(); + crit_enter(); while (softticks != ticks) { softticks++; /* @@ -109,8 +109,8 @@ swi_softclock(void *dummy) 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 @@ swi_softclock(void *dummy) 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(); } /* diff --git a/sys/kern/lwkt_thread.c b/sys/kern/lwkt_thread.c index 6d50012fbd..a5622872d9 100644 --- a/sys/kern/lwkt_thread.c +++ b/sys/kern/lwkt_thread.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.48 2004/01/18 12:29:49 dillon Exp $ + * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.49 2004/01/30 05:42:17 dillon Exp $ */ /* @@ -1502,6 +1502,10 @@ lwkt_wait_ipiq(int dcpu, int seq) * 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 @@ lwkt_process_ipiq(void) 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; } diff --git a/sys/kern/subr_param.c b/sys/kern/subr_param.c index ebb96ac9e3..bec0c78a87 100644 --- a/sys/kern/subr_param.c +++ b/sys/kern/subr_param.c @@ -37,7 +37,7 @@ * * @(#)param.c 8.3 (Berkeley) 8/20/94 * $FreeBSD: src/sys/kern/subr_param.c,v 1.42.2.10 2002/03/09 21:05:47 silby Exp $ - * $DragonFly: src/sys/kern/subr_param.c,v 1.2 2003/06/17 04:28:41 dillon Exp $ + * $DragonFly: src/sys/kern/subr_param.c,v 1.3 2004/01/30 05:42:17 dillon Exp $ */ #include "opt_param.h" @@ -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 @@ init_param1(void) { 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 */ diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index a3ccf7e7da..6c67537abf 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1,5 +1,5 @@ /* $FreeBSD: src/sys/netinet6/icmp6.c,v 1.6.2.13 2003/05/06 06:46:58 suz Exp $ */ -/* $DragonFly: src/sys/netinet6/icmp6.c,v 1.6 2003/08/23 11:02:45 rob Exp $ */ +/* $DragonFly: src/sys/netinet6/icmp6.c,v 1.7 2004/01/30 05:42:17 dillon Exp $ */ /* $KAME: icmp6.c,v 1.211 2001/04/04 05:56:20 itojun Exp $ */ /* @@ -2809,11 +2809,9 @@ ppsratecheck(lasttime, curpps, maxpps) int maxpps; /* maximum pps allowed */ { struct timeval tv, delta; - int s, rv; + int rv; - s = splclock(); microtime(&tv); - splx(s); timersub(&tv, lasttime, &delta); diff --git a/sys/netproto/ns/ns_error.c b/sys/netproto/ns/ns_error.c index 83ff030881..3cea7aad05 100644 --- a/sys/netproto/ns/ns_error.c +++ b/sys/netproto/ns/ns_error.c @@ -32,7 +32,7 @@ * * @(#)ns_error.c 8.1 (Berkeley) 6/10/93 * $FreeBSD: src/sys/netns/ns_error.c,v 1.9 1999/08/28 00:49:49 peter Exp $ - * $DragonFly: src/sys/netproto/ns/ns_error.c,v 1.5 2003/09/06 21:51:12 drhodus Exp $ + * $DragonFly: src/sys/netproto/ns/ns_error.c,v 1.6 2004/01/30 05:42:17 dillon Exp $ */ #include @@ -294,19 +294,6 @@ freeit: 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; diff --git a/sys/platform/pc32/apic/apic_vector.s b/sys/platform/pc32/apic/apic_vector.s index 5360943f65..ed2e57b70b 100644 --- a/sys/platform/pc32/apic/apic_vector.s +++ b/sys/platform/pc32/apic/apic_vector.s @@ -1,7 +1,7 @@ /* * from: vector.s, 386BSD 0.1 unknown origin * $FreeBSD: src/sys/i386/isa/apic_vector.s,v 1.47.2.5 2001/09/01 22:33:38 tegge Exp $ - * $DragonFly: src/sys/platform/pc32/apic/apic_vector.s,v 1.14 2003/09/25 23:49:08 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/apic/apic_vector.s,v 1.15 2004/01/30 05:42:16 dillon Exp $ */ @@ -484,11 +484,13 @@ Xipiq: 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 @@ MCOUNT_LABEL(bintr) 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,) diff --git a/sys/platform/pc32/i386/elan-mmcr.c b/sys/platform/pc32/i386/elan-mmcr.c index 5f548c6820..ea2e0ab3f5 100644 --- a/sys/platform/pc32/i386/elan-mmcr.c +++ b/sys/platform/pc32/i386/elan-mmcr.c @@ -7,7 +7,7 @@ * ---------------------------------------------------------------------------- * * $FreeBSD: src/sys/i386/i386/elan-mmcr.c,v 1.6.2.1 2002/09/17 22:39:53 sam Exp $ - * $DragonFly: src/sys/platform/pc32/i386/elan-mmcr.c,v 1.4 2003/07/21 07:57:43 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/i386/elan-mmcr.c,v 1.5 2004/01/30 05:42:16 dillon Exp $ * The AMD Elan sc520 is a system-on-chip gadget which is used in embedded * kind of things, see www.soekris.com for instance, and it has a few quirks * we need to deal with. @@ -34,6 +34,7 @@ uint16_t *elan_mmcr; +#if 0 static unsigned elan_get_timecount(struct timecounter *tc) @@ -49,6 +50,8 @@ static struct timecounter elan_timecounter = { "ELAN" }; +#endif + void init_AMD_Elan_sc520(void) { @@ -74,9 +77,11 @@ init_AMD_Elan_sc520(void) 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 } diff --git a/sys/platform/pc32/i386/mp_clock.c b/sys/platform/pc32/i386/mp_clock.c index b5aee2e0f4..c4a95a0d13 100644 --- a/sys/platform/pc32/i386/mp_clock.c +++ b/sys/platform/pc32/i386/mp_clock.c @@ -30,7 +30,7 @@ * directions. If we only cared about monosity two reads would be enough. * * $FreeBSD: src/sys/i386/i386/mp_clock.c,v 1.4.2.2 2000/09/30 02:49:32 ps Exp $ - * $DragonFly: src/sys/platform/pc32/i386/mp_clock.c,v 1.3 2003/08/07 21:17:22 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/i386/mp_clock.c,v 1.4 2004/01/30 05:42:16 dillon Exp $ * */ @@ -46,6 +46,8 @@ #include +#if 0 + static unsigned piix_get_timecount(struct timecounter *tc); static u_int32_t piix_timecounter_address; @@ -140,3 +142,6 @@ static driver_t piix_driver = { static devclass_t piix_devclass; DRIVER_MODULE(piix, pci, piix_driver, piix_devclass, 0, 0); + +#endif + diff --git a/sys/platform/pc32/i386/mp_machdep.c b/sys/platform/pc32/i386/mp_machdep.c index 1e09ad8739..4f116faf63 100644 --- a/sys/platform/pc32/i386/mp_machdep.c +++ b/sys/platform/pc32/i386/mp_machdep.c @@ -23,7 +23,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/i386/i386/mp_machdep.c,v 1.115.2.15 2003/03/14 21:22:35 jhb Exp $ - * $DragonFly: src/sys/platform/pc32/i386/mp_machdep.c,v 1.20 2004/01/09 20:49:39 drhodus Exp $ + * $DragonFly: src/sys/platform/pc32/i386/mp_machdep.c,v 1.21 2004/01/30 05:42:16 dillon Exp $ */ #include "opt_cpu.h" @@ -2452,10 +2452,8 @@ ap_init(void) */ 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 @@ addupc_intr_forwarded(struct proc *p, int id, int *astmap) } #endif +#if 0 static void forwarded_statclock(int id, int pscnt, int *astmap) { @@ -2595,7 +2594,9 @@ forwarded_statclock(int id, int pscnt, int *astmap) } #endif } +#endif +#if 0 void forward_statclock(int pscnt) { @@ -2655,7 +2656,9 @@ forward_statclock(int pscnt) if (map != 0) resched_cpus(map); } +#endif +#if 0 void forward_hardclock(int pscnt) { @@ -2739,6 +2742,7 @@ forward_hardclock(int pscnt) if (map != 0) resched_cpus(map); } +#endif #endif /* BETTER_CLOCK */ diff --git a/sys/platform/pc32/icu/icu_vector.s b/sys/platform/pc32/icu/icu_vector.s index d54f7be474..65ed718d8e 100644 --- a/sys/platform/pc32/icu/icu_vector.s +++ b/sys/platform/pc32/icu/icu_vector.s @@ -1,7 +1,7 @@ /* * from: vector.s, 386BSD 0.1 unknown origin * $FreeBSD: src/sys/i386/isa/icu_vector.s,v 1.14.2.2 2000/07/18 21:12:42 dfr Exp $ - * $DragonFly: src/sys/platform/pc32/icu/icu_vector.s,v 1.15 2004/01/07 20:21:20 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/icu/icu_vector.s,v 1.16 2004/01/30 05:42:16 dillon Exp $ */ /* @@ -256,10 +256,8 @@ IDTVEC(vec_name) ; \ 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 @@ MCOUNT_LABEL(bintr) 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,) diff --git a/sys/platform/pc32/include/clock.h b/sys/platform/pc32/include/clock.h index b2366c2e9d..3767b4796d 100644 --- a/sys/platform/pc32/include/clock.h +++ b/sys/platform/pc32/include/clock.h @@ -4,7 +4,7 @@ * This file is in the public domain. * * $FreeBSD: src/sys/i386/include/clock.h,v 1.38.2.1 2002/11/02 04:41:50 iwasaki Exp $ - * $DragonFly: src/sys/platform/pc32/include/clock.h,v 1.4 2003/08/26 21:42:18 rob Exp $ + * $DragonFly: src/sys/platform/pc32/include/clock.h,v 1.5 2004/01/30 05:42:16 dillon Exp $ */ #ifndef _MACHINE_CLOCK_H_ @@ -30,19 +30,12 @@ extern int apic_8254_intr; /* * 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); diff --git a/sys/platform/pc32/isa/apic_vector.s b/sys/platform/pc32/isa/apic_vector.s index edd9c6493a..91f4c68d6c 100644 --- a/sys/platform/pc32/isa/apic_vector.s +++ b/sys/platform/pc32/isa/apic_vector.s @@ -1,7 +1,7 @@ /* * from: vector.s, 386BSD 0.1 unknown origin * $FreeBSD: src/sys/i386/isa/apic_vector.s,v 1.47.2.5 2001/09/01 22:33:38 tegge Exp $ - * $DragonFly: src/sys/platform/pc32/isa/Attic/apic_vector.s,v 1.14 2003/09/25 23:49:08 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/isa/Attic/apic_vector.s,v 1.15 2004/01/30 05:42:16 dillon Exp $ */ @@ -484,11 +484,13 @@ Xipiq: 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 @@ MCOUNT_LABEL(bintr) 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,) diff --git a/sys/platform/pc32/isa/clock.c b/sys/platform/pc32/isa/clock.c index 01c8176d55..48c2286ffa 100644 --- a/sys/platform/pc32/isa/clock.c +++ b/sys/platform/pc32/isa/clock.c @@ -35,7 +35,7 @@ * * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 * $FreeBSD: src/sys/i386/isa/clock.c,v 1.149.2.6 2002/11/02 04:41:50 iwasaki Exp $ - * $DragonFly: src/sys/platform/pc32/isa/clock.c,v 1.10 2004/01/08 08:11:12 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/isa/clock.c,v 1.11 2004/01/30 05:42:16 dillon Exp $ */ /* @@ -62,6 +62,10 @@ #endif #include #include +#include +#include +#include +#include #include #ifdef CLK_CALIBRATION_LOOP @@ -98,6 +102,7 @@ int apic_8254_intr; 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 @@ static void setup_8254_mixed_mode (void); #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))) +#ifndef TIMER_FREQ +#define TIMER_FREQ 1193182 +#endif -/* - * 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 - -/* - * 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 + timer0_running = 0; - timer_func(&frame); - - 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 @@ clkintr(struct clockframe frame) #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 @@ acquire_timer2(int mode) /* * 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 @@ release_timer2() * 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 @@ DB_SHOW_COMMAND(rtc, rtc) } #endif /* DDB */ -static int -getit(void) +/* + * Convert a frequency to a cpu timer count. + */ +sysclock_t +cputimer_fromhz(int freq) { - int high, low; - - clock_lock(); + return(cputimer_freq / freq + 1); +} - /* Select timer0 and latch counter value. */ - outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); +sysclock_t +cputimer_fromus(int us) +{ + return((int64_t)cputimer_freq * us / 1000000); +} - low = inb(TIMER_CNTR0); - high = inb(TIMER_CNTR0); +/* + * 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 ((high << 8) | low); + return(ret); +} + +/* + * 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; + + if ((int)reload < 2) + reload = 2; + + 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 @@ DELAY(int n) * 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 @@ DELAY(int n) * 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 @@ DELAY(int n) * 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 @@ static void 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 @@ calibrate_clocks(void) } /* 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 @@ calibrate_clocks(void) 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 @@ calibrate_clocks(void) 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 @@ rtc_restore(void) 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 @@ startrtclock() * 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 @@ startrtclock() 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 @@ inittodr(time_t base) 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 @@ inittodr(time_t base) /* 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 @@ inittodr(time_t base) year += 100; #endif if (year < 1970) { - splx(s); + crit_exit(); goto wrong_time; } month = readrtc(RTC_MONTH); @@ -942,9 +793,9 @@ inittodr(time_t base) /* 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 @@ void 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 @@ resettodr() /* 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 @@ cpu_initclocks() 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 @@ cpu_initclocks() } 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 @@ setstatclockrate(int newhz) 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 diff --git a/sys/platform/pc32/isa/icu_vector.s b/sys/platform/pc32/isa/icu_vector.s index ec4e0b32a3..b6a34c2e9e 100644 --- a/sys/platform/pc32/isa/icu_vector.s +++ b/sys/platform/pc32/isa/icu_vector.s @@ -1,7 +1,7 @@ /* * from: vector.s, 386BSD 0.1 unknown origin * $FreeBSD: src/sys/i386/isa/icu_vector.s,v 1.14.2.2 2000/07/18 21:12:42 dfr Exp $ - * $DragonFly: src/sys/platform/pc32/isa/Attic/icu_vector.s,v 1.15 2004/01/07 20:21:20 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/isa/Attic/icu_vector.s,v 1.16 2004/01/30 05:42:16 dillon Exp $ */ /* @@ -256,10 +256,8 @@ IDTVEC(vec_name) ; \ 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 @@ MCOUNT_LABEL(bintr) 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,) diff --git a/sys/platform/pc32/isa/ipl.s b/sys/platform/pc32/isa/ipl.s index 257f37f2b9..d768e05f24 100644 --- a/sys/platform/pc32/isa/ipl.s +++ b/sys/platform/pc32/isa/ipl.s @@ -37,7 +37,7 @@ * @(#)ipl.s * * $FreeBSD: src/sys/i386/isa/ipl.s,v 1.32.2.3 2002/05/16 16:03:56 bde Exp $ - * $DragonFly: src/sys/platform/pc32/isa/ipl.s,v 1.15 2003/11/21 05:29:08 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/isa/ipl.s,v 1.16 2004/01/30 05:42:16 dillon Exp $ */ @@ -273,7 +273,9 @@ doreti_ast: 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 diff --git a/sys/platform/pc32/isa/timerreg.h b/sys/platform/pc32/isa/timerreg.h index 295c9698ca..212afa06ed 100644 --- a/sys/platform/pc32/isa/timerreg.h +++ b/sys/platform/pc32/isa/timerreg.h @@ -32,7 +32,7 @@ * * from: Header: timerreg.h,v 1.2 93/02/28 15:08:58 mccanne Exp * $FreeBSD: src/sys/i386/isa/timerreg.h,v 1.6 1999/08/28 00:45:04 peter Exp $ - * $DragonFly: src/sys/platform/pc32/isa/timerreg.h,v 1.2 2003/06/17 04:28:37 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/isa/timerreg.h,v 1.3 2004/01/30 05:42:16 dillon Exp $ */ /* @@ -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. */ /* diff --git a/sys/sys/globaldata.h b/sys/sys/globaldata.h index 6804425746..5a21e4710a 100644 --- a/sys/sys/globaldata.h +++ b/sys/sys/globaldata.h @@ -24,7 +24,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/i386/include/globaldata.h,v 1.11.2.1 2000/05/16 06:58:10 dillon Exp $ - * $DragonFly: src/sys/sys/globaldata.h,v 1.22 2003/12/30 03:19:04 dillon Exp $ + * $DragonFly: src/sys/sys/globaldata.h,v 1.23 2004/01/30 05:42:17 dillon Exp $ */ #ifndef _SYS_GLOBALDATA_H_ @@ -47,6 +47,9 @@ #ifndef _SYS_SLABALLOC_H_ #include /* SLGlobalData */ #endif +#ifndef _SYS_SYSTIMER_H_ +#include /* fine-grained system timers */ +#endif /* * This structure maps out the global data that needs to be kept on a @@ -93,8 +96,6 @@ struct globaldata { __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 @@ struct globaldata { 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 */ }; diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h index 0022cc75ec..c4c4bf6b5a 100644 --- a/sys/sys/kernel.h +++ b/sys/sys/kernel.h @@ -40,7 +40,7 @@ * * @(#)kernel.h 8.3 (Berkeley) 1/21/94 * $FreeBSD: src/sys/sys/kernel.h,v 1.63.2.9 2002/07/02 23:00:30 archie Exp $ - * $DragonFly: src/sys/sys/kernel.h,v 1.7 2003/11/22 19:30:57 asmodai Exp $ + * $DragonFly: src/sys/sys/kernel.h,v 1.8 2004/01/30 05:42:17 dillon Exp $ */ #ifndef _SYS_KERNEL_H_ @@ -64,7 +64,8 @@ extern int domainnamelen; 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 stathz; /* statistics clock's frequency */ 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 */ diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 7e780ccae0..810c0f85bf 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -37,7 +37,7 @@ * * @(#)proc.h 8.15 (Berkeley) 5/19/95 * $FreeBSD: src/sys/sys/proc.h,v 1.99.2.9 2003/06/06 20:21:32 tegge Exp $ - * $DragonFly: src/sys/sys/proc.h,v 1.41 2004/01/04 22:14:39 dillon Exp $ + * $DragonFly: src/sys/sys/proc.h,v 1.42 2004/01/30 05:42:18 dillon Exp $ */ #ifndef _SYS_PROC_H_ @@ -420,7 +420,7 @@ int p_trespass (struct ucred *cr1, struct ucred *cr2); 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 *); diff --git a/sys/sys/systimer.h b/sys/sys/systimer.h new file mode 100644 index 0000000000..b2be384471 --- /dev/null +++ b/sys/sys/systimer.h @@ -0,0 +1,84 @@ +/* + * SYS/SYSTIMER.H + * + * Copyright (c) 2003 Matthew Dillon + * 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: src/sys/sys/systimer.h,v 1.1 2004/01/30 05:42:18 dillon Exp $ + */ + +#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 + diff --git a/sys/sys/systm.h b/sys/sys/systm.h index 6390287316..7ed671ed01 100644 --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -37,7 +37,7 @@ * * @(#)systm.h 8.7 (Berkeley) 3/29/95 * $FreeBSD: src/sys/sys/systm.h,v 1.111.2.18 2002/12/17 18:04:02 sam Exp $ - * $DragonFly: src/sys/sys/systm.h,v 1.16 2003/11/15 21:05:43 dillon Exp $ + * $DragonFly: src/sys/sys/systm.h,v 1.17 2004/01/30 05:42:18 dillon Exp $ */ #ifndef _SYS_SYSTM_H_ @@ -104,7 +104,7 @@ extern int ncpus; /* total number of cpus (real, hyper, virtual)*/ * General function declarations. */ -struct clockframe; +struct intrframe; struct malloc_type; struct proc; struct xwait; @@ -186,8 +186,6 @@ int susword (void *base, int word); 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 *); diff --git a/sys/sys/thread.h b/sys/sys/thread.h index bf12f9645b..dfe1ddadb0 100644 --- a/sys/sys/thread.h +++ b/sys/sys/thread.h @@ -7,7 +7,7 @@ * Types which must already be defined when this header is included by * userland: struct md_thread * - * $DragonFly: src/sys/sys/thread.h,v 1.39 2004/01/18 12:29:50 dillon Exp $ + * $DragonFly: src/sys/sys/thread.h,v 1.40 2004/01/30 05:42:18 dillon Exp $ */ #ifndef _SYS_THREAD_H_ @@ -62,6 +62,11 @@ typedef TAILQ_HEAD(lwkt_queue, thread) lwkt_queue; #ifndef _MACHINE_THREAD_H_ #include /* md_thread */ #endif +#ifndef _MACHINE_FRAME_H_ +#include +#endif +#else +struct intrframe; #endif /* @@ -90,13 +95,19 @@ typedef struct lwkt_wait { #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 int lwkt_send_ipiq(int dcpu, ipifunc_t func, void *arg); 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); diff --git a/sys/sys/time.h b/sys/sys/time.h index 45f929fb61..e3c84cb25e 100644 --- a/sys/sys/time.h +++ b/sys/sys/time.h @@ -32,7 +32,7 @@ * * @(#)time.h 8.5 (Berkeley) 5/4/95 * $FreeBSD: src/sys/sys/time.h,v 1.42 1999/12/29 04:24:48 peter Exp $ - * $DragonFly: src/sys/sys/time.h,v 1.7 2004/01/07 11:04:20 dillon Exp $ + * $DragonFly: src/sys/sys/time.h,v 1.8 2004/01/30 05:42:18 dillon Exp $ */ #ifndef _SYS_TIME_H_ @@ -82,86 +82,6 @@ struct timezone { #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 @@ struct clockinfo { #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 microuptime (struct timeval *tv); 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 diff --git a/sys/sys/timepps.h b/sys/sys/timepps.h index c365db9c1c..c149d255ef 100644 --- a/sys/sys/timepps.h +++ b/sys/sys/timepps.h @@ -7,7 +7,7 @@ * ---------------------------------------------------------------------------- * * $FreeBSD: src/sys/sys/timepps.h,v 1.12 1999/12/29 04:24:48 peter Exp $ - * $DragonFly: src/sys/sys/timepps.h,v 1.3 2003/08/20 07:31:21 rob Exp $ + * $DragonFly: src/sys/sys/timepps.h,v 1.4 2004/01/30 05:42:18 dillon Exp $ * * The is a FreeBSD protype version of the "draft-mogul-pps-api-05.txt" * specification for Pulse Per Second timing interfaces. @@ -17,6 +17,7 @@ #define _SYS_TIMEPPS_H_ #include +#include #define PPS_API_VERS_1 1 @@ -109,11 +110,10 @@ struct pps_state { 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); diff --git a/sys/sys/timex.h b/sys/sys/timex.h index 07b8a6ad18..11191ca4f7 100644 --- a/sys/sys/timex.h +++ b/sys/sys/timex.h @@ -45,7 +45,7 @@ * Created file * * $FreeBSD: src/sys/sys/timex.h,v 1.12.2.1 2001/04/22 11:19:39 jhay Exp $ - * $DragonFly: src/sys/sys/timex.h,v 1.3 2003/08/20 07:31:21 rob Exp $ + * $DragonFly: src/sys/sys/timex.h,v 1.4 2004/01/30 05:42:18 dillon Exp $ */ /* * This header file defines the Network Time Protocol (NTP) interfaces @@ -221,9 +221,11 @@ struct timex { #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 __BEGIN_DECLS -- 2.41.0