Skip to content

Commit 25aa286

Browse files
YananWang-hubMarc Zyngier
authored andcommitted
KVM: arm64: Move guest CMOs to the fault handlers
We currently uniformly perform CMOs of D-cache and I-cache in function user_mem_abort before calling the fault handlers. If we get concurrent guest faults(e.g. translation faults, permission faults) or some really unnecessary guest faults caused by BBM, CMOs for the first vcpu are necessary while the others later are not. By moving CMOs to the fault handlers, we can easily identify conditions where they are really needed and avoid the unnecessary ones. As it's a time consuming process to perform CMOs especially when flushing a block range, so this solution reduces much load of kvm and improve efficiency of the stage-2 page table code. We can imagine two specific scenarios which will gain much benefit: 1) In a normal VM startup, this solution will improve the efficiency of handling guest page faults incurred by vCPUs, when initially populating stage-2 page tables. 2) After live migration, the heavy workload will be resumed on the destination VM, however all the stage-2 page tables need to be rebuilt at the moment. So this solution will ease the performance drop during resuming stage. Reviewed-by: Fuad Tabba <tabba@google.com> Signed-off-by: Yanan Wang <wangyanan55@huawei.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20210617105824.31752-5-wangyanan55@huawei.com
1 parent 378e6a9 commit 25aa286

File tree

2 files changed

+38
-21
lines changed

2 files changed

+38
-21
lines changed

arch/arm64/kvm/hyp/pgtable.c

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -577,12 +577,24 @@ static void stage2_put_pte(kvm_pte_t *ptep, struct kvm_s2_mmu *mmu, u64 addr,
577577
mm_ops->put_page(ptep);
578578
}
579579

580+
static bool stage2_pte_cacheable(struct kvm_pgtable *pgt, kvm_pte_t pte)
581+
{
582+
u64 memattr = pte & KVM_PTE_LEAF_ATTR_LO_S2_MEMATTR;
583+
return memattr == KVM_S2_MEMATTR(pgt, NORMAL);
584+
}
585+
586+
static bool stage2_pte_executable(kvm_pte_t pte)
587+
{
588+
return !(pte & KVM_PTE_LEAF_ATTR_HI_S2_XN);
589+
}
590+
580591
static int stage2_map_walker_try_leaf(u64 addr, u64 end, u32 level,
581592
kvm_pte_t *ptep,
582593
struct stage2_map_data *data)
583594
{
584595
kvm_pte_t new, old = *ptep;
585596
u64 granule = kvm_granule_size(level), phys = data->phys;
597+
struct kvm_pgtable *pgt = data->mmu->pgt;
586598
struct kvm_pgtable_mm_ops *mm_ops = data->mm_ops;
587599

588600
if (!kvm_block_mapping_supported(addr, end, phys, level))
@@ -606,6 +618,14 @@ static int stage2_map_walker_try_leaf(u64 addr, u64 end, u32 level,
606618
stage2_put_pte(ptep, data->mmu, addr, level, mm_ops);
607619
}
608620

621+
/* Perform CMOs before installation of the guest stage-2 PTE */
622+
if (mm_ops->dcache_clean_inval_poc && stage2_pte_cacheable(pgt, new))
623+
mm_ops->dcache_clean_inval_poc(kvm_pte_follow(new, mm_ops),
624+
granule);
625+
626+
if (mm_ops->icache_inval_pou && stage2_pte_executable(new))
627+
mm_ops->icache_inval_pou(kvm_pte_follow(new, mm_ops), granule);
628+
609629
smp_store_release(ptep, new);
610630
if (stage2_pte_is_counted(new))
611631
mm_ops->get_page(ptep);
@@ -798,12 +818,6 @@ int kvm_pgtable_stage2_set_owner(struct kvm_pgtable *pgt, u64 addr, u64 size,
798818
return ret;
799819
}
800820

801-
static bool stage2_pte_cacheable(struct kvm_pgtable *pgt, kvm_pte_t pte)
802-
{
803-
u64 memattr = pte & KVM_PTE_LEAF_ATTR_LO_S2_MEMATTR;
804-
return memattr == KVM_S2_MEMATTR(pgt, NORMAL);
805-
}
806-
807821
static int stage2_unmap_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
808822
enum kvm_pgtable_walk_flags flag,
809823
void * const arg)
@@ -874,6 +888,7 @@ static int stage2_attr_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
874888
{
875889
kvm_pte_t pte = *ptep;
876890
struct stage2_attr_data *data = arg;
891+
struct kvm_pgtable_mm_ops *mm_ops = data->mm_ops;
877892

878893
if (!kvm_pte_valid(pte))
879894
return 0;
@@ -888,8 +903,17 @@ static int stage2_attr_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
888903
* but worst-case the access flag update gets lost and will be
889904
* set on the next access instead.
890905
*/
891-
if (data->pte != pte)
906+
if (data->pte != pte) {
907+
/*
908+
* Invalidate instruction cache before updating the guest
909+
* stage-2 PTE if we are going to add executable permission.
910+
*/
911+
if (mm_ops->icache_inval_pou &&
912+
stage2_pte_executable(pte) && !stage2_pte_executable(*ptep))
913+
mm_ops->icache_inval_pou(kvm_pte_follow(pte, mm_ops),
914+
kvm_granule_size(level));
892915
WRITE_ONCE(*ptep, pte);
916+
}
893917

894918
return 0;
895919
}

arch/arm64/kvm/mmu.c

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,8 @@ static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = {
442442
.page_count = kvm_host_page_count,
443443
.phys_to_virt = kvm_host_va,
444444
.virt_to_phys = kvm_host_pa,
445+
.dcache_clean_inval_poc = clean_dcache_guest_page,
446+
.icache_inval_pou = invalidate_icache_guest_page,
445447
};
446448

447449
/**
@@ -971,15 +973,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
971973
if (writable)
972974
prot |= KVM_PGTABLE_PROT_W;
973975

974-
if (fault_status != FSC_PERM && !device)
975-
clean_dcache_guest_page(page_address(pfn_to_page(pfn)),
976-
vma_pagesize);
977-
978-
if (exec_fault) {
976+
if (exec_fault)
979977
prot |= KVM_PGTABLE_PROT_X;
980-
invalidate_icache_guest_page(page_address(pfn_to_page(pfn)),
981-
vma_pagesize);
982-
}
983978

984979
if (device)
985980
prot |= KVM_PGTABLE_PROT_DEVICE;
@@ -1177,12 +1172,10 @@ bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
11771172
WARN_ON(range->end - range->start != 1);
11781173

11791174
/*
1180-
* We've moved a page around, probably through CoW, so let's treat it
1181-
* just like a translation fault and clean the cache to the PoC.
1182-
*/
1183-
clean_dcache_guest_page(page_address(pfn_to_page(pfn)), PAGE_SIZE);
1184-
1185-
/*
1175+
* We've moved a page around, probably through CoW, so let's treat
1176+
* it just like a translation fault and the map handler will clean
1177+
* the cache to the PoC.
1178+
*
11861179
* The MMU notifiers will have unmapped a huge PMD before calling
11871180
* ->change_pte() (which in turn calls kvm_set_spte_gfn()) and
11881181
* therefore we never need to clear out a huge PMD through this

0 commit comments

Comments
 (0)