KVM: use get_user_pages_fast
Convert gfn_to_pfn() to use get_user_pages_fast(), which can do lockless pagetable lookups. Kernel compilation on 4-way guest is 3.7% faster on VMX.
Andrea, please review (especially mmu notifiers synchronization).
kvm_vm_fault, used by older userspace without CAP_USER_MEMORY, had gfn_to_page opencoded without VM_PFNMAP support (hopefully thats not an issue).
A compat version of get_user_pages_fast() can be found in mm/util.c.
arch/x86/kvm/mmu.c +9 -14
@@ -405,16 +405,19 @@ static int host_largepage_backed(struct {struct vm_area_struct *vma;unsigned long addr; +int ret =0 ; addr =gfn_to_hva (kvm, gfn);if (kvm_is_error_hva (addr)) -return 0 ; +return ret; +down_read (¤t->mm->mmap_sem); vma =find_vma (current->mm, addr);if (vma &&is_vm_hugetlb_page (vma)) -return 1 ; + ret =1 ; +up_read (¤t->mm->mmap_sem); -return 0 ; +return ret; }static int is_largepage_backed (struct kvm_vcpu *vcpu,gfn_t large_gfn)
@@ -1137,9 +1140,7 @@ struct page *gva_to_page(struct kvm_vcpuif (gpa ==UNMAPPED_GVA )return NULL ; -down_read (¤t->mm->mmap_sem); page =gfn_to_page (vcpu->kvm, gpa >>PAGE_SHIFT ); -up_read (¤t->mm->mmap_sem);return page; }
@@ -1327,16 +1328,14 @@ static int nonpaging_map(struct kvm_vcpupfn_t pfn;unsigned long mmu_seq; -down_read (¤t->mm->mmap_sem);if (is_largepage_backed (vcpu, gfn & ~(KVM_PAGES_PER_HPAGE -1 ))) { gfn &= ~(KVM_PAGES_PER_HPAGE -1 ); largepage =1 ; } mmu_seq = vcpu->kvm->mmu_notifier_seq; -/* implicit mb(), we'll read before PT lock is unlocked */ +smp_rmb (); pfn =gfn_to_pfn (vcpu->kvm, gfn); -up_read (¤t->mm->mmap_sem);/* mmio */ if (is_error_pfn (pfn)) {
@@ -1485,15 +1484,13 @@ static int tdp_page_fault(struct kvm_vcpif (r)return r; -down_read (¤t->mm->mmap_sem);if (is_largepage_backed (vcpu, gfn & ~(KVM_PAGES_PER_HPAGE -1 ))) { gfn &= ~(KVM_PAGES_PER_HPAGE -1 ); largepage =1 ; } mmu_seq = vcpu->kvm->mmu_notifier_seq; -/* implicit mb(), we'll read before PT lock is unlocked */ +smp_rmb (); pfn =gfn_to_pfn (vcpu->kvm, gfn); -up_read (¤t->mm->mmap_sem);if (is_error_pfn (pfn)) {kvm_release_pfn_clean (pfn);return 1 ;
@@ -1806,15 +1803,13 @@ static void mmu_guess_page_from_pte_writreturn ; gfn = (gpte &PT64_BASE_ADDR_MASK ) >>PAGE_SHIFT ; -down_read (¤t->mm->mmap_sem);if (is_large_pte (gpte) &&is_largepage_backed (vcpu, gfn)) { gfn &= ~(KVM_PAGES_PER_HPAGE -1 ); vcpu->arch.update_pte.largepage =1 ; } vcpu->arch.update_pte.mmu_seq = vcpu->kvm->mmu_notifier_seq; -/* implicit mb(), we'll read before PT lock is unlocked */ +smp_rmb (); pfn =gfn_to_pfn (vcpu->kvm, gfn); -up_read (¤t->mm->mmap_sem);if (is_error_pfn (pfn)) {kvm_release_pfn_clean (pfn);
arch/x86/kvm/paging_tmpl.h +1 -7
@@ -102,14 +102,10 @@ static bool FNAME(cmpxchg_gpte)(struct kpt_element_t *table;struct page *page; -down_read (¤t->mm->mmap_sem); page =gfn_to_page (kvm, table_gfn); -up_read (¤t->mm->mmap_sem); table =kmap_atomic (page,KM_USER0 ); - ret =CMPXCHG (&table[index], orig_pte, new_pte); -kunmap_atomic (table,KM_USER0 );kvm_release_page_dirty (page);
@@ -418,7 +414,6 @@ static int FNAME(page_fault)(struct kvm_return 0 ; } -down_read (¤t->mm->mmap_sem);if (walker.level ==PT_DIRECTORY_LEVEL ) {gfn_t large_gfn; large_gfn = walker.gfn & ~(KVM_PAGES_PER_HPAGE -1 );
@@ -428,9 +423,8 @@ static int FNAME(page_fault)(struct kvm_ } } mmu_seq = vcpu->kvm->mmu_notifier_seq; -/* implicit mb(), we'll read before PT lock is unlocked */ +smp_rmb (); pfn =gfn_to_pfn (vcpu->kvm, walker.gfn); -up_read (¤t->mm->mmap_sem);/* mmio */ if (is_error_pfn (pfn)) {
arch/x86/kvm/vmx.c +0 -4
@@ -2010,9 +2010,7 @@ static int alloc_apic_access_page(structif (r) goto out; -down_read (¤t->mm->mmap_sem); kvm->arch.apic_access_page =gfn_to_page (kvm,0xfee00 ); -up_read (¤t->mm->mmap_sem); out:up_write (&kvm->slots_lock);return r;
@@ -2034,10 +2032,8 @@ static int alloc_identity_pagetable(struif (r) goto out; -down_read (¤t->mm->mmap_sem); kvm->arch.ept_identity_pagetable =gfn_to_page (kvm,VMX_EPT_IDENTITY_PAGETABLE_ADDR >>PAGE_SHIFT ); -up_read (¤t->mm->mmap_sem); out:up_write (&kvm->slots_lock);return r;
arch/x86/kvm/x86.c +0 -6
@@ -931,10 +931,8 @@ int kvm_set_msr_common(struct kvm_vcpu */* ...but clean it before doing the actual write */ vcpu->arch.time_offset = data & ~(PAGE_MASK |1 ); -down_read (¤t->mm->mmap_sem); vcpu->arch.time_page =gfn_to_page (vcpu->kvm, data >>PAGE_SHIFT ); -up_read (¤t->mm->mmap_sem);if (is_error_page (vcpu->arch.time_page)) {kvm_release_page_clean (vcpu->arch.time_page);
@@ -2303,9 +2301,7 @@ static int emulator_cmpxchg_emulated(uns val = *(u64 *)new; -down_read (¤t->mm->mmap_sem); page =gfn_to_page (vcpu->kvm, gpa >>PAGE_SHIFT ); -up_read (¤t->mm->mmap_sem); kaddr =kmap_atomic (page,KM_USER0 );set_64bit ((u64 *)(kaddr +offset_in_page (gpa)), val);
@@ -3075,9 +3071,7 @@ static void vapic_enter(struct kvm_vcpuif (!apic || !apic->vapic_addr)return ; -down_read (¤t->mm->mmap_sem); page =gfn_to_page (vcpu->kvm, apic->vapic_addr >>PAGE_SHIFT ); -up_read (¤t->mm->mmap_sem); vcpu->arch.apic->vapic_page = page; }
virt/kvm/kvm_main.c +17 -12
@@ -716,9 +716,6 @@ unsigned long gfn_to_hva(struct kvm *kvm }EXPORT_SYMBOL_GPL (gfn_to_hva); -/* -* Requires current->mm->mmap_sem to be held -*/ pfn_t gfn_to_pfn (struct kvm *kvm,gfn_t gfn) {struct page *page[1 ];
@@ -734,20 +731,23 @@ pfn_t gfn_to_pfn(struct kvm *kvm, gfn_treturn page_to_pfn (bad_page); } - npages =get_user_pages (current, current->mm, addr,1 ,1 ,0 , page, -NULL ); + npages =get_user_pages_fast (addr,1 ,1 , page);if (unlikely (npages !=1 )) {struct vm_area_struct *vma; +down_read (¤t->mm->mmap_sem); vma =find_vma (current->mm, addr); +if (vma ==NULL || addr < vma->vm_start || !(vma->vm_flags &VM_PFNMAP )) { +up_read (¤t->mm->mmap_sem);get_page (bad_page);return page_to_pfn (bad_page); } pfn = ((addr - vma->vm_start) >>PAGE_SHIFT ) + vma->vm_pgoff; +up_read (¤t->mm->mmap_sem);BUG_ON (!is_mmio_pfn (pfn)); }else pfn =page_to_pfn (page[0 ]);
@@ -1387,17 +1387,22 @@ out:static int kvm_vm_fault (struct vm_area_struct *vma,struct vm_fault *vmf) { +struct page *page[1 ]; +unsigned long addr; +int npages; +gfn_t gfn = vmf->pgoff;struct kvm *kvm = vma->vm_file->private_data; -struct page *page; -if (!kvm_is_visible_gfn (kvm, vmf->pgoff)) + addr =gfn_to_hva (kvm, gfn); +if (kvm_is_error_hva (addr))return VM_FAULT_SIGBUS ; - page =gfn_to_page (kvm, vmf->pgoff); -if (is_error_page (page)) { -kvm_release_page_clean (page); + + npages =get_user_pages (current, current->mm, addr,1 ,1 ,0 , page, +NULL ); +if (unlikely (npages !=1 ))return VM_FAULT_SIGBUS ; - } - vmf->page = page; + + vmf->page = page[0 ];return 0 ; }