aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/kvm
diff options
context:
space:
mode:
authorMarcelo Tosatti <marcelo@kvack.org>2007-12-07 07:56:58 -0500
committerAvi Kivity <avi@qumranet.com>2008-01-30 10:53:19 -0500
commitb3e4e63fd9ee8cd5d9047b89e0d463d5c48ee5b5 (patch)
treea7e1cd372a26b36d9fc4d6a7a12523e07f7a9f5e /drivers/kvm
parent80a8119ca3f021037b8513d39dbb0ffd1af86b20 (diff)
KVM: MMU: Use cmpxchg for pte updates on walk_addr()
In preparation for multi-threaded guest pte walking, use cmpxchg() when updating guest pte's. This guarantees that the assignment of the dirty bit can't be lost if two CPU's are faulting the same address simultaneously. [avi: fix kunmap_atomic() parameters] Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers/kvm')
-rw-r--r--drivers/kvm/paging_tmpl.h36
1 files changed, 34 insertions, 2 deletions
diff --git a/drivers/kvm/paging_tmpl.h b/drivers/kvm/paging_tmpl.h
index 3c40a598ceb8..8086f827120f 100644
--- a/drivers/kvm/paging_tmpl.h
+++ b/drivers/kvm/paging_tmpl.h
@@ -34,7 +34,9 @@
34 #define PT_LEVEL_BITS PT64_LEVEL_BITS 34 #define PT_LEVEL_BITS PT64_LEVEL_BITS
35 #ifdef CONFIG_X86_64 35 #ifdef CONFIG_X86_64
36 #define PT_MAX_FULL_LEVELS 4 36 #define PT_MAX_FULL_LEVELS 4
37 #define CMPXCHG cmpxchg
37 #else 38 #else
39 #define CMPXCHG cmpxchg64
38 #define PT_MAX_FULL_LEVELS 2 40 #define PT_MAX_FULL_LEVELS 2
39 #endif 41 #endif
40#elif PTTYPE == 32 42#elif PTTYPE == 32
@@ -48,6 +50,7 @@
48 #define PT_LEVEL_MASK(level) PT32_LEVEL_MASK(level) 50 #define PT_LEVEL_MASK(level) PT32_LEVEL_MASK(level)
49 #define PT_LEVEL_BITS PT32_LEVEL_BITS 51 #define PT_LEVEL_BITS PT32_LEVEL_BITS
50 #define PT_MAX_FULL_LEVELS 2 52 #define PT_MAX_FULL_LEVELS 2
53 #define CMPXCHG cmpxchg
51#else 54#else
52 #error Invalid PTTYPE value 55 #error Invalid PTTYPE value
53#endif 56#endif
@@ -78,6 +81,26 @@ static gfn_t gpte_to_gfn_pde(pt_element_t gpte)
78 return (gpte & PT_DIR_BASE_ADDR_MASK) >> PAGE_SHIFT; 81 return (gpte & PT_DIR_BASE_ADDR_MASK) >> PAGE_SHIFT;
79} 82}
80 83
84static bool FNAME(cmpxchg_gpte)(struct kvm *kvm,
85 gfn_t table_gfn, unsigned index,
86 pt_element_t orig_pte, pt_element_t new_pte)
87{
88 pt_element_t ret;
89 pt_element_t *table;
90 struct page *page;
91
92 page = gfn_to_page(kvm, table_gfn);
93 table = kmap_atomic(page, KM_USER0);
94
95 ret = CMPXCHG(&table[index], orig_pte, new_pte);
96
97 kunmap_atomic(table, KM_USER0);
98
99 kvm_release_page_dirty(page);
100
101 return (ret != orig_pte);
102}
103
81/* 104/*
82 * Fetch a guest pte for a guest virtual address 105 * Fetch a guest pte for a guest virtual address
83 */ 106 */
@@ -91,6 +114,7 @@ static int FNAME(walk_addr)(struct guest_walker *walker,
91 gpa_t pte_gpa; 114 gpa_t pte_gpa;
92 115
93 pgprintk("%s: addr %lx\n", __FUNCTION__, addr); 116 pgprintk("%s: addr %lx\n", __FUNCTION__, addr);
117walk:
94 walker->level = vcpu->mmu.root_level; 118 walker->level = vcpu->mmu.root_level;
95 pte = vcpu->cr3; 119 pte = vcpu->cr3;
96#if PTTYPE == 64 120#if PTTYPE == 64
@@ -135,8 +159,10 @@ static int FNAME(walk_addr)(struct guest_walker *walker,
135 159
136 if (!(pte & PT_ACCESSED_MASK)) { 160 if (!(pte & PT_ACCESSED_MASK)) {
137 mark_page_dirty(vcpu->kvm, table_gfn); 161 mark_page_dirty(vcpu->kvm, table_gfn);
162 if (FNAME(cmpxchg_gpte)(vcpu->kvm, table_gfn,
163 index, pte, pte|PT_ACCESSED_MASK))
164 goto walk;
138 pte |= PT_ACCESSED_MASK; 165 pte |= PT_ACCESSED_MASK;
139 kvm_write_guest(vcpu->kvm, pte_gpa, &pte, sizeof(pte));
140 } 166 }
141 167
142 if (walker->level == PT_PAGE_TABLE_LEVEL) { 168 if (walker->level == PT_PAGE_TABLE_LEVEL) {
@@ -159,9 +185,14 @@ static int FNAME(walk_addr)(struct guest_walker *walker,
159 } 185 }
160 186
161 if (write_fault && !is_dirty_pte(pte)) { 187 if (write_fault && !is_dirty_pte(pte)) {
188 bool ret;
189
162 mark_page_dirty(vcpu->kvm, table_gfn); 190 mark_page_dirty(vcpu->kvm, table_gfn);
191 ret = FNAME(cmpxchg_gpte)(vcpu->kvm, table_gfn, index, pte,
192 pte|PT_DIRTY_MASK);
193 if (ret)
194 goto walk;
163 pte |= PT_DIRTY_MASK; 195 pte |= PT_DIRTY_MASK;
164 kvm_write_guest(vcpu->kvm, pte_gpa, &pte, sizeof(pte));
165 kvm_mmu_pte_write(vcpu, pte_gpa, (u8 *)&pte, sizeof(pte)); 196 kvm_mmu_pte_write(vcpu, pte_gpa, (u8 *)&pte, sizeof(pte));
166 } 197 }
167 198
@@ -484,3 +515,4 @@ static void FNAME(prefetch_page)(struct kvm_vcpu *vcpu,
484#undef PT_MAX_FULL_LEVELS 515#undef PT_MAX_FULL_LEVELS
485#undef gpte_to_gfn 516#undef gpte_to_gfn
486#undef gpte_to_gfn_pde 517#undef gpte_to_gfn_pde
518#undef CMPXCHG