Skip to content

Commit b88835a

Browse files
author
Marc Zyngier
committed
Merge branch kvm-arm64/mmu/stage2-cmos into kvmarm-master/next
Cache maintenance updates from Yanan Wang, moving the CMOs down into the page-table code. This ensures that we only issue them when actually performing a mapping rather than upfront. * kvm-arm64/mmu/stage2-cmos: KVM: arm64: Move guest CMOs to the fault handlers KVM: arm64: Tweak parameters of guest cache maintenance functions KVM: arm64: Introduce mm_ops member for structure stage2_attr_data KVM: arm64: Introduce two cache maintenance callbacks
2 parents fbba7e6 + 25aa286 commit b88835a

4 files changed

Lines changed: 81 additions & 57 deletions

File tree

arch/arm64/include/asm/kvm_mmu.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,8 @@ static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)
187187
return (vcpu_read_sys_reg(vcpu, SCTLR_EL1) & 0b101) == 0b101;
188188
}
189189

190-
static inline void __clean_dcache_guest_page(kvm_pfn_t pfn, unsigned long size)
190+
static inline void __clean_dcache_guest_page(void *va, size_t size)
191191
{
192-
void *va = page_address(pfn_to_page(pfn));
193-
194192
/*
195193
* With FWB, we ensure that the guest always accesses memory using
196194
* cacheable attributes, and we don't have to clean to PoC when
@@ -203,16 +201,13 @@ static inline void __clean_dcache_guest_page(kvm_pfn_t pfn, unsigned long size)
203201
kvm_flush_dcache_to_poc(va, size);
204202
}
205203

206-
static inline void __invalidate_icache_guest_page(kvm_pfn_t pfn,
207-
unsigned long size)
204+
static inline void __invalidate_icache_guest_page(void *va, size_t size)
208205
{
209206
if (icache_is_aliasing()) {
210207
/* any kind of VIPT cache */
211208
__flush_icache_all();
212209
} else if (is_kernel_in_hyp_mode() || !icache_is_vpipt()) {
213210
/* PIPT or VPIPT at EL2 (see comment in __kvm_tlb_flush_vmid_ipa) */
214-
void *va = page_address(pfn_to_page(pfn));
215-
216211
invalidate_icache_range((unsigned long)va,
217212
(unsigned long)va + size);
218213
}

arch/arm64/include/asm/kvm_pgtable.h

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,29 @@ typedef u64 kvm_pte_t;
2727

2828
/**
2929
* struct kvm_pgtable_mm_ops - Memory management callbacks.
30-
* @zalloc_page: Allocate a single zeroed memory page. The @arg parameter
31-
* can be used by the walker to pass a memcache. The
32-
* initial refcount of the page is 1.
33-
* @zalloc_pages_exact: Allocate an exact number of zeroed memory pages. The
34-
* @size parameter is in bytes, and is rounded-up to the
35-
* next page boundary. The resulting allocation is
36-
* physically contiguous.
37-
* @free_pages_exact: Free an exact number of memory pages previously
38-
* allocated by zalloc_pages_exact.
39-
* @get_page: Increment the refcount on a page.
40-
* @put_page: Decrement the refcount on a page. When the refcount
41-
* reaches 0 the page is automatically freed.
42-
* @page_count: Return the refcount of a page.
43-
* @phys_to_virt: Convert a physical address into a virtual address mapped
44-
* in the current context.
45-
* @virt_to_phys: Convert a virtual address mapped in the current context
46-
* into a physical address.
30+
* @zalloc_page: Allocate a single zeroed memory page.
31+
* The @arg parameter can be used by the walker
32+
* to pass a memcache. The initial refcount of
33+
* the page is 1.
34+
* @zalloc_pages_exact: Allocate an exact number of zeroed memory pages.
35+
* The @size parameter is in bytes, and is rounded
36+
* up to the next page boundary. The resulting
37+
* allocation is physically contiguous.
38+
* @free_pages_exact: Free an exact number of memory pages previously
39+
* allocated by zalloc_pages_exact.
40+
* @get_page: Increment the refcount on a page.
41+
* @put_page: Decrement the refcount on a page. When the
42+
* refcount reaches 0 the page is automatically
43+
* freed.
44+
* @page_count: Return the refcount of a page.
45+
* @phys_to_virt: Convert a physical address into a virtual
46+
* address mapped in the current context.
47+
* @virt_to_phys: Convert a virtual address mapped in the current
48+
* context into a physical address.
49+
* @dcache_clean_inval_poc: Clean and invalidate the data cache to the PoC
50+
* for the specified memory address range.
51+
* @icache_inval_pou: Invalidate the instruction cache to the PoU
52+
* for the specified memory address range.
4753
*/
4854
struct kvm_pgtable_mm_ops {
4955
void* (*zalloc_page)(void *arg);
@@ -54,6 +60,8 @@ struct kvm_pgtable_mm_ops {
5460
int (*page_count)(void *addr);
5561
void* (*phys_to_virt)(phys_addr_t phys);
5662
phys_addr_t (*virt_to_phys)(void *addr);
63+
void (*dcache_clean_inval_poc)(void *addr, size_t size);
64+
void (*icache_inval_pou)(void *addr, size_t size);
5765
};
5866

5967
/**

arch/arm64/kvm/hyp/pgtable.c

Lines changed: 37 additions & 11 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)
@@ -861,10 +875,11 @@ int kvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size)
861875
}
862876

863877
struct stage2_attr_data {
864-
kvm_pte_t attr_set;
865-
kvm_pte_t attr_clr;
866-
kvm_pte_t pte;
867-
u32 level;
878+
kvm_pte_t attr_set;
879+
kvm_pte_t attr_clr;
880+
kvm_pte_t pte;
881+
u32 level;
882+
struct kvm_pgtable_mm_ops *mm_ops;
868883
};
869884

870885
static int stage2_attr_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
@@ -873,6 +888,7 @@ static int stage2_attr_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
873888
{
874889
kvm_pte_t pte = *ptep;
875890
struct stage2_attr_data *data = arg;
891+
struct kvm_pgtable_mm_ops *mm_ops = data->mm_ops;
876892

877893
if (!kvm_pte_valid(pte))
878894
return 0;
@@ -887,8 +903,17 @@ static int stage2_attr_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
887903
* but worst-case the access flag update gets lost and will be
888904
* set on the next access instead.
889905
*/
890-
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));
891915
WRITE_ONCE(*ptep, pte);
916+
}
892917

