[PATCH v2] kvm-unit-tests: VMX: Test suite for preemption timer

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxx>
---
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

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux