From: Takuya Yoshikawa <yoshikawa.takuya@xxxxxxxxxxxxx> We optimize multi level guest page table walk as follows: 1. We cache the memslot which, probably, includes the next guest page tables to avoid searching for it many times. 2. We use get_user() instead of copy_from_user(). Note that this is kind of a restricted way of Xiao's more generic work: "KVM: optimize memslots searching and cache GPN to GFN." With this patch applied, paging64_walk_addr_generic() has improved as the following tracing results show. Before: 3.169 us | paging64_walk_addr_generic(); 1.880 us | paging64_walk_addr_generic(); 1.243 us | paging64_walk_addr_generic(); 1.517 us | paging64_walk_addr_generic(); 3.009 us | paging64_walk_addr_generic(); 1.814 us | paging64_walk_addr_generic(); 1.340 us | paging64_walk_addr_generic(); 1.659 us | paging64_walk_addr_generic(); 1.748 us | paging64_walk_addr_generic(); 1.488 us | paging64_walk_addr_generic(); After: 1.714 us | paging64_walk_addr_generic(); 0.806 us | paging64_walk_addr_generic(); 0.664 us | paging64_walk_addr_generic(); 0.619 us | paging64_walk_addr_generic(); 0.645 us | paging64_walk_addr_generic(); 0.605 us | paging64_walk_addr_generic(); 1.388 us | paging64_walk_addr_generic(); 0.753 us | paging64_walk_addr_generic(); 0.594 us | paging64_walk_addr_generic(); 0.833 us | paging64_walk_addr_generic(); Signed-off-by: Takuya Yoshikawa <yoshikawa.takuya@xxxxxxxxxxxxx> --- arch/x86/kvm/paging_tmpl.h | 37 ++++++++++++++++++++++++++++++++----- 1 files changed, 32 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index 109939a..614aa3f 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -109,12 +109,37 @@ static unsigned FNAME(gpte_access)(struct kvm_vcpu *vcpu, pt_element_t gpte) return access; } +/* + * Read the guest PTE refered to by table_gfn and offset and put it into ptep. + * + * *slot_hint, if not NULL, should point to a memslot which probably includes + * the guest PTE. The actual memslot will be put back into this so that + * callers can cache it. + */ static int FNAME(read_guest_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, - gfn_t table_gfn, int offset, pt_element_t *ptep) + gfn_t table_gfn, int offset, pt_element_t *ptep, + struct kvm_memory_slot **slot_hint) { - return kvm_read_guest_page_mmu(vcpu, mmu, table_gfn, ptep, - offset, sizeof(*ptep), - PFERR_USER_MASK | PFERR_WRITE_MASK); + unsigned long addr; + pt_element_t __user *ptep_user; + gfn_t real_gfn; + + real_gfn = mmu->translate_gpa(vcpu, gfn_to_gpa(table_gfn), + PFERR_USER_MASK | PFERR_WRITE_MASK); + if (real_gfn == UNMAPPED_GVA) + return -EFAULT; + + real_gfn = gpa_to_gfn(real_gfn); + + if (!(*slot_hint) || !gfn_in_memslot(*slot_hint, real_gfn)) + *slot_hint = gfn_to_memslot(vcpu->kvm, real_gfn); + + addr = gfn_to_hva_memslot(*slot_hint, real_gfn); + if (kvm_is_error_hva(addr)) + return -EFAULT; + + ptep_user = (pt_element_t __user *)((void *)addr + offset); + return get_user(*ptep, ptep_user); } /* @@ -130,6 +155,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, gpa_t pte_gpa; bool eperm, present, rsvd_fault; int offset, write_fault, user_fault, fetch_fault; + struct kvm_memory_slot *slot_cache = NULL; write_fault = access & PFERR_WRITE_MASK; user_fault = access & PFERR_USER_MASK; @@ -168,7 +194,8 @@ walk: walker->table_gfn[walker->level - 1] = table_gfn; walker->pte_gpa[walker->level - 1] = pte_gpa; - if (FNAME(read_guest_pte)(vcpu, mmu, table_gfn, offset, &pte)) { + if (FNAME(read_guest_pte)(vcpu, mmu, table_gfn, + offset, &pte, &slot_cache)) { present = false; break; } -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html