KVM: Replace get_mt_mask_shift with get_mt_mask
PATCH 1/2 KVM: Replace get_mt_mask_shift with get_mt_mask
Shadow_mt_mask is out of date, now it have only been used as a flag to indicate if TDP enabled. Get rid of it and use tdp_enabled instead.
Also put memory type logical in kvm_x86_ops->get_mt_mask().
--- arch/x86/include/asm/kvm_host.h | 5 +++-- arch/x86/kvm/mmu.c | 21 ++++++--------------- arch/x86/kvm/svm.c | 4 ++-- arch/x86/kvm/vmx.c | 17 ++++++++++++----- arch/x86/kvm/x86.c | 2 +- 5 files changed, 24 insertions(+), 25 deletions(-)
arch/x86/include/asm/kvm_host.h +3 -2
@@ -523,7 +523,7 @@ struct kvm_x86_ops {int (*interrupt_allowed)(struct kvm_vcpu *vcpu);int (*set_tss_addr)(struct kvm *kvm,unsigned int addr);int (*get_tdp_level)(void ); -int (*get_mt_mask_shift)(void ); +u64 (*get_mt_mask)(struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio); };extern struct kvm_x86_ops *kvm_x86_ops;
@@ -537,7 +537,7 @@ int kvm_mmu_setup(struct kvm_vcpu *vcpu);void kvm_mmu_set_nonpresent_ptes (u64 trap_pte,u64 notrap_pte);void kvm_mmu_set_base_ptes (u64 base_pte);void kvm_mmu_set_mask_ptes (u64 user_mask,u64 accessed_mask, -u64 dirty_mask,u64 nx_mask,u64 x_mask,u64 mt_mask); +u64 dirty_mask,u64 nx_mask,u64 x_mask);int kvm_mmu_reset_context (struct kvm_vcpu *vcpu);void kvm_mmu_slot_remove_write_access (struct kvm *kvm,int slot);
@@ -551,6 +551,7 @@ int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, const void *val, int bytes);int kvm_pv_mmu_op (struct kvm_vcpu *vcpu,unsigned long bytes,gpa_t addr,unsigned long *ret); +u8 kvm_get_guest_memory_type (struct kvm_vcpu *vcpu,gfn_t gfn);extern bool tdp_enabled;
arch/x86/kvm/mmu.c +6 -15
@@ -178,7 +178,6 @@ static u64 __read_mostly shadow_x_mask; /* mutual exclusive with nx_mask */static u64 __read_mostly shadow_user_mask;static u64 __read_mostly shadow_accessed_mask;static u64 __read_mostly shadow_dirty_mask; -static u64 __read_mostly shadow_mt_mask;static inline u64 rsvd_bits (int s,int e) {
@@ -199,14 +198,13 @@ void kvm_mmu_set_base_ptes(u64 base_pte)EXPORT_SYMBOL_GPL (kvm_mmu_set_base_ptes);void kvm_mmu_set_mask_ptes (u64 user_mask,u64 accessed_mask, -u64 dirty_mask,u64 nx_mask,u64 x_mask,u64 mt_mask) +u64 dirty_mask,u64 nx_mask,u64 x_mask) { shadow_user_mask = user_mask; shadow_accessed_mask = accessed_mask; shadow_dirty_mask = dirty_mask; shadow_nx_mask = nx_mask; shadow_x_mask = x_mask; - shadow_mt_mask = mt_mask; }EXPORT_SYMBOL_GPL (kvm_mmu_set_mask_ptes);
@@ -1608,7 +1606,7 @@ static int get_mtrr_type(struct mtrr_state_type *mtrr_state,return mtrr_state->def_type; } -static u8 get_memory_type (struct kvm_vcpu *vcpu,gfn_t gfn) +u8 kvm_get_guest_memory_type (struct kvm_vcpu *vcpu,gfn_t gfn) {u8 mtrr;
@@ -1618,6 +1616,7 @@ static u8 get_memory_type(struct kvm_vcpu *vcpu, gfn_t gfn) mtrr =MTRR_TYPE_WRBACK ;return mtrr; } +EXPORT_SYMBOL_GPL (kvm_get_guest_memory_type);static int kvm_unsync_page (struct kvm_vcpu *vcpu,struct kvm_mmu_page *sp) {
@@ -1670,7 +1669,6 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte, {u64 spte;int ret =0 ; -u64 mt_mask = shadow_mt_mask;/* * We don't set the accessed bit, since we sometimes want to see
@@ -1690,16 +1688,9 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte, spte |= shadow_user_mask;if (largepage) spte |=PT_PAGE_SIZE_MASK ; -if (mt_mask) { -if (!kvm_is_mmio_pfn (pfn)) { - mt_mask =get_memory_type (vcpu, gfn) << - kvm_x86_ops->get_mt_mask_shift (); - mt_mask |=VMX_EPT_IGMT_BIT ; - }else - mt_mask =MTRR_TYPE_UNCACHABLE << - kvm_x86_ops->get_mt_mask_shift (); - spte |= mt_mask; - } +if (tdp_enabled) + spte |= kvm_x86_ops->get_mt_mask (vcpu, gfn, +kvm_is_mmio_pfn (pfn)); spte |= (u64 )pfn <<PAGE_SHIFT ;
arch/x86/kvm/svm.c +2 -2
@@ -2617,7 +2617,7 @@ static int get_npt_level(void)#endif } -static int svm_get_mt_mask_shift (void ) +static u64 svm_get_mt_mask (struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio) {return 0 ; }
@@ -2678,7 +2678,7 @@ static struct kvm_x86_ops svm_x86_ops = { .set_tss_addr = svm_set_tss_addr, .get_tdp_level = get_npt_level, - .get_mt_mask_shift = svm_get_mt_mask_shift, + .get_mt_mask = svm_get_mt_mask, };static int __init svm_init (void )
arch/x86/kvm/vmx.c +12 -5
@@ -3678,9 +3678,17 @@ static int get_ept_level(void)return VMX_EPT_DEFAULT_GAW +1 ; } -static int vmx_get_mt_mask_shift (void ) +static u64 vmx_get_mt_mask (struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio) { -return VMX_EPT_MT_EPTE_SHIFT ; +u64 ret; + +if (is_mmio) + ret =MTRR_TYPE_UNCACHABLE <<VMX_EPT_MT_EPTE_SHIFT ; +else + ret = (kvm_get_guest_memory_type (vcpu, gfn) << +VMX_EPT_MT_EPTE_SHIFT ) |VMX_EPT_IGMT_BIT ; + +return ret; }static struct kvm_x86_ops vmx_x86_ops = {
@@ -3737,7 +3745,7 @@ static struct kvm_x86_ops vmx_x86_ops = { .interrupt_allowed = vmx_interrupt_allowed, .set_tss_addr = vmx_set_tss_addr, .get_tdp_level = get_ept_level, - .get_mt_mask_shift = vmx_get_mt_mask_shift, + .get_mt_mask = vmx_get_mt_mask, };static int __init vmx_init (void )
@@ -3796,8 +3804,7 @@ static int __init vmx_init(void)kvm_mmu_set_base_ptes (VMX_EPT_READABLE_MASK |VMX_EPT_WRITABLE_MASK );kvm_mmu_set_mask_ptes (0ull ,0ull ,0ull ,0ull , -VMX_EPT_EXECUTABLE_MASK , -VMX_EPT_DEFAULT_MT <<VMX_EPT_MT_EPTE_SHIFT ); +VMX_EPT_EXECUTABLE_MASK );kvm_enable_tdp (); }else kvm_disable_tdp ();
arch/x86/kvm/x86.c +1 -1
@@ -2768,7 +2768,7 @@ int kvm_arch_init(void *opaque)kvm_mmu_set_nonpresent_ptes (0ull ,0ull );kvm_mmu_set_base_ptes (PT_PRESENT_MASK );kvm_mmu_set_mask_ptes (PT_USER_MASK ,PT_ACCESSED_MASK , -PT_DIRTY_MASK ,PT64_NX_MASK ,0 ,0 ); +PT_DIRTY_MASK ,PT64_NX_MASK ,0 );for_each_possible_cpu (cpu)per_cpu (cpu_tsc_khz, cpu) = tsc_khz;
Signature · 1 line
1.5.4.5
PATCH 2/2 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.
--- arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/vmx.c | 19 +++++++++++++++++-- virt/kvm/iommu.c | 27 ++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 5 deletions(-)
arch/x86/include/asm/kvm_host.h +2 -0
@@ -395,6 +395,8 @@ struct kvm_arch{struct list_head active_mmu_pages;struct list_head assigned_dev_head;struct iommu_domain *iommu_domain; +#define KVM_IOMMU_CACHE_COHERENCY 0x1 +int iommu_flags;struct kvm_pic *vpic;struct kvm_ioapic *vioapic;struct kvm_pit *vpit;
arch/x86/kvm/vmx.c +17 -2
@@ -3682,11 +3682,26 @@ static u64 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) {u64 ret; +/* For VT-d and EPT combination +* 1. MMIO: always map as UC +* 2. EPT with VT-d: +* a. VT-d without snooping control feature: can't guarantee the +* result, try to trust guest. +* b. VT-d with snooping control feature: snooping control feature of +* VT-d engine can guarantee the cache correctness. Just set it +* to WB to keep consistent with host. So the same as item 3. +* 3. EPT without VT-d: always map as WB and set IGMT=1 to keep +* consistent with host MTRR +*/ if (is_mmio) ret =MTRR_TYPE_UNCACHABLE <<VMX_EPT_MT_EPTE_SHIFT ; +else if (vcpu->kvm->arch.iommu_domain && + !(vcpu->kvm->arch.iommu_flags &KVM_IOMMU_CACHE_COHERENCY )) + ret =kvm_get_guest_memory_type (vcpu, gfn) << +VMX_EPT_MT_EPTE_SHIFT ;else - ret = (kvm_get_guest_memory_type (vcpu, gfn) << -VMX_EPT_MT_EPTE_SHIFT ) |VMX_EPT_IGMT_BIT ; + ret = (MTRR_TYPE_WRBACK <<VMX_EPT_MT_EPTE_SHIFT ) + |VMX_EPT_IGMT_BIT ;return ret; }
virt/kvm/iommu.c +24 -3
@@ -39,11 +39,16 @@ int kvm_iommu_map_pages(struct kvm *kvm,pfn_t pfn;int i, r =0 ;struct iommu_domain *domain = kvm->arch.iommu_domain; +int flags;/* check if iommu exists and in use */ if (!domain)return 0 ; + flags =IOMMU_READ |IOMMU_WRITE ; +if (kvm->arch.iommu_flags &KVM_IOMMU_CACHE_COHERENCY ) + flags |=IOMMU_CACHE ; +for (i =0 ; i < npages; i++) {/* check if already mapped */ if (iommu_iova_to_phys (domain,gfn_to_gpa (gfn)))
@@ -53,8 +58,7 @@ int kvm_iommu_map_pages(struct kvm *kvm, r =iommu_map_range (domain,gfn_to_gpa (gfn),pfn_to_hpa (pfn), -PAGE_SIZE , -IOMMU_READ |IOMMU_WRITE ); +PAGE_SIZE , flags);if (r) {printk (KERN_ERR "kvm_iommu_map_address:" "iommu failed to map pfn=%lx\n" , pfn);
@@ -88,7 +92,7 @@ int kvm_assign_device(struct kvm *kvm, {struct pci_dev *pdev =NULL ;struct iommu_domain *domain = kvm->arch.iommu_domain; -int r; +int r, last_flags;/* check if iommu exists and in use */ if (!domain)
@@ -107,12 +111,29 @@ int kvm_assign_device(struct kvm *kvm,return r; } + last_flags = kvm->arch.iommu_flags; +if (iommu_domain_has_cap (kvm->arch.iommu_domain, +IOMMU_CAP_CACHE_COHERENCY )) + kvm->arch.iommu_flags |=KVM_IOMMU_CACHE_COHERENCY ; + +/* Check if need to update IOMMU page table for guest memory */ +if ((last_flags ^ kvm->arch.iommu_flags) == +KVM_IOMMU_CACHE_COHERENCY ) { +kvm_iommu_unmap_memslots (kvm); + r =kvm_iommu_map_memslots (kvm); +if (r) + goto out_unmap; + } +printk (KERN_DEBUG "assign device: host bdf = %x:%x:%x\n" , assigned_dev->host_busnr,PCI_SLOT (assigned_dev->host_devfn),PCI_FUNC (assigned_dev->host_devfn));return 0 ; +out_unmap: +kvm_iommu_unmap_memslots (kvm); +return r; } int kvm_deassign_device(struct kvm *kvm,
Signature · 1 line
1.5.4.5
Avi Kivity
AppliedQuoted 5 lines · Sheng Yang
Shadow_mt_maskis out of date, now it have only been used as a flag to indicate if TDP enabled. Get rid of it and usetdp_enabledinstead.Also put memory type logical in
kvm_x86_ops->get_mt_mask().
Applied both, thanks.
Signature · 1 line
error compiling committee.c: too many arguments to function