[kvm-unit-tests PATCH v1 2/3] x86: Add async page fault int test

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

 



KVM switched to use interrupt for 'page ready' APF event since Linux v5.10 and
the legacy mechanism using #PF was deprecated. Add a new test for interrupt
based 'page ready' APF event delivery.

Signed-off-by: Dan Wu <dan1.wu@xxxxxxxxx>
Reviewed-by: Xiaoyao Li <xiaoyao.li@xxxxxxxxx>
---
The test is based on asyncpf.c and simplifies implementation.
---
 ci/cirrus-ci-fedora.yml |   1 +
 lib/x86/processor.h     |   6 ++
 x86/Makefile.common     |   3 +-
 x86/asyncpf_int.c       | 127 ++++++++++++++++++++++++++++++++++++++++
 x86/unittests.cfg       |   4 ++
 5 files changed, 140 insertions(+), 1 deletion(-)
 create mode 100644 x86/asyncpf_int.c

diff --git a/ci/cirrus-ci-fedora.yml b/ci/cirrus-ci-fedora.yml
index 918c9a36..52cb10c6 100644
--- a/ci/cirrus-ci-fedora.yml
+++ b/ci/cirrus-ci-fedora.yml
@@ -22,6 +22,7 @@ fedora_task:
     - ./run_tests.sh
         access
         asyncpf
+        asyncpf_int
         debug
         emulator
         ept
diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index 44f4fd1e..1a0f1243 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -263,6 +263,12 @@ static inline bool is_intel(void)
 #define	X86_FEATURE_ARCH_CAPABILITIES	(CPUID(0x7, 0, EDX, 29))
 #define	X86_FEATURE_PKS			(CPUID(0x7, 0, ECX, 31))
 
+/*
+ * KVM defined leafs
+ */
+#define	KVM_FEATURE_ASYNC_PF		(CPUID(0x40000001, 0, EAX, 4))
+#define	KVM_FEATURE_ASYNC_PF_INT	(CPUID(0x40000001, 0, EAX, 14))
+
 /*
  * Extended Leafs, a.k.a. AMD defined
  */
diff --git a/x86/Makefile.common b/x86/Makefile.common
index 4ae9a557..c4b309e3 100644
--- a/x86/Makefile.common
+++ b/x86/Makefile.common
@@ -90,7 +90,8 @@ tests-common = $(TEST_DIR)/vmexit.$(exe) $(TEST_DIR)/tsc.$(exe) \
                $(TEST_DIR)/emulator.$(exe) \
                $(TEST_DIR)/eventinj.$(exe) \
                $(TEST_DIR)/smap.$(exe) \
-               $(TEST_DIR)/umip.$(exe)
+               $(TEST_DIR)/umip.$(exe) \
+               $(TEST_DIR)/asyncpf_int.$(exe)
 
 # The following test cases are disabled when building EFI tests because they
 # use absolute addresses in their inline assembly code, which cannot compile
