ANDROID: KVM: arm64: Flag pages needing poisoning in hyp_vmemmap

During teardown, we currently walk the guest stage-2 page-table and
annotate all of its pages as 'pending poisoning' in the host stage-2.
Sadly, this requires a host stage-2 walk for every guest page, which is
rather inefficient and can lead to a long non-preemptible amount of time
spent at EL2. This gets particularly bad with IOMMUs as, in its current
form, the host stage-2 annotation triggers IOMMU updates.

To avoid the host stage-2 walks, let's annotate the pages pending
poisoning using a flag in the hyp_vmemmap instead.

Bug: 219180169
Signed-off-by: Quentin Perret <qperret@google.com>
Change-Id: I8894bd8e0b10ea8817763479412b540c0291e8f5
This commit is contained in:
Quentin Perret
2022-03-04 16:48:32 +00:00
parent c93e44a30f
commit a0f22aaf98
3 changed files with 15 additions and 25 deletions

View File

@@ -58,7 +58,6 @@ typedef u32 pkvm_id;
static const pkvm_id pkvm_host_id = 0;
static const pkvm_id pkvm_hyp_id = (1 << 16);
static const pkvm_id pkvm_ffa_id = pkvm_hyp_id + 1; /* Secure world */
static const pkvm_id pkvm_host_poison = pkvm_ffa_id + 1;
extern unsigned long hyp_nr_cpus;

View File

@@ -7,6 +7,12 @@
#include <linux/types.h>
/*
* Accesses to struct hyp_page flags must be serialized by the host stage-2
* page-table lock due to the lack of atomics at EL2.
*/
#define HOST_PAGE_NEED_POISONING BIT(0)
struct hyp_page {
unsigned short refcount;
u8 order;

View File

@@ -286,17 +286,13 @@ static int reclaim_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
void * const arg)
{
kvm_pte_t pte = *ptep;
phys_addr_t phys;
struct hyp_page *page;
if (!kvm_pte_valid(pte))
return 0;
/*
* Only update the host stage-2 -- we're about to tear-down the guest
* stage-2 so no need to waste effort trying to keep it in sync.
*/
phys = kvm_pte_to_phys(pte);
BUG_ON(host_stage2_set_owner_locked(phys, PAGE_SIZE, pkvm_host_poison));
page = hyp_phys_to_page(kvm_pte_to_phys(pte));
page->flags |= HOST_PAGE_NEED_POISONING;
return 0;
}
@@ -520,11 +516,6 @@ static kvm_pte_t kvm_init_invalid_leaf_owner(pkvm_id owner_id)
return FIELD_PREP(KVM_INVALID_PTE_OWNER_MASK, owner_id);
}
static pkvm_id kvm_get_owner_id(kvm_pte_t pte)
{
return FIELD_GET(KVM_INVALID_PTE_OWNER_MASK, pte);
}
int host_stage2_set_owner_locked(phys_addr_t addr, u64 size,
pkvm_id owner_id)
{
@@ -1864,7 +1855,7 @@ static int hyp_zero_page(phys_addr_t phys)
int __pkvm_host_reclaim_page(u64 pfn)
{
u64 addr = hyp_pfn_to_phys(pfn);
enum pkvm_page_state state;
struct hyp_page *page;
kvm_pte_t pte;
int ret;
@@ -1874,23 +1865,17 @@ int __pkvm_host_reclaim_page(u64 pfn)
if (ret)
goto unlock;
if (kvm_pte_valid(pte)) {
state = host_get_page_state(pte);
ret = (state == PKVM_PAGE_OWNED) ? 0 : -EPERM;
if (host_get_page_state(pte) == PKVM_PAGE_OWNED)
goto unlock;
}
switch (kvm_get_owner_id(pte)) {
case pkvm_host_id:
ret = 0;
break;
case pkvm_host_poison:
page = hyp_phys_to_page(addr);
if (page->flags & HOST_PAGE_NEED_POISONING) {
ret = hyp_zero_page(addr);
if (ret)
goto unlock;
page->flags &= ~HOST_PAGE_NEED_POISONING;
ret = host_stage2_set_owner_locked(addr, PAGE_SIZE, pkvm_host_id);
break;
default:
} else {
ret = -EPERM;
}