--- linux/arch/um/kernel/time_kern.c.orig Mon Sep 23 17:01:16 2002 +++ linux/arch/um/kernel/time_kern.c Mon Sep 23 16:46:42 2002 @@ -19,12 +19,24 @@ extern rwlock_t xtime_lock; +#ifdef CONFIG_UM_PRECISION_CLOCK + +#define HZ_INTERVAL 1000000/HZ +long CPUKHZ; /* eg. 996701 */ +double MULF; /* 1000/996701 */ +unsigned long prev_tsc=0,tsc=0; /* Lag boundaries */ +static long delta=0; /* Deviation per interval */ +extern unsigned long time_stamp(); + +#endif + int hz(void) { return(HZ); } int timer_irq_inited = 0; +int first_tick = 0; /* kern_timer_on and missed_ticks are modified after kernel memory has been * write-protected, so this puts it in a section which will be left @@ -35,10 +47,34 @@ void timer_irq(struct uml_pt_regs *regs) { + long ms; int ticks = missed_ticks; if(!timer_irq_inited) return; missed_ticks = 0; + +#ifdef CONFIG_UM_PRECISION_CLOCK + if (first_tick) /* We've had 1 tick */{ + tsc = time_stamp(); + ms = (tsc - prev_tsc) * MULF; + prev_tsc = tsc; + delta += ms - HZ_INTERVAL; + if (delta > HZ_INTERVAL) { + ticks++; + delta-=HZ_INTERVAL; + } + else + if ((delta < -HZ_INTERVAL) && (ticks /*Paranoia*/)) { + ticks--; + delta+=HZ_INTERVAL; + } + } + else { + prev_tsc = time_stamp(); + first_tick = 1; + } +#endif + while(ticks--) do_IRQ(TIMER_IRQ, regs); } --- linux/arch/um/kernel/time.c.orig Mon Sep 23 17:01:39 2002 +++ linux/arch/um/kernel/time.c Mon Sep 23 17:19:33 2002 @@ -1,6 +1,7 @@ /* * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL + * */ #include @@ -15,8 +16,24 @@ #include "process.h" #include "signal_user.h" -extern struct timeval xtime; +#ifdef CONFIG_UM_PRECISION_CLOCK +extern long CPUKHZ; +extern double MULF; +extern unsigned long prev_tsc; +#endif + +/* This should be inside the ifdef, but gcc 2.95/96 gives a linker error + * in time_kern.c even if it's commented out + */ + +inline unsigned long time_stamp(void) +{ + unsigned long counter; + asm ("rdtsc":"=a"(counter)::"edx"); + return counter; +} +extern struct timeval xtime; void timer_handler(int sig, struct uml_pt_regs *regs) { timer_irq(regs); @@ -64,8 +81,13 @@ void user_time_init(void) { +#ifdef CONFIG_UM_PRECISION_CLOCK + CPUKHZ = get_host_khz(); + MULF = 1000.0 / CPUKHZ; +#endif if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == SIG_ERR) panic("Couldn't set SIGVTALRM handler"); + set_interval(ITIMER_VIRTUAL); } --- linux/arch/um/kernel/user_util.c.orig Mon Sep 23 17:02:05 2002 +++ linux/arch/um/kernel/user_util.c Mon Sep 23 12:22:30 2002 @@ -31,6 +31,8 @@ #include "helper.h" #define COMMAND_LINE_SIZE _POSIX_ARG_MAX +#define CPU_PROC_MHZ_HEADER "cpu MHz\t\t: " +#define CPU_PROC_MHZ_HEADER_LEN 11 char saved_command_line[COMMAND_LINE_SIZE] = { 0 }; char command_line[COMMAND_LINE_SIZE] = { 0 }; @@ -139,6 +141,30 @@ } } +long get_host_khz() +{ + char mhzline[27]; + double mhz; + FILE *fp = fopen("/proc/cpuinfo", "r"); + if (!fp) goto bad_fd; + + while (!feof(fp)) { + fgets(mhzline, 19, fp); + if (!mhzline) + goto bad; + if (strncmp(mhzline, CPU_PROC_MHZ_HEADER, + CPU_PROC_MHZ_HEADER_LEN) == 0) { + fclose(fp); + return (atof(mhzline + CPU_PROC_MHZ_HEADER_LEN)*1000); + } + } + +bad: + fclose(fp); +bad_fd: + return -1; +} + int clone_and_wait(int (*fn)(void *), void *arg, void *sp, int flags) { int pid;