Convert CPU to topology device then it can be added into topology tree. Because CPU then inherits properties and settings of topology device, make the following changes to take into account the special case for CPU: * Omit setting category since topology device has already set. * Make realize() of topology device as the parent realize(). * Clean up some cases that assume parent obj is DeviceState and access parent_obj directly. * Set CPU's topology level as thread. * And one complex change: mask bus_type as NULL. - This is because for the arches don't support topology tree, there's no CPU bus bridge so that CPUs of these arches can't be created. So, only the CPU with arch supporting topology tree should override the bus_type field. * Further, support cpu_create() for the CPU with bus_type. - This is a corner case, some arch CPUs may set bus_type, and cpu_create() would be called in system emulation case (e.g., none machine). To handle such case, try to find the machine's CPU bus in cpu_create(). Signed-off-by: Zhao Liu <zhao1.liu@xxxxxxxxx> --- accel/kvm/kvm-all.c | 4 ++-- hw/core/cpu-common.c | 42 +++++++++++++++++++++++++++++++++++++----- include/hw/core/cpu.h | 7 +++++-- target/ppc/kvm.c | 2 +- 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index beb1988d12cf..48c040f6861d 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -4173,7 +4173,7 @@ static void query_stats(StatsResultList **result, StatsTarget target, break; case STATS_TARGET_VCPU: add_stats_entry(result, STATS_PROVIDER_KVM, - cpu->parent_obj.canonical_path, + DEVICE(cpu)->canonical_path, stats_list); break; default: @@ -4265,7 +4265,7 @@ static void query_stats_cb(StatsResultList **result, StatsTarget target, stats_args.names = names; stats_args.errp = errp; CPU_FOREACH(cpu) { - if (!apply_str_list_filter(cpu->parent_obj.canonical_path, targets)) { + if (!apply_str_list_filter(DEVICE(cpu)->canonical_path, targets)) { continue; } query_stats_vcpu(cpu, &stats_args); diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 7982ecd39a53..08f2d536ff6d 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -57,7 +57,19 @@ CPUState *cpu_create(const char *typename) { Error *err = NULL; CPUState *cpu = CPU(object_new(typename)); - if (!qdev_realize(DEVICE(cpu), NULL, &err)) { + BusState *bus = NULL; + + if (DEVICE_GET_CLASS(cpu)->bus_type) { + MachineState *ms; + + ms = (MachineState *)object_dynamic_cast(qdev_get_machine(), + TYPE_MACHINE); + if (ms) { + bus = BUS(&ms->topo->bus); + } + } + + if (!qdev_realize(DEVICE(cpu), bus, &err)) { error_report_err(err); object_unref(OBJECT(cpu)); exit(EXIT_FAILURE); @@ -196,6 +208,12 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) { CPUState *cpu = CPU(dev); Object *machine = qdev_get_machine(); + CPUClass *cc = CPU_GET_CLASS(cpu); + + cc->parent_realize(dev, errp); + if (*errp) { + return; + } /* qdev_get_machine() can return something that's not TYPE_MACHINE * if this is one of the user-only emulators; in that case there's @@ -302,6 +320,7 @@ static void cpu_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); + CPUTopoClass *tc = CPU_TOPO_CLASS(klass); CPUClass *k = CPU_CLASS(klass); k->parse_features = cpu_common_parse_features; @@ -309,9 +328,6 @@ static void cpu_common_class_init(ObjectClass *klass, void *data) k->has_work = cpu_common_has_work; k->gdb_read_register = cpu_common_gdb_read_register; k->gdb_write_register = cpu_common_gdb_write_register; - set_bit(DEVICE_CATEGORY_CPU, dc->categories); - dc->realize = cpu_common_realizefn; - dc->unrealize = cpu_common_unrealizefn; rc->phases.hold = cpu_common_reset_hold; cpu_class_init_props(dc); /* @@ -319,11 +335,27 @@ static void cpu_common_class_init(ObjectClass *klass, void *data) * IRQs, adding reset handlers, halting non-first CPUs, ... */ dc->user_creatable = false; + /* + * CPU is the minimum granularity for hotplug in most case, and + * often its hotplug handler is ultimately decided by the machine. + * For generality, set this flag to avoid blocking possible hotplug + * support. + */ + dc->hotpluggable = true; + device_class_set_parent_realize(dc, cpu_common_realizefn, + &k->parent_realize); + dc->unrealize = cpu_common_unrealizefn; + /* + * Avoid archs that do not support topology device trees from + * encountering error when creating CPUs. + */ + dc->bus_type = NULL; + tc->level = CPU_TOPOLOGY_LEVEL_THREAD; } static const TypeInfo cpu_type_info = { .name = TYPE_CPU, - .parent = TYPE_DEVICE, + .parent = TYPE_CPU_TOPO, .instance_size = sizeof(CPUState), .instance_init = cpu_common_initfn, .instance_finalize = cpu_common_finalize, diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 1c9c775df658..d7268bcb48cb 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -20,6 +20,7 @@ #ifndef QEMU_CPU_H #define QEMU_CPU_H +#include "hw/cpu/cpu-topology.h" #include "hw/qdev-core.h" #include "disas/dis-asm.h" #include "exec/breakpoint.h" @@ -144,7 +145,7 @@ struct SysemuCPUOps; */ struct CPUClass { /*< private >*/ - DeviceClass parent_class; + CPUTopoClass parent_class; /*< public >*/ ObjectClass *(*class_by_name)(const char *cpu_model); @@ -189,6 +190,8 @@ struct CPUClass { int reset_dump_flags; int gdb_num_core_regs; bool gdb_stop_before_watchpoint; + + DeviceRealize parent_realize; }; /* @@ -456,7 +459,7 @@ struct qemu_work_item; */ struct CPUState { /*< private >*/ - DeviceState parent_obj; + CPUTopoState parent_obj; /* cache to avoid expensive CPU_GET_CLASS */ CPUClass *cc; /*< public >*/ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 907dba60d1b5..b3cc42e545af 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2351,7 +2351,7 @@ static void alter_insns(uint64_t *word, uint64_t flags, bool on) static bool kvmppc_cpu_realize(CPUState *cs, Error **errp) { int ret; - const char *vcpu_str = (cs->parent_obj.hotplugged == true) ? + const char *vcpu_str = (DEVICE(cs)->hotplugged == true) ? "hotplug" : "create"; cs->cpu_index = cpu_get_free_index(); -- 2.34.1