]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: Split teardown hypercall into two phases
authorWill Deacon <will@kernel.org>
Mon, 30 Mar 2026 14:48:12 +0000 (15:48 +0100)
committerMarc Zyngier <maz@kernel.org>
Mon, 30 Mar 2026 15:58:07 +0000 (16:58 +0100)
In preparation for reclaiming protected guest VM pages from the host
during teardown, split the current 'pkvm_teardown_vm' hypercall into
separate 'start' and 'finalise' calls.

The 'pkvm_start_teardown_vm' hypercall puts the VM into a new 'is_dying'
state, which is a point of no return past which no vCPU of the pVM is
allowed to run any more.  Once in this new state,
'pkvm_finalize_teardown_vm' can be used to reclaim meta-data and
page-table pages from the VM. A subsequent patch will add support for
reclaiming the individual guest memory pages.

Reviewed-by: Fuad Tabba <tabba@google.com>
Tested-by: Fuad Tabba <tabba@google.com>
Tested-by: Mostafa Saleh <smostafa@google.com>
Co-developed-by: Quentin Perret <qperret@google.com>
Signed-off-by: Quentin Perret <qperret@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
Link: https://patch.msgid.link/20260330144841.26181-12-will@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/include/asm/kvm_asm.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/kvm/hyp/include/nvhe/pkvm.h
arch/arm64/kvm/hyp/nvhe/hyp-main.c
arch/arm64/kvm/hyp/nvhe/pkvm.c
arch/arm64/kvm/pkvm.c

index 7b72aac4730d9b88990c580f29e8297c7feb4aba..df6b661701b6f736ac73c12b378c0148f3707359 100644 (file)
@@ -89,7 +89,8 @@ enum __kvm_host_smccc_func {
        __KVM_HOST_SMCCC_FUNC___pkvm_unreserve_vm,
        __KVM_HOST_SMCCC_FUNC___pkvm_init_vm,
        __KVM_HOST_SMCCC_FUNC___pkvm_init_vcpu,
-       __KVM_HOST_SMCCC_FUNC___pkvm_teardown_vm,
+       __KVM_HOST_SMCCC_FUNC___pkvm_start_teardown_vm,
+       __KVM_HOST_SMCCC_FUNC___pkvm_finalize_teardown_vm,
        __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load,
        __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put,
        __KVM_HOST_SMCCC_FUNC___pkvm_tlb_flush_vmid,
index 70cb9cfd760a3689426a7aab829de9136f8c22c7..31b9454bb74dbe17031f48c4aa7c395825bb38c6 100644 (file)
@@ -255,6 +255,13 @@ struct kvm_protected_vm {
        struct kvm_hyp_memcache stage2_teardown_mc;
        bool is_protected;
        bool is_created;
+
+       /*
+        * True when the guest is being torn down. When in this state, the
+        * guest's vCPUs can't be loaded anymore, but its pages can be
+        * reclaimed by the host.
+        */
+       bool is_dying;
 };
 
 struct kvm_mpidr_data {
index 184ad7a3995072e2c4f708b39a2ad4a4d9c86b61..04c7ca7030144d38325520d81ed25d9a8b750103 100644 (file)
@@ -73,7 +73,9 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
                   unsigned long pgd_hva);
 int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
                     unsigned long vcpu_hva);
-int __pkvm_teardown_vm(pkvm_handle_t handle);
+
+int __pkvm_start_teardown_vm(pkvm_handle_t handle);
+int __pkvm_finalize_teardown_vm(pkvm_handle_t handle);
 
 struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
                                         unsigned int vcpu_idx);
index 127decc2dd2b8c3ea740fc8be9e4bba43d441996..634ea27662407bb38b85753d676dc5f877b2e516 100644 (file)
@@ -553,11 +553,18 @@ static void handle___pkvm_init_vcpu(struct kvm_cpu_context *host_ctxt)
        cpu_reg(host_ctxt, 1) = __pkvm_init_vcpu(handle, host_vcpu, vcpu_hva);
 }
 
-static void handle___pkvm_teardown_vm(struct kvm_cpu_context *host_ctxt)
+static void handle___pkvm_start_teardown_vm(struct kvm_cpu_context *host_ctxt)
 {
        DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
 
-       cpu_reg(host_ctxt, 1) = __pkvm_teardown_vm(handle);
+       cpu_reg(host_ctxt, 1) = __pkvm_start_teardown_vm(handle);
+}
+
+static void handle___pkvm_finalize_teardown_vm(struct kvm_cpu_context *host_ctxt)
+{
+       DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
+
+       cpu_reg(host_ctxt, 1) = __pkvm_finalize_teardown_vm(handle);
 }
 
 typedef void (*hcall_t)(struct kvm_cpu_context *);
