aboutsummaryrefslogtreecommitdiffstats
path: root/xenolinux-2.4.16-sparse/arch/xeno
diff options
context:
space:
mode:
authorrn@wyvis.camb.intel-research.net <rn@wyvis.camb.intel-research.net>2003-02-11 16:44:27 +0000
committerrn@wyvis.camb.intel-research.net <rn@wyvis.camb.intel-research.net>2003-02-11 16:44:27 +0000
commit6b0982272b177306647d2bf3d42554651351bc06 (patch)
treecde2d8ba79b6a22351b216ce4b7504ca408dd451 /xenolinux-2.4.16-sparse/arch/xeno
parentc71e36cb2493c70921866121ac94c0bd83a9dcec (diff)
downloadxen-6b0982272b177306647d2bf3d42554651351bc06.tar.gz
xen-6b0982272b177306647d2bf3d42554651351bc06.tar.bz2
xen-6b0982272b177306647d2bf3d42554651351bc06.zip
bitkeeper revision 1.24 (3e49286b1h6iIeNsarUfoWbDnP6Ndw)
New time implementation. Does system and wallclock time for Xen and GuestOSes
Diffstat (limited to 'xenolinux-2.4.16-sparse/arch/xeno')
-rw-r--r--xenolinux-2.4.16-sparse/arch/xeno/kernel/time.c336
1 files changed, 212 insertions, 124 deletions
diff --git a/xenolinux-2.4.16-sparse/arch/xeno/kernel/time.c b/xenolinux-2.4.16-sparse/arch/xeno/kernel/time.c
index 2557918c6d..9f53010b73 100644
--- a/xenolinux-2.4.16-sparse/arch/xeno/kernel/time.c
+++ b/xenolinux-2.4.16-sparse/arch/xeno/kernel/time.c
@@ -1,3 +1,25 @@
+/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
+ ****************************************************************************
+ * (C) 2002 - Rolf Neugebauer - Intel Research Cambridge
+ ****************************************************************************
+ *
+ * File: arch.xeno/time.c
+ * Author: Rolf Neugebauer
+ * Changes:
+ *
+ * Date: Nov 2002
+ *
+ * Environment: XenoLinux
+ * Description: Interface with Hypervisor to get correct notion of time
+ * Currently supports Systemtime and WallClock time.
+ *
+ * (This has hardly any resemblence with the Linux code but left the
+ * copyright notice anyway. Ignore the comments in the copyright notice.)
+ ****************************************************************************
+ * $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $
+ ****************************************************************************
+ */
+
/*
* linux/arch/i386/kernel/time.c
*
@@ -30,19 +52,6 @@
* serialize accesses to xtime/lost_ticks).
*/
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/param.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/time.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/smp.h>
-
-#include <asm/io.h>
#include <asm/smp.h>
#include <asm/irq.h>
#include <asm/msr.h>
@@ -51,94 +60,110 @@
#include <asm/uaccess.h>
#include <asm/processor.h>
-#include <linux/mc146818rtc.h>
-#include <linux/timex.h>
-#include <linux/config.h>
-
+#include <asm/div64.h>
#include <asm/hypervisor.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/smp.h>
#include <linux/irq.h>
-
-unsigned long cpu_khz; /* Detected as we calibrate the TSC */
-
-/* Cached *multiplier* to convert TSC counts to microseconds.
- * (see the equation below).
- * Equal to 2^32 * (1 / (clocks per usec) ).
- * Initialized in time_init.
- */
-unsigned long fast_gettimeoffset_quotient;
-
-extern rwlock_t xtime_lock;
-extern unsigned long wall_jiffies;
+#undef XENO_TIME_DEBUG /* adds sanity checks and periodic printouts */
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+extern rwlock_t xtime_lock;
-static inline unsigned long ticks_to_secs(unsigned long long ticks)
-{
- unsigned long lo, hi;
- unsigned long little_ticks;
-
- little_ticks = ticks /* XXX URK! XXX / 1000000ULL */;
-
- __asm__ __volatile__ (
- "mull %2"
- : "=a" (lo), "=d" (hi)
- : "rm" (fast_gettimeoffset_quotient), "0" (little_ticks) );
+unsigned long cpu_khz; /* get this from Xen, used elsewhere */
+static spinlock_t hyp_stime_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t hyp_wctime_lock = SPIN_LOCK_UNLOCKED;
- return(hi);
-}
+static u32 st_scale_f;
+static u32 st_scale_i;
+static u32 shadow_st_pcc;
+static s64 shadow_st;
-/* NB. Only 32 bits of ticks are considered here. */
-static inline unsigned long ticks_to_us(unsigned long ticks)
+/*
+ * System time.
+ * Although the rest of the Linux kernel doesn't know about this, we
+ * we use it to extrapolate passage of wallclock time.
+ * We need to read the values from the shared info page "atomically"
+ * and use the cycle counter value as the "version" number. Clashes
+ * should be very rare.
+ */
+static inline long long get_s_time(void)
{
- unsigned long lo, hi;
+ unsigned long flags;
+ u32 delta_tsc, low, pcc;
+ u64 delta;
+ s64 now;
- __asm__ __volatile__ (
- "mull %2"
- : "=a" (lo), "=d" (hi)
- : "rm" (fast_gettimeoffset_quotient), "0" (ticks) );
-
- return(hi);
-}
-
-static long long get_s_time(void)
-{
- u32 delta, low, pcc;
- long long now;
- long long incr;
+ spin_lock_irqsave(&hyp_stime_lock, flags);
- /* read two values (pcc, now) "atomically" */
-again:
pcc = HYPERVISOR_shared_info->st_timestamp;
- now = HYPERVISOR_shared_info->system_time;
- if (HYPERVISOR_shared_info->st_timestamp != pcc) goto again;
-
+ mb();
+ if (pcc != shadow_st_pcc) {
+st_again:
+ shadow_st_pcc = HYPERVISOR_shared_info->st_timestamp;
+ shadow_st = HYPERVISOR_shared_info->system_time;
+ pcc = HYPERVISOR_shared_info->st_timestamp;
+ mb();
+ if (pcc != shadow_st_pcc)
+ goto st_again;
+ }
+
+ now = shadow_st;
/* only use bottom 32bits of TSC. This should be sufficient */
rdtscl(low);
- delta = low - pcc;
+ delta_tsc = low - pcc;
+ delta = ((u64)delta_tsc * st_scale_f);
+ delta >>= 32;
+ delta += ((u64)delta_tsc * st_scale_i);
+ spin_unlock_irqrestore(&hyp_time_lock, flags);
+ return now + delta;
- incr = ((long long)(ticks_to_us(delta)*1000));
- return now + incr;
}
#define NOW() ((long long)get_s_time())
/*
- * This version of gettimeofday has microsecond resolution
- * and better than microsecond precision on fast x86 machines with TSC.
+ * Wallclock time.
+ * Based on what the hypervisor tells us, extrapolated using system time.
+ * Again need to read a number of values from the shared page "atomically".
+ * this time using a version number.
*/
+static u32 shadow_wc_version=0;
+static long shadow_tv_sec;
+static long shadow_tv_usec;
+static long long shadow_wc_timestamp;
void do_gettimeofday(struct timeval *tv)
{
- unsigned long flags;
- unsigned long usec, sec;
-
- read_lock_irqsave(&xtime_lock, flags);
-
- usec = ((unsigned long)(NOW()-HYPERVISOR_shared_info->wc_timestamp))/1000;
- sec = HYPERVISOR_shared_info->tv_sec;
- usec += HYPERVISOR_shared_info->tv_usec;
-
- read_unlock_irqrestore(&xtime_lock, flags);
+ unsigned long flags;
+ long usec, sec;
+ u32 version;
+ u64 now;
+
+ spin_lock_irqsave(&hyp_wctime_lock, flags);
+
+ version = HYPERVISOR_shared_info->wc_version;
+ mb();
+ if (version != shadow_wc_version) {
+ wc_again:
+ shadow_wc_version = HYPERVISOR_shared_info->wc_version;
+ shadow_tv_sec = HYPERVISOR_shared_info->tv_sec;
+ shadow_tv_usec = HYPERVISOR_shared_info->tv_usec;
+ shadow_wc_timestamp = HYPERVISOR_shared_info->wc_timestamp;
+ shadow_wc_version = HYPERVISOR_shared_info->wc_version;
+ version = HYPERVISOR_shared_info->wc_version;
+ mb();
+ if (version != shadow_wc_version)
+ goto wc_again;
+ }
+
+ now = NOW();
+ usec = ((unsigned long)(now-shadow_wc_timestamp))/1000;
+ sec = shadow_tv_sec;
+ usec += shadow_tv_usec;
while ( usec >= 1000000 )
{
@@ -148,11 +173,39 @@ void do_gettimeofday(struct timeval *tv)
tv->tv_sec = sec;
tv->tv_usec = usec;
+
+ spin_unlock_irqrestore(&hyp_time_lock, flags);
+
+#ifdef XENO_TIME_DEBUG
+ {
+ static long long old_now=0;
+ static long long wct=0, old_wct=0;
+
+ /* This debug code checks if time increase over two subsequent calls */
+ wct=(((long long)sec) * 1000000) + usec;
+ /* wall clock time going backwards */
+ if ((wct < old_wct) ) {
+ printk("Urgh1: wc diff=%6ld, usec = %ld (0x%lX)\n",
+ (long)(wct-old_wct), usec, usec);
+ printk(" st diff=%lld cur st=0x%016llX old st=0x%016llX\n",
+ now-old_now, now, old_now);
+ }
+
+ /* system time going backwards */
+ if (now<=old_now) {
+ printk("Urgh2: st diff=%lld cur st=0x%016llX old st=0x%016llX\n",
+ now-old_now, now, old_now);
+ }
+ old_wct = wct;
+ old_now = now;
+ }
+#endif
+
}
void do_settimeofday(struct timeval *tv)
{
-/* XXX RN: shoudl do something special here for dom0 */
+/* XXX RN: should do something special here for dom0 */
#if 0
write_lock_irq(&xtime_lock);
/*
@@ -181,25 +234,68 @@ void do_settimeofday(struct timeval *tv)
/*
- * timer_interrupt() needs to keep up the real-time clock,
- * as well as call the "do_timer()" routine every clocktick
+ * Timer ISR.
+ * Unlike normal Linux these don't come in at a fixed rate of HZ.
+ * In here we wrok out how often it should have been called and then call
+ * the architecture independent part (do_timer()) the appropriate number of
+ * times. A bit of a nasty hack, to keep the "other" notion of wallclock time
+ * happy.
*/
-static inline void do_timer_interrupt(
- int irq, void *dev_id, struct pt_regs *regs)
+static long long us_per_tick=1000000/HZ;
+static long long last_irq;
+static inline void do_timer_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
{
- do_timer(regs);
+ struct timeval tv;
+ long long time, delta;
+
+#ifdef XENO_TIME_DEBUG
+ static u32 foo_count = 0;
+ foo_count++;
+ if (foo_count>= 10000) {
+ s64 n = NOW();
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ printk("0x%08X%08X %ld:%ld\n",
+ (u32)(n>>32), (u32)n, tv.tv_sec, tv.tv_usec);
+ foo_count = 0;
+ }
+#endif
+
+ /*
+ * The next bit really sucks:
+ * Linux not only uses do_gettimeofday() to keep a notion of
+ * wallclock time, but also maintains the xtime struct and jiffies.
+ * (Even worse some userland code accesses this via the sys_time()
+ * system call)
+ * Unfortunately, xtime is maintain in the architecture independent
+ * part of the timer ISR (./kernel/timer.c sic!). So, although we have
+ * perfectly valid notion of wallclock time from the hypervisor we here
+ * fake missed timer interrupts so that the arch independent part of
+ * the Timer ISR updates jiffies for us *and* once the bh gets run
+ * updates xtime accordingly. Yuck!
+ */
+
+ /* work out the number of jiffies past and update them */
+ do_gettimeofday(&tv);
+ time = (((long long)tv.tv_sec) * 1000000) + tv.tv_usec;
+ delta = time - last_irq;
+ if (delta <= 0) {
+ printk ("Timer ISR: Time went backwards: %lld\n", delta);
+ return;
+ }
+ while (delta >= us_per_tick) {
+ do_timer(regs);
+ delta -= us_per_tick;
+ last_irq += us_per_tick;
+ }
+
#if 0
if (!user_mode(regs))
x86_do_profile(regs->eip);
#endif
}
-
-/*
- * This is the same as the above, except we _also_ save the current
- * Time Stamp Counter value at the time of the timer interrupt, so that
- * we later on can estimate the time of day more exactly.
- */
static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
write_lock(&xtime_lock);
@@ -216,42 +312,32 @@ static struct irqaction irq_timer = {
NULL
};
-
-
-/* Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset(). */
-static unsigned long __init calibrate_tsc(void)
-{
- unsigned long quo, rem;
-
- /* quotient == (1000 * 2^32) / ticks_per ms */
- __asm__ __volatile__ (
- "divl %2"
- : "=a" (quo), "=d" (rem)
- : "r" (HYPERVISOR_shared_info->ticks_per_ms), "0" (0), "1" (1000) );
-
- return(quo);
-}
-
void __init time_init(void)
{
unsigned long long alarm;
-
- fast_gettimeoffset_quotient = calibrate_tsc();
+ u64 cpu_freq = HYPERVISOR_shared_info->cpu_freq;
+ u64 scale;
+
+ do_get_fast_time = do_gettimeofday;
+
+ cpu_khz = (u32)cpu_freq/1000;
+ printk("Xen reported: %lu.%03lu MHz processor.\n",
+ cpu_khz / 1000, cpu_khz % 1000);
+
+ /*
+ * calculate systemtime scaling factor
+ * XXX RN: have to cast cpu_freq to u32 limits it to 4.29 GHz.
+ * Get a better do_div!
+ */
+ scale = 1000000000LL << 32;
+ do_div(scale,(u32)cpu_freq);
+ st_scale_f = scale & 0xffffffff;
+ st_scale_i = scale >> 32;
+ printk("System Time scale: %X %X\n",st_scale_i, st_scale_f);
- /* report CPU clock rate in Hz.
- * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
- * clock/second. Our precision is about 100 ppm.
- */
- {
- unsigned long eax=0, edx=1000;
- __asm__ __volatile__
- ("divl %2"
- :"=a" (cpu_khz), "=d" (edx)
- :"r" (fast_gettimeoffset_quotient),
- "0" (eax), "1" (edx));
- printk("Detected %lu.%03lu MHz processor.\n",
- cpu_khz / 1000, cpu_khz % 1000);
- }
+ do_gettimeofday(&xtime);
+ last_irq = (((long long)xtime.tv_sec) * 1000000) + xtime.tv_usec;
+ printk("last: %lld\n", last_irq);
setup_irq(TIMER_IRQ, &irq_timer);
@@ -260,12 +346,14 @@ void __init time_init(void)
* 'domain' time. This means that clock sshould run at the correct
* rate. For things like scheduling, it's not clear whether it
* matters which sort of time we use.
+ * XXX RN: unimplemented.
*/
+
rdtscll(alarm);
+#if 0
alarm += (1000/HZ)*HYPERVISOR_shared_info->ticks_per_ms;
HYPERVISOR_shared_info->wall_timeout = alarm;
HYPERVISOR_shared_info->domain_timeout = ~0ULL;
+#endif
clear_bit(_EVENT_TIMER, &HYPERVISOR_shared_info->events);
-
- do_gettimeofday(&xtime);
}