* [PATCH v2] kvm-unit-tests: VMX: Test suite for preemption timer
@ 2013-09-06 2:11 Arthur Chunqi Li
2013-09-09 13:59 ` Paolo Bonzini
0 siblings, 1 reply; 2+ messages in thread
From: Arthur Chunqi Li @ 2013-09-06 2:11 UTC (permalink / raw)
To: kvm; +Cc: jan.kiszka, gleb, pbonzini, Arthur Chunqi Li
Test cases for preemption timer in nested VMX. Two aspects are tested:
1. Save preemption timer on VMEXIT if relevant bit set in EXIT_CONTROL
2. Test a relevant bug of KVM. The bug will not save preemption timer
value if exit L2->L0 for some reason and enter L0->L2. Thus preemption
timer will never trigger if the value is large enough.
3. Some other aspects are tested, e.g. preempt without save, preempt
when value is 0.
Signed-off-by: Arthur Chunqi Li <yzt356@gmail.com>
---
ChangeLog to v1:
1. Add test of EXI_SAVE_PREEMPT enable and PIN_PREEMPT disable
2. Add test of PIN_PREEMPT enable and EXI_SAVE_PREEMPT enable/disable
3. Add test of preemption value is 0
x86/vmx.h | 3 +
x86/vmx_tests.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 178 insertions(+)
diff --git a/x86/vmx.h b/x86/vmx.h
index 28595d8..ebc8cfd 100644
--- a/x86/vmx.h
+++ b/x86/vmx.h
@@ -210,6 +210,7 @@ enum Encoding {
GUEST_ACTV_STATE = 0x4826ul,
GUEST_SMBASE = 0x4828ul,
GUEST_SYSENTER_CS = 0x482aul,
+ PREEMPT_TIMER_VALUE = 0x482eul,
/* 32-Bit Host State Fields */
HOST_SYSENTER_CS = 0x4c00ul,
@@ -331,6 +332,7 @@ enum Ctrl_exi {
EXI_LOAD_PERF = 1UL << 12,
EXI_INTA = 1UL << 15,
EXI_LOAD_EFER = 1UL << 21,
+ EXI_SAVE_PREEMPT = 1UL << 22,
};
enum Ctrl_ent {
@@ -342,6 +344,7 @@ enum Ctrl_pin {
PIN_EXTINT = 1ul << 0,
PIN_NMI = 1ul << 3,
PIN_VIRT_NMI = 1ul << 5,
+ PIN_PREEMPT = 1ul << 6,
};
enum Ctrl0 {
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index c1b39f4..2e32031 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -1,4 +1,30 @@
#include "vmx.h"
+#include "msr.h"
+#include "processor.h"
+
+volatile u32 stage;
+
+static inline void vmcall()
+{
+ asm volatile("vmcall");
+}
+
+static inline void set_stage(u32 s)
+{
+ barrier();
+ stage = s;
+ barrier();
+}
+
+static inline u32 get_stage()
+{
+ u32 s;
+
+ barrier();
+ s = stage;
+ barrier();
+ return s;
+}
void basic_init()
{
@@ -76,6 +102,153 @@ int vmenter_exit_handler()
return VMX_TEST_VMEXIT;
}
+u32 preempt_scale;
+volatile unsigned long long tsc_val;
+volatile u32 preempt_val;
+
+void preemption_timer_init()
+{
+ u32 ctrl_exit;
+
+ // Enable EXI_SAVE_PREEMPT with PIN_PREEMPT dieabled
+ ctrl_exit = (vmcs_read(EXI_CONTROLS) |
+ EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr;
+ vmcs_write(EXI_CONTROLS, ctrl_exit);
+ preempt_val = 10000000;
+ vmcs_write(PREEMPT_TIMER_VALUE, preempt_val);
+ set_stage(0);
+ preempt_scale = rdmsr(MSR_IA32_VMX_MISC) & 0x1F;
+}
+
+void preemption_timer_main()
+{
+ int i, j;
+
+ if (!(ctrl_pin_rev.clr & PIN_PREEMPT)) {
+ printf("\tPreemption timer is not supported\n");
+ return;
+ }
+ if (!(ctrl_exit_rev.clr & EXI_SAVE_PREEMPT))
+ printf("\tSave preemption value is not supported\n");
+ else {
+ // Test EXI_SAVE_PREEMPT enable and PIN_PREEMPT disable
+ set_stage(0);
+ // Consume enough time to let L2->L0->L2 occurs
+ for(i = 0; i < 100000; i++)
+ for (j = 0; j < 10000; j++);
+ vmcall();
+ // Test PIN_PREEMPT enable and EXI_SAVE_PREEMPT enable/disable
+ set_stage(1);
+ vmcall();
+ // Test both enable
+ if (get_stage() == 2)
+ vmcall();
+ }
+ // Test the bug of reseting preempt value when L2->L0->L2
+ set_stage(3);
+ vmcall();
+ tsc_val = rdtsc();
+ while (1) {
+ if (((rdtsc() - tsc_val) >> preempt_scale)
+ > 10 * preempt_val) {
+ report("Preemption timer timeout", 0);
+ break;
+ }
+ if (get_stage() == 4)
+ break;
+ }
+ // Test preempt val is 0
+ set_stage(4);
+ report("Preemption timer, val=0", 0);
+}
+
+int preemption_timer_exit_handler()
+{
+ u64 guest_rip;
+ ulong reason;
+ u32 insn_len;
+ u32 ctrl_exit;
+ u32 ctrl_pin;
+
+ guest_rip = vmcs_read(GUEST_RIP);
+ reason = vmcs_read(EXI_REASON) & 0xff;
+ insn_len = vmcs_read(EXI_INST_LEN);
+ switch (reason) {
+ case VMX_PREEMPT:
+ switch (get_stage()) {
+ case 3:
+ if (((rdtsc() - tsc_val) >> preempt_scale) < preempt_val)
+ report("Preemption timer timeout", 0);
+ else
+ report("Preemption timer timeout", 1);
+ set_stage(get_stage() + 1);
+ break;
+ case 4:
+ if (vmcs_read(PREEMPT_TIMER_VALUE) == 0)
+ report("Preemption timer, val=0", 1);
+ else
+ report("Preemption timer, val=0", 0);
+ return VMX_TEST_VMEXIT;
+ }
+ return VMX_TEST_RESUME;
+ case VMX_VMCALL:
+ switch (get_stage()) {
+ case 0:
+ if (vmcs_read(PREEMPT_TIMER_VALUE) != preempt_val)
+ report("Preemption timer off", 0);
+ else
+ report("Preemption timer off", 1);
+ // Enable PIN_PREEMPT and disable EXI_SAVE_PREEMPT
+ ctrl_pin = (vmcs_read(PIN_CONTROLS) | PIN_PREEMPT) &
+ ctrl_pin_rev.clr;
+ vmcs_write(PIN_CONTROLS, ctrl_pin);
+ ctrl_exit = (vmcs_read(EXI_CONTROLS) &
+ ~EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr;
+ vmcs_write(EXI_CONTROLS, ctrl_exit);
+ vmcs_write(PREEMPT_TIMER_VALUE, preempt_val);
+ break;
+ case 1:
+ if (vmcs_read(PREEMPT_TIMER_VALUE) != preempt_val)
+ report("Save preemption value", 0);
+ else {
+ set_stage(get_stage() + 1);
+ // Enable EXI_SAVE_PREEMPT with PIN_PREEMPT
+ // already enabled
+ ctrl_exit = (vmcs_read(EXI_CONTROLS) |
+ EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr;
+ vmcs_write(EXI_CONTROLS, ctrl_exit);
+ }
+ vmcs_write(PREEMPT_TIMER_VALUE, preempt_val);
+ break;
+ case 2:
+ if (vmcs_read(PREEMPT_TIMER_VALUE) >= preempt_val)
+ report("Save preemption value", 0);
+ else
+ report("Save preemption value", 1);
+ break;
+ case 3:
+ vmcs_write(PREEMPT_TIMER_VALUE, preempt_val);
+ ctrl_pin = (vmcs_read(PIN_CONTROLS) | PIN_PREEMPT) &
+ ctrl_pin_rev.clr;
+ vmcs_write(PIN_CONTROLS, ctrl_pin);
+ ctrl_exit = (vmcs_read(EXI_CONTROLS) |
+ EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr;
+ vmcs_write(EXI_CONTROLS, ctrl_exit);
+ break;
+ default:
+ printf("Invalid stage.\n");
+ print_vmexit_info();
+ return VMX_TEST_VMEXIT;
+ }
+ vmcs_write(GUEST_RIP, guest_rip + insn_len);
+ return VMX_TEST_RESUME;
+ default:
+ printf("Unknown exit reason, %d\n", reason);
+ print_vmexit_info();
+ }
+ return VMX_TEST_VMEXIT;
+}
+
/* name/init/guest_main/exit_handler/syscall_handler/guest_regs
basic_* just implement some basic functions */
struct vmx_test vmx_tests[] = {
@@ -83,5 +256,7 @@ struct vmx_test vmx_tests[] = {
basic_syscall_handler, {0} },
{ "vmenter", basic_init, vmenter_main, vmenter_exit_handler,
basic_syscall_handler, {0} },
+ { "preemption timer", preemption_timer_init, preemption_timer_main,
+ preemption_timer_exit_handler, basic_syscall_handler, {0} },
{ NULL, NULL, NULL, NULL, NULL, {0} },
};
--
1.7.9.5
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v2] kvm-unit-tests: VMX: Test suite for preemption timer
2013-09-06 2:11 [PATCH v2] kvm-unit-tests: VMX: Test suite for preemption timer Arthur Chunqi Li
@ 2013-09-09 13:59 ` Paolo Bonzini
0 siblings, 0 replies; 2+ messages in thread
From: Paolo Bonzini @ 2013-09-09 13:59 UTC (permalink / raw)
To: Arthur Chunqi Li; +Cc: kvm, jan.kiszka, gleb
Il 06/09/2013 04:11, Arthur Chunqi Li ha scritto:
> Test cases for preemption timer in nested VMX. Two aspects are tested:
> 1. Save preemption timer on VMEXIT if relevant bit set in EXIT_CONTROL
> 2. Test a relevant bug of KVM. The bug will not save preemption timer
> value if exit L2->L0 for some reason and enter L0->L2. Thus preemption
> timer will never trigger if the value is large enough.
> 3. Some other aspects are tested, e.g. preempt without save, preempt
> when value is 0.
>
> Signed-off-by: Arthur Chunqi Li <yzt356@gmail.com>
> ---
> ChangeLog to v1:
> 1. Add test of EXI_SAVE_PREEMPT enable and PIN_PREEMPT disable
> 2. Add test of PIN_PREEMPT enable and EXI_SAVE_PREEMPT enable/disable
> 3. Add test of preemption value is 0
The patch looks good, however we have now 3 series that are conflicting
(basic tests, EPT, preemption timer).
I ordered them and pushed them to "vmx" branch of kvm-unit-tests.git.
Can you send them as a single series from now on?
Thanks,
Paolo
> x86/vmx.h | 3 +
> x86/vmx_tests.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 178 insertions(+)
>
> diff --git a/x86/vmx.h b/x86/vmx.h
> index 28595d8..ebc8cfd 100644
> --- a/x86/vmx.h
> +++ b/x86/vmx.h
> @@ -210,6 +210,7 @@ enum Encoding {
> GUEST_ACTV_STATE = 0x4826ul,
> GUEST_SMBASE = 0x4828ul,
> GUEST_SYSENTER_CS = 0x482aul,
> + PREEMPT_TIMER_VALUE = 0x482eul,
>
> /* 32-Bit Host State Fields */
> HOST_SYSENTER_CS = 0x4c00ul,
> @@ -331,6 +332,7 @@ enum Ctrl_exi {
> EXI_LOAD_PERF = 1UL << 12,
> EXI_INTA = 1UL << 15,
> EXI_LOAD_EFER = 1UL << 21,
> + EXI_SAVE_PREEMPT = 1UL << 22,
> };
>
> enum Ctrl_ent {
> @@ -342,6 +344,7 @@ enum Ctrl_pin {
> PIN_EXTINT = 1ul << 0,
> PIN_NMI = 1ul << 3,
> PIN_VIRT_NMI = 1ul << 5,
> + PIN_PREEMPT = 1ul << 6,
> };
>
> enum Ctrl0 {
> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
> index c1b39f4..2e32031 100644
> --- a/x86/vmx_tests.c
> +++ b/x86/vmx_tests.c
> @@ -1,4 +1,30 @@
> #include "vmx.h"
> +#include "msr.h"
> +#include "processor.h"
> +
> +volatile u32 stage;
> +
> +static inline void vmcall()
> +{
> + asm volatile("vmcall");
> +}
> +
> +static inline void set_stage(u32 s)
> +{
> + barrier();
> + stage = s;
> + barrier();
> +}
> +
> +static inline u32 get_stage()
> +{
> + u32 s;
> +
> + barrier();
> + s = stage;
> + barrier();
> + return s;
> +}
>
> void basic_init()
> {
> @@ -76,6 +102,153 @@ int vmenter_exit_handler()
> return VMX_TEST_VMEXIT;
> }
>
> +u32 preempt_scale;
> +volatile unsigned long long tsc_val;
> +volatile u32 preempt_val;
> +
> +void preemption_timer_init()
> +{
> + u32 ctrl_exit;
> +
> + // Enable EXI_SAVE_PREEMPT with PIN_PREEMPT dieabled
> + ctrl_exit = (vmcs_read(EXI_CONTROLS) |
> + EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr;
> + vmcs_write(EXI_CONTROLS, ctrl_exit);
> + preempt_val = 10000000;
> + vmcs_write(PREEMPT_TIMER_VALUE, preempt_val);
> + set_stage(0);
> + preempt_scale = rdmsr(MSR_IA32_VMX_MISC) & 0x1F;
> +}
> +
> +void preemption_timer_main()
> +{
> + int i, j;
> +
> + if (!(ctrl_pin_rev.clr & PIN_PREEMPT)) {
> + printf("\tPreemption timer is not supported\n");
> + return;
> + }
> + if (!(ctrl_exit_rev.clr & EXI_SAVE_PREEMPT))
> + printf("\tSave preemption value is not supported\n");
> + else {
> + // Test EXI_SAVE_PREEMPT enable and PIN_PREEMPT disable
> + set_stage(0);
> + // Consume enough time to let L2->L0->L2 occurs
> + for(i = 0; i < 100000; i++)
> + for (j = 0; j < 10000; j++);
> + vmcall();
> + // Test PIN_PREEMPT enable and EXI_SAVE_PREEMPT enable/disable
> + set_stage(1);
> + vmcall();
> + // Test both enable
> + if (get_stage() == 2)
> + vmcall();
> + }
> + // Test the bug of reseting preempt value when L2->L0->L2
> + set_stage(3);
> + vmcall();
> + tsc_val = rdtsc();
> + while (1) {
> + if (((rdtsc() - tsc_val) >> preempt_scale)
> + > 10 * preempt_val) {
> + report("Preemption timer timeout", 0);
> + break;
> + }
> + if (get_stage() == 4)
> + break;
> + }
> + // Test preempt val is 0
> + set_stage(4);
> + report("Preemption timer, val=0", 0);
> +}
> +
> +int preemption_timer_exit_handler()
> +{
> + u64 guest_rip;
> + ulong reason;
> + u32 insn_len;
> + u32 ctrl_exit;
> + u32 ctrl_pin;
> +
> + guest_rip = vmcs_read(GUEST_RIP);
> + reason = vmcs_read(EXI_REASON) & 0xff;
> + insn_len = vmcs_read(EXI_INST_LEN);
> + switch (reason) {
> + case VMX_PREEMPT:
> + switch (get_stage()) {
> + case 3:
> + if (((rdtsc() - tsc_val) >> preempt_scale) < preempt_val)
> + report("Preemption timer timeout", 0);
> + else
> + report("Preemption timer timeout", 1);
> + set_stage(get_stage() + 1);
> + break;
> + case 4:
> + if (vmcs_read(PREEMPT_TIMER_VALUE) == 0)
> + report("Preemption timer, val=0", 1);
> + else
> + report("Preemption timer, val=0", 0);
> + return VMX_TEST_VMEXIT;
> + }
> + return VMX_TEST_RESUME;
> + case VMX_VMCALL:
> + switch (get_stage()) {
> + case 0:
> + if (vmcs_read(PREEMPT_TIMER_VALUE) != preempt_val)
> + report("Preemption timer off", 0);
> + else
> + report("Preemption timer off", 1);
> + // Enable PIN_PREEMPT and disable EXI_SAVE_PREEMPT
> + ctrl_pin = (vmcs_read(PIN_CONTROLS) | PIN_PREEMPT) &
> + ctrl_pin_rev.clr;
> + vmcs_write(PIN_CONTROLS, ctrl_pin);
> + ctrl_exit = (vmcs_read(EXI_CONTROLS) &
> + ~EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr;
> + vmcs_write(EXI_CONTROLS, ctrl_exit);
> + vmcs_write(PREEMPT_TIMER_VALUE, preempt_val);
> + break;
> + case 1:
> + if (vmcs_read(PREEMPT_TIMER_VALUE) != preempt_val)
> + report("Save preemption value", 0);
> + else {
> + set_stage(get_stage() + 1);
> + // Enable EXI_SAVE_PREEMPT with PIN_PREEMPT
> + // already enabled
> + ctrl_exit = (vmcs_read(EXI_CONTROLS) |
> + EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr;
> + vmcs_write(EXI_CONTROLS, ctrl_exit);
> + }
> + vmcs_write(PREEMPT_TIMER_VALUE, preempt_val);
> + break;
> + case 2:
> + if (vmcs_read(PREEMPT_TIMER_VALUE) >= preempt_val)
> + report("Save preemption value", 0);
> + else
> + report("Save preemption value", 1);
> + break;
> + case 3:
> + vmcs_write(PREEMPT_TIMER_VALUE, preempt_val);
> + ctrl_pin = (vmcs_read(PIN_CONTROLS) | PIN_PREEMPT) &
> + ctrl_pin_rev.clr;
> + vmcs_write(PIN_CONTROLS, ctrl_pin);
> + ctrl_exit = (vmcs_read(EXI_CONTROLS) |
> + EXI_SAVE_PREEMPT) & ctrl_exit_rev.clr;
> + vmcs_write(EXI_CONTROLS, ctrl_exit);
> + break;
> + default:
> + printf("Invalid stage.\n");
> + print_vmexit_info();
> + return VMX_TEST_VMEXIT;
> + }
> + vmcs_write(GUEST_RIP, guest_rip + insn_len);
> + return VMX_TEST_RESUME;
> + default:
> + printf("Unknown exit reason, %d\n", reason);
> + print_vmexit_info();
> + }
> + return VMX_TEST_VMEXIT;
> +}
> +
> /* name/init/guest_main/exit_handler/syscall_handler/guest_regs
> basic_* just implement some basic functions */
> struct vmx_test vmx_tests[] = {
> @@ -83,5 +256,7 @@ struct vmx_test vmx_tests[] = {
> basic_syscall_handler, {0} },
> { "vmenter", basic_init, vmenter_main, vmenter_exit_handler,
> basic_syscall_handler, {0} },
> + { "preemption timer", preemption_timer_init, preemption_timer_main,
> + preemption_timer_exit_handler, basic_syscall_handler, {0} },
> { NULL, NULL, NULL, NULL, NULL, {0} },
> };
>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2013-09-09 13:59 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-09-06 2:11 [PATCH v2] kvm-unit-tests: VMX: Test suite for preemption timer Arthur Chunqi Li
2013-09-09 13:59 ` Paolo Bonzini
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).