@@ -598,7 +605,8 @@ static const hcall_t host_hcall[] = {
        HANDLE_FUNC(__pkvm_unreserve_vm),
        HANDLE_FUNC(__pkvm_init_vm),
        HANDLE_FUNC(__pkvm_init_vcpu),
-       HANDLE_FUNC(__pkvm_teardown_vm),
+       HANDLE_FUNC(__pkvm_start_teardown_vm),
+       HANDLE_FUNC(__pkvm_finalize_teardown_vm),
        HANDLE_FUNC(__pkvm_vcpu_load),
        HANDLE_FUNC(__pkvm_vcpu_put),
        HANDLE_FUNC(__pkvm_tlb_flush_vmid),
index 2f029bfe4755a5088eeb40381248d4748df83f9c..61e69e24656a5a71ec25c95135b0387001cdb8d4 100644 (file)
@@ -255,7 +255,10 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
 
        hyp_spin_lock(&vm_table_lock);
        hyp_vm = get_vm_by_handle(handle);
-       if (!hyp_vm || hyp_vm->kvm.created_vcpus <= vcpu_idx)
+       if (!hyp_vm || hyp_vm->kvm.arch.pkvm.is_dying)
+               goto unlock;
+
+       if (hyp_vm->kvm.created_vcpus <= vcpu_idx)
                goto unlock;
 
        hyp_vcpu = hyp_vm->vcpus[vcpu_idx];
@@ -301,8 +304,14 @@ struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle)
 
        hyp_spin_lock(&vm_table_lock);
        hyp_vm = get_vm_by_handle(handle);
-       if (hyp_vm)
+       if (!hyp_vm)
+               goto unlock;
+
+       if (hyp_vm->kvm.arch.pkvm.is_dying)
+               hyp_vm = NULL;
+       else
                hyp_page_ref_inc(hyp_virt_to_page(hyp_vm));
+unlock:
        hyp_spin_unlock(&vm_table_lock);
 
        return hyp_vm;
@@ -859,7 +868,32 @@ teardown_donated_memory(struct kvm_hyp_memcache *mc, void *addr, size_t size)
        unmap_donated_memory_noclear(addr, size);
 }
 
-int __pkvm_teardown_vm(pkvm_handle_t handle)
+int __pkvm_start_teardown_vm(pkvm_handle_t handle)
+{
+       struct pkvm_hyp_vm *hyp_vm;
+       int ret = 0;
+
+       hyp_spin_lock(&vm_table_lock);
+       hyp_vm = get_vm_by_handle(handle);
+       if (!hyp_vm) {
+               ret = -ENOENT;
+               goto unlock;
+       } else if (WARN_ON(hyp_page_count(hyp_vm))) {
+               ret = -EBUSY;
+               goto unlock;
+       } else if (hyp_vm->kvm.arch.pkvm.is_dying) {
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       hyp_vm->kvm.arch.pkvm.is_dying = true;
+unlock:
+       hyp_spin_unlock(&vm_table_lock);
+
+       return ret;
+}
+
+int __pkvm_finalize_teardown_vm(pkvm_handle_t handle)
 {
        struct kvm_hyp_memcache *mc, *stage2_mc;
        struct pkvm_hyp_vm *hyp_vm;
@@ -873,9 +907,7 @@ int __pkvm_teardown_vm(pkvm_handle_t handle)
        if (!hyp_vm) {
                err = -ENOENT;
                goto err_unlock;
-       }
-
-       if (WARN_ON(hyp_page_count(hyp_vm))) {
+       } else if (!hyp_vm->kvm.arch.pkvm.is_dying) {
                err = -EBUSY;
                goto err_unlock;
        }
index 94b70eb80aa818223ab2eb27ddfbdafb87c6bffb..ea7f267ee7ad3b03b418dad74b422814a0ff6ee7 100644 (file)
@@ -88,7 +88,7 @@ void __init kvm_hyp_reserve(void)
 static void __pkvm_destroy_hyp_vm(struct kvm *kvm)
 {
        if (pkvm_hyp_vm_is_created(kvm)) {
-               WARN_ON(kvm_call_hyp_nvhe(__pkvm_teardown_vm,
+               WARN_ON(kvm_call_hyp_nvhe(__pkvm_finalize_teardown_vm,
                                          kvm->arch.pkvm.handle));
        } else if (kvm->arch.pkvm.handle) {
                /*
@@ -356,6 +356,11 @@ void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
        if (!handle)
                return;
 
+       if (pkvm_hyp_vm_is_created(kvm) && !kvm->arch.pkvm.is_dying) {
+               WARN_ON(kvm_call_hyp_nvhe(__pkvm_start_teardown_vm, handle));
+               kvm->arch.pkvm.is_dying = true;
+       }
+
        __pkvm_pgtable_stage2_unshare(pgt, addr, addr + size);
 }