893918
return 0;
894919
}
@@ -903,6 +928,7 @@ static int stage2_update_leaf_attrs(struct kvm_pgtable *pgt, u64 addr,
903928
struct stage2_attr_data data = {
904929
.attr_set = attr_set & attr_mask,
905930
.attr_clr = attr_clr & attr_mask,
931+
.mm_ops = pgt->mm_ops,
906932
};
907933
struct kvm_pgtable_walker walker = {
908934
.cb = stage2_attr_walker,

arch/arm64/kvm/mmu.c

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,16 @@ static void *kvm_host_va(phys_addr_t phys)
126126
return __va(phys);
127127
}
128128

129+
static void clean_dcache_guest_page(void *va, size_t size)
130+
{
131+
__clean_dcache_guest_page(va, size);
132+
}
133+
134+
static void invalidate_icache_guest_page(void *va, size_t size)
135+
{
136+
__invalidate_icache_guest_page(va, size);
137+
}
138+
129139
/*
130140
* Unmapping vs dcache management:
131141
*
@@ -432,6 +442,8 @@ static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = {
432442
.page_count = kvm_host_page_count,
433443
.phys_to_virt = kvm_host_va,
434444
.virt_to_phys = kvm_host_pa,
445+
.dcache_clean_inval_poc = clean_dcache_guest_page,
446+
.icache_inval_pou = invalidate_icache_guest_page,
435447
};
436448

437449
/**
@@ -693,16 +705,6 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
693705
kvm_mmu_write_protect_pt_masked(kvm, slot, gfn_offset, mask);
694706
}
695707

696-
static void clean_dcache_guest_page(kvm_pfn_t pfn, unsigned long size)
697-
{
698-
__clean_dcache_guest_page(pfn, size);
699-
}
700-
701-
static void invalidate_icache_guest_page(kvm_pfn_t pfn, unsigned long size)
702-
{
703-
__invalidate_icache_guest_page(pfn, size);
704-
}
705-
706708
static void kvm_send_hwpoison_signal(unsigned long address, short lsb)
707709
{
708710
send_sig_mceerr(BUS_MCEERR_AR, (void __user *)address, lsb, current);
@@ -1012,13 +1014,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
10121014
if (writable)
10131015
prot |= KVM_PGTABLE_PROT_W;
10141016

1015-
if (fault_status != FSC_PERM && !device)
1016-
clean_dcache_guest_page(pfn, vma_pagesize);
1017-
1018-
if (exec_fault) {
1017+
if (exec_fault)
10191018
prot |= KVM_PGTABLE_PROT_X;
1020-
invalidate_icache_guest_page(pfn, vma_pagesize);
1021-
}
10221019

10231020
if (device)
10241021
prot |= KVM_PGTABLE_PROT_DEVICE;
@@ -1216,12 +1213,10 @@ bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
12161213
WARN_ON(range->end - range->start != 1);
12171214

12181215
/*
1219-
* We've moved a page around, probably through CoW, so let's treat it
1220-
* just like a translation fault and clean the cache to the PoC.
1221-
*/
1222-
clean_dcache_guest_page(pfn, PAGE_SIZE);
1223-
1224-
/*
1216+
* We've moved a page around, probably through CoW, so let's treat
1217+
* it just like a translation fault and the map handler will clean
1218+
* the cache to the PoC.
1219+
*
12251220
* The MMU notifiers will have unmapped a huge PMD before calling
12261221
* ->change_pte() (which in turn calls kvm_set_spte_gfn()) and
12271222
* therefore we never need to clear out a huge PMD through this

0 commit comments

Comments
 (0)