aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAvi Kivity <avi@qumranet.com>2008-05-15 06:51:35 -0400
committerAvi Kivity <avi@qumranet.com>2008-07-20 05:40:50 -0400
commit1b7fcd3263e5f12dba43d27b64e1578bec070c28 (patch)
tree7f8d9679404f0b46803c6b1cf926235d170606ee
parent7682f2d0dd3ff5bd2756eac018a5b4e7e30ef16c (diff)
KVM: MMU: Fix false flooding when a pte points to page table
The KVM MMU tries to detect when a speculative pte update is not actually used by demand fault, by checking the accessed bit of the shadow pte. If the shadow pte has not been accessed, we deem that page table flooded and remove the shadow page table, allowing further pte updates to proceed without emulation. However, if the pte itself points at a page table and only used for write operations, the accessed bit will never be set since all access will happen through the emulator. This is exactly what happens with kscand on old (2.4.x) HIGHMEM kernels. The kernel points a kmap_atomic() pte at a page table, and then proceeds with read-modify-write operations to look at the dirty and accessed bits. We get a false flood trigger on the kmap ptes, which results in the mmu spending all its time setting up and tearing down shadows. Fix by setting the shadow accessed bit on emulated accesses. Signed-off-by: Avi Kivity <avi@qumranet.com>
-rw-r--r--arch/x86/kvm/mmu.c17
-rw-r--r--arch/x86/kvm/mmu.h3
-rw-r--r--include/asm-x86/kvm_host.h1
3 files changed, 19 insertions, 2 deletions
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 8e449dbcc596..53f1ed852ca2 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -1122,8 +1122,10 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
1122 else 1122 else
1123 kvm_release_pfn_clean(pfn); 1123 kvm_release_pfn_clean(pfn);
1124 } 1124 }
1125 if (!ptwrite || !*ptwrite) 1125 if (speculative) {
1126 vcpu->arch.last_pte_updated = shadow_pte; 1126 vcpu->arch.last_pte_updated = shadow_pte;
1127 vcpu->arch.last_pte_gfn = gfn;
1128 }
1127} 1129}
1128 1130
1129static void nonpaging_new_cr3(struct kvm_vcpu *vcpu) 1131static void nonpaging_new_cr3(struct kvm_vcpu *vcpu)
@@ -1671,6 +1673,18 @@ static void mmu_guess_page_from_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
1671 vcpu->arch.update_pte.pfn = pfn; 1673 vcpu->arch.update_pte.pfn = pfn;
1672} 1674}
1673 1675
1676static void kvm_mmu_access_page(struct kvm_vcpu *vcpu, gfn_t gfn)
1677{
1678 u64 *spte = vcpu->arch.last_pte_updated;
1679
1680 if (spte
1681 && vcpu->arch.last_pte_gfn == gfn
1682 && shadow_accessed_mask
1683 && !(*spte & shadow_accessed_mask)
1684 && is_shadow_present_pte(*spte))
1685 set_bit(PT_ACCESSED_SHIFT, (unsigned long *)spte);
1686}
1687
1674void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, 1688void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
1675 const u8 *new, int bytes) 1689 const u8 *new, int bytes)
1676{ 1690{
@@ -1694,6 +1708,7 @@ void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
1694 pgprintk("%s: gpa %llx bytes %d\n", __func__, gpa, bytes); 1708 pgprintk("%s: gpa %llx bytes %d\n", __func__, gpa, bytes);
1695 mmu_guess_page_from_pte_write(vcpu, gpa, new, bytes); 1709 mmu_guess_page_from_pte_write(vcpu, gpa, new, bytes);
1696 spin_lock(&vcpu->kvm->mmu_lock); 1710 spin_lock(&vcpu->kvm->mmu_lock);
1711 kvm_mmu_access_page(vcpu, gfn);
1697 kvm_mmu_free_some_pages(vcpu); 1712 kvm_mmu_free_some_pages(vcpu);
1698 ++vcpu->kvm->stat.mmu_pte_write; 1713 ++vcpu->kvm->stat.mmu_pte_write;
1699 kvm_mmu_audit(vcpu, "pre pte write"); 1714 kvm_mmu_audit(vcpu, "pre pte write");
diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h
index 1730757bbc7a..258e5d56298e 100644
--- a/arch/x86/kvm/mmu.h
+++ b/arch/x86/kvm/mmu.h
@@ -15,7 +15,8 @@
15#define PT_USER_MASK (1ULL << 2) 15#define PT_USER_MASK (1ULL << 2)
16#define PT_PWT_MASK (1ULL << 3) 16#define PT_PWT_MASK (1ULL << 3)
17#define PT_PCD_MASK (1ULL << 4) 17#define PT_PCD_MASK (1ULL << 4)
18#define PT_ACCESSED_MASK (1ULL << 5) 18#define PT_ACCESSED_SHIFT 5
19#define PT_ACCESSED_MASK (1ULL << PT_ACCESSED_SHIFT)
19#define PT_DIRTY_MASK (1ULL << 6) 20#define PT_DIRTY_MASK (1ULL << 6)
20#define PT_PAGE_SIZE_MASK (1ULL << 7) 21#define PT_PAGE_SIZE_MASK (1ULL << 7)
21#define PT_PAT_MASK (1ULL << 7) 22#define PT_PAT_MASK (1ULL << 7)
diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h
index 844f2a89afbc..c2d066e185f4 100644
--- a/include/asm-x86/kvm_host.h
+++ b/include/asm-x86/kvm_host.h
@@ -243,6 +243,7 @@ struct kvm_vcpu_arch {
243 gfn_t last_pt_write_gfn; 243 gfn_t last_pt_write_gfn;
244 int last_pt_write_count; 244 int last_pt_write_count;
245 u64 *last_pte_updated; 245 u64 *last_pte_updated;
246 gfn_t last_pte_gfn;
246 247
247 struct { 248 struct {
248 gfn_t gfn; /* presumed gfn during guest pte update */ 249 gfn_t gfn; /* presumed gfn during guest pte update */