ANDROID: KVM: arm64: Huge page support for pKVM guest memory reclaim

When a pKVM guest dies, all its memory must be returned to the host
which can then unpin it. In preparation to handling THP for guest
stage-2, extend that reclaiming interface to take order > 0 pages.

Bug: 278749606
Bug: 278011447
Change-Id: I47c7069c783ca7e7a47b40aee2a4f7029ee233f1
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
This commit is contained in:
Vincent Donnefort 2024-04-03 14:33:05 +01:00 committed by Treehugger Robot
parent ea6a2e00c8
commit 39c6e8914d
6 changed files with 55 additions and 27 deletions

View File

@ -28,7 +28,7 @@ extern unsigned long hyp_nr_cpus;
int __pkvm_prot_finalize(void);
int __pkvm_host_share_hyp(u64 pfn);
int __pkvm_host_unshare_hyp(u64 pfn);
int __pkvm_host_reclaim_page(struct pkvm_hyp_vm *vm, u64 pfn, u64 ipa);
int __pkvm_host_reclaim_page(struct pkvm_hyp_vm *vm, u64 pfn, u64 ipa, u8 order);
int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages);
int ___pkvm_host_donate_hyp(u64 pfn, u64 nr_pages, bool accept_mmio);
int ___pkvm_host_donate_hyp_prot(u64 pfn, u64 nr_pages,

View File

@ -119,7 +119,7 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long pgd_hva);
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu);
int __pkvm_start_teardown_vm(pkvm_handle_t handle);
int __pkvm_finalize_teardown_vm(pkvm_handle_t handle);
int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 pfn, u64 gfn);
int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 pfn, u64 gfn, u8 order);
struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
unsigned int vcpu_idx);

View File

@ -1294,8 +1294,10 @@ static void handle___pkvm_reclaim_dying_guest_page(struct kvm_cpu_context *host_
DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
DECLARE_REG(u64, pfn, host_ctxt, 2);
DECLARE_REG(u64, gfn, host_ctxt, 3);
DECLARE_REG(u64, order, host_ctxt, 4);
cpu_reg(host_ctxt, 1) = __pkvm_reclaim_dying_guest_page(handle, pfn, gfn);
cpu_reg(host_ctxt, 1) =
__pkvm_reclaim_dying_guest_page(handle, pfn, gfn, order);
}
static void handle___pkvm_create_private_mapping(struct kvm_cpu_context *host_ctxt)

View File

