diff options
author | Marcelo Tosatti <mtosatti@redhat.com> | 2008-09-16 19:54:47 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2008-10-15 08:25:06 -0400 |
commit | 4c2155ce81c193788082d4b8cdbc26d79edebc58 (patch) | |
tree | 5c028fe4a18c55b5c0e9c4e80be459dad3f96da7 | |
parent | 777b3f49d297e387866604093b635e5bc9b9d2a6 (diff) |
KVM: switch to get_user_pages_fast
Convert gfn_to_pfn to use get_user_pages_fast, which can do lockless
pagetable lookups on x86. Kernel compilation on 4-way guest is 3.7%
faster on VMX.
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r-- | arch/powerpc/kvm/44x_tlb.c | 2 | ||||
-rw-r--r-- | arch/x86/kvm/mmu.c | 23 | ||||
-rw-r--r-- | arch/x86/kvm/paging_tmpl.h | 8 | ||||
-rw-r--r-- | arch/x86/kvm/vmx.c | 4 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 6 | ||||
-rw-r--r-- | virt/kvm/kvm_main.c | 10 |
6 files changed, 15 insertions, 38 deletions
diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c index 7b11fd7be542..2e227a412bc2 100644 --- a/arch/powerpc/kvm/44x_tlb.c +++ b/arch/powerpc/kvm/44x_tlb.c | |||
@@ -147,9 +147,7 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn, u64 asid, | |||
147 | stlbe = &vcpu->arch.shadow_tlb[victim]; | 147 | stlbe = &vcpu->arch.shadow_tlb[victim]; |
148 | 148 | ||
149 | /* Get reference to new page. */ | 149 | /* Get reference to new page. */ |
150 | down_read(¤t->mm->mmap_sem); | ||
151 | new_page = gfn_to_page(vcpu->kvm, gfn); | 150 | new_page = gfn_to_page(vcpu->kvm, gfn); |
152 | up_read(¤t->mm->mmap_sem); | ||
153 | if (is_error_page(new_page)) { | 151 | if (is_error_page(new_page)) { |
154 | printk(KERN_ERR "Couldn't get guest page for gfn %lx!\n", gfn); | 152 | printk(KERN_ERR "Couldn't get guest page for gfn %lx!\n", gfn); |
155 | kvm_release_page_clean(new_page); | 153 | kvm_release_page_clean(new_page); |
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index bce3e25ec79b..5779a2323e23 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c | |||
@@ -405,16 +405,19 @@ static int host_largepage_backed(struct kvm *kvm, gfn_t gfn) | |||
405 | { | 405 | { |
406 | struct vm_area_struct *vma; | 406 | struct vm_area_struct *vma; |
407 | unsigned long addr; | 407 | unsigned long addr; |
408 | int ret = 0; | ||
408 | 409 | ||
409 | addr = gfn_to_hva(kvm, gfn); | 410 | addr = gfn_to_hva(kvm, gfn); |
410 | if (kvm_is_error_hva(addr)) | 411 | if (kvm_is_error_hva(addr)) |
411 | return 0; | 412 | return ret; |
412 | 413 | ||
414 | down_read(¤t->mm->mmap_sem); | ||
413 | vma = find_vma(current->mm, addr); | 415 | vma = find_vma(current->mm, addr); |
414 | if (vma && is_vm_hugetlb_page(vma)) | 416 | if (vma && is_vm_hugetlb_page(vma)) |
415 | return 1; | 417 | ret = 1; |
418 | up_read(¤t->mm->mmap_sem); | ||
416 | 419 | ||
417 | return 0; | 420 | return ret; |
418 | } | 421 | } |
419 | 422 | ||
420 | static int is_largepage_backed(struct kvm_vcpu *vcpu, gfn_t large_gfn) | 423 | static int is_largepage_backed(struct kvm_vcpu *vcpu, gfn_t large_gfn) |
@@ -1140,9 +1143,7 @@ struct page *gva_to_page(struct kvm_vcpu *vcpu, gva_t gva) | |||
1140 | if (gpa == UNMAPPED_GVA) | 1143 | if (gpa == UNMAPPED_GVA) |
1141 | return NULL; | 1144 | return NULL; |
1142 | 1145 | ||
1143 | down_read(¤t->mm->mmap_sem); | ||
1144 | page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT); | 1146 | page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT); |
1145 | up_read(¤t->mm->mmap_sem); | ||
1146 | 1147 | ||
1147 | return page; | 1148 | return page; |
1148 | } | 1149 | } |
@@ -1330,16 +1331,14 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn) | |||
1330 | pfn_t pfn; | 1331 | pfn_t pfn; |
1331 | unsigned long mmu_seq; | 1332 | unsigned long mmu_seq; |
1332 | 1333 | ||
1333 | down_read(¤t->mm->mmap_sem); | ||
1334 | if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) { | 1334 | if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) { |
1335 | gfn &= ~(KVM_PAGES_PER_HPAGE-1); | 1335 | gfn &= ~(KVM_PAGES_PER_HPAGE-1); |
1336 | largepage = 1; | 1336 | largepage = 1; |
1337 | } | 1337 | } |
1338 | 1338 | ||
1339 | mmu_seq = vcpu->kvm->mmu_notifier_seq; | 1339 | mmu_seq = vcpu->kvm->mmu_notifier_seq; |
1340 | /* implicit mb(), we'll read before PT lock is unlocked */ | 1340 | smp_rmb(); |
1341 | pfn = gfn_to_pfn(vcpu->kvm, gfn); | 1341 | pfn = gfn_to_pfn(vcpu->kvm, gfn); |
1342 | up_read(¤t->mm->mmap_sem); | ||
1343 | 1342 | ||
1344 | /* mmio */ | 1343 | /* mmio */ |
1345 | if (is_error_pfn(pfn)) { | 1344 | if (is_error_pfn(pfn)) { |
@@ -1488,15 +1487,13 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, | |||
1488 | if (r) | 1487 | if (r) |
1489 | return r; | 1488 | return r; |
1490 | 1489 | ||
1491 | down_read(¤t->mm->mmap_sem); | ||
1492 | if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) { | 1490 | if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) { |
1493 | gfn &= ~(KVM_PAGES_PER_HPAGE-1); | 1491 | gfn &= ~(KVM_PAGES_PER_HPAGE-1); |
1494 | largepage = 1; | 1492 | largepage = 1; |
1495 | } | 1493 | } |
1496 | mmu_seq = vcpu->kvm->mmu_notifier_seq; | 1494 | mmu_seq = vcpu->kvm->mmu_notifier_seq; |
1497 | /* implicit mb(), we'll read before PT lock is unlocked */ | 1495 | smp_rmb(); |
1498 | pfn = gfn_to_pfn(vcpu->kvm, gfn); | 1496 | pfn = gfn_to_pfn(vcpu->kvm, gfn); |
1499 | up_read(¤t->mm->mmap_sem); | ||
1500 | if (is_error_pfn(pfn)) { | 1497 | if (is_error_pfn(pfn)) { |
1501 | kvm_release_pfn_clean(pfn); | 1498 | kvm_release_pfn_clean(pfn); |
1502 | return 1; | 1499 | return 1; |
@@ -1809,15 +1806,13 @@ static void mmu_guess_page_from_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, | |||
1809 | return; | 1806 | return; |
1810 | gfn = (gpte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT; | 1807 | gfn = (gpte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT; |
1811 | 1808 | ||
1812 | down_read(¤t->mm->mmap_sem); | ||
1813 | if (is_large_pte(gpte) && is_largepage_backed(vcpu, gfn)) { | 1809 | if (is_large_pte(gpte) && is_largepage_backed(vcpu, gfn)) { |
1814 | gfn &= ~(KVM_PAGES_PER_HPAGE-1); | 1810 | gfn &= ~(KVM_PAGES_PER_HPAGE-1); |
1815 | vcpu->arch.update_pte.largepage = 1; | 1811 | vcpu->arch.update_pte.largepage = 1; |
1816 | } | 1812 | } |
1817 | vcpu->arch.update_pte.mmu_seq = vcpu->kvm->mmu_notifier_seq; | 1813 | vcpu->arch.update_pte.mmu_seq = vcpu->kvm->mmu_notifier_seq; |
1818 | /* implicit mb(), we'll read before PT lock is unlocked */ | 1814 | smp_rmb(); |
1819 | pfn = gfn_to_pfn(vcpu->kvm, gfn); | 1815 | pfn = gfn_to_pfn(vcpu->kvm, gfn); |
1820 | up_read(¤t->mm->mmap_sem); | ||
1821 | 1816 | ||
1822 | if (is_error_pfn(pfn)) { | 1817 | if (is_error_pfn(pfn)) { |
1823 | kvm_release_pfn_clean(pfn); | 1818 | kvm_release_pfn_clean(pfn); |
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index b671f61be41e..6dd08e096e24 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h | |||
@@ -102,14 +102,10 @@ static bool FNAME(cmpxchg_gpte)(struct kvm *kvm, | |||
102 | pt_element_t *table; | 102 | pt_element_t *table; |
103 | struct page *page; | 103 | struct page *page; |
104 | 104 | ||
105 | down_read(¤t->mm->mmap_sem); | ||
106 | page = gfn_to_page(kvm, table_gfn); | 105 | page = gfn_to_page(kvm, table_gfn); |
107 | up_read(¤t->mm->mmap_sem); | ||
108 | 106 | ||
109 | table = kmap_atomic(page, KM_USER0); | 107 | table = kmap_atomic(page, KM_USER0); |
110 | |||
111 | ret = CMPXCHG(&table[index], orig_pte, new_pte); | 108 | ret = CMPXCHG(&table[index], orig_pte, new_pte); |
112 | |||
113 | kunmap_atomic(table, KM_USER0); | 109 | kunmap_atomic(table, KM_USER0); |
114 | 110 | ||
115 | kvm_release_page_dirty(page); | 111 | kvm_release_page_dirty(page); |
@@ -418,7 +414,6 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, | |||
418 | return 0; | 414 | return 0; |
419 | } | 415 | } |
420 | 416 | ||
421 | down_read(¤t->mm->mmap_sem); | ||
422 | if (walker.level == PT_DIRECTORY_LEVEL) { | 417 | if (walker.level == PT_DIRECTORY_LEVEL) { |
423 | gfn_t large_gfn; | 418 | gfn_t large_gfn; |
424 | large_gfn = walker.gfn & ~(KVM_PAGES_PER_HPAGE-1); | 419 | large_gfn = walker.gfn & ~(KVM_PAGES_PER_HPAGE-1); |
@@ -428,9 +423,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, | |||
428 | } | 423 | } |
429 | } | 424 | } |
430 | mmu_seq = vcpu->kvm->mmu_notifier_seq; | 425 | mmu_seq = vcpu->kvm->mmu_notifier_seq; |
431 | /* implicit mb(), we'll read before PT lock is unlocked */ | 426 | smp_rmb(); |
432 | pfn = gfn_to_pfn(vcpu->kvm, walker.gfn); | 427 | pfn = gfn_to_pfn(vcpu->kvm, walker.gfn); |
433 | up_read(¤t->mm->mmap_sem); | ||
434 | 428 | ||
435 | /* mmio */ | 429 | /* mmio */ |
436 | if (is_error_pfn(pfn)) { | 430 | if (is_error_pfn(pfn)) { |
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 046a91b5a4ba..025bf4011abc 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c | |||
@@ -2010,9 +2010,7 @@ static int alloc_apic_access_page(struct kvm *kvm) | |||
2010 | if (r) | 2010 | if (r) |
2011 | goto out; | 2011 | goto out; |
2012 | 2012 | ||
2013 | down_read(¤t->mm->mmap_sem); | ||
2014 | kvm->arch.apic_access_page = gfn_to_page(kvm, 0xfee00); | 2013 | kvm->arch.apic_access_page = gfn_to_page(kvm, 0xfee00); |
2015 | up_read(¤t->mm->mmap_sem); | ||
2016 | out: | 2014 | out: |
2017 | up_write(&kvm->slots_lock); | 2015 | up_write(&kvm->slots_lock); |
2018 | return r; | 2016 | return r; |
@@ -2034,10 +2032,8 @@ static int alloc_identity_pagetable(struct kvm *kvm) | |||
2034 | if (r) | 2032 | if (r) |
2035 | goto out; | 2033 | goto out; |
2036 | 2034 | ||
2037 | down_read(¤t->mm->mmap_sem); | ||
2038 | kvm->arch.ept_identity_pagetable = gfn_to_page(kvm, | 2035 | kvm->arch.ept_identity_pagetable = gfn_to_page(kvm, |
2039 | VMX_EPT_IDENTITY_PAGETABLE_ADDR >> PAGE_SHIFT); | 2036 | VMX_EPT_IDENTITY_PAGETABLE_ADDR >> PAGE_SHIFT); |
2040 | up_read(¤t->mm->mmap_sem); | ||
2041 | out: | 2037 | out: |
2042 | up_write(&kvm->slots_lock); | 2038 | up_write(&kvm->slots_lock); |
2043 | return r; | 2039 | return r; |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 61eddbeabeb4..108f07267e87 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -946,10 +946,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) | |||
946 | /* ...but clean it before doing the actual write */ | 946 | /* ...but clean it before doing the actual write */ |
947 | vcpu->arch.time_offset = data & ~(PAGE_MASK | 1); | 947 | vcpu->arch.time_offset = data & ~(PAGE_MASK | 1); |
948 | 948 | ||
949 | down_read(¤t->mm->mmap_sem); | ||
950 | vcpu->arch.time_page = | 949 | vcpu->arch.time_page = |
951 | gfn_to_page(vcpu->kvm, data >> PAGE_SHIFT); | 950 | gfn_to_page(vcpu->kvm, data >> PAGE_SHIFT); |
952 | up_read(¤t->mm->mmap_sem); | ||
953 | 951 | ||
954 | if (is_error_page(vcpu->arch.time_page)) { | 952 | if (is_error_page(vcpu->arch.time_page)) { |
955 | kvm_release_page_clean(vcpu->arch.time_page); | 953 | kvm_release_page_clean(vcpu->arch.time_page); |
@@ -2322,9 +2320,7 @@ static int emulator_cmpxchg_emulated(unsigned long addr, | |||
2322 | 2320 | ||
2323 | val = *(u64 *)new; | 2321 | val = *(u64 *)new; |
2324 | 2322 | ||
2325 | down_read(¤t->mm->mmap_sem); | ||
2326 | page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT); | 2323 | page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT); |
2327 | up_read(¤t->mm->mmap_sem); | ||
2328 | 2324 | ||
2329 | kaddr = kmap_atomic(page, KM_USER0); | 2325 | kaddr = kmap_atomic(page, KM_USER0); |
2330 | set_64bit((u64 *)(kaddr + offset_in_page(gpa)), val); | 2326 | set_64bit((u64 *)(kaddr + offset_in_page(gpa)), val); |
@@ -3089,9 +3085,7 @@ static void vapic_enter(struct kvm_vcpu *vcpu) | |||
3089 | if (!apic || !apic->vapic_addr) | 3085 | if (!apic || !apic->vapic_addr) |
3090 | return; | 3086 | return; |
3091 | 3087 | ||
3092 | down_read(¤t->mm->mmap_sem); | ||
3093 | page = gfn_to_page(vcpu->kvm, apic->vapic_addr >> PAGE_SHIFT); | 3088 | page = gfn_to_page(vcpu->kvm, apic->vapic_addr >> PAGE_SHIFT); |
3094 | up_read(¤t->mm->mmap_sem); | ||
3095 | 3089 | ||
3096 | vcpu->arch.apic->vapic_page = page; | 3090 | vcpu->arch.apic->vapic_page = page; |
3097 | } | 3091 | } |
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 2907d05cfcc3..cd34f73513d3 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
@@ -723,9 +723,6 @@ unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn) | |||
723 | } | 723 | } |
724 | EXPORT_SYMBOL_GPL(gfn_to_hva); | 724 | EXPORT_SYMBOL_GPL(gfn_to_hva); |
725 | 725 | ||
726 | /* | ||
727 | * Requires current->mm->mmap_sem to be held | ||
728 | */ | ||
729 | pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn) | 726 | pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn) |
730 | { | 727 | { |
731 | struct page *page[1]; | 728 | struct page *page[1]; |
@@ -741,20 +738,23 @@ pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn) | |||
741 | return page_to_pfn(bad_page); | 738 | return page_to_pfn(bad_page); |
742 | } | 739 | } |
743 | 740 | ||
744 | npages = get_user_pages(current, current->mm, addr, 1, 1, 0, page, | 741 | npages = get_user_pages_fast(addr, 1, 1, page); |
745 | NULL); | ||
746 | 742 | ||
747 | if (unlikely(npages != 1)) { | 743 | if (unlikely(npages != 1)) { |
748 | struct vm_area_struct *vma; | 744 | struct vm_area_struct *vma; |
749 | 745 | ||
746 | down_read(¤t->mm->mmap_sem); | ||
750 | vma = find_vma(current->mm, addr); | 747 | vma = find_vma(current->mm, addr); |
748 | |||
751 | if (vma == NULL || addr < vma->vm_start || | 749 | if (vma == NULL || addr < vma->vm_start || |
752 | !(vma->vm_flags & VM_PFNMAP)) { | 750 | !(vma->vm_flags & VM_PFNMAP)) { |
751 | up_read(¤t->mm->mmap_sem); | ||
753 | get_page(bad_page); | 752 | get_page(bad_page); |
754 | return page_to_pfn(bad_page); | 753 | return page_to_pfn(bad_page); |
755 | } | 754 | } |
756 | 755 | ||
757 | pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; | 756 | pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; |
757 | up_read(¤t->mm->mmap_sem); | ||
758 | BUG_ON(!is_mmio_pfn(pfn)); | 758 | BUG_ON(!is_mmio_pfn(pfn)); |
759 | } else | 759 | } else |
760 | pfn = page_to_pfn(page[0]); | 760 | pfn = page_to_pfn(page[0]); |