Spaces:
Runtime error
Runtime error
| struct pvclock_vcpu_time_info __attribute__((aligned(4))) hv_clock[MAX_CPU]; | |
| struct pvclock_wall_clock wall_clock; | |
| static unsigned char valid_flags = 0; | |
| static atomic64_t last_value = ATOMIC64_INIT(0); | |
| /* | |
| * 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) | |
| { | |
| u64 product; | |
| u32 tmp1, tmp2; | |
| if (shift < 0) | |
| delta >>= -shift; | |
| else | |
| delta <<= shift; | |
| __asm__ ( | |
| "mul %5 ; " | |
| "mov %4,%%eax ; " | |
| "mov %%edx,%4 ; " | |
| "mul %5 ; " | |
| "xor %5,%5 ; " | |
| "add %4,%%eax ; " | |
| "adc %5,%%edx ; " | |
| : "=A" (product), "=r" (tmp1), "=r" (tmp2) | |
| : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) ); | |
| __asm__ ( | |
| "mul %%rdx ; shrd $32,%%rdx,%%rax" | |
| : "=a" (product) : "0" (delta), "d" ((u64)mul_frac) ); | |
| return product; | |
| } | |
| u32 __attribute__((weak)) __div64_32(u64 *n, u32 base) | |
| { | |
| u64 rem = *n; | |
| u64 b = base; | |
| u64 res, d = 1; | |
| u32 high = rem >> 32; | |
| /* Reduce the thing a bit first */ | |
| res = 0; | |
| if (high >= base) { | |
| high /= base; | |
| res = (u64) high << 32; | |
| rem -= (u64) (high*base) << 32; | |
| } | |
| while ((s64)b > 0 && b < rem) { | |
| b = b+b; | |
| d = d+d; | |
| } | |
| do { | |
| if (rem >= b) { | |
| rem -= b; | |
| res += d; | |
| } | |
| b >>= 1; | |
| d >>= 1; | |
| } while (d); | |
| *n = res; | |
| return rem; | |
| } | |
| /** | |
| * set_normalized_timespec - set timespec sec and nsec parts and normalize | |
| * | |
| * @ts: pointer to timespec variable to be set | |
| * @sec: seconds to set | |
| * @nsec: nanoseconds to set | |
| * | |
| * Set seconds and nanoseconds field of a timespec variable and | |
| * normalize to the timespec storage format | |
| * | |
| * Note: The tv_nsec part is always in the range of | |
| * 0 <= tv_nsec < NSEC_PER_SEC | |
| * For negative values only the tv_sec field is negative ! | |
| */ | |
| void set_normalized_timespec(struct timespec *ts, long sec, s64 nsec) | |
| { | |
| while (nsec >= NSEC_PER_SEC) { | |
| /* | |
| * The following asm() prevents the compiler from | |
| * optimising this loop into a modulo operation. See | |
| * also __iter_div_u64_rem() in include/linux/time.h | |
| */ | |
| asm("" : "+rm"(nsec)); | |
| nsec -= NSEC_PER_SEC; | |
| ++sec; | |
| } | |
| while (nsec < 0) { | |
| asm("" : "+rm"(nsec)); | |
| nsec += NSEC_PER_SEC; | |
| --sec; | |
| } | |
| ts->tv_sec = sec; | |
| ts->tv_nsec = nsec; | |
| } | |
| static inline | |
| unsigned pvclock_read_begin(const struct pvclock_vcpu_time_info *src) | |
| { | |
| unsigned version = src->version & ~1; | |
| /* Make sure that the version is read before the data. */ | |
| smp_rmb(); | |
| return version; | |
| } | |
| static inline | |
| bool pvclock_read_retry(const struct pvclock_vcpu_time_info *src, | |
| unsigned version) | |
| { | |
| /* Make sure that the version is re-read after the data. */ | |
| smp_rmb(); | |
| return version != src->version; | |
| } | |
| static inline u64 rdtsc_ordered() | |
| { | |
| /* | |
| * FIXME: on Intel CPUs rmb() aka lfence is sufficient which brings up | |
| * to 2x speedup | |
| */ | |
| mb(); | |
| return rdtsc(); | |
| } | |
| static inline | |
| cycle_t __pvclock_read_cycles(const struct pvclock_vcpu_time_info *src) | |
| { | |
| u64 delta = rdtsc_ordered() - src->tsc_timestamp; | |
| cycle_t offset = scale_delta(delta, src->tsc_to_system_mul, | |
| src->tsc_shift); | |
| return src->system_time + offset; | |
| } | |
| cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src) | |
| { | |
| unsigned version; | |
| cycle_t ret; | |
| u64 last; | |
| u8 flags; | |
| do { | |
| version = pvclock_read_begin(src); | |
| ret = __pvclock_read_cycles(src); | |
| flags = src->flags; | |
| } while (pvclock_read_retry(src, version)); | |
| if ((valid_flags & PVCLOCK_RAW_CYCLE_BIT) || | |
| ((valid_flags & PVCLOCK_TSC_STABLE_BIT) && | |
| (flags & PVCLOCK_TSC_STABLE_BIT))) | |
| return ret; | |
| /* | |
| * Assumption here is that last_value, a global accumulator, always goes | |
| * forward. If we are less than that, we should not be much smaller. | |
| * We assume there is an error marging we're inside, and then the correction | |
| * does not sacrifice accuracy. | |
| * | |
| * For reads: global may have changed between test and return, | |
| * but this means someone else updated poked the clock at a later time. | |
| * We just need to make sure we are not seeing a backwards event. | |
| * | |
| * For updates: last_value = ret is not enough, since two vcpus could be | |
| * updating at the same time, and one of them could be slightly behind, | |
| * making the assumption that last_value always go forward fail to hold. | |
| */ | |
| last = atomic64_read(&last_value); | |
| do { | |
| if (ret < last) | |
| return last; | |
| last = atomic64_cmpxchg(&last_value, last, ret); | |
| } while (unlikely(last != ret)); | |
| return ret; | |
| } | |
| cycle_t kvm_clock_read() | |
| { | |
| struct pvclock_vcpu_time_info *src; | |
| cycle_t ret; | |
| int index = smp_id(); | |
| src = &hv_clock[index]; | |
| ret = pvclock_clocksource_read(src); | |
| return ret; | |
| } | |
| void kvm_clock_init(void *data) | |
| { | |
| int index = smp_id(); | |
| struct pvclock_vcpu_time_info *hvc = &hv_clock[index]; | |
| printf("kvm-clock: cpu %d, msr %p\n", index, hvc); | |
| wrmsr(MSR_KVM_SYSTEM_TIME_NEW, (unsigned long)hvc | 1); | |
| } | |
| void kvm_clock_clear(void *data) | |
| { | |
| wrmsr(MSR_KVM_SYSTEM_TIME_NEW, 0LL); | |
| } | |
| void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock, | |
| struct pvclock_vcpu_time_info *vcpu_time, | |
| struct timespec *ts) | |
| { | |
| u32 version; | |
| u64 delta; | |
| struct timespec now; | |
| /* get wallclock at system boot */ | |
| do { | |
| version = wall_clock->version; | |
| rmb(); /* fetch version before time */ | |
| now.tv_sec = wall_clock->sec; | |
| now.tv_nsec = wall_clock->nsec; | |
| rmb(); /* fetch time before checking version */ | |
| } while ((wall_clock->version & 1) || (version != wall_clock->version)); | |
| delta = pvclock_clocksource_read(vcpu_time); /* time since system boot */ | |
| delta += now.tv_sec * (u64)NSEC_PER_SEC + now.tv_nsec; | |
| now.tv_nsec = do_div(delta, NSEC_PER_SEC); | |
| now.tv_sec = delta; | |
| set_normalized_timespec(ts, now.tv_sec, now.tv_nsec); | |
| } | |
| void kvm_get_wallclock(struct timespec *ts) | |
| { | |
| struct pvclock_vcpu_time_info *vcpu_time; | |
| int index = smp_id(); | |
| wrmsr(MSR_KVM_WALL_CLOCK_NEW, (unsigned long)&wall_clock); | |
| vcpu_time = &hv_clock[index]; | |
| pvclock_read_wallclock(&wall_clock, vcpu_time, ts); | |
| } | |
| void pvclock_set_flags(unsigned char flags) | |
| { | |
| valid_flags = flags; | |
| } | |