Spaces:
Runtime error
Runtime error
File size: 4,515 Bytes
8df6da4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
#include "libcflat.h"
#include "smp.h"
#include "atomic.h"
#include "processor.h"
#include "hyperv.h"
#include "vm.h"
#define MAX_CPU 4
#define TICKS_PER_SEC (1000000000 / 100)
struct hv_reference_tsc_page *hv_clock;
/*
* 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, u64 mul_frac)
{
u64 product, unused;
__asm__ (
"mulq %3"
: "=d" (product), "=a" (unused) : "1" (delta), "rm" ((u64)mul_frac) );
return product;
}
static u64 hvclock_tsc_to_ticks(struct hv_reference_tsc_page *shadow, uint64_t tsc)
{
u64 delta = tsc;
return scale_delta(delta, shadow->tsc_scale) + shadow->tsc_offset;
}
/*
* Reads a consistent set of time-base values from hypervisor,
* into a shadow data area.
*/
static void hvclock_get_time_values(struct hv_reference_tsc_page *shadow,
struct hv_reference_tsc_page *page)
{
int seq;
do {
seq = page->tsc_sequence;
rmb(); /* fetch version before data */
*shadow = *page;
rmb(); /* test version after fetching data */
} while (shadow->tsc_sequence != seq);
}
uint64_t hv_clock_read(void)
{
struct hv_reference_tsc_page shadow;
hvclock_get_time_values(&shadow, hv_clock);
return hvclock_tsc_to_ticks(&shadow, rdtsc());
}
bool ok[MAX_CPU];
uint64_t loops[MAX_CPU];
#define iabs(x) ((x) < 0 ? -(x) : (x))
static void hv_clock_test(void *data)
{
int i = smp_id();
uint64_t t = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
uint64_t end = t + 3 * TICKS_PER_SEC;
uint64_t msr_sample = t + TICKS_PER_SEC;
int min_delta = 123456, max_delta = -123456;
bool got_drift = false;
bool got_warp = false;
ok[i] = true;
do {
uint64_t now = hv_clock_read();
int delta = rdmsr(HV_X64_MSR_TIME_REF_COUNT) - now;
min_delta = delta < min_delta ? delta : min_delta;
if (t < msr_sample) {
max_delta = delta > max_delta ? delta: max_delta;
} else if (delta < 0 || delta > max_delta * 3 / 2) {
printf("suspecting drift on CPU %d? delta = %d, acceptable [0, %d)\n", smp_id(),
delta, max_delta);
ok[i] = false;
got_drift = true;
max_delta *= 2;
}
if (now < t && !got_warp) {
printf("warp on CPU %d!\n", smp_id());
ok[i] = false;
got_warp = true;
break;
}
t = now;
} while(t < end);
if (!got_drift)
printf("delta on CPU %d was %d...%d\n", smp_id(), min_delta, max_delta);
barrier();
}
static void check_test(int ncpus)
{
int i;
bool pass;
on_cpus(hv_clock_test, NULL);
pass = true;
for (i = ncpus - 1; i >= 0; i--)
pass &= ok[i];
report("TSC reference precision test", pass);
}
static void hv_perf_test(void *data)
{
uint64_t t = hv_clock_read();
uint64_t end = t + 1000000000 / 100;
uint64_t local_loops = 0;
do {
t = hv_clock_read();
local_loops++;
} while(t < end);
loops[smp_id()] = local_loops;
}
static void perf_test(int ncpus)
{
int i;
uint64_t total_loops;
on_cpus(hv_perf_test, NULL);
total_loops = 0;
for (i = ncpus - 1; i >= 0; i--)
total_loops += loops[i];
printf("iterations/sec: %" PRId64"\n", total_loops / ncpus);
}
int main(int ac, char **av)
{
int nerr = 0;
int ncpus;
struct hv_reference_tsc_page shadow;
uint64_t tsc1, t1, tsc2, t2;
uint64_t ref1, ref2;
setup_vm();
smp_init();
ncpus = cpu_count();
if (ncpus > MAX_CPU)
report_abort("number cpus exceeds %d", MAX_CPU);
hv_clock = alloc_page();
wrmsr(HV_X64_MSR_REFERENCE_TSC, (u64)(uintptr_t)hv_clock | 1);
report("MSR value after enabling",
rdmsr(HV_X64_MSR_REFERENCE_TSC) == ((u64)(uintptr_t)hv_clock | 1));
hvclock_get_time_values(&shadow, hv_clock);
if (shadow.tsc_sequence == 0 || shadow.tsc_sequence == 0xFFFFFFFF) {
printf("Reference TSC page not available\n");
exit(1);
}
printf("scale: %" PRIx64" offset: %" PRId64"\n", shadow.tsc_scale, shadow.tsc_offset);
ref1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
tsc1 = rdtsc();
t1 = hvclock_tsc_to_ticks(&shadow, tsc1);
printf("refcnt %" PRId64", TSC %" PRIx64", TSC reference %" PRId64"\n",
ref1, tsc1, t1);
do
ref2 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
while (ref2 < ref1 + 2 * TICKS_PER_SEC);
tsc2 = rdtsc();
t2 = hvclock_tsc_to_ticks(&shadow, tsc2);
printf("refcnt %" PRId64" (delta %" PRId64"), TSC %" PRIx64", "
"TSC reference %" PRId64" (delta %" PRId64")\n",
ref2, ref2 - ref1, tsc2, t2, t2 - t1);
check_test(ncpus);
perf_test(ncpus);
wrmsr(HV_X64_MSR_REFERENCE_TSC, 0LL);
report("MSR value after disabling", rdmsr(HV_X64_MSR_REFERENCE_TSC) == 0);
return nerr > 0 ? 1 : 0;
}
|