diff --git a/x86/asyncpf_int.c b/x86/asyncpf_int.c
new file mode 100644
index 00000000..84268f6b
--- /dev/null
+++ b/x86/asyncpf_int.c
@@ -0,0 +1,127 @@
+/*
+ * Async PF Int test. For the test to actually do anything it needs to be started
+ * in memory cgroup with 512M of memory and with more than 1G memory provided
+ * to the guest.
+ *
+ * To identify the cgroup version on Linux:
+ * stat -fc %T /sys/fs/cgroup/
+ *
+ * If the output is tmpfs, your system is using cgroup v1:
+ * To create cgroup do as root:
+ * mkdir /dev/cgroup
+ * mount -t cgroup none -omemory /dev/cgroup
+ * chmod a+rxw /dev/cgroup/
+ * From a shell you will start qemu from:
+ * mkdir /dev/cgroup/1
+ * echo $$ >  /dev/cgroup/1/tasks
+ * echo 512M > /dev/cgroup/1/memory.limit_in_bytes
+ *
+ * If the output is cgroup2fs, your system is using cgroup v2:
+ * mkdir /sys/fs/cgroup/cg1
+ * echo $$ >  /sys/fs/cgroup/cg1/cgroup.procs
+ * echo 512M > /sys/fs/cgroup/cg1/memory.max
+ *
+ */
+#include "x86/processor.h"
+#include "x86/apic.h"
+#include "x86/isr.h"
+#include "x86/vm.h"
+#include "alloc.h"
+#include "vmalloc.h"
+#include "asyncpf.h"
+
+struct kvm_vcpu_pv_apf_data apf_reason;
+
+char *buf;
+void* virt;
+volatile uint64_t  i;
+volatile uint64_t phys;
+volatile uint32_t saved_token;
+volatile uint32_t asyncpf_num;
+
+static inline uint32_t get_and_clear_apf_reason(void)
+{
+	uint32_t r = apf_reason.flags;
+	apf_reason.flags = 0;
+	return r;
+}
+
+static void handle_interrupt(isr_regs_t *regs)
+{
+	uint32_t apf_token = apf_reason.token;
+
+	apf_reason.token = 0;
+	wrmsr(MSR_KVM_ASYNC_PF_ACK, 1);
+
+	if (apf_token == 0xffffffff) {
+		report_pass("Wakeup all, got token 0x%x", apf_token);
+	} else if (apf_token == saved_token) {
+		asyncpf_num++;
+		install_pte(phys_to_virt(read_cr3()), 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
+		phys = 0;
+	} else {
+		report_fail("unexpected async pf int token 0x%x", apf_token);
+	}
+
+	eoi();
+}
+
+static void handle_pf(struct ex_regs *r)
+{
+	virt = (void*)((ulong)(buf+i) & ~(PAGE_SIZE-1));
+	uint32_t reason = get_and_clear_apf_reason();
+	switch (reason) {
+	case 0:
+		report_fail("unexpected #PF at %#lx", read_cr2());
+		exit(report_summary());
+	case KVM_PV_REASON_PAGE_NOT_PRESENT:
+		phys = virt_to_pte_phys(phys_to_virt(read_cr3()), virt);
+		install_pte(phys_to_virt(read_cr3()), 1, virt, phys, 0);
+		write_cr3(read_cr3());
+		saved_token = read_cr2();
+		while (phys) {
+			safe_halt(); /* enables irq */
+		}
+		break;
+	default:
+		report_fail("unexpected async pf with reason 0x%x", reason);
+		exit(report_summary());
+	}
+}
+
+#define MEM (1ull*1024*1024*1024)
+
+int main(int ac, char **av)
+{
+	if (!this_cpu_has(KVM_FEATURE_ASYNC_PF)) {
+		report_skip("KVM_FEATURE_ASYNC_PF is not supported\n");
+		return report_summary();
+	}
+
+	if (!this_cpu_has(KVM_FEATURE_ASYNC_PF_INT)) {
+		report_skip("KVM_FEATURE_ASYNC_PF_INT is not supported\n");
+		return report_summary();
+	}
+
+	setup_vm();
+
+	handle_exception(PF_VECTOR, handle_pf);
+	handle_irq(HYPERVISOR_CALLBACK_VECTOR, handle_interrupt);
+	memset(&apf_reason, 0, sizeof(apf_reason));
+
+	wrmsr(MSR_KVM_ASYNC_PF_INT, HYPERVISOR_CALLBACK_VECTOR);
+	wrmsr(MSR_KVM_ASYNC_PF_EN, virt_to_phys((void*)&apf_reason) |
+		KVM_ASYNC_PF_SEND_ALWAYS | KVM_ASYNC_PF_ENABLED | KVM_ASYNC_PF_DELIVERY_AS_INT);
+
+	buf = malloc(MEM);
+	sti();
+
+	/* access a lot of memory to make host swap it out */
+	for (i = 0; i < MEM; i += 4096)
+		buf[i] = 1;
+
+	cli();
+	report(asyncpf_num > 0, "get %d async pf events ('page not present' #PF event with matched "
+		"'page ready' interrupt event )", asyncpf_num);
+	return report_summary();
+}
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 3fe59449..8735ba34 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -174,6 +174,10 @@ extra_params = -cpu max
 file = asyncpf.flat
 extra_params = -m 2048
 
+[asyncpf_int]
+file = asyncpf_int.flat
+extra_params = -cpu host -m 2048
+
 [emulator]
 file = emulator.flat
 
-- 
2.39.3





[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