mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-09 19:05:21 +02:00
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:
parent
ea6a2e00c8
commit
39c6e8914d
|
@ -28,7 +28,7 @@ extern unsigned long hyp_nr_cpus;
|
||||||
int __pkvm_prot_finalize(void);
|
int __pkvm_prot_finalize(void);
|
||||||
int __pkvm_host_share_hyp(u64 pfn);
|
int __pkvm_host_share_hyp(u64 pfn);
|
||||||
int __pkvm_host_unshare_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);
|
||||||
int ___pkvm_host_donate_hyp(u64 pfn, u64 nr_pages, bool accept_mmio);
|
int ___pkvm_host_donate_hyp(u64 pfn, u64 nr_pages, bool accept_mmio);
|
||||||
int ___pkvm_host_donate_hyp_prot(u64 pfn, u64 nr_pages,
|
int ___pkvm_host_donate_hyp_prot(u64 pfn, u64 nr_pages,
|
||||||
|
|
|
@ -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_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu);
|
||||||
int __pkvm_start_teardown_vm(pkvm_handle_t handle);
|
int __pkvm_start_teardown_vm(pkvm_handle_t handle);
|
||||||
int __pkvm_finalize_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,
|
struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
|
||||||
unsigned int vcpu_idx);
|
unsigned int vcpu_idx);
|
||||||
|
|
|
@ -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(pkvm_handle_t, handle, host_ctxt, 1);
|
||||||
DECLARE_REG(u64, pfn, host_ctxt, 2);
|
DECLARE_REG(u64, pfn, host_ctxt, 2);
|
||||||
DECLARE_REG(u64, gfn, host_ctxt, 3);
|
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)
|
static void handle___pkvm_create_private_mapping(struct kvm_cpu_context *host_ctxt)
|
||||||
|
|
|
@ -396,7 +396,8 @@ int __pkvm_guest_relinquish_to_host(struct pkvm_hyp_vcpu *vcpu,
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
/* Zap the guest stage2 pte and return ownership to the host */
|
/* 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)
|
if (ret)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
@ -2502,6 +2503,30 @@ int __pkvm_host_share_guest(struct pkvm_hyp_vcpu *vcpu, u64 pfn, u64 gfn,
|
||||||
return ret;
|
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 __pkvm_host_unshare_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vm *vm)
|
||||||
{
|
{
|
||||||
int ret;
|
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);
|
phys_addr_t phys = hyp_pfn_to_phys(pfn);
|
||||||
|
size_t page_size = PAGE_SIZE << order;
|
||||||
kvm_pte_t pte;
|
kvm_pte_t pte;
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
host_lock_component();
|
host_lock_component();
|
||||||
guest_lock_component(vm);
|
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)
|
if (ret)
|
||||||
goto unlock;
|
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 */
|
/* 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)) {
|
switch((int)guest_get_page_state(pte, ipa)) {
|
||||||
case PKVM_PAGE_OWNED:
|
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);
|
hyp_poison_page(phys);
|
||||||
psci_mem_protect_dec(1);
|
psci_mem_protect_dec(order);
|
||||||
break;
|
break;
|
||||||
case PKVM_PAGE_SHARED_BORROWED:
|
case PKVM_PAGE_SHARED_BORROWED:
|
||||||
case PKVM_PAGE_SHARED_BORROWED | PKVM_PAGE_RESTRICTED_PROT:
|
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;
|
break;
|
||||||
case PKVM_PAGE_SHARED_OWNED:
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
BUG_ON(1);
|
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:
|
unlock:
|
||||||
guest_unlock_component(vm);
|
guest_unlock_component(vm);
|
||||||
|
|
|
@ -373,7 +373,7 @@ static struct pkvm_hyp_vm *get_vm_by_handle(pkvm_handle_t handle)
|
||||||
return vm_table[idx];
|
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;
|
struct pkvm_hyp_vm *hyp_vm;
|
||||||
int ret = -EINVAL;
|
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)
|
if (!hyp_vm || !hyp_vm->is_dying)
|
||||||
goto unlock;
|
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)
|
if (ret)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
|
|
|
@ -307,6 +307,15 @@ int pkvm_call_hyp_nvhe_ppage(struct kvm_pinned_page *ppage,
|
||||||
return 0;
|
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)
|
static void __pkvm_destroy_hyp_vm(struct kvm *host_kvm)
|
||||||
{
|
{
|
||||||
struct mm_struct *mm = current->mm;
|
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));
|
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) {
|
mt_for_each(&host_kvm->arch.pkvm.pinned_pages, ppage, ipa, ULONG_MAX) {
|
||||||
WARN_ON(kvm_call_hyp_nvhe(__pkvm_reclaim_dying_guest_page,
|
WARN_ON(pkvm_call_hyp_nvhe_ppage(ppage,
|
||||||
host_kvm->arch.pkvm.handle,
|
__reclaim_dying_guest_page_call,
|
||||||
page_to_pfn(ppage->page),
|
host_kvm, true));
|
||||||
ppage->ipa >> PAGE_SHIFT));
|
|
||||||
cond_resched();
|
cond_resched();
|
||||||
|
|
||||||
account_locked_vm(mm, 1, false);
|
account_locked_vm(mm, 1, false);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user