Merge branch 'nohv_v2_wstimer_direct' into 'master'
x86: hyperv-v: Various unmerged patches
See merge request kvm-unit-tests/kvm-unit-tests!52
diff --git a/x86/hyperv.h b/x86/hyperv.h
index e3803e0..a144384 100644
--- a/x86/hyperv.h
+++ b/x86/hyperv.h
@@ -10,6 +10,8 @@
#define HV_X64_MSR_SYNIC_AVAILABLE (1 << 2)
#define HV_X64_MSR_SYNTIMER_AVAILABLE (1 << 3)
+#define HV_STIMER_DIRECT_MODE_AVAILABLE (1 << 19)
+
#define HV_X64_MSR_GUEST_OS_ID 0x40000000
#define HV_X64_MSR_HYPERCALL 0x40000001
@@ -60,11 +62,23 @@
#define HV_SYNIC_SINT_VECTOR_MASK (0xFF)
#define HV_SYNIC_SINT_COUNT 16
-#define HV_STIMER_ENABLE (1ULL << 0)
-#define HV_STIMER_PERIODIC (1ULL << 1)
-#define HV_STIMER_LAZY (1ULL << 2)
-#define HV_STIMER_AUTOENABLE (1ULL << 3)
-#define HV_STIMER_SINT(config) (__u8)(((config) >> 16) & 0x0F)
+/*
+ * Synthetic timer configuration.
+ */
+union hv_stimer_config {
+ u64 as_uint64;
+ struct {
+ u64 enable:1;
+ u64 periodic:1;
+ u64 lazy:1;
+ u64 auto_enable:1;
+ u64 apic_vector:8;
+ u64 direct_mode:1;
+ u64 reserved_z0:3;
+ u64 sintx:4;
+ u64 reserved_z1:44;
+ };
+};
#define HV_SYNIC_STIMER_COUNT (4)
@@ -183,14 +197,19 @@
u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
};
-static inline bool synic_supported(void)
+static inline bool hv_synic_supported(void)
{
return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE;
}
-static inline bool stimer_supported(void)
+static inline bool hv_stimer_supported(void)
{
- return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE;
+ return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNTIMER_AVAILABLE;
+}
+
+static inline bool stimer_direct_supported(void)
+{
+ return cpuid(HYPERV_CPUID_FEATURES).d & HV_STIMER_DIRECT_MODE_AVAILABLE;
}
static inline bool hv_time_ref_counter_supported(void)
diff --git a/x86/hyperv_clock.c b/x86/hyperv_clock.c
index f1e7204..9061da8 100644
--- a/x86/hyperv_clock.c
+++ b/x86/hyperv_clock.c
@@ -63,41 +63,52 @@
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;
+ int i = (long)data;
+ uint64_t t_msr_prev = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
+ uint64_t t_page_prev = hv_clock_read();
+ uint64_t end = t_page_prev + TICKS_PER_SEC;
bool got_drift = false;
- bool got_warp = false;
+ bool got_warp_msr = false;
+ bool got_warp_page = false;
ok[i] = true;
do {
- uint64_t now = hv_clock_read();
- int delta = rdmsr(HV_X64_MSR_TIME_REF_COUNT) - now;
+ uint64_t t_page_1, t_page_2, t_msr;
- 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);
+ t_page_1 = hv_clock_read();
+ barrier();
+ t_msr = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
+ barrier();
+ t_page_2 = hv_clock_read();
+
+ if (!got_drift && (t_msr < t_page_1 || t_msr > t_page_2)) {
+ printf("drift on CPU %d, MSR value = %ld, acceptable [%ld, %ld]\n", i,
+ t_msr, t_page_1, t_page_2);
ok[i] = false;
got_drift = true;
- max_delta *= 2;
}
- if (now < t && !got_warp) {
- printf("warp on CPU %d!\n", smp_id());
+ if (!got_warp_msr && t_msr < t_msr_prev) {
+ printf("warp on CPU %d, MSR value = %ld prev MSR value = %ld!\n", i,
+ t_msr, t_msr_prev);
ok[i] = false;
- got_warp = true;
+ got_warp_msr = 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);
+ if (!got_warp_page && t_page_1 < t_page_prev) {
+ printf("warp on CPU %d, TSC page value = %ld prev TSC page value = %ld!\n", i,
+ t_page_1, t_page_prev);
+ ok[i] = false;
+ got_warp_page = true;
+ break;
+ }
+
+ t_page_prev = t_page_1;
+ t_msr_prev = t_msr;
+
+ } while(t_page_prev < end);
+
barrier();
}
@@ -106,7 +117,11 @@
int i;
bool pass;
- on_cpus(hv_clock_test, NULL);
+ for (i = ncpus - 1; i >= 0; i--)
+ on_cpu_async(i, hv_clock_test, (void *)(long)i);
+
+ while (cpus_active() > 1)
+ pause();
pass = true;
for (i = ncpus - 1; i >= 0; i--)
@@ -117,6 +132,7 @@
static void hv_perf_test(void *data)
{
+ int i = (long)data;
uint64_t t = hv_clock_read();
uint64_t end = t + 1000000000 / 100;
uint64_t local_loops = 0;
@@ -126,7 +142,7 @@
local_loops++;
} while(t < end);
- loops[smp_id()] = local_loops;
+ loops[i] = local_loops;
}
static void perf_test(int ncpus)
@@ -134,7 +150,11 @@
int i;
uint64_t total_loops;
- on_cpus(hv_perf_test, NULL);
+ for (i = ncpus - 1; i >= 0; i--)
+ on_cpu_async(i, hv_perf_test, (void *)(long)i);
+
+ while (cpus_active() > 1)
+ pause();
total_loops = 0;
for (i = ncpus - 1; i >= 0; i--)
@@ -144,7 +164,6 @@
int main(int ac, char **av)
{
- int nerr = 0;
int ncpus;
struct hv_reference_tsc_page shadow;
uint64_t tsc1, t1, tsc2, t2;
@@ -152,7 +171,7 @@
if (!hv_time_ref_counter_supported()) {
report_skip("time reference counter is unsupported");
- return report_summary();
+ goto done;
}
setup_vm();
@@ -167,12 +186,11 @@
"MSR value after enabling");
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);
- }
+ if (shadow.tsc_sequence == 0 || shadow.tsc_sequence == 0xFFFFFFFF)
+ report_abort("Reference TSC page not available\n");
- printf("scale: %" PRIx64" offset: %" PRId64"\n", shadow.tsc_scale, shadow.tsc_offset);
+ printf("sequence: %u. scale: %" PRIx64" offset: %" PRId64"\n",
+ shadow.tsc_sequence, shadow.tsc_scale, shadow.tsc_offset);
ref1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
tsc1 = rdtsc();
t1 = hvclock_tsc_to_ticks(&shadow, tsc1);
@@ -196,5 +214,6 @@
report(rdmsr(HV_X64_MSR_REFERENCE_TSC) == 0,
"MSR value after disabling");
- return nerr > 0 ? 1 : 0;
+done:
+ return report_summary();
}
diff --git a/x86/hyperv_connections.c b/x86/hyperv_connections.c
index cf04366..2c68415 100644
--- a/x86/hyperv_connections.c
+++ b/x86/hyperv_connections.c
@@ -266,7 +266,7 @@
{
int ncpus, ncpus_ok, i;
- if (!synic_supported()) {
+ if (!hv_synic_supported()) {
report_skip("Hyper-V SynIC is not supported");
goto summary;
}
diff --git a/x86/hyperv_stimer.c b/x86/hyperv_stimer.c
index f7c6791..e50903b 100644
--- a/x86/hyperv_stimer.c
+++ b/x86/hyperv_stimer.c
@@ -16,6 +16,9 @@
#define SINT1_VEC 0xF1
#define SINT2_VEC 0xF2
+#define APIC_VEC1 0xF3
+#define APIC_VEC2 0xF4
+
#define SINT1_NUM 2
#define SINT2_NUM 3
#define ONE_MS_IN_100NS 10000
@@ -25,6 +28,8 @@
struct stimer {
int sint;
int index;
+ bool direct;
+ int apic_vec;
atomic_t fire_count;
};
@@ -81,8 +86,7 @@
wrmsr(HV_X64_MSR_STIMER0_CONFIG + 2*timer->index, 0);
}
-static void process_stimer_expired(struct svcpu *svcpu, struct stimer *timer,
- u64 expiration_time, u64 delivery_time)
+static void process_stimer_expired(struct stimer *timer)
{
atomic_inc(&timer->fire_count);
}
@@ -119,8 +123,14 @@
abort();
}
timer = &svcpu->timer[payload->timer_index];
- process_stimer_expired(svcpu, timer, payload->expiration_time,
- payload->delivery_time);
+
+ if (timer->direct) {
+ report(false, "VMBus message in direct mode received");
+ report_summary();
+ abort();
+ }
+
+ process_stimer_expired(timer);
msg->header.message_type = HVMSG_NONE;
mb();
@@ -159,24 +169,49 @@
__stimer_isr(vcpu);
}
+static void __stimer_isr_direct(int vcpu, int timer_index)
+{
+ struct svcpu *svcpu = &g_synic_vcpu[vcpu];
+ struct stimer *timer = &svcpu->timer[timer_index];
+
+ process_stimer_expired(timer);
+}
+
+static void stimer_isr_direct1(isr_regs_t *regs)
+{
+ int vcpu = smp_id();
+
+ __stimer_isr_direct(vcpu, 0);
+
+ eoi();
+}
+
+static void stimer_isr_direct2(isr_regs_t *regs)
+{
+ int vcpu = smp_id();
+
+ __stimer_isr_direct(vcpu, 1);
+
+ eoi();
+}
+
static void stimer_start(struct stimer *timer,
bool auto_enable, bool periodic,
- u64 tick_100ns, int sint)
+ u64 tick_100ns)
{
- u64 config, count;
+ u64 count;
+ union hv_stimer_config config = {.as_uint64 = 0};
- timer->sint = sint;
atomic_set(&timer->fire_count, 0);
- config = 0;
- if (periodic) {
- config |= HV_STIMER_PERIODIC;
- }
-
- config |= ((u8)(sint & 0xFF)) << 16;
- config |= HV_STIMER_ENABLE;
- if (auto_enable) {
- config |= HV_STIMER_AUTOENABLE;
+ config.periodic = periodic;
+ config.enable = 1;
+ config.auto_enable = auto_enable;
+ if (!timer->direct) {
+ config.sintx = timer->sint;
+ } else {
+ config.direct_mode = 1;
+ config.apic_vector = timer->apic_vec;
}
if (periodic) {
@@ -187,9 +222,9 @@
if (!auto_enable) {
wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
- wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config);
+ wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config.as_uint64);
} else {
- wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config);
+ wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config.as_uint64);
wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
}
}
@@ -218,18 +253,51 @@
static void stimer_test_prepare(void *ctx)
{
+ int vcpu = smp_id();
+ struct svcpu *svcpu = &g_synic_vcpu[vcpu];
+ struct stimer *timer1, *timer2;
+
write_cr3((ulong)ctx);
synic_enable();
+
synic_sint_create(SINT1_NUM, SINT1_VEC, false);
synic_sint_create(SINT2_NUM, SINT2_VEC, true);
+
+ timer1 = &svcpu->timer[0];
+ timer2 = &svcpu->timer[1];
+
+ timer1->sint = SINT1_NUM;
+ timer2->sint = SINT2_NUM;
}
+static void stimer_test_prepare_direct(void *ctx)
+{
+ int vcpu = smp_id();
+ struct svcpu *svcpu = &g_synic_vcpu[vcpu];
+ struct stimer *timer1, *timer2;
+
+ write_cr3((ulong)ctx);
+
+ timer1 = &svcpu->timer[0];
+ timer2 = &svcpu->timer[1];
+
+ stimer_init(timer1, 0);
+ stimer_init(timer2, 1);
+
+ timer1->apic_vec = APIC_VEC1;
+ timer2->apic_vec = APIC_VEC2;
+
+ timer1->direct = true;
+ timer2->direct = true;
+}
+
+
static void stimer_test_periodic(int vcpu, struct stimer *timer1,
struct stimer *timer2)
{
/* Check periodic timers */
- stimer_start(timer1, false, true, ONE_MS_IN_100NS, SINT1_NUM);
- stimer_start(timer2, false, true, ONE_MS_IN_100NS, SINT2_NUM);
+ stimer_start(timer1, false, true, ONE_MS_IN_100NS);
+ stimer_start(timer2, false, true, ONE_MS_IN_100NS);
while ((atomic_read(&timer1->fire_count) < 1000) ||
(atomic_read(&timer2->fire_count) < 1000)) {
pause();
@@ -242,7 +310,7 @@
static void stimer_test_one_shot(int vcpu, struct stimer *timer)
{
/* Check one-shot timer */
- stimer_start(timer, false, false, ONE_MS_IN_100NS, SINT1_NUM);
+ stimer_start(timer, false, false, ONE_MS_IN_100NS);
while (atomic_read(&timer->fire_count) < 1) {
pause();
}
@@ -253,7 +321,7 @@
static void stimer_test_auto_enable_one_shot(int vcpu, struct stimer *timer)
{
/* Check auto-enable one-shot timer */
- stimer_start(timer, true, false, ONE_MS_IN_100NS, SINT1_NUM);
+ stimer_start(timer, true, false, ONE_MS_IN_100NS);
while (atomic_read(&timer->fire_count) < 1) {
pause();
}
@@ -264,7 +332,7 @@
static void stimer_test_auto_enable_periodic(int vcpu, struct stimer *timer)
{
/* Check auto-enable periodic timer */
- stimer_start(timer, true, true, ONE_MS_IN_100NS, SINT1_NUM);
+ stimer_start(timer, true, true, ONE_MS_IN_100NS);
while (atomic_read(&timer->fire_count) < 1000) {
pause();
}
@@ -274,13 +342,20 @@
static void stimer_test_one_shot_busy(int vcpu, struct stimer *timer)
{
- struct hv_message_page *msg_page = g_synic_vcpu[vcpu].msg_page;
- struct hv_message *msg = &msg_page->sint_message[timer->sint];
+ struct hv_message_page *msg_page;
+ struct hv_message *msg;
+
+ /* Skipping msg slot busy test in direct mode */
+ if (timer->direct)
+ return;
+
+ msg_page = g_synic_vcpu[vcpu].msg_page;
+ msg = &msg_page->sint_message[timer->sint];
msg->header.message_type = HVMSG_TIMER_EXPIRED;
wmb();
- stimer_start(timer, false, false, ONE_MS_IN_100NS, SINT1_NUM);
+ stimer_start(timer, false, false, ONE_MS_IN_100NS);
do
rmb();
@@ -329,7 +404,12 @@
synic_disable();
}
-static void stimer_test_all(void)
+static void stimer_test_cleanup_direct(void *ctx)
+{
+ stimers_shutdown();
+}
+
+static void stimer_test_all(bool direct)
{
int ncpus;
@@ -341,33 +421,51 @@
report_abort("number cpus exceeds %d", MAX_CPUS);
printf("cpus = %d\n", ncpus);
- handle_irq(SINT1_VEC, stimer_isr);
- handle_irq(SINT2_VEC, stimer_isr_auto_eoi);
+ if (!direct) {
+ printf("Starting Hyper-V SynIC timers tests: message mode\n");
- on_cpus(stimer_test_prepare, (void *)read_cr3());
- on_cpus(stimer_test, NULL);
- on_cpus(stimer_test_cleanup, NULL);
+ handle_irq(SINT1_VEC, stimer_isr);
+ handle_irq(SINT2_VEC, stimer_isr_auto_eoi);
+
+ on_cpus(stimer_test_prepare, (void *)read_cr3());
+ on_cpus(stimer_test, NULL);
+ on_cpus(stimer_test_cleanup, NULL);
+ } else {
+ printf("Starting Hyper-V SynIC timers tests: direct mode\n");
+
+ handle_irq(APIC_VEC1, stimer_isr_direct1);
+ handle_irq(APIC_VEC2, stimer_isr_direct2);
+
+ on_cpus(stimer_test_prepare_direct, (void *)read_cr3());
+ on_cpus(stimer_test, NULL);
+ on_cpus(stimer_test_cleanup_direct, NULL);
+ }
}
-int main(int ac, char **av)
+int main(int argc, char **argv)
{
+ bool direct = argc >= 2 && !strcmp(argv[1], "direct");
- if (!synic_supported()) {
- report_pass("Hyper-V SynIC is not supported");
+ if (!hv_synic_supported()) {
+ report_skip("Hyper-V SynIC is not supported");
goto done;
}
- if (!stimer_supported()) {
- report_pass("Hyper-V SynIC timers are not supported");
+ if (!hv_stimer_supported()) {
+ report_skip("Hyper-V SynIC timers are not supported");
goto done;
}
if (!hv_time_ref_counter_supported()) {
- report_pass("Hyper-V time reference counter is not supported");
+ report_skip("Hyper-V time reference counter is not supported");
goto done;
}
- stimer_test_all();
+ if (direct && !stimer_direct_supported()) {
+ report_skip("Hyper-V SinIC timer direct mode is not supported");
+ }
+
+ stimer_test_all(direct);
done:
return report_summary();
}
diff --git a/x86/hyperv_synic.c b/x86/hyperv_synic.c
index 9d61d83..761df8a 100644
--- a/x86/hyperv_synic.c
+++ b/x86/hyperv_synic.c
@@ -141,45 +141,46 @@
int main(int ac, char **av)
{
+ int ncpus, i;
+ bool ok;
- if (synic_supported()) {
- int ncpus, i;
- bool ok;
-
- setup_vm();
- enable_apic();
-
- ncpus = cpu_count();
- if (ncpus > MAX_CPUS)
- report_abort("number cpus exceeds %d", MAX_CPUS);
- printf("ncpus = %d\n", ncpus);
-
- synic_prepare_sint_vecs();
-
- printf("prepare\n");
- on_cpus(synic_test_prepare, (void *)read_cr3());
-
- for (i = 0; i < ncpus; i++) {
- printf("test %d -> %d\n", i, ncpus - 1 - i);
- on_cpu_async(i, synic_test, (void *)(ulong)(ncpus - 1 - i));
- }
- while (cpus_active() > 1)
- pause();
-
- printf("cleanup\n");
- on_cpus(synic_test_cleanup, NULL);
-
- ok = true;
- for (i = 0; i < ncpus; ++i) {
- printf("isr_enter_count[%d] = %d\n",
- i, atomic_read(&isr_enter_count[i]));
- ok &= atomic_read(&isr_enter_count[i]) == 16;
- }
-
- report(ok, "Hyper-V SynIC test");
- } else {
- printf("Hyper-V SynIC is not supported");
+ if (!hv_synic_supported()) {
+ report_skip("Hyper-V SynIC is not supported");
+ goto done;
}
+ setup_vm();
+ enable_apic();
+
+ ncpus = cpu_count();
+ if (ncpus > MAX_CPUS)
+ report_abort("number cpus exceeds %d", MAX_CPUS);
+ printf("ncpus = %d\n", ncpus);
+
+ synic_prepare_sint_vecs();
+
+ printf("prepare\n");
+ on_cpus(synic_test_prepare, (void *)read_cr3());
+
+ for (i = 0; i < ncpus; i++) {
+ printf("test %d -> %d\n", i, ncpus - 1 - i);
+ on_cpu_async(i, synic_test, (void *)(ulong)(ncpus - 1 - i));
+ }
+ while (cpus_active() > 1)
+ pause();
+
+ printf("cleanup\n");
+ on_cpus(synic_test_cleanup, NULL);
+
+ ok = true;
+ for (i = 0; i < ncpus; ++i) {
+ printf("isr_enter_count[%d] = %d\n",
+ i, atomic_read(&isr_enter_count[i]));
+ ok &= atomic_read(&isr_enter_count[i]) == 16;
+ }
+
+ report(ok, "Hyper-V SynIC test");
+
+done:
return report_summary();
}
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 3fe5944..124be7a 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -451,25 +451,31 @@
[hyperv_synic]
file = hyperv_synic.flat
smp = 2
-extra_params = -cpu kvm64,hv_vpindex,hv_synic -device hyperv-testdev
+extra_params = -cpu host,hv_passthrough -device hyperv-testdev
groups = hyperv
[hyperv_connections]
file = hyperv_connections.flat
smp = 2
-extra_params = -cpu kvm64,hv_vpindex,hv_synic -device hyperv-testdev
+extra_params = -cpu host,hv_passthrough -device hyperv-testdev
groups = hyperv
[hyperv_stimer]
file = hyperv_stimer.flat
smp = 2
-extra_params = -cpu kvm64,hv_vpindex,hv_time,hv_synic,hv_stimer -device hyperv-testdev
+extra_params = -cpu host,hv_passthrough
+groups = hyperv
+
+[hyperv_stimer_direct]
+file = hyperv_stimer.flat
+smp = 2
+extra_params = -cpu host,hv_passthrough -append direct
groups = hyperv
[hyperv_clock]
file = hyperv_clock.flat
smp = 2
-extra_params = -cpu kvm64,hv_time
+extra_params = -cpu host,hv_passthrough
arch = x86_64
groups = hyperv
check = /sys/devices/system/clocksource/clocksource0/current_clocksource=tsc