xref: /kvm-unit-tests/x86/asyncpf.c (revision dca3f4c041143c8e8dc70c6890a19a5730310230)
1 /*
2  * Async PF test. For the test to actually do anything it needs to be started
3  * in memory cgroup with 512M of memory and with more than 1G memory provided
4  * to the guest.
5  *
6  * To identify the cgroup version on Linux:
7  * stat -fc %T /sys/fs/cgroup/
8  *
9  * If the output is tmpfs, your system is using cgroup v1:
10  * To create cgroup do as root:
11  * mkdir /dev/cgroup
12  * mount -t cgroup none -omemory /dev/cgroup
13  * chmod a+rxw /dev/cgroup/
14  *
15  * From a shell you will start qemu from:
16  * mkdir /dev/cgroup/1
17  * echo $$ >  /dev/cgroup/1/tasks
18  * echo 512M > /dev/cgroup/1/memory.limit_in_bytes
19  *
20  * If the output is cgroup2fs, your system is using cgroup v2:
21  * mkdir /sys/fs/cgroup/cg1
22  * echo $$ >  /sys/fs/cgroup/cg1/cgroup.procs
23  * echo 512M > /sys/fs/cgroup/cg1/memory.max
24  *
25  */
26 #include "x86/processor.h"
27 #include "x86/apic.h"
28 #include "x86/isr.h"
29 #include "x86/vm.h"
30 #include "alloc.h"
31 #include "vmalloc.h"
32 
33 #define KVM_PV_REASON_PAGE_NOT_PRESENT 1
34 
35 #define MSR_KVM_ASYNC_PF_EN 0x4b564d02
36 #define MSR_KVM_ASYNC_PF_INT    0x4b564d06
37 #define MSR_KVM_ASYNC_PF_ACK    0x4b564d07
38 
39 #define KVM_ASYNC_PF_ENABLED                    (1 << 0)
40 #define KVM_ASYNC_PF_SEND_ALWAYS                (1 << 1)
41 #define KVM_ASYNC_PF_DELIVERY_AS_INT            (1 << 3)
42 
43 #define HYPERVISOR_CALLBACK_VECTOR	0xf3
44 
45 struct kvm_vcpu_pv_apf_data {
46       /* Used for 'page not present' events delivered via #PF */
47       uint32_t  flags;
48 
49       /* Used for 'page ready' events delivered via interrupt notification */
50       uint32_t  token;
51 
52       uint8_t  pad[56];
53 } apf_reason __attribute__((aligned(64)));
54 
55 char *buf;
56 void* virt;
57 volatile uint64_t  i;
58 volatile uint64_t phys;
59 volatile uint32_t saved_token;
60 volatile unsigned asyncpf_num;
61 
get_and_clear_apf_reason(void)62 static inline uint32_t get_and_clear_apf_reason(void)
63 {
64 	uint32_t r = apf_reason.flags;
65 	apf_reason.flags = 0;
66 	return r;
67 }
68 
handle_interrupt(isr_regs_t * regs)69 static void handle_interrupt(isr_regs_t *regs)
70 {
71 	uint32_t apf_token = apf_reason.token;
72 
73 	apf_reason.token = 0;
74 	wrmsr(MSR_KVM_ASYNC_PF_ACK, 1);
75 
76 	if (apf_token == 0xffffffff) {
77 		report_pass("Wakeup all, got token 0x%" PRIx32, apf_token);
78 	} else if (apf_token == saved_token) {
79 		asyncpf_num++;
80 		install_pte(phys_to_virt(read_cr3()), 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
81 		phys = 0;
82 	} else {
83 		report_fail("unexpected async pf int token 0x%" PRIx32, apf_token);
84 	}
85 
86 	eoi();
87 }
88 
handle_pf(struct ex_regs * r)89 static void handle_pf(struct ex_regs *r)
90 {
91 	virt = (void*)((ulong)(buf+i) & ~(PAGE_SIZE-1));
92 	uint32_t reason = get_and_clear_apf_reason();
93 	switch (reason) {
94 	case 0:
95 		report_fail("unexpected #PF at %#lx", read_cr2());
96 		exit(report_summary());
97 	case KVM_PV_REASON_PAGE_NOT_PRESENT:
98 		phys = virt_to_pte_phys(phys_to_virt(read_cr3()), virt);
99 		install_pte(phys_to_virt(read_cr3()), 1, virt, phys, 0);
100 		write_cr3(read_cr3());
101 		saved_token = read_cr2();
102 		while (phys) {
103 			safe_halt(); /* enables irq */
104 		}
105 		break;
106 	default:
107 		report_fail("unexpected async pf with reason 0x%" PRIx32, reason);
108 		exit(report_summary());
109 	}
110 }
111 
112 #define MEM (1ull*1024*1024*1024)
113 
main(int ac,char ** av)114 int main(int ac, char **av)
115 {
116 	if (!this_cpu_has(KVM_FEATURE_ASYNC_PF)) {
117 		report_skip("KVM_FEATURE_ASYNC_PF is not supported\n");
118 		return report_summary();
119 	}
120 
121 	if (!this_cpu_has(KVM_FEATURE_ASYNC_PF_INT)) {
122 		report_skip("KVM_FEATURE_ASYNC_PF_INT is not supported\n");
123 		return report_summary();
124 	}
125 
126 	setup_vm();
127 
128 	handle_exception(PF_VECTOR, handle_pf);
129 	handle_irq(HYPERVISOR_CALLBACK_VECTOR, handle_interrupt);
130 	memset(&apf_reason, 0, sizeof(apf_reason));
131 
132 	wrmsr(MSR_KVM_ASYNC_PF_INT, HYPERVISOR_CALLBACK_VECTOR);
133 	wrmsr(MSR_KVM_ASYNC_PF_EN, virt_to_phys((void*)&apf_reason) |
134 			KVM_ASYNC_PF_SEND_ALWAYS | KVM_ASYNC_PF_ENABLED | KVM_ASYNC_PF_DELIVERY_AS_INT);
135 
136 	buf = malloc(MEM);
137 	sti();
138 
139 	/* access a lot of memory to make host swap it out */
140 	for (i = 0; i < MEM; i += 4096)
141 		buf[i] = 1;
142 
143 	cli();
144 	if (!asyncpf_num)
145 		report_skip("No async page fault events, cgroup configuration likely needed");
146 	else
147 		report_pass("Serviced %d async page faults events (!PRESENT #PF + READY IRQ)",
148 			    asyncpf_num);
149 	return report_summary();
150 }
151