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 <vdonnefort@google.com>
Signed-off-by: Quentin Perret <qperret@google.com>
This commit is contained in:
Vincent Donnefort
2022-07-04 14:32:56 +01:00
committed by Quentin Perret
parent 059a19c4ef
commit b75ae68d19
5 changed files with 57 additions and 13 deletions

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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 = {

View File

@@ -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;
}
/**

View File

@@ -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) {