From b75ae68d193758b2a2e18dfc9f0b1668735dbbae Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Mon, 4 Jul 2022 14:32:56 +0100 Subject: [PATCH] ANDROID: KVM: arm64: Add protected_hyp_mem VM statistic When using nVHE in protected mode, the host allocates memory for the hypervisor to store shadow structures and the stage-2 page tables. This has been proven to be an interesting value to follow, for debug and health purpose. Account for those allocations in bytes, in a newly created VM statistic "protected_hyp_mem". It is expected, on VM teardown to reclaim all that memory. Raise a warning if not all the donations are recovered. Bug: 222044477 Change-Id: I18657d275f2ced67ceb6d0e4bd5ce41cf1d41dc8 Signed-off-by: Vincent Donnefort Signed-off-by: Quentin Perret --- arch/arm64/include/asm/kvm_host.h | 3 +- arch/arm64/kvm/arm.c | 6 +++- arch/arm64/kvm/guest.c | 3 +- arch/arm64/kvm/mmu.c | 48 +++++++++++++++++++++++++------ arch/arm64/kvm/pkvm.c | 10 +++++-- 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 9409aa82ff6a..7b8b328ebf0c 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -127,7 +127,7 @@ static inline void __free_hyp_memcache(struct kvm_hyp_memcache *mc, free_fn(pop_hyp_memcache(mc, to_va), arg); } -void free_hyp_memcache(struct kvm_hyp_memcache *mc); +void free_hyp_memcache(struct kvm_hyp_memcache *mc, struct kvm *kvm); int topup_hyp_memcache(struct kvm_vcpu *vcpu); struct kvm_vmid { @@ -821,6 +821,7 @@ static inline bool __vcpu_write_sys_reg_to_cpu(u64 val, int reg) struct kvm_vm_stat { struct kvm_vm_stat_generic generic; + atomic64_t protected_hyp_mem; }; struct kvm_vcpu_stat { diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 09f35ba6ed1d..eee234b3db88 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -212,6 +212,10 @@ void kvm_arch_destroy_vm(struct kvm *kvm) kvm_destroy_vcpus(kvm); + if (atomic64_read(&kvm->stat.protected_hyp_mem)) + pr_warn("%lluB of donations to the nVHE hyp are missing\n", + atomic64_read(&kvm->stat.protected_hyp_mem)); + kvm_unshare_hyp(kvm, kvm + 1); } @@ -459,7 +463,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) static_branch_dec(&userspace_irqchip_in_use); if (is_protected_kvm_enabled()) - free_hyp_memcache(&vcpu->arch.pkvm_memcache); + free_hyp_memcache(&vcpu->arch.pkvm_memcache, vcpu->kvm); else kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index 2ff13a3f8479..ee2bd0c0ad90 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -29,7 +29,8 @@ #include "trace.h" const struct _kvm_stats_desc kvm_vm_stats_desc[] = { - KVM_GENERIC_VM_STATS() + KVM_GENERIC_VM_STATS(), + STATS_DESC_ICOUNTER(VM, protected_hyp_mem), }; const struct kvm_stats_header kvm_vm_stats_header = { diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index b23af71ebef6..0de691fb3541 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -871,22 +871,54 @@ static void *hyp_mc_alloc_fn(void *unused) return (void *)__get_free_page(GFP_KERNEL_ACCOUNT); } -void free_hyp_memcache(struct kvm_hyp_memcache *mc) +static void account_hyp_memcache(struct kvm_hyp_memcache *mc, + unsigned long prev_nr_pages, + struct kvm *kvm) { - if (is_protected_kvm_enabled()) - __free_hyp_memcache(mc, hyp_mc_free_fn, - kvm_host_va, NULL); + unsigned long nr_pages = mc->nr_pages; + + if (prev_nr_pages == nr_pages) + return; + + if (nr_pages > prev_nr_pages) { + atomic64_add((nr_pages - prev_nr_pages) << PAGE_SHIFT, + &kvm->stat.protected_hyp_mem); + } else { + atomic64_sub((prev_nr_pages - nr_pages) << PAGE_SHIFT, + &kvm->stat.protected_hyp_mem); + } +} + +void free_hyp_memcache(struct kvm_hyp_memcache *mc, struct kvm *kvm) +{ + unsigned long prev_nr_pages; + + if (!is_protected_kvm_enabled()) + return; + + prev_nr_pages = mc->nr_pages; + __free_hyp_memcache(mc, hyp_mc_free_fn, kvm_host_va, NULL); + account_hyp_memcache(mc, prev_nr_pages, kvm); } int topup_hyp_memcache(struct kvm_vcpu *vcpu) { + struct kvm_hyp_memcache *mc = &vcpu->arch.pkvm_memcache; + unsigned long prev_nr_pages; + int err; + if (!is_protected_kvm_enabled()) return 0; - return __topup_hyp_memcache(&vcpu->arch.pkvm_memcache, - kvm_mmu_cache_min_pages(vcpu->kvm), - hyp_mc_alloc_fn, - kvm_host_pa, NULL); + prev_nr_pages = mc->nr_pages; + + err = __topup_hyp_memcache(mc, kvm_mmu_cache_min_pages(vcpu->kvm), + hyp_mc_alloc_fn, + kvm_host_pa, NULL); + if (!err) + account_hyp_memcache(mc, prev_nr_pages, vcpu->kvm); + + return err; } /** diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c index a86328a92374..3c0f232d2c1c 100644 --- a/arch/arm64/kvm/pkvm.c +++ b/arch/arm64/kvm/pkvm.c @@ -117,7 +117,7 @@ void __init kvm_hyp_reserve(void) */ static int __pkvm_create_hyp_vm(struct kvm *host_kvm) { - size_t pgd_sz, hyp_vm_sz, hyp_vcpu_sz, last_ran_sz; + size_t pgd_sz, hyp_vm_sz, hyp_vcpu_sz, last_ran_sz, total_sz; struct kvm_vcpu *host_vcpu; pkvm_handle_t handle; void *pgd, *hyp_vm, *last_ran; @@ -165,6 +165,8 @@ static int __pkvm_create_hyp_vm(struct kvm *host_kvm) host_kvm->arch.pkvm.handle = handle; + total_sz = hyp_vm_sz + last_ran_sz + pgd_sz; + /* Donate memory for the vcpus at hyp and initialize it. */ hyp_vcpu_sz = PAGE_ALIGN(PKVM_HYP_VCPU_SIZE); kvm_for_each_vcpu(idx, host_vcpu, host_kvm) { @@ -182,6 +184,8 @@ static int __pkvm_create_hyp_vm(struct kvm *host_kvm) goto destroy_vm; } + total_sz += hyp_vcpu_sz; + ret = kvm_call_hyp_nvhe(__pkvm_init_vcpu, handle, host_vcpu, hyp_vcpu); if (ret) { @@ -190,6 +194,8 @@ static int __pkvm_create_hyp_vm(struct kvm *host_kvm) } } + atomic64_set(&host_kvm->stat.protected_hyp_mem, total_sz); + return 0; destroy_vm: @@ -228,7 +234,7 @@ void pkvm_destroy_hyp_vm(struct kvm *host_kvm) } host_kvm->arch.pkvm.handle = 0; - free_hyp_memcache(&host_kvm->arch.pkvm.teardown_mc); + free_hyp_memcache(&host_kvm->arch.pkvm.teardown_mc, host_kvm); node = rb_first(&host_kvm->arch.pkvm.pinned_pages); while (node) {