diff options
Diffstat (limited to 'extras')
-rw-r--r-- | extras/mini-os/include/time.h | 10 | ||||
-rw-r--r-- | extras/mini-os/kernel.c | 14 | ||||
-rw-r--r-- | extras/mini-os/time.c | 226 |
3 files changed, 129 insertions, 121 deletions
diff --git a/extras/mini-os/include/time.h b/extras/mini-os/include/time.h index 40b4c212fa..067b8b36a1 100644 --- a/extras/mini-os/include/time.h +++ b/extras/mini-os/include/time.h @@ -28,7 +28,7 @@ * of real time into system time */ typedef s64 s_time_t; -#define NOW() ((s_time_t)get_s_time()) +#define NOW() ((s_time_t)monotonic_clock()) #define SECONDS(_s) (((s_time_t)(_s)) * 1000000000UL ) #define TENTHS(_ts) (((s_time_t)(_ts)) * 100000000UL ) #define HUNDREDTHS(_hs) (((s_time_t)(_hs)) * 10000000UL ) @@ -36,7 +36,8 @@ typedef s64 s_time_t; #define MICROSECS(_us) (((s_time_t)(_us)) * 1000UL ) #define Time_Max ((s_time_t) 0x7fffffffffffffffLL) #define FOREVER Time_Max - +#define NSEC_TO_USEC(_nsec) (_nsec / 1000UL) +#define NSEC_TO_SEC(_nsec) (_nsec / 1000000000ULL) /* wall clock time */ typedef long time_t; @@ -46,6 +47,11 @@ struct timeval { suseconds_t tv_usec; /* microseconds */ }; +struct timespec { + time_t ts_sec; + long ts_nsec; +}; + /* prototypes */ void init_time(void); diff --git a/extras/mini-os/kernel.c b/extras/mini-os/kernel.c index 85b55f1ab0..b275dd371f 100644 --- a/extras/mini-os/kernel.c +++ b/extras/mini-os/kernel.c @@ -132,20 +132,6 @@ void start_kernel(start_info_t *si) i = 0; for ( ; ; ) { - if(i >= 1000) - { - { - unsigned long saved; - __asm__ ("movl %%esp, %0" - :"=r"(saved) /* y is output operand */ - /* x is input operand */); -// :"a"); /* %eax is clobbered register */ - printk("ESP=0x%lx\n", saved); - } - - printk("1000 bloks\n"); - i=0; - } // HYPERVISOR_yield(); block(1); i++; diff --git a/extras/mini-os/time.c b/extras/mini-os/time.c index ca48a6ae1d..c03c86df35 100644 --- a/extras/mini-os/time.c +++ b/extras/mini-os/time.c @@ -43,19 +43,20 @@ * Time functions *************************************************************************/ -/* Cached *multiplier* to convert TSC counts to microseconds. - * (see the equation below). - * Equal to 2^32 * (1 / (clocks per usec) ). - * Initialized in time_init. - */ -static unsigned long fast_gettimeoffset_quotient; +/* These are peridically updated in shared_info, and then copied here. */ +struct shadow_time_info { + u64 tsc_timestamp; /* TSC at last update of time vals. */ + u64 system_timestamp; /* Time, in nanosecs, since boot. */ + u32 tsc_to_nsec_mul; + u32 tsc_to_usec_mul; + int tsc_shift; + u32 version; +}; +static struct timespec shadow_ts; +static u32 shadow_ts_version; +static struct shadow_time_info shadow; -/* These are peridically updated in shared_info, and then copied here. */ -static u32 shadow_tsc_stamp; -static s64 shadow_system_time; -static u32 shadow_time_version; -static struct timeval shadow_tv; #ifndef rmb #define rmb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory") @@ -63,116 +64,150 @@ static struct timeval shadow_tv; #define HANDLE_USEC_OVERFLOW(_tv) \ do { \ - while ( (_tv).tv_usec >= 1000000 ) \ + while ( (_tv)->tv_usec >= 1000000 ) \ { \ - (_tv).tv_usec -= 1000000; \ - (_tv).tv_sec++; \ + (_tv)->tv_usec -= 1000000; \ + (_tv)->tv_sec++; \ } \ } while ( 0 ) -static void get_time_values_from_xen(void) +static inline int time_values_up_to_date(void) { - do { - shadow_time_version = HYPERVISOR_shared_info->time_version2; - rmb(); - shadow_tv.tv_sec = HYPERVISOR_shared_info->wc_sec; - shadow_tv.tv_usec = HYPERVISOR_shared_info->wc_usec; - shadow_tsc_stamp = (u32)HYPERVISOR_shared_info->tsc_timestamp; - shadow_system_time = HYPERVISOR_shared_info->system_time; - rmb(); - } - while ( shadow_time_version != HYPERVISOR_shared_info->time_version1 ); -} + struct vcpu_time_info *src = &HYPERVISOR_shared_info->vcpu_time[0]; + return (shadow.version == src->version); +} -#define TIME_VALUES_UP_TO_DATE \ - (shadow_time_version == HYPERVISOR_shared_info->time_version2) -static u32 get_time_delta_usecs(void) +/* + * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, + * yielding a 64-bit result. + */ +static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift) { - register unsigned long eax, edx; + u64 product; +#ifdef __i386__ + u32 tmp1, tmp2; +#endif - /* Read the Time Stamp Counter */ + if ( shift < 0 ) + delta >>= -shift; + else + delta <<= shift; + +#ifdef __i386__ + __asm__ ( + "mul %5 ; " + "mov %4,%%eax ; " + "mov %%edx,%4 ; " + "mul %5 ; " + "add %4,%%eax ; " + "xor %5,%5 ; " + "adc %5,%%edx ; " + : "=A" (product), "=r" (tmp1), "=r" (tmp2) + : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) ); +#else + __asm__ ( + "mul %%rdx ; shrd $32,%%rdx,%%rax" + : "=a" (product) : "0" (delta), "d" ((u64)mul_frac) ); +#endif - rdtsc(eax,edx); + return product; +} - /* .. relative to previous jiffy (32 bits is enough) */ - eax -= shadow_tsc_stamp; - /* - * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient - * = (tsc_low delta) * (usecs_per_clock) - * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) - * - * Using a mull instead of a divl saves up to 31 clock cycles - * in the critical path. - */ +static unsigned long get_nsec_offset(void) +{ + u64 now, delta; + rdtscll(now); + delta = now - shadow.tsc_timestamp; + return scale_delta(delta, shadow.tsc_to_nsec_mul, shadow.tsc_shift); +} - __asm__("mull %2" - :"=a" (eax), "=d" (edx) - :"rm" (fast_gettimeoffset_quotient), - "0" (eax)); - /* our adjusted time offset in microseconds */ - return edx; +static void get_time_values_from_xen(void) +{ + struct vcpu_time_info *src = &HYPERVISOR_shared_info->vcpu_time[0]; + + do { + shadow.version = src->version; + rmb(); + shadow.tsc_timestamp = src->tsc_timestamp; + shadow.system_timestamp = src->system_time; + shadow.tsc_to_nsec_mul = src->tsc_to_system_mul; + shadow.tsc_shift = src->tsc_shift; + rmb(); + } + while ((src->version & 1) | (shadow.version ^ src->version)); + + shadow.tsc_to_usec_mul = shadow.tsc_to_nsec_mul / 1000; } -s64 get_s_time (void) -{ - u64 u_delta; - s64 ret; - - again: - - u_delta = get_time_delta_usecs(); - ret = shadow_system_time + (1000 * u_delta); - - if ( unlikely(!TIME_VALUES_UP_TO_DATE) ) - { - /* - * We may have blocked for a long time, rendering our calculations - * invalid (e.g. the time delta may have overflowed). Detect that - * and recalculate with fresh values. - */ - get_time_values_from_xen(); - goto again; - } - return ret; + + +/* monotonic_clock(): returns # of nanoseconds passed since time_init() + * Note: This function is required to return accurate + * time even in the absence of multiple timer ticks. + */ +u64 monotonic_clock(void) +{ + u64 time; + u32 local_time_version; + + do { + local_time_version = shadow.version; + rmb(); + time = shadow.system_timestamp + get_nsec_offset(); + if (!time_values_up_to_date()) + get_time_values_from_xen(); + rmb(); + } while (local_time_version != shadow.version); + + return time; } -void gettimeofday(struct timeval *tv) +static void update_wallclock(void) { - struct timeval _tv; + shared_info_t *s = HYPERVISOR_shared_info; + + do { + shadow_ts_version = s->wc_version; + rmb(); + shadow_ts.ts_sec = s->wc_sec; + shadow_ts.ts_nsec = s->wc_nsec; + rmb(); + } + while ((s->wc_version & 1) | (shadow_ts_version ^ s->wc_version)); +} - do { - get_time_values_from_xen(); - _tv.tv_usec = get_time_delta_usecs(); - _tv.tv_sec = shadow_tv.tv_sec; - _tv.tv_usec += shadow_tv.tv_usec; - } - while ( unlikely(!TIME_VALUES_UP_TO_DATE) ); - HANDLE_USEC_OVERFLOW(_tv); - *tv = _tv; +void gettimeofday(struct timeval *tv) +{ + u64 nsec = monotonic_clock(); + nsec += shadow_ts.ts_nsec; + + + tv->tv_sec = shadow_ts.ts_sec; + tv->tv_sec += NSEC_TO_SEC(nsec); + tv->tv_usec = NSEC_TO_USEC(nsec % 1000000000UL); } + static void print_current_time(void) { - struct timeval tv; - - get_time_values_from_xen(); + struct timeval tv; gettimeofday(&tv); printk("T(s=%ld us=%ld)\n", tv.tv_sec, tv.tv_usec); } + void block(u32 millisecs) { struct timeval tv; gettimeofday(&tv); - //printk("tv.tv_sec=%ld, tv.tv_usec=%ld, shadow_system_time=%lld\n", tv.tv_sec, tv.tv_usec, shadow_system_time ); - HYPERVISOR_set_timer_op(get_s_time() + 1000000LL * (s64) millisecs); + HYPERVISOR_set_timer_op(monotonic_clock() + 1000000LL * (s64) millisecs); HYPERVISOR_block(); } @@ -185,7 +220,7 @@ static void timer_handler(int ev, struct pt_regs *regs) static int i; get_time_values_from_xen(); - + update_wallclock(); i++; if (i >= 1000) { print_current_time(); @@ -197,24 +232,5 @@ static void timer_handler(int ev, struct pt_regs *regs) void init_time(void) { - u64 __cpu_khz; - unsigned long cpu_khz; - - __cpu_khz = HYPERVISOR_shared_info->cpu_freq; - - cpu_khz = (u32) (__cpu_khz/1000); - - printk("Xen reported: %lu.%03lu MHz processor.\n", - cpu_khz / 1000, cpu_khz % 1000); - /* (10^6 * 2^32) / cpu_hz = (10^3 * 2^32) / cpu_khz = - (2^32 * 1 / (clocks/us)) */ - { - unsigned long eax=0, edx=1000; - __asm__("divl %2" - :"=a" (fast_gettimeoffset_quotient), "=d" (edx) - :"r" (cpu_khz), - "0" (eax), "1" (edx)); - } - bind_virq(VIRQ_TIMER, &timer_handler); } |