From cde57fcae2ed16a10e1ef7f2da0ec368883988ba Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 6 Apr 2020 10:54:35 +0800 Subject: [PATCH] arm/virt: Add CPU topology support The CPU topology specified by user (through -smp options) is used in ACPI PPTT. Now we will use this information to locate which CPU to plug or unplug. Signed-off-by: Keqian Zhu Signed-off-by: Salil Mehta --- hw/arm/virt.c | 68 +++++++++++++++++++++++++++++++++++++-- include/hw/arm/topology.h | 61 +++++++++++++++++++++++++++++++++++ target/arm/cpu.c | 3 ++ target/arm/cpu.h | 3 ++ 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 include/hw/arm/topology.h diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 0bd37af26c..64532b61b2 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -36,6 +36,7 @@ #include "hw/sysbus.h" #include "hw/arm/boot.h" #include "hw/arm/primecell.h" +#include "hw/arm/topology.h" #include "hw/arm/virt.h" #include "hw/block/flash.h" #include "hw/vfio/vfio-calxeda-xgmac.h" @@ -2020,6 +2021,7 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) int n; unsigned int max_cpus = ms->smp.max_cpus; VirtMachineState *vms = VIRT_MACHINE(ms); + ARMCPUTopoInfo topo; if (ms->possible_cpus) { assert(ms->possible_cpus->len == max_cpus); @@ -2031,10 +2033,17 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) ms->possible_cpus->len = max_cpus; for (n = 0; n < ms->possible_cpus->len; n++) { ms->possible_cpus->cpus[n].type = ms->cpu_type; + ms->possible_cpus->cpus[n].vcpus_count = 1; ms->possible_cpus->cpus[n].arch_id = virt_cpu_mp_affinity(vms, n); + + topo_ids_from_idx(n, ms->smp.cores, ms->smp.threads, &topo); + ms->possible_cpus->cpus[n].props.has_socket_id = true; + ms->possible_cpus->cpus[n].props.socket_id = topo.pkg_id; + ms->possible_cpus->cpus[n].props.has_core_id = true; + ms->possible_cpus->cpus[n].props.core_id = topo.core_id; ms->possible_cpus->cpus[n].props.has_thread_id = true; - ms->possible_cpus->cpus[n].props.thread_id = n; + ms->possible_cpus->cpus[n].props.thread_id = topo.smt_id; } return ms->possible_cpus; } @@ -2080,7 +2089,62 @@ out: static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - /* Currently nothing to do */ + CPUState *cs = CPU(dev); + ARMCPUTopoInfo topo; + ARMCPU *cpu = ARM_CPU(dev); + MachineState *ms = MACHINE(hotplug_dev); + int smp_cores = ms->smp.cores; + int smp_threads = ms->smp.threads; + + /* if cpu idx is not set, set it based on socket/core/thread properties */ + if (cs->cpu_index == UNASSIGNED_CPU_INDEX) { + int max_socket = ms->smp.max_cpus / smp_threads / smp_cores; + if (cpu->socket_id < 0 || cpu->socket_id >= max_socket) { + error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", + cpu->socket_id, max_socket - 1); + return; + } + if (cpu->core_id < 0 || cpu->core_id >= smp_cores) { + error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", + cpu->core_id, smp_cores - 1); + return; + } + if (cpu->thread_id < 0 || cpu->thread_id >= smp_threads) { + error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", + cpu->thread_id, smp_threads - 1); + return; + } + + topo.pkg_id = cpu->socket_id; + topo.core_id = cpu->core_id; + topo.smt_id = cpu->thread_id; + cs->cpu_index = idx_from_topo_ids(smp_cores, smp_threads, &topo); + } + + /* if 'address' properties socket-id/core-id/thread-id are not set, set them + * so that machine_query_hotpluggable_cpus would show correct values + */ + topo_ids_from_idx(cs->cpu_index, smp_cores, smp_threads, &topo); + if (cpu->socket_id != -1 && cpu->socket_id != topo.pkg_id) { + error_setg(errp, "property socket-id: %u doesn't match set idx:" + " 0x%x (socket-id: %u)", cpu->socket_id, cs->cpu_index, topo.pkg_id); + return; + } + cpu->socket_id = topo.pkg_id; + + if (cpu->core_id != -1 && cpu->core_id != topo.core_id) { + error_setg(errp, "property core-id: %u doesn't match set idx:" + " 0x%x (core-id: %u)", cpu->core_id, cs->cpu_index, topo.core_id); + return; + } + cpu->core_id = topo.core_id; + + if (cpu->thread_id != -1 && cpu->thread_id != topo.smt_id) { + error_setg(errp, "property thread-id: %u doesn't match set idx:" + " 0x%x (thread-id: %u)", cpu->thread_id, cs->cpu_index, topo.smt_id); + return; + } + cpu->thread_id = topo.smt_id; } static void virt_cpu_plug(HotplugHandler *hotplug_dev, diff --git a/include/hw/arm/topology.h b/include/hw/arm/topology.h new file mode 100644 index 0000000000..a3e5f436c5 --- /dev/null +++ b/include/hw/arm/topology.h @@ -0,0 +1,61 @@ +/* + * ARM CPU topology data structures and functions + * + * Copyright (c) 2020 HUAWEI TECHNOLOGIES CO.,LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef HW_ARM_TOPOLOGY_H +#define HW_ARM_TOPOLOGY_H + +typedef struct ARMCPUTopoInfo { + unsigned pkg_id; + unsigned core_id; + unsigned smt_id; +} ARMCPUTopoInfo; + +/* Calculate (contiguous) CPU index based on topology */ +static inline unsigned idx_from_topo_ids(unsigned nr_cores, + unsigned nr_threads, + const ARMCPUTopoInfo *topo) +{ + assert(nr_cores > 0); + assert(nr_threads > 0); + assert(topo != NULL); + + return topo->pkg_id * nr_cores * nr_threads + + topo->core_id * nr_threads + + topo->smt_id; +} + +/* Calculate thread/core/package topology + * based on (contiguous) CPU index + */ +static inline void topo_ids_from_idx(unsigned cpu_index, + unsigned nr_cores, + unsigned nr_threads, + ARMCPUTopoInfo *topo) +{ + assert(nr_cores > 0); + assert(nr_threads > 0); + assert(topo != NULL); + + topo->smt_id = cpu_index % nr_threads; + topo->core_id = cpu_index / nr_threads % nr_cores; + topo->pkg_id = cpu_index / nr_threads / nr_cores; +} + +#endif /* HW_ARM_TOPOLOGY_H */ + diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 1ccb30e5eb..91f1e36cd8 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2560,6 +2560,9 @@ static Property arm_cpu_properties[] = { DEFINE_PROP_UINT64("mp-affinity", ARMCPU, mp_affinity, ARM64_AFFINITY_INVALID), DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID), + DEFINE_PROP_INT32("socket-id", ARMCPU, socket_id, -1), + DEFINE_PROP_INT32("core-id", ARMCPU, core_id, -1), + DEFINE_PROP_INT32("thread-id", ARMCPU, thread_id, -1), DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1), DEFINE_PROP_END_OF_LIST() }; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index e19531a77b..219c222b89 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -916,6 +916,9 @@ struct ARMCPU { QLIST_HEAD(, ARMELChangeHook) el_change_hooks; int32_t node_id; /* NUMA node this CPU belongs to */ + int32_t socket_id; + int32_t core_id; + int32_t thread_id; /* Used to synchronize KVM and QEMU in-kernel device levels */ uint8_t device_irq_level; -- 2.19.1