aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/kvm.c
diff options
context:
space:
mode:
authorMarcelo Tosatti <mtosatti@redhat.com>2008-02-22 12:21:37 -0500
committerAvi Kivity <avi@qumranet.com>2008-04-27 05:00:28 -0400
commit1da8a77bdc294acdc37e8504926383b86f72d6be (patch)
treed4a65e134a8d087ef23ba6ece6788bf15e1740b6 /arch/x86/kernel/kvm.c
parent2f333bcb4edd8daef99dabe4e7df8277af73cff1 (diff)
x86: KVM guest: hypercall based pte updates and TLB flushes
Hypercall based pte updates are faster than faults, and also allow use of the lazy MMU mode to batch operations. Don't report the feature if two dimensional paging is enabled. [avi: - guest/host split - fix 32-bit truncation issues - adjust to mmu_op - adjust to ->release_*() renamed - add ->release_pud()] Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'arch/x86/kernel/kvm.c')
-rw-r--r--arch/x86/kernel/kvm.c138
1 files changed, 138 insertions, 0 deletions
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index a8e36da60120..cbadc730496a 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -25,6 +25,7 @@
25#include <linux/kvm_para.h> 25#include <linux/kvm_para.h>
26#include <linux/cpu.h> 26#include <linux/cpu.h>
27#include <linux/mm.h> 27#include <linux/mm.h>
28#include <linux/highmem.h>
28 29
29/* 30/*
30 * No need for any "IO delay" on KVM 31 * No need for any "IO delay" on KVM
@@ -33,6 +34,122 @@ static void kvm_io_delay(void)
33{ 34{
34} 35}
35 36
37static void kvm_mmu_op(void *buffer, unsigned len)
38{
39 int r;
40 unsigned long a1, a2;
41
42 do {
43 a1 = __pa(buffer);
44 a2 = 0; /* on i386 __pa() always returns <4G */
45 r = kvm_hypercall3(KVM_HC_MMU_OP, len, a1, a2);
46 buffer += r;
47 len -= r;
48 } while (len);
49}
50
51static void kvm_mmu_write(void *dest, u64 val)
52{
53 __u64 pte_phys;
54 struct kvm_mmu_op_write_pte wpte;
55
56#ifdef CONFIG_HIGHPTE
57 struct page *page;
58 unsigned long dst = (unsigned long) dest;
59
60 page = kmap_atomic_to_page(dest);
61 pte_phys = page_to_pfn(page);
62 pte_phys <<= PAGE_SHIFT;
63 pte_phys += (dst & ~(PAGE_MASK));
64#else
65 pte_phys = (unsigned long)__pa(dest);
66#endif
67 wpte.header.op = KVM_MMU_OP_WRITE_PTE;
68 wpte.pte_val = val;
69 wpte.pte_phys = pte_phys;
70
71 kvm_mmu_op(&wpte, sizeof wpte);
72}
73
74/*
75 * We only need to hook operations that are MMU writes. We hook these so that
76 * we can use lazy MMU mode to batch these operations. We could probably
77 * improve the performance of the host code if we used some of the information
78 * here to simplify processing of batched writes.
79 */
80static void kvm_set_pte(pte_t *ptep, pte_t pte)
81{
82 kvm_mmu_write(ptep, pte_val(pte));
83}
84
85static void kvm_set_pte_at(struct mm_struct *mm, unsigned long addr,
86 pte_t *ptep, pte_t pte)
87{
88 kvm_mmu_write(ptep, pte_val(pte));
89}
90
91static void kvm_set_pmd(pmd_t *pmdp, pmd_t pmd)
92{
93 kvm_mmu_write(pmdp, pmd_val(pmd));
94}
95
96#if PAGETABLE_LEVELS >= 3
97#ifdef CONFIG_X86_PAE
98static void kvm_set_pte_atomic(pte_t *ptep, pte_t pte)
99{
100 kvm_mmu_write(ptep, pte_val(pte));
101}
102
103static void kvm_set_pte_present(struct mm_struct *mm, unsigned long addr,
104 pte_t *ptep, pte_t pte)
105{
106 kvm_mmu_write(ptep, pte_val(pte));
107}
108
109static void kvm_pte_clear(struct mm_struct *mm,
110 unsigned long addr, pte_t *ptep)
111{
112 kvm_mmu_write(ptep, 0);
113}
114
115static void kvm_pmd_clear(pmd_t *pmdp)
116{
117 kvm_mmu_write(pmdp, 0);
118}
119#endif
120
121static void kvm_set_pud(pud_t *pudp, pud_t pud)
122{
123 kvm_mmu_write(pudp, pud_val(pud));
124}
125
126#if PAGETABLE_LEVELS == 4
127static void kvm_set_pgd(pgd_t *pgdp, pgd_t pgd)
128{
129 kvm_mmu_write(pgdp, pgd_val(pgd));
130}
131#endif
132#endif /* PAGETABLE_LEVELS >= 3 */
133
134static void kvm_flush_tlb(void)
135{
136 struct kvm_mmu_op_flush_tlb ftlb = {
137 .header.op = KVM_MMU_OP_FLUSH_TLB,
138 };
139
140 kvm_mmu_op(&ftlb, sizeof ftlb);
141}
142
143static void kvm_release_pt(u32 pfn)
144{
145 struct kvm_mmu_op_release_pt rpt = {
146 .header.op = KVM_MMU_OP_RELEASE_PT,
147 .pt_phys = (u64)pfn << PAGE_SHIFT,
148 };
149
150 kvm_mmu_op(&rpt, sizeof rpt);
151}
152
36static void paravirt_ops_setup(void) 153static void paravirt_ops_setup(void)
37{ 154{
38 pv_info.name = "KVM"; 155 pv_info.name = "KVM";
@@ -41,6 +158,27 @@ static void paravirt_ops_setup(void)
41 if (kvm_para_has_feature(KVM_FEATURE_NOP_IO_DELAY)) 158 if (kvm_para_has_feature(KVM_FEATURE_NOP_IO_DELAY))
42 pv_cpu_ops.io_delay = kvm_io_delay; 159 pv_cpu_ops.io_delay = kvm_io_delay;
43 160
161 if (kvm_para_has_feature(KVM_FEATURE_MMU_OP)) {
162 pv_mmu_ops.set_pte = kvm_set_pte;
163 pv_mmu_ops.set_pte_at = kvm_set_pte_at;
164 pv_mmu_ops.set_pmd = kvm_set_pmd;
165#if PAGETABLE_LEVELS >= 3
166#ifdef CONFIG_X86_PAE
167 pv_mmu_ops.set_pte_atomic = kvm_set_pte_atomic;
168 pv_mmu_ops.set_pte_present = kvm_set_pte_present;
169 pv_mmu_ops.pte_clear = kvm_pte_clear;
170 pv_mmu_ops.pmd_clear = kvm_pmd_clear;
171#endif
172 pv_mmu_ops.set_pud = kvm_set_pud;
173#if PAGETABLE_LEVELS == 4
174 pv_mmu_ops.set_pgd = kvm_set_pgd;
175#endif
176#endif
177 pv_mmu_ops.flush_tlb_user = kvm_flush_tlb;
178 pv_mmu_ops.release_pte = kvm_release_pt;
179 pv_mmu_ops.release_pmd = kvm_release_pt;
180 pv_mmu_ops.release_pud = kvm_release_pt;
181 }
44} 182}
45 183
46void __init kvm_guest_init(void) 184void __init kvm_guest_init(void)