export __get_user_pages_fast() function
Subsystems (5)
PATCH v7 1/5 export __get_user_pages_fast() function
This function is used by KVM to pin process's page in the atomic context.
Define the 'weak' function to avoid other architecture not support it
--- mm/util.c | 13 +++++++++++++ 1 files changed, 13 insertions(+), 0 deletions(-)
mm/util.c +13 -0
@@ -250,6 +250,19 @@ void arch_pick_mmap_layout(struct mm_struct *mm) }#endif +/* +* Like get_user_pages_fast() except its IRQ-safe in that it won't fall +* back to the regular GUP. +* If the architecture not support this fucntion, simply return with no +* page pinned +*/ +int __attribute__((weak))__get_user_pages_fast (unsigned long start, +int nr_pages,int write,struct page **pages) +{ +return 0 ; +} +EXPORT_SYMBOL_GPL (__get_user_pages_fast); +/** * get_user_pages_fast() - pin user pages in memory * @start: starting user address
Signature · 1 line
1.7.0.4
PATCH v7 2/5 KVM: MMU: introduce hva_to_pfn_atomic function
Introduce hva_to_pfn_atomic(), it's the fast path and can used in atomic context, the later patch will use it
--- include/linux/kvm_host.h | 7 +++++++ virt/kvm/kvm_main.c | 30 +++++++++++++++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-)
include/linux/kvm_host.h +7 -0
@@ -296,6 +296,7 @@ void kvm_release_page_dirty(struct page *page);void kvm_set_page_dirty (struct page *page);void kvm_set_page_accessed (struct page *page); +pfn_t hva_to_pfn_atomic (struct kvm *kvm,unsigned long addr);pfn_t gfn_to_pfn (struct kvm *kvm,gfn_t gfn);pfn_t gfn_to_pfn_memslot (struct kvm *kvm,struct kvm_memory_slot *slot,gfn_t gfn);
@@ -518,6 +519,12 @@ static inline void kvm_guest_exit(void) current->flags &= ~PF_VCPU ; } +static inline unsigned long gfn_to_hva_memslot (struct kvm_memory_slot *slot, +gfn_t gfn) +{ +return slot->userspace_addr + (gfn - slot->base_gfn) *PAGE_SIZE ; +} +static inline gpa_t gfn_to_gpa (gfn_t gfn) {return (gpa_t )gfn <<PAGE_SHIFT ;
virt/kvm/kvm_main.c +19 -11
@@ -927,11 +927,6 @@ int memslot_id(struct kvm *kvm, gfn_t gfn)return memslot - slots->memslots; } -static unsigned long gfn_to_hva_memslot (struct kvm_memory_slot *slot,gfn_t gfn) -{ -return slot->userspace_addr + (gfn - slot->base_gfn) *PAGE_SIZE ; -} -unsigned long gfn_to_hva (struct kvm *kvm,gfn_t gfn) {struct kvm_memory_slot *slot;
@@ -943,19 +938,25 @@ unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn) }EXPORT_SYMBOL_GPL (gfn_to_hva); -static pfn_t hva_to_pfn (struct kvm *kvm,unsigned long addr) +static pfn_t hva_to_pfn (struct kvm *kvm,unsigned long addr,bool atomic) {struct page *page[1 ];int npages;pfn_t pfn; -might_sleep (); - - npages =get_user_pages_fast (addr,1 ,1 , page); +if (atomic) + npages =__get_user_pages_fast (addr,1 ,1 , page); +else { +might_sleep (); + npages =get_user_pages_fast (addr,1 ,1 , page); + }if (unlikely (npages !=1 )) {struct vm_area_struct *vma; +if (atomic) + goto return_fault_page; +down_read (¤t->mm->mmap_sem);if (is_hwpoison_address (addr)) {up_read (¤t->mm->mmap_sem);
@@ -968,6 +969,7 @@ static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr)if (vma ==NULL || addr < vma->vm_start || !(vma->vm_flags &VM_PFNMAP )) {up_read (¤t->mm->mmap_sem); +return_fault_page:get_page (fault_page);return page_to_pfn (fault_page); }
@@ -981,6 +983,12 @@ static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr)return pfn; } +pfn_t hva_to_pfn_atomic (struct kvm *kvm,unsigned long addr) +{ +return hva_to_pfn (kvm, addr, true); +} +EXPORT_SYMBOL_GPL (hva_to_pfn_atomic); +pfn_t gfn_to_pfn (struct kvm *kvm,gfn_t gfn) {unsigned long addr;
@@ -991,7 +999,7 @@ pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)return page_to_pfn (bad_page); } -return hva_to_pfn (kvm, addr); +return hva_to_pfn (kvm, addr, false); }EXPORT_SYMBOL_GPL (gfn_to_pfn);
@@ -999,7 +1007,7 @@ pfn_t gfn_to_pfn_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn) {unsigned long addr =gfn_to_hva_memslot (slot, gfn); -return hva_to_pfn (kvm, addr); +return hva_to_pfn (kvm, addr, false); }struct page *gfn_to_page (struct kvm *kvm,gfn_t gfn)
Signature · 1 line
1.7.0.4
PATCH v7 2/5 KVM: MMU: introduce hva_to_pfn_atomic function
In reply to Xiao GuangrongIntroduce hva_to_pfn_atomic(), it's the fast path and can used in atomic context, the later patch will use it
--- include/linux/kvm_host.h | 7 +++++++ virt/kvm/kvm_main.c | 30 +++++++++++++++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-)
include/linux/kvm_host.h +7 -0
@@ -296,6 +296,7 @@ void kvm_release_page_dirty(struct page *page);void kvm_set_page_dirty (struct page *page);void kvm_set_page_accessed (struct page *page); +pfn_t hva_to_pfn_atomic (struct kvm *kvm,unsigned long addr);pfn_t gfn_to_pfn (struct kvm *kvm,gfn_t gfn);pfn_t gfn_to_pfn_memslot (struct kvm *kvm,struct kvm_memory_slot *slot,gfn_t gfn);
@@ -518,6 +519,12 @@ static inline void kvm_guest_exit(void) current->flags &= ~PF_VCPU ; } +static inline unsigned long gfn_to_hva_memslot (struct kvm_memory_slot *slot, +gfn_t gfn) +{ +return slot->userspace_addr + (gfn - slot->base_gfn) *PAGE_SIZE ; +} +static inline gpa_t gfn_to_gpa (gfn_t gfn) {return (gpa_t )gfn <<PAGE_SHIFT ;
virt/kvm/kvm_main.c +19 -11
@@ -927,11 +927,6 @@ int memslot_id(struct kvm *kvm, gfn_t gfn)return memslot - slots->memslots; } -static unsigned long gfn_to_hva_memslot (struct kvm_memory_slot *slot,gfn_t gfn) -{ -return slot->userspace_addr + (gfn - slot->base_gfn) *PAGE_SIZE ; -} -unsigned long gfn_to_hva (struct kvm *kvm,gfn_t gfn) {struct kvm_memory_slot *slot;
@@ -943,19 +938,25 @@ unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn) }EXPORT_SYMBOL_GPL (gfn_to_hva); -static pfn_t hva_to_pfn (struct kvm *kvm,unsigned long addr) +static pfn_t hva_to_pfn (struct kvm *kvm,unsigned long addr,bool atomic) {struct page *page[1 ];int npages;pfn_t pfn; -might_sleep (); - - npages =get_user_pages_fast (addr,1 ,1 , page); +if (atomic) + npages =__get_user_pages_fast (addr,1 ,1 , page); +else { +might_sleep (); + npages =get_user_pages_fast (addr,1 ,1 , page); + }if (unlikely (npages !=1 )) {struct vm_area_struct *vma; +if (atomic) + goto return_fault_page; +down_read (¤t->mm->mmap_sem);if (is_hwpoison_address (addr)) {up_read (¤t->mm->mmap_sem);
@@ -968,6 +969,7 @@ static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr)if (vma ==NULL || addr < vma->vm_start || !(vma->vm_flags &VM_PFNMAP )) {up_read (¤t->mm->mmap_sem); +return_fault_page:get_page (fault_page);return page_to_pfn (fault_page); }
@@ -981,6 +983,12 @@ static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr)return pfn; } +pfn_t hva_to_pfn_atomic (struct kvm *kvm,unsigned long addr) +{ +return hva_to_pfn (kvm, addr, true); +} +EXPORT_SYMBOL_GPL (hva_to_pfn_atomic); +pfn_t gfn_to_pfn (struct kvm *kvm,gfn_t gfn) {unsigned long addr;
@@ -991,7 +999,7 @@ pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)return page_to_pfn (bad_page); } -return hva_to_pfn (kvm, addr); +return hva_to_pfn (kvm, addr, false); }EXPORT_SYMBOL_GPL (gfn_to_pfn);
@@ -999,7 +1007,7 @@ pfn_t gfn_to_pfn_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn) {unsigned long addr =gfn_to_hva_memslot (slot, gfn); -return hva_to_pfn (kvm, addr); +return hva_to_pfn (kvm, addr, false); }struct page *gfn_to_page (struct kvm *kvm,gfn_t gfn)
Signature · 1 line
1.7.0.4
PATCH v7 2/5 KVM: MMU: introduce hva_to_pfn_atomic function
In reply to Xiao GuangrongIntroduce hva_to_pfn_atomic(), it's the fast path and can used in atomic context, the later patch will use it
--- include/linux/kvm_host.h | 7 +++++++ virt/kvm/kvm_main.c | 30 +++++++++++++++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-)
include/linux/kvm_host.h +7 -0
@@ -296,6 +296,7 @@ void kvm_release_page_dirty(struct page *page);void kvm_set_page_dirty (struct page *page);void kvm_set_page_accessed (struct page *page); +pfn_t hva_to_pfn_atomic (struct kvm *kvm,unsigned long addr);pfn_t gfn_to_pfn (struct kvm *kvm,gfn_t gfn);pfn_t gfn_to_pfn_memslot (struct kvm *kvm,struct kvm_memory_slot *slot,gfn_t gfn);
@@ -518,6 +519,12 @@ static inline void kvm_guest_exit(void) current->flags &= ~PF_VCPU ; } +static inline unsigned long gfn_to_hva_memslot (struct kvm_memory_slot *slot, +gfn_t gfn) +{ +return slot->userspace_addr + (gfn - slot->base_gfn) *PAGE_SIZE ; +} +static inline gpa_t gfn_to_gpa (gfn_t gfn) {return (gpa_t )gfn <<PAGE_SHIFT ;
virt/kvm/kvm_main.c +19 -11
@@ -927,11 +927,6 @@ int memslot_id(struct kvm *kvm, gfn_t gfn)return memslot - slots->memslots; } -static unsigned long gfn_to_hva_memslot (struct kvm_memory_slot *slot,gfn_t gfn) -{ -return slot->userspace_addr + (gfn - slot->base_gfn) *PAGE_SIZE ; -} -unsigned long gfn_to_hva (struct kvm *kvm,gfn_t gfn) {struct kvm_memory_slot *slot;
@@ -943,19 +938,25 @@ unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn) }EXPORT_SYMBOL_GPL (gfn_to_hva); -static pfn_t hva_to_pfn (struct kvm *kvm,unsigned long addr) +static pfn_t hva_to_pfn (struct kvm *kvm,unsigned long addr,bool atomic) {struct page *page[1 ];int npages;pfn_t pfn; -might_sleep (); - - npages =get_user_pages_fast (addr,1 ,1 , page); +if (atomic) + npages =__get_user_pages_fast (addr,1 ,1 , page); +else { +might_sleep (); + npages =get_user_pages_fast (addr,1 ,1 , page); + }if (unlikely (npages !=1 )) {struct vm_area_struct *vma; +if (atomic) + goto return_fault_page; +down_read (¤t->mm->mmap_sem);if (is_hwpoison_address (addr)) {up_read (¤t->mm->mmap_sem);
@@ -968,6 +969,7 @@ static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr)if (vma ==NULL || addr < vma->vm_start || !(vma->vm_flags &VM_PFNMAP )) {up_read (¤t->mm->mmap_sem); +return_fault_page:get_page (fault_page);return page_to_pfn (fault_page); }
@@ -981,6 +983,12 @@ static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr)return pfn; } +pfn_t hva_to_pfn_atomic (struct kvm *kvm,unsigned long addr) +{ +return hva_to_pfn (kvm, addr, true); +} +EXPORT_SYMBOL_GPL (hva_to_pfn_atomic); +pfn_t gfn_to_pfn (struct kvm *kvm,gfn_t gfn) {unsigned long addr;
@@ -991,7 +999,7 @@ pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)return page_to_pfn (bad_page); } -return hva_to_pfn (kvm, addr); +return hva_to_pfn (kvm, addr, false); }EXPORT_SYMBOL_GPL (gfn_to_pfn);
@@ -999,7 +1007,7 @@ pfn_t gfn_to_pfn_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn) {unsigned long addr =gfn_to_hva_memslot (slot, gfn); -return hva_to_pfn (kvm, addr); +return hva_to_pfn (kvm, addr, false); }struct page *gfn_to_page (struct kvm *kvm,gfn_t gfn)
Signature · 1 line
1.7.0.4
PATCH v7 3/5 KVM: MMU: introduce gfn_to_page_many_atomic() function
In reply to Xiao GuangrongIntroduce this function to get consecutive gfn's pages, it can reduce gup's overload, used by later patch
--- include/linux/kvm_host.h | 3 +++ virt/kvm/kvm_main.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletions(-)
include/linux/kvm_host.h +3 -0
@@ -289,6 +289,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,void kvm_disable_largepages (void );void kvm_arch_flush_shadow (struct kvm *kvm); +int gfn_to_page_many_atomic (struct kvm *kvm,gfn_t gfn,struct page **pages, +int nr_pages); +struct page *gfn_to_page (struct kvm *kvm,gfn_t gfn);unsigned long gfn_to_hva (struct kvm *kvm,gfn_t gfn);void kvm_release_page_clean (struct page *page);
virt/kvm/kvm_main.c +28 -1
@@ -927,15 +927,25 @@ int memslot_id(struct kvm *kvm, gfn_t gfn)return memslot - slots->memslots; } -unsigned long gfn_to_hva (struct kvm *kvm,gfn_t gfn) +static unsigned long gfn_to_hva_many (struct kvm *kvm,gfn_t gfn, +gfn_t *nr_pages) {struct kvm_memory_slot *slot; slot =gfn_to_memslot (kvm, gfn);if (!slot || slot->flags &KVM_MEMSLOT_INVALID )return bad_hva (); + +if (nr_pages) + *nr_pages = slot->npages - (gfn - slot->base_gfn); +return gfn_to_hva_memslot (slot, gfn); } + +unsigned long gfn_to_hva (struct kvm *kvm,gfn_t gfn) +{ +return gfn_to_hva_many (kvm, gfn,NULL ); +}EXPORT_SYMBOL_GPL (gfn_to_hva);static pfn_t hva_to_pfn (struct kvm *kvm,unsigned long addr,bool atomic)
@@ -1010,6 +1020,23 @@ pfn_t gfn_to_pfn_memslot(struct kvm *kvm,return hva_to_pfn (kvm, addr, false); } +int gfn_to_page_many_atomic (struct kvm *kvm,gfn_t gfn,struct page **pages, +int nr_pages) +{ +unsigned long addr; +gfn_t entry; + + addr =gfn_to_hva_many (kvm, gfn, &entry); +if (kvm_is_error_hva (addr)) +return -1 ; + +if (entry < nr_pages) +return 0 ; + +return __get_user_pages_fast (addr, nr_pages,1 , pages); +} +EXPORT_SYMBOL_GPL (gfn_to_page_many_atomic); +struct page *gfn_to_page (struct kvm *kvm,gfn_t gfn) {pfn_t pfn;
Signature · 1 line
1.7.0.4
PATCH v7 3/5 KVM: MMU: introduce gfn_to_page_many_atomic() function
In reply to Xiao GuangrongIntroduce this function to get consecutive gfn's pages, it can reduce gup's overload, used by later patch
--- include/linux/kvm_host.h | 3 +++ virt/kvm/kvm_main.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletions(-)
include/linux/kvm_host.h +3 -0
@@ -289,6 +289,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,void kvm_disable_largepages (void );void kvm_arch_flush_shadow (struct kvm *kvm); +int gfn_to_page_many_atomic (struct kvm *kvm,gfn_t gfn,struct page **pages, +int nr_pages); +struct page *gfn_to_page (struct kvm *kvm,gfn_t gfn);unsigned long gfn_to_hva (struct kvm *kvm,gfn_t gfn);void kvm_release_page_clean (struct page *page);
virt/kvm/kvm_main.c +28 -1
@@ -927,15 +927,25 @@ int memslot_id(struct kvm *kvm, gfn_t gfn)return memslot - slots->memslots; } -unsigned long gfn_to_hva (struct kvm *kvm,gfn_t gfn) +static unsigned long gfn_to_hva_many (struct kvm *kvm,gfn_t gfn, +gfn_t *nr_pages) {struct kvm_memory_slot *slot; slot =gfn_to_memslot (kvm, gfn);if (!slot || slot->flags &KVM_MEMSLOT_INVALID )return bad_hva (); + +if (nr_pages) + *nr_pages = slot->npages - (gfn - slot->base_gfn); +return gfn_to_hva_memslot (slot, gfn); } + +unsigned long gfn_to_hva (struct kvm *kvm,gfn_t gfn) +{ +return gfn_to_hva_many (kvm, gfn,NULL ); +}EXPORT_SYMBOL_GPL (gfn_to_hva);static pfn_t hva_to_pfn (struct kvm *kvm,unsigned long addr,bool atomic)
@@ -1010,6 +1020,23 @@ pfn_t gfn_to_pfn_memslot(struct kvm *kvm,return hva_to_pfn (kvm, addr, false); } +int gfn_to_page_many_atomic (struct kvm *kvm,gfn_t gfn,struct page **pages, +int nr_pages) +{ +unsigned long addr; +gfn_t entry; + + addr =gfn_to_hva_many (kvm, gfn, &entry); +if (kvm_is_error_hva (addr)) +return -1 ; + +if (entry < nr_pages) +return 0 ; + +return __get_user_pages_fast (addr, nr_pages,1 , pages); +} +EXPORT_SYMBOL_GPL (gfn_to_page_many_atomic); +struct page *gfn_to_page (struct kvm *kvm,gfn_t gfn) {pfn_t pfn;
Signature · 1 line
1.7.0.4
PATCH v7 4/5 KVM: MMU: prefetch ptes when intercepted guest #PF
In reply to Xiao GuangrongSupport prefetch ptes when intercept guest #PF, avoid to #PF by later access
If we meet any failure in the prefetch path, we will exit it and not try other ptes to avoid become heavy path
--- arch/x86/kvm/mmu.c | 104 +++++++++++++++++++++++++++++++++++++++++++- arch/x86/kvm/paging_tmpl.h | 72 ++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletions(-)
arch/x86/kvm/mmu.c +103 -1
@@ -89,6 +89,8 @@ module_param(oos_shadow, bool, 0644); }#endif +#define PTE_PREFETCH_NUM 8 +#define PT_FIRST_AVAIL_BITS_SHIFT 9#define PT64_SECOND_AVAIL_BITS_SHIFT 52
@@ -403,7 +405,7 @@ static int mmu_topup_memory_caches(struct kvm_vcpu *vcpu)if (r) goto out; r =mmu_topup_memory_cache (&vcpu->arch.mmu_rmap_desc_cache, - rmap_desc_cache,4 ); + rmap_desc_cache,4 +PTE_PREFETCH_NUM );if (r) goto out; r =mmu_topup_memory_cache_page (&vcpu->arch.mmu_page_cache,8 );
@@ -2090,6 +2092,105 @@ static void nonpaging_new_cr3(struct kvm_vcpu *vcpu) { } +static struct kvm_memory_slot * +pte_prefetch_gfn_to_memslot (struct kvm_vcpu *vcpu,gfn_t gfn,bool no_dirty_log) +{ +struct kvm_memory_slot *slot; + + slot =gfn_to_memslot (vcpu->kvm, gfn); +if (!slot || slot->flags &KVM_MEMSLOT_INVALID || + (no_dirty_log && slot->dirty_bitmap)) + slot =NULL ; + +return slot; +} + +static pfn_t pte_prefetch_gfn_to_pfn (struct kvm_vcpu *vcpu,gfn_t gfn, +bool no_dirty_log) +{ +struct kvm_memory_slot *slot; +unsigned long hva; + + slot =pte_prefetch_gfn_to_memslot (vcpu, gfn, no_dirty_log); +if (!slot) { +get_page (bad_page); +return page_to_pfn (bad_page); + } + + hva =gfn_to_hva_memslot (slot, gfn); + +return hva_to_pfn_atomic (vcpu->kvm, hva); +} + +static int direct_pte_prefetch_many (struct kvm_vcpu *vcpu, +struct kvm_mmu_page *sp, +u64 *start,u64 *end) +{ +struct page *pages[PTE_PREFETCH_NUM ]; +unsigned access = sp->role.access; +int i, ret; +gfn_t gfn; + + gfn =kvm_mmu_page_get_gfn (sp, start - sp->spt); +if (!pte_prefetch_gfn_to_memslot (vcpu, gfn, access &ACC_WRITE_MASK )) +return -1 ; + + ret =gfn_to_page_many_atomic (vcpu->kvm, gfn, pages, end - start); +if (ret <=0 ) +return -1 ; + +for (i =0 ; i < ret; i++, gfn++, start++) +mmu_set_spte (vcpu, start,ACC_ALL , + access,0 ,0 ,1 ,NULL , + sp->role.level, gfn, +page_to_pfn (pages[i]), true, true); + +return 0 ; +} + +static void __direct_pte_prefetch (struct kvm_vcpu *vcpu, +struct kvm_mmu_page *sp,u64 *sptep) +{ +u64 *spte, *start =NULL ; +int i; + +WARN_ON (!sp->role.direct); + + i = (sptep - sp->spt) & ~(PTE_PREFETCH_NUM -1 ); + spte = sp->spt + i; + +for (i =0 ; i <PTE_PREFETCH_NUM ; i++, spte++) { +if (*spte != shadow_trap_nonpresent_pte || spte == sptep) { +if (!start) +continue ; +if (direct_pte_prefetch_many (vcpu, sp, start, spte) <0 ) +break ; + start =NULL ; + }else if (!start) + start = spte; + } +} + +static void direct_pte_prefetch (struct kvm_vcpu *vcpu,u64 *sptep) +{ +struct kvm_mmu_page *sp; + +/* +* Since it's no accessed bit on EPT, it's no way to +* distinguish between actually accessed translations +* and prefetched, so disable pte prefetch if EPT is +* enabled. +*/ +if (!shadow_accessed_mask) +return ; + + sp =page_header (__pa (sptep)); +if (sp->role.level >PT_PAGE_TABLE_LEVEL ) +return ; + +__direct_pte_prefetch (vcpu, sp, sptep); +} +static int __direct_map (struct kvm_vcpu *vcpu,gpa_t v,int write,int level,gfn_t gfn,pfn_t pfn) {
@@ -2103,6 +2204,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,mmu_set_spte (vcpu, iterator.sptep,ACC_ALL ,ACC_ALL ,0 , write,1 , &pt_write, level, gfn, pfn, false, true); +direct_pte_prefetch (vcpu, iterator.sptep); ++vcpu->stat.pf_fixed;break ; }
arch/x86/kvm/paging_tmpl.h +72 -0
84 lines not on this page · raw message
Signature · 1 line
1.7.0.4
PATCH v7 4/5 KVM: MMU: prefetch ptes when intercepted guest #PF
In reply to Xiao GuangrongSupport prefetch ptes when intercept guest #PF, avoid to #PF by later access
If we meet any failure in the prefetch path, we will exit it and not try other ptes to avoid become heavy path
--- arch/x86/kvm/mmu.c | 104 +++++++++++++++++++++++++++++++++++++++++++- arch/x86/kvm/paging_tmpl.h | 72 ++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletions(-)
arch/x86/kvm/mmu.c +103 -1
@@ -89,6 +89,8 @@ module_param(oos_shadow, bool, 0644); }#endif +#define PTE_PREFETCH_NUM 8 +#define PT_FIRST_AVAIL_BITS_SHIFT 9#define PT64_SECOND_AVAIL_BITS_SHIFT 52
@@ -403,7 +405,7 @@ static int mmu_topup_memory_caches(struct kvm_vcpu *vcpu)if (r) goto out; r =mmu_topup_memory_cache (&vcpu->arch.mmu_rmap_desc_cache, - rmap_desc_cache,4 ); + rmap_desc_cache,4 +PTE_PREFETCH_NUM );if (r) goto out; r =mmu_topup_memory_cache_page (&vcpu->arch.mmu_page_cache,8 );
@@ -2090,6 +2092,105 @@ static void nonpaging_new_cr3(struct kvm_vcpu *vcpu) { } +static struct kvm_memory_slot * +pte_prefetch_gfn_to_memslot (struct kvm_vcpu *vcpu,gfn_t gfn,bool no_dirty_log) +{ +struct kvm_memory_slot *slot; + + slot =gfn_to_memslot (vcpu->kvm, gfn); +if (!slot || slot->flags &KVM_MEMSLOT_INVALID || + (no_dirty_log && slot->dirty_bitmap)) + slot =NULL ; + +return slot; +} + +static pfn_t pte_prefetch_gfn_to_pfn (struct kvm_vcpu *vcpu,gfn_t gfn, +bool no_dirty_log) +{ +struct kvm_memory_slot *slot; +unsigned long hva; + + slot =pte_prefetch_gfn_to_memslot (vcpu, gfn, no_dirty_log); +if (!slot) { +get_page (bad_page); +return page_to_pfn (bad_page); + } + + hva =gfn_to_hva_memslot (slot, gfn); + +return hva_to_pfn_atomic (vcpu->kvm, hva); +} + +static int direct_pte_prefetch_many (struct kvm_vcpu *vcpu, +struct kvm_mmu_page *sp, +u64 *start,u64 *end) +{ +struct page *pages[PTE_PREFETCH_NUM ]; +unsigned access = sp->role.access; +int i, ret; +gfn_t gfn; + + gfn =kvm_mmu_page_get_gfn (sp, start - sp->spt); +if (!pte_prefetch_gfn_to_memslot (vcpu, gfn, access &ACC_WRITE_MASK )) +return -1 ; + + ret =gfn_to_page_many_atomic (vcpu->kvm, gfn, pages, end - start); +if (ret <=0 ) +return -1 ; + +for (i =0 ; i < ret; i++, gfn++, start++) +mmu_set_spte (vcpu, start,ACC_ALL , + access,0 ,0 ,1 ,NULL , + sp->role.level, gfn, +page_to_pfn (pages[i]), true, true); + +return 0 ; +} + +static void __direct_pte_prefetch (struct kvm_vcpu *vcpu, +struct kvm_mmu_page *sp,u64 *sptep) +{ +u64 *spte, *start =NULL ; +int i; + +WARN_ON (!sp->role.direct); + + i = (sptep - sp->spt) & ~(PTE_PREFETCH_NUM -1 ); + spte = sp->spt + i; + +for (i =0 ; i <PTE_PREFETCH_NUM ; i++, spte++) { +if (*spte != shadow_trap_nonpresent_pte || spte == sptep) { +if (!start) +continue ; +if (direct_pte_prefetch_many (vcpu, sp, start, spte) <0 ) +break ; + start =NULL ; + }else if (!start) + start = spte; + } +} + +static void direct_pte_prefetch (struct kvm_vcpu *vcpu,u64 *sptep) +{ +struct kvm_mmu_page *sp; + +/* +* Since it's no accessed bit on EPT, it's no way to +* distinguish between actually accessed translations +* and prefetched, so disable pte prefetch if EPT is +* enabled. +*/ +if (!shadow_accessed_mask) +return ; + + sp =page_header (__pa (sptep)); +if (sp->role.level >PT_PAGE_TABLE_LEVEL ) +return ; + +__direct_pte_prefetch (vcpu, sp, sptep); +} +static int __direct_map (struct kvm_vcpu *vcpu,gpa_t v,int write,int level,gfn_t gfn,pfn_t pfn) {
@@ -2103,6 +2204,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,mmu_set_spte (vcpu, iterator.sptep,ACC_ALL ,ACC_ALL ,0 , write,1 , &pt_write, level, gfn, pfn, false, true); +direct_pte_prefetch (vcpu, iterator.sptep); ++vcpu->stat.pf_fixed;break ; }
arch/x86/kvm/paging_tmpl.h +72 -0
84 lines not on this page · raw message
Signature · 1 line
1.7.0.4
PATCH v7 5/5 KVM: MMU: combine guest pte read between fetch and pte prefetch
In reply to Xiao GuangrongCombine guest pte read between guest pte check in the fetch path and pte prefetch
--- arch/x86/kvm/paging_tmpl.h | 40 +++++++++++++++++++++------------------- 1 files changed, 21 insertions(+), 19 deletions(-)
arch/x86/kvm/paging_tmpl.h +21 -19
@@ -67,6 +67,7 @@ struct guest_walker {int level;gfn_t table_gfn[PT_MAX_FULL_LEVELS ];pt_element_t ptes[PT_MAX_FULL_LEVELS ]; +pt_element_t prefetch_ptes[PTE_PREFETCH_NUM ];gpa_t pte_gpa[PT_MAX_FULL_LEVELS ];unsigned pt_access;unsigned pte_access;
@@ -302,21 +303,33 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,static bool FNAME (gpte_changed )(struct kvm_vcpu *vcpu,struct guest_walker *gw,int level) { -int r;pt_element_t curr_pte; - - r =kvm_read_guest_atomic (vcpu->kvm, gw->pte_gpa[level -1 ], +gpa_t base_gpa, pte_gpa = gw->pte_gpa[level -1 ]; +u64 mask; +int r, index; + +if (level ==PT_PAGE_TABLE_LEVEL ) { + mask =PTE_PREFETCH_NUM *sizeof (pt_element_t) -1 ; + base_gpa = pte_gpa & ~mask; + index = (pte_gpa - base_gpa) /sizeof (pt_element_t); + + r =kvm_read_guest_atomic (vcpu->kvm, base_gpa, + gw->prefetch_ptes,sizeof (gw->prefetch_ptes)); + curr_pte = gw->prefetch_ptes[index]; + }else + r =kvm_read_guest_atomic (vcpu->kvm, pte_gpa, &curr_pte,sizeof (curr_pte)); +return r || curr_pte != gw->ptes[level -1 ]; } -static void FNAME (pte_prefetch )(struct kvm_vcpu *vcpu,u64 *sptep) +static void FNAME (pte_prefetch )(struct kvm_vcpu *vcpu,struct guest_walker *gw, +u64 *sptep) {struct kvm_mmu_page *sp; -pt_element_t gptep[PTE_PREFETCH_NUM ]; -gpa_t first_pte_gpa; -int offset =0 , i; +pt_element_t *gptep = gw->prefetch_ptes;u64 *spte; +int i; sp =page_header (__pa (sptep));
@@ -327,17 +340,6 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, u64 *sptep)return __direct_pte_prefetch (vcpu, sp, sptep); i = (sptep - sp->spt) & ~(PTE_PREFETCH_NUM -1 ); - -if (PTTYPE ==32 ) - offset = sp->role.quadrant <<PT64_LEVEL_BITS ; - - first_pte_gpa =gfn_to_gpa (sp->gfn) + - (offset + i) *sizeof (pt_element_t); - -if (kvm_read_guest_atomic (vcpu->kvm, first_pte_gpa, gptep, -sizeof (gptep)) <0 ) -return ; - spte = sp->spt + i;for (i =0 ; i <PTE_PREFETCH_NUM ; i++, spte++) {
@@ -462,7 +464,7 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,mmu_set_spte (vcpu, it.sptep, access, gw->pte_access & access, user_fault, write_fault, dirty, ptwrite, it.level, gw->gfn, pfn, false, true); -FNAME (pte_prefetch)(vcpu, it.sptep); +FNAME (pte_prefetch)(vcpu, gw, it.sptep);return it.sptep;
Signature · 1 line
1.7.0.4
Marcelo Tosatti
AppliedRe: [PATCH v7 5/5] KVM: MMU: combine guest pte read between fetch and pte prefetch
Quoted 6 lines · Xiao Guangrong
Combine guest pte read between guest pte check in the fetch path and pte prefetch
Signed-off-by Xiao Guangrong <[email protected]>--- arch/x86/kvm/paging_tmpl.h | 40 +++++++++++++++++++++------------------- 1 files changed, 21 insertions(+), 19 deletions(-)
Applied all, thanks.
PATCH v7 5/5 KVM: MMU: combine guest pte read between fetch and pte prefetch
In reply to Xiao GuangrongCombine guest pte read between guest pte check in the fetch path and pte prefetch
--- arch/x86/kvm/paging_tmpl.h | 40 +++++++++++++++++++++------------------- 1 files changed, 21 insertions(+), 19 deletions(-)
arch/x86/kvm/paging_tmpl.h +21 -19
@@ -67,6 +67,7 @@ struct guest_walker {int level;gfn_t table_gfn[PT_MAX_FULL_LEVELS ];pt_element_t ptes[PT_MAX_FULL_LEVELS ]; +pt_element_t prefetch_ptes[PTE_PREFETCH_NUM ];gpa_t pte_gpa[PT_MAX_FULL_LEVELS ];unsigned pt_access;unsigned pte_access;
@@ -302,21 +303,33 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,static bool FNAME (gpte_changed )(struct kvm_vcpu *vcpu,struct guest_walker *gw,int level) { -int r;pt_element_t curr_pte; - - r =kvm_read_guest_atomic (vcpu->kvm, gw->pte_gpa[level -1 ], +gpa_t base_gpa, pte_gpa = gw->pte_gpa[level -1 ]; +u64 mask; +int r, index; + +if (level ==PT_PAGE_TABLE_LEVEL ) { + mask =PTE_PREFETCH_NUM *sizeof (pt_element_t) -1 ; + base_gpa = pte_gpa & ~mask; + index = (pte_gpa - base_gpa) /sizeof (pt_element_t); + + r =kvm_read_guest_atomic (vcpu->kvm, base_gpa, + gw->prefetch_ptes,sizeof (gw->prefetch_ptes)); + curr_pte = gw->prefetch_ptes[index]; + }else + r =kvm_read_guest_atomic (vcpu->kvm, pte_gpa, &curr_pte,sizeof (curr_pte)); +return r || curr_pte != gw->ptes[level -1 ]; } -static void FNAME (pte_prefetch )(struct kvm_vcpu *vcpu,u64 *sptep) +static void FNAME (pte_prefetch )(struct kvm_vcpu *vcpu,struct guest_walker *gw, +u64 *sptep) {struct kvm_mmu_page *sp; -pt_element_t gptep[PTE_PREFETCH_NUM ]; -gpa_t first_pte_gpa; -int offset =0 , i; +pt_element_t *gptep = gw->prefetch_ptes;u64 *spte; +int i; sp =page_header (__pa (sptep));
@@ -327,17 +340,6 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, u64 *sptep)return __direct_pte_prefetch (vcpu, sp, sptep); i = (sptep - sp->spt) & ~(PTE_PREFETCH_NUM -1 ); - -if (PTTYPE ==32 ) - offset = sp->role.quadrant <<PT64_LEVEL_BITS ; - - first_pte_gpa =gfn_to_gpa (sp->gfn) + - (offset + i) *sizeof (pt_element_t); - -if (kvm_read_guest_atomic (vcpu->kvm, first_pte_gpa, gptep, -sizeof (gptep)) <0 ) -return ; - spte = sp->spt + i;for (i =0 ; i <PTE_PREFETCH_NUM ; i++, spte++) {
@@ -462,7 +464,7 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,mmu_set_spte (vcpu, it.sptep, access, gw->pte_access & access, user_fault, write_fault, dirty, ptwrite, it.level, gw->gfn, pfn, false, true); -FNAME (pte_prefetch)(vcpu, it.sptep); +FNAME (pte_prefetch)(vcpu, gw, it.sptep);return it.sptep;
Signature · 1 line
1.7.0.4