From: Isaku Yamahata <[email protected]>
In order to reclaim TDX HKID, (i.e. when deleting guest TD), needs to call
TDH.PHYMEM.PAGE.WBINVD on all packages. If we have used TDX HKID, refuse
to offline the last online cpu. Add arch callback for cpu offline.
Signed-off-by: Isaku Yamahata <[email protected]>
---
arch/x86/include/asm/kvm-x86-ops.h | 1 +
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/vmx/main.c | 1 +
arch/x86/kvm/vmx/tdx.c | 40 +++++++++++++++++++++++++++++-
arch/x86/kvm/vmx/x86_ops.h | 2 ++
arch/x86/kvm/x86.c | 5 ++++
include/linux/kvm_host.h | 1 +
virt/kvm/kvm_main.c | 12 +++++++--
8 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index 552de893af75..1a27f3aee982 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -18,6 +18,7 @@ KVM_X86_OP(check_processor_compatibility)
KVM_X86_OP(hardware_enable)
KVM_X86_OP(hardware_disable)
KVM_X86_OP(hardware_unsetup)
+KVM_X86_OP_OPTIONAL_RET0(offline_cpu)
KVM_X86_OP(has_emulated_msr)
KVM_X86_OP(vcpu_after_set_cpuid)
KVM_X86_OP(is_vm_type_supported)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index e199ddf0bb00..30f4ddb18548 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1534,6 +1534,7 @@ struct kvm_x86_ops {
int (*hardware_enable)(void);
void (*hardware_disable)(void);
void (*hardware_unsetup)(void);
+ int (*offline_cpu)(void);
bool (*has_emulated_msr)(struct kvm *kvm, u32 index);
void (*vcpu_after_set_cpuid)(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c
index c5f2515026e9..ddf0742f1f67 100644
--- a/arch/x86/kvm/vmx/main.c
+++ b/arch/x86/kvm/vmx/main.c
@@ -77,6 +77,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
.check_processor_compatibility = vmx_check_processor_compat,
.hardware_unsetup = vt_hardware_unsetup,
+ .offline_cpu = tdx_offline_cpu,
.hardware_enable = vmx_hardware_enable,
.hardware_disable = vmx_hardware_disable,
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index 0b309bbfe4e5..557a609c5147 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -42,6 +42,7 @@ static struct tdx_capabilities tdx_caps;
*/
static DEFINE_MUTEX(tdx_lock);
static struct mutex *tdx_mng_key_config_lock;
+static atomic_t nr_configured_hkid;
static __always_inline hpa_t set_hkid_to_hpa(hpa_t pa, u16 hkid)
{
@@ -209,7 +210,8 @@ void tdx_mmu_release_hkid(struct kvm *kvm)
pr_err("tdh_mng_key_freeid failed. HKID %d is leaked.\n",
kvm_tdx->hkid);
return;
- }
+ } else
+ atomic_dec(&nr_configured_hkid);
free_hkid:
tdx_hkid_free(kvm_tdx);
@@ -560,6 +562,8 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params)
if (ret)
break;
}
+ if (!ret)
+ atomic_inc(&nr_configured_hkid);
cpus_read_unlock();
free_cpumask_var(packages);
if (ret)
@@ -791,3 +795,37 @@ void tdx_hardware_unsetup(void)
/* kfree accepts NULL. */
kfree(tdx_mng_key_config_lock);
}
+
+int tdx_offline_cpu(void)
+{
+ int curr_cpu = smp_processor_id();
+ cpumask_var_t packages;
+ int ret = 0;
+ int i;
+
+ if (!atomic_read(&nr_configured_hkid))
+ return 0;
+
+ /*
+ * To reclaim hkid, need to call TDH.PHYMEM.PAGE.WBINVD on all packages.
+ * If this is the last online cpu on the package, refuse offline.
+ */
+ if (!zalloc_cpumask_var(&packages, GFP_KERNEL))
+ return -ENOMEM;
+
+ for_each_online_cpu(i) {
+ if (i != curr_cpu)
+ cpumask_set_cpu(topology_physical_package_id(i), packages);
+ }
+ if (!cpumask_test_cpu(topology_physical_package_id(curr_cpu), packages))
+ ret = -EBUSY;
+ free_cpumask_var(packages);
+ if (ret)
+ /*
+ * Because it's hard for human operator to understand the
+ * reason, warn it.
+ */
+ pr_warn("TDX requires all packages to have an online CPU. "
+ "Delete all TDs in order to offline all CPUs of a package.\n");
+ return ret;
+}
diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h
index 3d0f519727c6..6c40dda1cc2f 100644
--- a/arch/x86/kvm/vmx/x86_ops.h
+++ b/arch/x86/kvm/vmx/x86_ops.h
@@ -142,6 +142,7 @@ int __init tdx_hardware_setup(struct kvm_x86_ops *x86_ops);
void tdx_hardware_unsetup(void);
bool tdx_is_vm_type_supported(unsigned long type);
int tdx_dev_ioctl(void __user *argp);
+int tdx_offline_cpu(void);
int tdx_vm_init(struct kvm *kvm);
void tdx_mmu_release_hkid(struct kvm *kvm);
@@ -152,6 +153,7 @@ static inline int tdx_hardware_setup(struct kvm_x86_ops *x86_ops) { return 0; }
static inline void tdx_hardware_unsetup(void) {}
static inline bool tdx_is_vm_type_supported(unsigned long type) { return false; }
static inline int tdx_dev_ioctl(void __user *argp) { return -EOPNOTSUPP; };
+static inline int tdx_offline_cpu(void) { return 0; }
static inline int tdx_vm_init(struct kvm *kvm) { return -EOPNOTSUPP; }
static inline void tdx_mmu_release_hkid(struct kvm *kvm) {}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0fa91a9708aa..1fb135e0c98f 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -12100,6 +12100,11 @@ void kvm_arch_hardware_disable(void)
drop_user_return_notifiers();
}
+int kvm_arch_offline_cpu(unsigned int cpu)
+{
+ return static_call(kvm_x86_offline_cpu)();
+}
+
bool kvm_vcpu_is_reset_bsp(struct kvm_vcpu *vcpu)
{
return vcpu->kvm->arch.bsp_vcpu_id == vcpu->vcpu_id;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 6fada852c064..cd1f3634dd6a 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1459,6 +1459,7 @@ static inline void kvm_create_vcpu_debugfs(struct kvm_vcpu *vcpu) {}
int kvm_arch_hardware_enable(void);
void kvm_arch_hardware_disable(void);
#endif
+int kvm_arch_offline_cpu(unsigned int cpu);
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu);
bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu);
int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu);
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 1cfa7da92ad0..6c61b71b56d2 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -5516,13 +5516,21 @@ static void hardware_disable_nolock(void *junk)
__this_cpu_write(hardware_enabled, false);
}
+__weak int kvm_arch_offline_cpu(unsigned int cpu)
+{
+ return 0;
+}
+
static int kvm_offline_cpu(unsigned int cpu)
{
+ int r = 0;
+
mutex_lock(&kvm_lock);
- if (kvm_usage_count)
+ r = kvm_arch_offline_cpu(cpu);
+ if (!r && kvm_usage_count)
hardware_disable_nolock(NULL);
mutex_unlock(&kvm_lock);
- return 0;
+ return r;
}
static void hardware_disable_all_nolock(void)
--
2.25.1
On Thu, 2023-01-12 at 08:31 -0800, [email protected] wrote:
> From: Isaku Yamahata <[email protected]>
>
> In order to reclaim TDX HKID, (i.e. when deleting guest TD), needs to call
> TDH.PHYMEM.PAGE.WBINVD on all packages. If we have used TDX HKID, refuse
> to offline the last online cpu. Add arch callback for cpu offline.
I think it is worth to talk about suspend staff, i.e. why we only refuse to
offline the last cpu when there's active TD, but not choose to offline the last
cpu when TDX is enabled in KVM. People may not be able to understand
immediately the reason behind this design.
Btw, I certainly don't want to speak for Sean, but it seems this was suggested
by Sean? If so, add a 'Suggested-by' tag?
>
> Signed-off-by: Isaku Yamahata <[email protected]>
> ---
>
[snip]
> +
> +int tdx_offline_cpu(void)
> +{
> + int curr_cpu = smp_processor_id();
> + cpumask_var_t packages;
> + int ret = 0;
> + int i;
> +
> + if (!atomic_read(&nr_configured_hkid))
> + return 0;
As mentioned above, I think it also worth to add some comment here. When people
are trying to understand some code, I think mostly they are just going to look
at the code itself, but won't use 'git blame' to dig out the entire changelog to
understand some code.
> +
> + /*
> + * To reclaim hkid, need to call TDH.PHYMEM.PAGE.WBINVD on all packages.
> + * If this is the last online cpu on the package, refuse offline.
> + */
> + if (!zalloc_cpumask_var(&packages, GFP_KERNEL))
> + return -ENOMEM;
> +
> + for_each_online_cpu(i) {
> + if (i != curr_cpu)
> + cpumask_set_cpu(topology_physical_package_id(i), packages);
> + }
> + if (!cpumask_test_cpu(topology_physical_package_id(curr_cpu), packages))
> + ret = -EBUSY;
> + free_cpumask_var(packages);
> + if (ret)
> + /*
> + * Because it's hard for human operator to understand the
> + * reason, warn it.
> + */
> + pr_warn("TDX requires all packages to have an online CPU. "
> + "Delete all TDs in order to offline all CPUs of a package.\n");
> + return ret;
> +}
>
[snip]
On Mon, Jan 16, 2023 at 10:23:16AM +0000,
"Huang, Kai" <[email protected]> wrote:
> On Thu, 2023-01-12 at 08:31 -0800, [email protected] wrote:
> > From: Isaku Yamahata <[email protected]>
> >
> > In order to reclaim TDX HKID, (i.e. when deleting guest TD), needs to call
> > TDH.PHYMEM.PAGE.WBINVD on all packages. If we have used TDX HKID, refuse
> > to offline the last online cpu. Add arch callback for cpu offline.
>
> I think it is worth to talk about suspend staff, i.e. why we only refuse to
> offline the last cpu when there's active TD, but not choose to offline the last
> cpu when TDX is enabled in KVM. People may not be able to understand
> immediately the reason behind this design.
Updated the comment.
> Btw, I certainly don't want to speak for Sean, but it seems this was suggested
> by Sean? If so, add a 'Suggested-by' tag?
Added suggested-by.
> >
> > Signed-off-by: Isaku Yamahata <[email protected]>
> > ---
> >
>
> [snip]
>
> > +
> > +int tdx_offline_cpu(void)
> > +{
> > + int curr_cpu = smp_processor_id();
> > + cpumask_var_t packages;
> > + int ret = 0;
> > + int i;
> > +
> > + if (!atomic_read(&nr_configured_hkid))
> > + return 0;
>
> As mentioned above, I think it also worth to add some comment here. When people
> are trying to understand some code, I think mostly they are just going to look
> at the code itself, but won't use 'git blame' to dig out the entire changelog to
> understand some code.
Makes sense. Added a comment.
--
Isaku Yamahata <[email protected]>