295 lines
9.1 KiB
Diff
295 lines
9.1 KiB
Diff
|
|
From b311feda2078e7ee8f060531d4d061beccbc2f77 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Salil Mehta <salil.mehta@huawei.com>
|
||
|
|
Date: Sat, 9 May 2020 20:13:10 +0100
|
||
|
|
Subject: [PATCH] target/arm: Add support of *unrealize* ARMCPU during vCPU
|
||
|
|
Hot-unplug
|
||
|
|
|
||
|
|
vCPU Hot-unplug will result in QOM CPU object unrealization which will do away
|
||
|
|
with all the vCPU thread creations, allocations, registrations that happened
|
||
|
|
as part of the realization process. This change introduces the ARM CPU unrealize
|
||
|
|
function taking care of exactly that.
|
||
|
|
|
||
|
|
Note, initialized KVM vCPUs are not destroyed in host KVM but their Qemu context
|
||
|
|
is parked at the QEMU KVM layer.
|
||
|
|
|
||
|
|
Co-developed-by: Salil Mehta <salil.mehta@huawei.com>
|
||
|
|
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
|
||
|
|
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
|
||
|
|
Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com>
|
||
|
|
Reported-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com>
|
||
|
|
[VP: Identified CPU stall issue & suggested probable fix]
|
||
|
|
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
|
||
|
|
---
|
||
|
|
target/arm/cpu.c | 101 +++++++++++++++++++++++++++++++++++++++++
|
||
|
|
target/arm/cpu.h | 14 ++++++
|
||
|
|
target/arm/gdbstub.c | 6 +++
|
||
|
|
target/arm/helper.c | 25 ++++++++++
|
||
|
|
target/arm/internals.h | 3 ++
|
||
|
|
target/arm/kvm64.c | 4 ++
|
||
|
|
6 files changed, 153 insertions(+)
|
||
|
|
|
||
|
|
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
|
||
|
|
index 18b8a79c8f..501f88eb2f 100644
|
||
|
|
--- a/target/arm/cpu.c
|
||
|
|
+++ b/target/arm/cpu.c
|
||
|
|
@@ -142,6 +142,16 @@ void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
|
||
|
|
QLIST_INSERT_HEAD(&cpu->pre_el_change_hooks, entry, node);
|
||
|
|
}
|
||
|
|
|
||
|
|
+void arm_unregister_pre_el_change_hooks(ARMCPU *cpu)
|
||
|
|
+{
|
||
|
|
+ ARMELChangeHook *entry, *next;
|
||
|
|
+
|
||
|
|
+ QLIST_FOREACH_SAFE(entry, &cpu->pre_el_change_hooks, node, next) {
|
||
|
|
+ QLIST_REMOVE(entry, node);
|
||
|
|
+ g_free(entry);
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
|
||
|
|
void *opaque)
|
||
|
|
{
|
||
|
|
@@ -153,6 +163,16 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
|
||
|
|
QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node);
|
||
|
|
}
|
||
|
|
|
||
|
|
+void arm_unregister_el_change_hooks(ARMCPU *cpu)
|
||
|
|
+{
|
||
|
|
+ ARMELChangeHook *entry, *next;
|
||
|
|
+
|
||
|
|
+ QLIST_FOREACH_SAFE(entry, &cpu->el_change_hooks, node, next) {
|
||
|
|
+ QLIST_REMOVE(entry, node);
|
||
|
|
+ g_free(entry);
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
|
||
|
|
{
|
||
|
|
/* Reset a single ARMCPRegInfo register */
|
||
|
|
@@ -2390,6 +2410,85 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||
|
|
acc->parent_realize(dev, errp);
|
||
|
|
}
|
||
|
|
|
||
|
|
+static void arm_cpu_unrealizefn(DeviceState *dev)
|
||
|
|
+{
|
||
|
|
+ ARMCPUClass *acc = ARM_CPU_GET_CLASS(dev);
|
||
|
|
+ ARMCPU *cpu = ARM_CPU(dev);
|
||
|
|
+ CPUARMState *env = &cpu->env;
|
||
|
|
+ CPUState *cs = CPU(dev);
|
||
|
|
+ bool has_secure;
|
||
|
|
+
|
||
|
|
+ has_secure = cpu->has_el3 || arm_feature(env, ARM_FEATURE_M_SECURITY);
|
||
|
|
+
|
||
|
|
+ /* rock 'n' un-roll, whatever happened in the arm_cpu_realizefn cleanly */
|
||
|
|
+ cpu_address_space_destroy(cs, ARMASIdx_NS);
|
||
|
|
+
|
||
|
|
+ if (cpu->tag_memory != NULL) {
|
||
|
|
+ cpu_address_space_destroy(cs, ARMASIdx_TagNS);
|
||
|
|
+ if (has_secure) {
|
||
|
|
+ cpu_address_space_destroy(cs, ARMASIdx_TagS);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if (has_secure) {
|
||
|
|
+ cpu_address_space_destroy(cs, ARMASIdx_S);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ destroy_cpreg_list(cpu);
|
||
|
|
+ arm_cpu_unregister_gdb_regs(cpu);
|
||
|
|
+ unregister_cp_regs_for_features(cpu);
|
||
|
|
+
|
||
|
|
+ if (cpu->sau_sregion && arm_feature(env, ARM_FEATURE_M_SECURITY)) {
|
||
|
|
+ g_free(env->sau.rbar);
|
||
|
|
+ g_free(env->sau.rlar);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if (arm_feature(env, ARM_FEATURE_PMSA) &&
|
||
|
|
+ arm_feature(env, ARM_FEATURE_V7) &&
|
||
|
|
+ cpu->pmsav7_dregion) {
|
||
|
|
+ if (arm_feature(env, ARM_FEATURE_V8)) {
|
||
|
|
+ g_free(env->pmsav8.rbar[M_REG_NS]);
|
||
|
|
+ g_free(env->pmsav8.rlar[M_REG_NS]);
|
||
|
|
+ if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
|
||
|
|
+ g_free(env->pmsav8.rbar[M_REG_S]);
|
||
|
|
+ g_free(env->pmsav8.rlar[M_REG_S]);
|
||
|
|
+ }
|
||
|
|
+ } else {
|
||
|
|
+ g_free(env->pmsav7.drbar);
|
||
|
|
+ g_free(env->pmsav7.drsr);
|
||
|
|
+ g_free(env->pmsav7.dracr);
|
||
|
|
+ }
|
||
|
|
+ if (cpu->pmsav8r_hdregion) {
|
||
|
|
+ g_free(env->pmsav8.hprbar);
|
||
|
|
+ g_free(env->pmsav8.hprlar);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if (arm_feature(env, ARM_FEATURE_PMU)) {
|
||
|
|
+ if (!kvm_enabled()) {
|
||
|
|
+ arm_unregister_pre_el_change_hooks(cpu);
|
||
|
|
+ arm_unregister_el_change_hooks(cpu);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+#ifndef CONFIG_USER_ONLY
|
||
|
|
+ if (cpu->pmu_timer) {
|
||
|
|
+ timer_del(cpu->pmu_timer);
|
||
|
|
+ }
|
||
|
|
+#endif
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ cpu_remove_sync(CPU(dev));
|
||
|
|
+ acc->parent_unrealize(dev);
|
||
|
|
+
|
||
|
|
+#ifndef CONFIG_USER_ONLY
|
||
|
|
+ timer_del(cpu->gt_timer[GTIMER_PHYS]);
|
||
|
|
+ timer_del(cpu->gt_timer[GTIMER_VIRT]);
|
||
|
|
+ timer_del(cpu->gt_timer[GTIMER_HYP]);
|
||
|
|
+ timer_del(cpu->gt_timer[GTIMER_SEC]);
|
||
|
|
+ timer_del(cpu->gt_timer[GTIMER_HYPVIRT]);
|
||
|
|
+#endif
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
static ObjectClass *arm_cpu_class_by_name(const char *cpu_model)
|
||
|
|
{
|
||
|
|
ObjectClass *oc;
|
||
|
|
@@ -2492,6 +2591,8 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
|
||
|
|
|
||
|
|
device_class_set_parent_realize(dc, arm_cpu_realizefn,
|
||
|
|
&acc->parent_realize);
|
||
|
|
+ device_class_set_parent_unrealize(dc, arm_cpu_unrealizefn,
|
||
|
|
+ &acc->parent_unrealize);
|
||
|
|
|
||
|
|
device_class_set_props(dc, arm_cpu_properties);
|
||
|
|
|
||
|
|
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
|
||
|
|
index 145d3dbf13..c51a0e3467 100644
|
||
|
|
--- a/target/arm/cpu.h
|
||
|
|
+++ b/target/arm/cpu.h
|
||
|
|
@@ -1138,6 +1138,7 @@ struct ARMCPUClass {
|
||
|
|
|
||
|
|
const ARMCPUInfo *info;
|
||
|
|
DeviceRealize parent_realize;
|
||
|
|
+ DeviceUnrealize parent_unrealize;
|
||
|
|
ResettablePhases parent_phases;
|
||
|
|
};
|
||
|
|
|
||
|
|
@@ -3359,6 +3360,13 @@ static inline AddressSpace *arm_addressspace(CPUState *cs, MemTxAttrs attrs)
|
||
|
|
*/
|
||
|
|
void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
|
||
|
|
void *opaque);
|
||
|
|
+/**
|
||
|
|
+ * arm_unregister_pre_el_change_hook:
|
||
|
|
+ * unregister all pre EL change hook functions. Generally called during
|
||
|
|
+ * unrealize'ing leg
|
||
|
|
+ */
|
||
|
|
+void arm_unregister_pre_el_change_hooks(ARMCPU *cpu);
|
||
|
|
+
|
||
|
|
/**
|
||
|
|
* arm_register_el_change_hook:
|
||
|
|
* Register a hook function which will be called immediately after this
|
||
|
|
@@ -3371,6 +3379,12 @@ void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
|
||
|
|
*/
|
||
|
|
void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void
|
||
|
|
*opaque);
|
||
|
|
+/**
|
||
|
|
+ * arm_unregister_el_change_hook:
|
||
|
|
+ * unregister all EL change hook functions. Generally called during
|
||
|
|
+ * unrealize'ing leg
|
||
|
|
+ */
|
||
|
|
+void arm_unregister_el_change_hooks(ARMCPU *cpu);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* arm_rebuild_hflags:
|
||
|
|
diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c
|
||
|
|
index 28f546a5ff..5ba1e28e34 100644
|
||
|
|
--- a/target/arm/gdbstub.c
|
||
|
|
+++ b/target/arm/gdbstub.c
|
||
|
|
@@ -553,3 +553,9 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
|
||
|
|
}
|
||
|
|
#endif /* CONFIG_TCG */
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+void arm_cpu_unregister_gdb_regs(ARMCPU *cpu)
|
||
|
|
+{
|
||
|
|
+ CPUState *cs = CPU(cpu);
|
||
|
|
+ gdb_unregister_coprocessor_all(cs);
|
||
|
|
+}
|
||
|
|
diff --git a/target/arm/helper.c b/target/arm/helper.c
|
||
|
|
index 2746d3fdac..e47498828c 100644
|
||
|
|
--- a/target/arm/helper.c
|
||
|
|
+++ b/target/arm/helper.c
|
||
|
|
@@ -263,6 +263,19 @@ void init_cpreg_list(ARMCPU *cpu)
|
||
|
|
g_list_free(keys);
|
||
|
|
}
|
||
|
|
|
||
|
|
+void destroy_cpreg_list(ARMCPU *cpu)
|
||
|
|
+{
|
||
|
|
+ assert(cpu->cpreg_indexes);
|
||
|
|
+ assert(cpu->cpreg_values);
|
||
|
|
+ assert(cpu->cpreg_vmstate_indexes);
|
||
|
|
+ assert(cpu->cpreg_vmstate_values);
|
||
|
|
+
|
||
|
|
+ g_free(cpu->cpreg_indexes);
|
||
|
|
+ g_free(cpu->cpreg_values);
|
||
|
|
+ g_free(cpu->cpreg_vmstate_indexes);
|
||
|
|
+ g_free(cpu->cpreg_vmstate_values);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
/*
|
||
|
|
* Some registers are not accessible from AArch32 EL3 if SCR.NS == 0.
|
||
|
|
*/
|
||
|
|
@@ -9438,6 +9451,18 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
+void unregister_cp_regs_for_features(ARMCPU *cpu)
|
||
|
|
+{
|
||
|
|
+ CPUARMState *env = &cpu->env;
|
||
|
|
+ if (arm_feature(env, ARM_FEATURE_M)) {
|
||
|
|
+ /* M profile has no coprocessor registers */
|
||
|
|
+ return;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ /* empty it all. unregister all the coprocessor registers */
|
||
|
|
+ g_hash_table_remove_all(cpu->cp_regs);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
/* Sort alphabetically by type name, except for "any". */
|
||
|
|
static gint arm_cpu_list_compare(gconstpointer a, gconstpointer b)
|
||
|
|
{
|
||
|
|
diff --git a/target/arm/internals.h b/target/arm/internals.h
|
||
|
|
index 143d57c0fe..c3a7682f05 100644
|
||
|
|
--- a/target/arm/internals.h
|
||
|
|
+++ b/target/arm/internals.h
|
||
|
|
@@ -187,9 +187,12 @@ void arm_cpu_register(const ARMCPUInfo *info);
|
||
|
|
void aarch64_cpu_register(const ARMCPUInfo *info);
|
||
|
|
|
||
|
|
void register_cp_regs_for_features(ARMCPU *cpu);
|
||
|
|
+void unregister_cp_regs_for_features(ARMCPU *cpu);
|
||
|
|
void init_cpreg_list(ARMCPU *cpu);
|
||
|
|
+void destroy_cpreg_list(ARMCPU *cpu);
|
||
|
|
|
||
|
|
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu);
|
||
|
|
+void arm_cpu_unregister_gdb_regs(ARMCPU *cpu);
|
||
|
|
void arm_translate_init(void);
|
||
|
|
|
||
|
|
void arm_restore_state_to_opc(CPUState *cs,
|
||
|
|
diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c
|
||
|
|
index 03ce1e7525..9c3a35d63a 100644
|
||
|
|
--- a/target/arm/kvm64.c
|
||
|
|
+++ b/target/arm/kvm64.c
|
||
|
|
@@ -647,6 +647,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
||
|
|
|
||
|
|
int kvm_arch_destroy_vcpu(CPUState *cs)
|
||
|
|
{
|
||
|
|
+ if (cs->thread_id) {
|
||
|
|
+ qemu_del_vm_change_state_handler(cs->vmcse);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
--
|
||
|
|
2.27.0
|
||
|
|
|