@ -396,7 +396,8 @@ int __pkvm_guest_relinquish_to_host(struct pkvm_hyp_vcpu *vcpu,
goto end;
/* Zap the guest stage2 pte and return ownership to the host */
ret = kvm_pgtable_stage2_unmap(&vm->pgt, ipa, PAGE_SIZE);
ret = kvm_pgtable_stage2_annotate(&vm->pgt, ipa, PAGE_SIZE,
&vcpu->vcpu.arch.stage2_mc, 0);
if (ret)
goto end;
@ -2502,6 +2503,30 @@ int __pkvm_host_share_guest(struct pkvm_hyp_vcpu *vcpu, u64 pfn, u64 gfn,
return ret;
}
static int guest_get_valid_pte(struct pkvm_hyp_vm *vm, u64 pfn, u64 ipa,
u8 order, kvm_pte_t *pte)
{
size_t size = PAGE_SIZE << order;
u64 phys = hyp_pfn_to_phys(pfn);
u32 level;
if (order && size != PMD_SIZE)
return -EINVAL;
WARN_ON(kvm_pgtable_get_leaf(&vm->pgt, ipa, pte, &level));
if (kvm_granule_size(level) != size)
return -E2BIG;
if (!kvm_pte_valid(*pte))
return -ENOENT;
if (phys != kvm_pte_to_phys(*pte))
return -EINVAL;
return 0;
}
int __pkvm_host_unshare_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vm *vm)
{
int ret;
@ -2700,48 +2725,41 @@ void drain_hyp_pool(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc)
}
}
int __pkvm_host_reclaim_page(struct pkvm_hyp_vm *vm, u64 pfn, u64 ipa)
int __pkvm_host_reclaim_page(struct pkvm_hyp_vm *vm, u64 pfn, u64 ipa, u8 order)
{
phys_addr_t phys = hyp_pfn_to_phys(pfn);
size_t page_size = PAGE_SIZE << order;
kvm_pte_t pte;
int ret;
int ret = 0;
host_lock_component();
guest_lock_component(vm);
ret = kvm_pgtable_get_leaf(&vm->pgt, ipa, &pte, NULL);
ret = guest_get_valid_pte(vm, pfn, ipa, order, &pte);
if (ret)
goto unlock;
if (!kvm_pte_valid(pte)) {
ret = -EINVAL;
goto unlock;
} else if (phys != kvm_pte_to_phys(pte)) {
ret = -EPERM;
goto unlock;
}
/* We could avoid TLB inval, it is done per VMID on the finalize path */
WARN_ON(kvm_pgtable_stage2_unmap(&vm->pgt, ipa, PAGE_SIZE));
WARN_ON(kvm_pgtable_stage2_unmap(&vm->pgt, ipa, page_size));
switch((int)guest_get_page_state(pte, ipa)) {
case PKVM_PAGE_OWNED:
WARN_ON(__host_check_page_state_range(phys, PAGE_SIZE, PKVM_NOPAGE));
WARN_ON(__host_check_page_state_range(phys, page_size, PKVM_NOPAGE));
hyp_poison_page(phys);
psci_mem_protect_dec(1);
psci_mem_protect_dec(order);
break;
case PKVM_PAGE_SHARED_BORROWED:
case PKVM_PAGE_SHARED_BORROWED | PKVM_PAGE_RESTRICTED_PROT:
WARN_ON(__host_check_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_SHARED_OWNED));
WARN_ON(__host_check_page_state_range(phys, page_size, PKVM_PAGE_SHARED_OWNED));
break;
case PKVM_PAGE_SHARED_OWNED:
WARN_ON(__host_check_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_SHARED_BORROWED));
WARN_ON(__host_check_page_state_range(phys, page_size, PKVM_PAGE_SHARED_BORROWED));
break;
default:
BUG_ON(1);
}
WARN_ON(host_stage2_set_owner_locked(phys, PAGE_SIZE, PKVM_ID_HOST));
WARN_ON(host_stage2_set_owner_locked(phys, page_size, PKVM_ID_HOST));
unlock:
guest_unlock_component(vm);

View File

@ -373,7 +373,7 @@ static struct pkvm_hyp_vm *get_vm_by_handle(pkvm_handle_t handle)
return vm_table[idx];
}
int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 pfn, u64 gfn)
int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 pfn, u64 gfn, u8 order)
{
struct pkvm_hyp_vm *hyp_vm;
int ret = -EINVAL;
@ -383,7 +383,7 @@ int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 pfn, u64 gfn)
if (!hyp_vm || !hyp_vm->is_dying)
goto unlock;
ret = __pkvm_host_reclaim_page(hyp_vm, pfn, gfn << PAGE_SHIFT);
ret = __pkvm_host_reclaim_page(hyp_vm, pfn, gfn << PAGE_SHIFT, order);
if (ret)
goto unlock;

View File

@ -307,6 +307,15 @@ int pkvm_call_hyp_nvhe_ppage(struct kvm_pinned_page *ppage,
return 0;
}
static int __reclaim_dying_guest_page_call(u64 pfn, u64 gfn, u8 order, void *args)
{
struct kvm *host_kvm = args;
return kvm_call_hyp_nvhe(__pkvm_reclaim_dying_guest_page,
host_kvm->arch.pkvm.handle,
pfn, gfn, order);
}
static void __pkvm_destroy_hyp_vm(struct kvm *host_kvm)
{
struct mm_struct *mm = current->mm;
@ -320,10 +329,9 @@ static void __pkvm_destroy_hyp_vm(struct kvm *host_kvm)
WARN_ON(kvm_call_hyp_nvhe(__pkvm_start_teardown_vm, host_kvm->arch.pkvm.handle));
mt_for_each(&host_kvm->arch.pkvm.pinned_pages, ppage, ipa, ULONG_MAX) {
WARN_ON(kvm_call_hyp_nvhe(__pkvm_reclaim_dying_guest_page,
host_kvm->arch.pkvm.handle,
page_to_pfn(ppage->page),
ppage->ipa >> PAGE_SHIFT));
WARN_ON(pkvm_call_hyp_nvhe_ppage(ppage,
__reclaim_dying_guest_page_call,
host_kvm, true));
cond_resched();
account_locked_vm(mm, 1, false);