diff options
author | Sheng Yang <sheng@linux.intel.com> | 2009-04-27 08:35:43 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2009-06-10 04:48:50 -0400 |
commit | 522c68c4416de3cd3e11a9ff10d58e776a69ae1e (patch) | |
tree | 62940e35988f5e2a52df10276882ec64518ee369 | |
parent | 4b12f0de33a64dfc624b2480f55b674f7fa23ef2 (diff) |
KVM: Enable snooping control for supported hardware
Memory aliases with different memory type is a problem for guest. For the guest
without assigned device, the memory type of guest memory would always been the
same as host(WB); but for the assigned device, some part of memory may be used
as DMA and then set to uncacheable memory type(UC/WC), which would be a conflict of
host memory type then be a potential issue.
Snooping control can guarantee the cache correctness of memory go through the
DMA engine of VT-d.
[avi: fix build on ia64]
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r-- | arch/ia64/include/asm/kvm_host.h | 1 | ||||
-rw-r--r-- | arch/x86/include/asm/kvm_host.h | 1 | ||||
-rw-r--r-- | arch/x86/kvm/vmx.c | 19 | ||||
-rw-r--r-- | include/linux/kvm_host.h | 3 | ||||
-rw-r--r-- | virt/kvm/iommu.c | 27 |
5 files changed, 46 insertions, 5 deletions
diff --git a/arch/ia64/include/asm/kvm_host.h b/arch/ia64/include/asm/kvm_host.h index 589536fa799d..5f43697aed30 100644 --- a/arch/ia64/include/asm/kvm_host.h +++ b/arch/ia64/include/asm/kvm_host.h | |||
@@ -474,6 +474,7 @@ struct kvm_arch { | |||
474 | 474 | ||
475 | struct list_head assigned_dev_head; | 475 | struct list_head assigned_dev_head; |
476 | struct iommu_domain *iommu_domain; | 476 | struct iommu_domain *iommu_domain; |
477 | int iommu_flags; | ||
477 | struct hlist_head irq_ack_notifier_list; | 478 | struct hlist_head irq_ack_notifier_list; |
478 | 479 | ||
479 | unsigned long irq_sources_bitmap; | 480 | unsigned long irq_sources_bitmap; |
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 8a6f6b643dfe..253d8f669cf6 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
@@ -393,6 +393,7 @@ struct kvm_arch{ | |||
393 | struct list_head active_mmu_pages; | 393 | struct list_head active_mmu_pages; |
394 | struct list_head assigned_dev_head; | 394 | struct list_head assigned_dev_head; |
395 | struct iommu_domain *iommu_domain; | 395 | struct iommu_domain *iommu_domain; |
396 | int iommu_flags; | ||
396 | struct kvm_pic *vpic; | 397 | struct kvm_pic *vpic; |
397 | struct kvm_ioapic *vioapic; | 398 | struct kvm_ioapic *vioapic; |
398 | struct kvm_pit *vpit; | 399 | struct kvm_pit *vpit; |
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 59b080c262e8..e8a5649f9c15 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c | |||
@@ -3581,11 +3581,26 @@ static u64 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) | |||
3581 | { | 3581 | { |
3582 | u64 ret; | 3582 | u64 ret; |
3583 | 3583 | ||
3584 | /* For VT-d and EPT combination | ||
3585 | * 1. MMIO: always map as UC | ||
3586 | * 2. EPT with VT-d: | ||
3587 | * a. VT-d without snooping control feature: can't guarantee the | ||
3588 | * result, try to trust guest. | ||
3589 | * b. VT-d with snooping control feature: snooping control feature of | ||
3590 | * VT-d engine can guarantee the cache correctness. Just set it | ||
3591 | * to WB to keep consistent with host. So the same as item 3. | ||
3592 | * 3. EPT without VT-d: always map as WB and set IGMT=1 to keep | ||
3593 | * consistent with host MTRR | ||
3594 | */ | ||
3584 | if (is_mmio) | 3595 | if (is_mmio) |
3585 | ret = MTRR_TYPE_UNCACHABLE << VMX_EPT_MT_EPTE_SHIFT; | 3596 | ret = MTRR_TYPE_UNCACHABLE << VMX_EPT_MT_EPTE_SHIFT; |
3597 | else if (vcpu->kvm->arch.iommu_domain && | ||
3598 | !(vcpu->kvm->arch.iommu_flags & KVM_IOMMU_CACHE_COHERENCY)) | ||
3599 | ret = kvm_get_guest_memory_type(vcpu, gfn) << | ||
3600 | VMX_EPT_MT_EPTE_SHIFT; | ||
3586 | else | 3601 | else |
3587 | ret = (kvm_get_guest_memory_type(vcpu, gfn) << | 3602 | ret = (MTRR_TYPE_WRBACK << VMX_EPT_MT_EPTE_SHIFT) |
3588 | VMX_EPT_MT_EPTE_SHIFT) | VMX_EPT_IGMT_BIT; | 3603 | | VMX_EPT_IGMT_BIT; |
3589 | 3604 | ||
3590 | return ret; | 3605 | return ret; |
3591 | } | 3606 | } |
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 72d56844f388..bdce8e1303c9 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h | |||
@@ -367,6 +367,9 @@ void kvm_unregister_irq_ack_notifier(struct kvm_irq_ack_notifier *kian); | |||
367 | int kvm_request_irq_source_id(struct kvm *kvm); | 367 | int kvm_request_irq_source_id(struct kvm *kvm); |
368 | void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); | 368 | void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); |
369 | 369 | ||
370 | /* For vcpu->arch.iommu_flags */ | ||
371 | #define KVM_IOMMU_CACHE_COHERENCY 0x1 | ||
372 | |||
370 | #ifdef CONFIG_IOMMU_API | 373 | #ifdef CONFIG_IOMMU_API |
371 | int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn, | 374 | int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn, |
372 | unsigned long npages); | 375 | unsigned long npages); |
diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c index 4c4037503600..15147583abd1 100644 --- a/virt/kvm/iommu.c +++ b/virt/kvm/iommu.c | |||
@@ -39,11 +39,16 @@ int kvm_iommu_map_pages(struct kvm *kvm, | |||
39 | pfn_t pfn; | 39 | pfn_t pfn; |
40 | int i, r = 0; | 40 | int i, r = 0; |
41 | struct iommu_domain *domain = kvm->arch.iommu_domain; | 41 | struct iommu_domain *domain = kvm->arch.iommu_domain; |
42 | int flags; | ||
42 | 43 | ||
43 | /* check if iommu exists and in use */ | 44 | /* check if iommu exists and in use */ |
44 | if (!domain) | 45 | if (!domain) |
45 | return 0; | 46 | return 0; |
46 | 47 | ||
48 | flags = IOMMU_READ | IOMMU_WRITE; | ||
49 | if (kvm->arch.iommu_flags & KVM_IOMMU_CACHE_COHERENCY) | ||
50 | flags |= IOMMU_CACHE; | ||
51 | |||
47 | for (i = 0; i < npages; i++) { | 52 | for (i = 0; i < npages; i++) { |
48 | /* check if already mapped */ | 53 | /* check if already mapped */ |
49 | if (iommu_iova_to_phys(domain, gfn_to_gpa(gfn))) | 54 | if (iommu_iova_to_phys(domain, gfn_to_gpa(gfn))) |
@@ -53,8 +58,7 @@ int kvm_iommu_map_pages(struct kvm *kvm, | |||
53 | r = iommu_map_range(domain, | 58 | r = iommu_map_range(domain, |
54 | gfn_to_gpa(gfn), | 59 | gfn_to_gpa(gfn), |
55 | pfn_to_hpa(pfn), | 60 | pfn_to_hpa(pfn), |
56 | PAGE_SIZE, | 61 | PAGE_SIZE, flags); |
57 | IOMMU_READ | IOMMU_WRITE); | ||
58 | if (r) { | 62 | if (r) { |
59 | printk(KERN_ERR "kvm_iommu_map_address:" | 63 | printk(KERN_ERR "kvm_iommu_map_address:" |
60 | "iommu failed to map pfn=%lx\n", pfn); | 64 | "iommu failed to map pfn=%lx\n", pfn); |
@@ -88,7 +92,7 @@ int kvm_assign_device(struct kvm *kvm, | |||
88 | { | 92 | { |
89 | struct pci_dev *pdev = NULL; | 93 | struct pci_dev *pdev = NULL; |
90 | struct iommu_domain *domain = kvm->arch.iommu_domain; | 94 | struct iommu_domain *domain = kvm->arch.iommu_domain; |
91 | int r; | 95 | int r, last_flags; |
92 | 96 | ||
93 | /* check if iommu exists and in use */ | 97 | /* check if iommu exists and in use */ |
94 | if (!domain) | 98 | if (!domain) |
@@ -107,12 +111,29 @@ int kvm_assign_device(struct kvm *kvm, | |||
107 | return r; | 111 | return r; |
108 | } | 112 | } |
109 | 113 | ||
114 | last_flags = kvm->arch.iommu_flags; | ||
115 | if (iommu_domain_has_cap(kvm->arch.iommu_domain, | ||
116 | IOMMU_CAP_CACHE_COHERENCY)) | ||
117 | kvm->arch.iommu_flags |= KVM_IOMMU_CACHE_COHERENCY; | ||
118 | |||
119 | /* Check if need to update IOMMU page table for guest memory */ | ||
120 | if ((last_flags ^ kvm->arch.iommu_flags) == | ||
121 | KVM_IOMMU_CACHE_COHERENCY) { | ||
122 | kvm_iommu_unmap_memslots(kvm); | ||
123 | r = kvm_iommu_map_memslots(kvm); | ||
124 | if (r) | ||
125 | goto out_unmap; | ||
126 | } | ||
127 | |||
110 | printk(KERN_DEBUG "assign device: host bdf = %x:%x:%x\n", | 128 | printk(KERN_DEBUG "assign device: host bdf = %x:%x:%x\n", |
111 | assigned_dev->host_busnr, | 129 | assigned_dev->host_busnr, |
112 | PCI_SLOT(assigned_dev->host_devfn), | 130 | PCI_SLOT(assigned_dev->host_devfn), |
113 | PCI_FUNC(assigned_dev->host_devfn)); | 131 | PCI_FUNC(assigned_dev->host_devfn)); |
114 | 132 | ||
115 | return 0; | 133 | return 0; |
134 | out_unmap: | ||
135 | kvm_iommu_unmap_memslots(kvm); | ||
136 | return r; | ||
116 | } | 137 | } |
117 | 138 | ||
118 | int kvm_deassign_device(struct kvm *kvm, | 139 | int kvm_deassign_device(struct kvm *kvm, |