diff options
author | Avi Kivity <avi@qumranet.com> | 2007-06-07 12:18:30 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2007-07-16 05:05:46 -0400 |
commit | d9e368d61263055eceac2966bb7ea31b89da3425 (patch) | |
tree | 9d507b851ea7bd667cdd50dde640e47e0d4773e9 /drivers | |
parent | 39c3b86e5c193e09f69f0e99c93600a4999ffc60 (diff) |
KVM: Flush remote tlbs when reducing shadow pte permissions
When a vcpu causes a shadow tlb entry to have reduced permissions, it
must also clear the tlb on remote vcpus. We do that by:
- setting a bit on the vcpu that requests a tlb flush before the next entry
- if the vcpu is currently executing, we send an ipi to make sure it
exits before we continue
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/kvm/kvm.h | 8 | ||||
-rw-r--r-- | drivers/kvm/kvm_main.c | 44 | ||||
-rw-r--r-- | drivers/kvm/mmu.c | 8 | ||||
-rw-r--r-- | drivers/kvm/svm.c | 17 | ||||
-rw-r--r-- | drivers/kvm/vmx.c | 22 |
5 files changed, 84 insertions, 15 deletions
diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h index 528a56b1790e..b08272bce213 100644 --- a/drivers/kvm/kvm.h +++ b/drivers/kvm/kvm.h | |||
@@ -84,6 +84,11 @@ | |||
84 | #define KVM_PIO_PAGE_OFFSET 1 | 84 | #define KVM_PIO_PAGE_OFFSET 1 |
85 | 85 | ||
86 | /* | 86 | /* |
87 | * vcpu->requests bit members | ||
88 | */ | ||
89 | #define KVM_TLB_FLUSH 0 | ||
90 | |||
91 | /* | ||
87 | * Address types: | 92 | * Address types: |
88 | * | 93 | * |
89 | * gva - guest virtual address | 94 | * gva - guest virtual address |
@@ -272,6 +277,8 @@ struct kvm_vcpu { | |||
272 | u64 host_tsc; | 277 | u64 host_tsc; |
273 | struct kvm_run *run; | 278 | struct kvm_run *run; |
274 | int interrupt_window_open; | 279 | int interrupt_window_open; |
280 | int guest_mode; | ||
281 | unsigned long requests; | ||
275 | unsigned long irq_summary; /* bit vector: 1 per word in irq_pending */ | 282 | unsigned long irq_summary; /* bit vector: 1 per word in irq_pending */ |
276 | #define NR_IRQ_WORDS KVM_IRQ_BITMAP_SIZE(unsigned long) | 283 | #define NR_IRQ_WORDS KVM_IRQ_BITMAP_SIZE(unsigned long) |
277 | unsigned long irq_pending[NR_IRQ_WORDS]; | 284 | unsigned long irq_pending[NR_IRQ_WORDS]; |
@@ -530,6 +537,7 @@ void save_msrs(struct vmx_msr_entry *e, int n); | |||
530 | void kvm_resched(struct kvm_vcpu *vcpu); | 537 | void kvm_resched(struct kvm_vcpu *vcpu); |
531 | void kvm_load_guest_fpu(struct kvm_vcpu *vcpu); | 538 | void kvm_load_guest_fpu(struct kvm_vcpu *vcpu); |
532 | void kvm_put_guest_fpu(struct kvm_vcpu *vcpu); | 539 | void kvm_put_guest_fpu(struct kvm_vcpu *vcpu); |
540 | void kvm_flush_remote_tlbs(struct kvm *kvm); | ||
533 | 541 | ||
534 | int kvm_read_guest(struct kvm_vcpu *vcpu, | 542 | int kvm_read_guest(struct kvm_vcpu *vcpu, |
535 | gva_t addr, | 543 | gva_t addr, |
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c index 4e1a017f3db7..633c2eded08d 100644 --- a/drivers/kvm/kvm_main.c +++ b/drivers/kvm/kvm_main.c | |||
@@ -41,6 +41,8 @@ | |||
41 | #include <linux/fs.h> | 41 | #include <linux/fs.h> |
42 | #include <linux/mount.h> | 42 | #include <linux/mount.h> |
43 | #include <linux/sched.h> | 43 | #include <linux/sched.h> |
44 | #include <linux/cpumask.h> | ||
45 | #include <linux/smp.h> | ||
44 | 46 | ||
45 | #include "x86_emulate.h" | 47 | #include "x86_emulate.h" |
46 | #include "segment_descriptor.h" | 48 | #include "segment_descriptor.h" |
@@ -309,6 +311,48 @@ static void vcpu_put(struct kvm_vcpu *vcpu) | |||
309 | mutex_unlock(&vcpu->mutex); | 311 | mutex_unlock(&vcpu->mutex); |
310 | } | 312 | } |
311 | 313 | ||
314 | static void ack_flush(void *_completed) | ||
315 | { | ||
316 | atomic_t *completed = _completed; | ||
317 | |||
318 | atomic_inc(completed); | ||
319 | } | ||
320 | |||
321 | void kvm_flush_remote_tlbs(struct kvm *kvm) | ||
322 | { | ||
323 | int i, cpu, needed; | ||
324 | cpumask_t cpus; | ||
325 | struct kvm_vcpu *vcpu; | ||
326 | atomic_t completed; | ||
327 | |||
328 | atomic_set(&completed, 0); | ||
329 | cpus_clear(cpus); | ||
330 | needed = 0; | ||
331 | for (i = 0; i < kvm->nvcpus; ++i) { | ||
332 | vcpu = &kvm->vcpus[i]; | ||
333 | if (test_and_set_bit(KVM_TLB_FLUSH, &vcpu->requests)) | ||
334 | continue; | ||
335 | cpu = vcpu->cpu; | ||
336 | if (cpu != -1 && cpu != raw_smp_processor_id()) | ||
337 | if (!cpu_isset(cpu, cpus)) { | ||
338 | cpu_set(cpu, cpus); | ||
339 | ++needed; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | /* | ||
344 | * We really want smp_call_function_mask() here. But that's not | ||
345 | * available, so ipi all cpus in parallel and wait for them | ||
346 | * to complete. | ||
347 | */ | ||
348 | for (cpu = first_cpu(cpus); cpu != NR_CPUS; cpu = next_cpu(cpu, cpus)) | ||
349 | smp_call_function_single(cpu, ack_flush, &completed, 1, 0); | ||
350 | while (atomic_read(&completed) != needed) { | ||
351 | cpu_relax(); | ||
352 | barrier(); | ||
353 | } | ||
354 | } | ||
355 | |||
312 | static struct kvm *kvm_create_vm(void) | 356 | static struct kvm *kvm_create_vm(void) |
313 | { | 357 | { |
314 | struct kvm *kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL); | 358 | struct kvm *kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL); |
diff --git a/drivers/kvm/mmu.c b/drivers/kvm/mmu.c index d4de988d1828..ad50cfda5ac1 100644 --- a/drivers/kvm/mmu.c +++ b/drivers/kvm/mmu.c | |||
@@ -441,7 +441,7 @@ static void rmap_write_protect(struct kvm_vcpu *vcpu, u64 gfn) | |||
441 | BUG_ON(!(*spte & PT_WRITABLE_MASK)); | 441 | BUG_ON(!(*spte & PT_WRITABLE_MASK)); |
442 | rmap_printk("rmap_write_protect: spte %p %llx\n", spte, *spte); | 442 | rmap_printk("rmap_write_protect: spte %p %llx\n", spte, *spte); |
443 | rmap_remove(vcpu, spte); | 443 | rmap_remove(vcpu, spte); |
444 | kvm_arch_ops->tlb_flush(vcpu); | 444 | kvm_flush_remote_tlbs(vcpu->kvm); |
445 | set_shadow_pte(spte, *spte & ~PT_WRITABLE_MASK); | 445 | set_shadow_pte(spte, *spte & ~PT_WRITABLE_MASK); |
446 | } | 446 | } |
447 | } | 447 | } |
@@ -656,7 +656,7 @@ static void kvm_mmu_page_unlink_children(struct kvm_vcpu *vcpu, | |||
656 | rmap_remove(vcpu, &pt[i]); | 656 | rmap_remove(vcpu, &pt[i]); |
657 | pt[i] = 0; | 657 | pt[i] = 0; |
658 | } | 658 | } |
659 | kvm_arch_ops->tlb_flush(vcpu); | 659 | kvm_flush_remote_tlbs(vcpu->kvm); |
660 | return; | 660 | return; |
661 | } | 661 | } |
662 | 662 | ||
@@ -669,6 +669,7 @@ static void kvm_mmu_page_unlink_children(struct kvm_vcpu *vcpu, | |||
669 | ent &= PT64_BASE_ADDR_MASK; | 669 | ent &= PT64_BASE_ADDR_MASK; |
670 | mmu_page_remove_parent_pte(vcpu, page_header(ent), &pt[i]); | 670 | mmu_page_remove_parent_pte(vcpu, page_header(ent), &pt[i]); |
671 | } | 671 | } |
672 | kvm_flush_remote_tlbs(vcpu->kvm); | ||
672 | } | 673 | } |
673 | 674 | ||
674 | static void kvm_mmu_put_page(struct kvm_vcpu *vcpu, | 675 | static void kvm_mmu_put_page(struct kvm_vcpu *vcpu, |
@@ -1093,6 +1094,7 @@ static void mmu_pte_write_zap_pte(struct kvm_vcpu *vcpu, | |||
1093 | } | 1094 | } |
1094 | } | 1095 | } |
1095 | *spte = 0; | 1096 | *spte = 0; |
1097 | kvm_flush_remote_tlbs(vcpu->kvm); | ||
1096 | } | 1098 | } |
1097 | 1099 | ||
1098 | static void mmu_pte_write_new_pte(struct kvm_vcpu *vcpu, | 1100 | static void mmu_pte_write_new_pte(struct kvm_vcpu *vcpu, |
@@ -1308,7 +1310,7 @@ void kvm_mmu_zap_all(struct kvm_vcpu *vcpu) | |||
1308 | } | 1310 | } |
1309 | 1311 | ||
1310 | mmu_free_memory_caches(vcpu); | 1312 | mmu_free_memory_caches(vcpu); |
1311 | kvm_arch_ops->tlb_flush(vcpu); | 1313 | kvm_flush_remote_tlbs(vcpu->kvm); |
1312 | init_kvm_mmu(vcpu); | 1314 | init_kvm_mmu(vcpu); |
1313 | } | 1315 | } |
1314 | 1316 | ||
diff --git a/drivers/kvm/svm.c b/drivers/kvm/svm.c index 70f386e04cbe..eb175c5cd499 100644 --- a/drivers/kvm/svm.c +++ b/drivers/kvm/svm.c | |||
@@ -1470,6 +1470,11 @@ static void load_db_regs(unsigned long *db_regs) | |||
1470 | asm volatile ("mov %0, %%dr3" : : "r"(db_regs[3])); | 1470 | asm volatile ("mov %0, %%dr3" : : "r"(db_regs[3])); |
1471 | } | 1471 | } |
1472 | 1472 | ||
1473 | static void svm_flush_tlb(struct kvm_vcpu *vcpu) | ||
1474 | { | ||
1475 | force_new_asid(vcpu); | ||
1476 | } | ||
1477 | |||
1473 | static int svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | 1478 | static int svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) |
1474 | { | 1479 | { |
1475 | u16 fs_selector; | 1480 | u16 fs_selector; |
@@ -1487,6 +1492,11 @@ again: | |||
1487 | 1492 | ||
1488 | clgi(); | 1493 | clgi(); |
1489 | 1494 | ||
1495 | vcpu->guest_mode = 1; | ||
1496 | if (vcpu->requests) | ||
1497 | if (test_and_clear_bit(KVM_TLB_FLUSH, &vcpu->requests)) | ||
1498 | svm_flush_tlb(vcpu); | ||
1499 | |||
1490 | pre_svm_run(vcpu); | 1500 | pre_svm_run(vcpu); |
1491 | 1501 | ||
1492 | save_host_msrs(vcpu); | 1502 | save_host_msrs(vcpu); |
@@ -1618,6 +1628,8 @@ again: | |||
1618 | #endif | 1628 | #endif |
1619 | : "cc", "memory" ); | 1629 | : "cc", "memory" ); |
1620 | 1630 | ||
1631 | vcpu->guest_mode = 0; | ||
1632 | |||
1621 | if (vcpu->fpu_active) { | 1633 | if (vcpu->fpu_active) { |
1622 | fx_save(vcpu->guest_fx_image); | 1634 | fx_save(vcpu->guest_fx_image); |
1623 | fx_restore(vcpu->host_fx_image); | 1635 | fx_restore(vcpu->host_fx_image); |
@@ -1682,11 +1694,6 @@ again: | |||
1682 | return r; | 1694 | return r; |
1683 | } | 1695 | } |
1684 | 1696 | ||
1685 | static void svm_flush_tlb(struct kvm_vcpu *vcpu) | ||
1686 | { | ||
1687 | force_new_asid(vcpu); | ||
1688 | } | ||
1689 | |||
1690 | static void svm_set_cr3(struct kvm_vcpu *vcpu, unsigned long root) | 1697 | static void svm_set_cr3(struct kvm_vcpu *vcpu, unsigned long root) |
1691 | { | 1698 | { |
1692 | vcpu->svm->vmcb->save.cr3 = root; | 1699 | vcpu->svm->vmcb->save.cr3 = root; |
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c index a1f51b9d482d..b969db1e0830 100644 --- a/drivers/kvm/vmx.c +++ b/drivers/kvm/vmx.c | |||
@@ -1972,6 +1972,11 @@ static int dm_request_for_irq_injection(struct kvm_vcpu *vcpu, | |||
1972 | (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF)); | 1972 | (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF)); |
1973 | } | 1973 | } |
1974 | 1974 | ||
1975 | static void vmx_flush_tlb(struct kvm_vcpu *vcpu) | ||
1976 | { | ||
1977 | vmcs_writel(GUEST_CR3, vmcs_readl(GUEST_CR3)); | ||
1978 | } | ||
1979 | |||
1975 | static int vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | 1980 | static int vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) |
1976 | { | 1981 | { |
1977 | u8 fail; | 1982 | u8 fail; |
@@ -1997,9 +2002,15 @@ again: | |||
1997 | */ | 2002 | */ |
1998 | vmcs_writel(HOST_CR0, read_cr0()); | 2003 | vmcs_writel(HOST_CR0, read_cr0()); |
1999 | 2004 | ||
2005 | local_irq_disable(); | ||
2006 | |||
2007 | vcpu->guest_mode = 1; | ||
2008 | if (vcpu->requests) | ||
2009 | if (test_and_clear_bit(KVM_TLB_FLUSH, &vcpu->requests)) | ||
2010 | vmx_flush_tlb(vcpu); | ||
2011 | |||
2000 | asm ( | 2012 | asm ( |
2001 | /* Store host registers */ | 2013 | /* Store host registers */ |
2002 | "pushf \n\t" | ||
2003 | #ifdef CONFIG_X86_64 | 2014 | #ifdef CONFIG_X86_64 |
2004 | "push %%rax; push %%rbx; push %%rdx;" | 2015 | "push %%rax; push %%rbx; push %%rdx;" |
2005 | "push %%rsi; push %%rdi; push %%rbp;" | 2016 | "push %%rsi; push %%rdi; push %%rbp;" |
@@ -2091,7 +2102,6 @@ again: | |||
2091 | "pop %%ecx; popa \n\t" | 2102 | "pop %%ecx; popa \n\t" |
2092 | #endif | 2103 | #endif |
2093 | "setbe %0 \n\t" | 2104 | "setbe %0 \n\t" |
2094 | "popf \n\t" | ||
2095 | : "=q" (fail) | 2105 | : "=q" (fail) |
2096 | : "r"(vcpu->launched), "d"((unsigned long)HOST_RSP), | 2106 | : "r"(vcpu->launched), "d"((unsigned long)HOST_RSP), |
2097 | "c"(vcpu), | 2107 | "c"(vcpu), |
@@ -2115,6 +2125,9 @@ again: | |||
2115 | [cr2]"i"(offsetof(struct kvm_vcpu, cr2)) | 2125 | [cr2]"i"(offsetof(struct kvm_vcpu, cr2)) |
2116 | : "cc", "memory" ); | 2126 | : "cc", "memory" ); |
2117 | 2127 | ||
2128 | vcpu->guest_mode = 0; | ||
2129 | local_irq_enable(); | ||
2130 | |||
2118 | ++vcpu->stat.exits; | 2131 | ++vcpu->stat.exits; |
2119 | 2132 | ||
2120 | vcpu->interrupt_window_open = (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0; | 2133 | vcpu->interrupt_window_open = (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0; |
@@ -2167,11 +2180,6 @@ out: | |||
2167 | return r; | 2180 | return r; |
2168 | } | 2181 | } |
2169 | 2182 | ||
2170 | static void vmx_flush_tlb(struct kvm_vcpu *vcpu) | ||
2171 | { | ||
2172 | vmcs_writel(GUEST_CR3, vmcs_readl(GUEST_CR3)); | ||
2173 | } | ||
2174 | |||
2175 | static void vmx_inject_page_fault(struct kvm_vcpu *vcpu, | 2183 | static void vmx_inject_page_fault(struct kvm_vcpu *vcpu, |
2176 | unsigned long addr, | 2184 | unsigned long addr, |
2177 | u32 err_code) | 2185 | u32 err_code) |