ANDROID: KVM: arm64: Optimise module_change_host_page_prot

Merge the relaxation and restriction paths to both only need to adjust
permissions. This avoids un-map + re-map on the restriction path; and
avoids installing an annotated entry on the relaxation path (which
will cause a translation fault on first access by the host).

Bug: 308373293
Change-Id: I9c7a6ac149aad64b19a5ce7808334188475b27cc
Signed-off-by: Keir Fraser <keirf@google.com>
This commit is contained in:
Keir Fraser 2023-10-31 17:09:30 +00:00
parent 01dd8c280b
commit f082d22541
2 changed files with 20 additions and 25 deletions

View file

@ -2008,19 +2008,6 @@ int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages)
return ret;
}
static int restrict_host_page_perms(u64 addr, kvm_pte_t pte, u32 level, enum kvm_pgtable_prot prot)
{
int ret = 0;
/* XXX: optimize ... */
if (kvm_pte_valid(pte) && (level == KVM_PGTABLE_MAX_LEVELS - 1))
ret = kvm_pgtable_stage2_unmap(&host_mmu.pgt, addr, PAGE_SIZE);
if (!ret)
ret = host_stage2_idmap_locked(addr, PAGE_SIZE, prot, false);
return ret;
}
#define MODULE_PROT_ALLOWLIST (KVM_PGTABLE_PROT_RWX | \
KVM_PGTABLE_PROT_DEVICE |\
KVM_PGTABLE_PROT_NC | \
@ -2065,12 +2052,12 @@ int module_change_host_page_prot(u64 pfn, enum kvm_pgtable_prot prot)
}
update:
if (prot == default_host_prot(!!page))
ret = host_stage2_set_owner_locked(addr, PAGE_SIZE, PKVM_ID_HOST);
else if (!prot)
ret = host_stage2_set_owner_locked(addr, PAGE_SIZE, PKVM_ID_PROTECTED);
else
ret = restrict_host_page_perms(addr, pte, level, prot);
if (!prot) {
ret = host_stage2_set_owner_locked(addr, PAGE_SIZE,
PKVM_ID_PROTECTED);
} else {
ret = host_stage2_idmap_locked(addr, PAGE_SIZE, prot, false);
}
if (ret || !page)
goto unlock;

View file

@ -645,8 +645,13 @@ enum kvm_pgtable_prot kvm_pgtable_stage2_pte_prot(kvm_pte_t pte)
return prot;
}
static bool stage2_pte_needs_update(kvm_pte_t old, kvm_pte_t new)
static bool stage2_pte_needs_update(struct kvm_pgtable *pgt,
kvm_pte_t old, kvm_pte_t new)
{
/* Following filter logic applies only to guest stage-2 entries. */
if (pgt->flags & KVM_PGTABLE_S2_IDMAP)
return true;
if (!kvm_pte_valid(old) || !kvm_pte_valid(new))
return true;
@ -715,12 +720,15 @@ static int stage2_map_walker_try_leaf(u64 addr, u64 end, u32 level,
new = data->annotation;
/*
* Skip updating the PTE if we are trying to recreate the exact
* same mapping or only change the access permissions. Instead,
* the vCPU will exit one more time from guest if still needed
* and then go through the path of relaxing permissions.
* Skip updating a guest PTE if we are trying to recreate the exact
* same mapping or change only the access permissions. Instead,
* the vCPU will exit one more time from the guest if still needed
* and then go through the path of relaxing permissions. This applies
* only to guest PTEs; Host PTEs are unconditionally updated. The
* host cannot livelock because the abort handler has done prior
* checks before calling here.
*/
if (!stage2_pte_needs_update(old, new))
if (!stage2_pte_needs_update(pgt, old, new))
return -EAGAIN;
if (pte_ops->pte_is_counted_cb(old, level))