mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-05 00:33:55 -06:00
hw/core/machine: Introduce CPU cluster topology support
The new Cluster-Aware Scheduling support has landed in Linux 5.16, which has been proved to benefit the scheduling performance (e.g. load balance and wake_affine strategy) on both x86_64 and AArch64. So now in Linux 5.16 we have four-level arch-neutral CPU topology definition like below and a new scheduler level for clusters. struct cpu_topology { int thread_id; int core_id; int cluster_id; int package_id; int llc_id; cpumask_t thread_sibling; cpumask_t core_sibling; cpumask_t cluster_sibling; cpumask_t llc_sibling; } A cluster generally means a group of CPU cores which share L2 cache or other mid-level resources, and it is the shared resources that is used to improve scheduler's behavior. From the point of view of the size range, it's between CPU die and CPU core. For example, on some ARM64 Kunpeng servers, we have 6 clusters in each NUMA node, and 4 CPU cores in each cluster. The 4 CPU cores share a separate L2 cache and a L3 cache tag, which brings cache affinity advantage. In virtualization, on the Hosts which have pClusters (physical clusters), if we can design a vCPU topology with cluster level for guest kernel and have a dedicated vCPU pinning. A Cluster-Aware Guest kernel can also make use of the cache affinity of CPU clusters to gain similar scheduling performance. This patch adds infrastructure for CPU cluster level topology configuration and parsing, so that the user can specify cluster parameter if their machines support it. Signed-off-by: Yanan Wang <wangyanan55@huawei.com> Message-Id: <20211228092221.21068-3-wangyanan55@huawei.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> [PMD: Added '(since 7.0)' to @clusters in qapi/machine.json] Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
This commit is contained in:
parent
0d87178523
commit
864c3b5c32
6 changed files with 38 additions and 12 deletions
|
@ -37,6 +37,10 @@ static char *cpu_hierarchy_to_string(MachineState *ms)
|
|||
g_string_append_printf(s, " * dies (%u)", ms->smp.dies);
|
||||
}
|
||||
|
||||
if (mc->smp_props.clusters_supported) {
|
||||
g_string_append_printf(s, " * clusters (%u)", ms->smp.clusters);
|
||||
}
|
||||
|
||||
g_string_append_printf(s, " * cores (%u)", ms->smp.cores);
|
||||
g_string_append_printf(s, " * threads (%u)", ms->smp.threads);
|
||||
|
||||
|
@ -71,6 +75,7 @@ void machine_parse_smp_config(MachineState *ms,
|
|||
unsigned cpus = config->has_cpus ? config->cpus : 0;
|
||||
unsigned sockets = config->has_sockets ? config->sockets : 0;
|
||||
unsigned dies = config->has_dies ? config->dies : 0;
|
||||
unsigned clusters = config->has_clusters ? config->clusters : 0;
|
||||
unsigned cores = config->has_cores ? config->cores : 0;
|
||||
unsigned threads = config->has_threads ? config->threads : 0;
|
||||
unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
|
||||
|
@ -82,6 +87,7 @@ void machine_parse_smp_config(MachineState *ms,
|
|||
if ((config->has_cpus && config->cpus == 0) ||
|
||||
(config->has_sockets && config->sockets == 0) ||
|
||||
(config->has_dies && config->dies == 0) ||
|
||||
(config->has_clusters && config->clusters == 0) ||
|
||||
(config->has_cores && config->cores == 0) ||
|
||||
(config->has_threads && config->threads == 0) ||
|
||||
(config->has_maxcpus && config->maxcpus == 0)) {
|
||||
|
@ -97,8 +103,13 @@ void machine_parse_smp_config(MachineState *ms,
|
|||
error_setg(errp, "dies not supported by this machine's CPU topology");
|
||||
return;
|
||||
}
|
||||
if (!mc->smp_props.clusters_supported && clusters > 1) {
|
||||
error_setg(errp, "clusters not supported by this machine's CPU topology");
|
||||
return;
|
||||
}
|
||||
|
||||
dies = dies > 0 ? dies : 1;
|
||||
clusters = clusters > 0 ? clusters : 1;
|
||||
|
||||
/* compute missing values based on the provided ones */
|
||||
if (cpus == 0 && maxcpus == 0) {
|
||||
|
@ -113,41 +124,42 @@ void machine_parse_smp_config(MachineState *ms,
|
|||
if (sockets == 0) {
|
||||
cores = cores > 0 ? cores : 1;
|
||||
threads = threads > 0 ? threads : 1;
|
||||
sockets = maxcpus / (dies * cores * threads);
|
||||
sockets = maxcpus / (dies * clusters * cores * threads);
|
||||
} else if (cores == 0) {
|
||||
threads = threads > 0 ? threads : 1;
|
||||
cores = maxcpus / (sockets * dies * threads);
|
||||
cores = maxcpus / (sockets * dies * clusters * threads);
|
||||
}
|
||||
} else {
|
||||
/* prefer cores over sockets since 6.2 */
|
||||
if (cores == 0) {
|
||||
sockets = sockets > 0 ? sockets : 1;
|
||||
threads = threads > 0 ? threads : 1;
|
||||
cores = maxcpus / (sockets * dies * threads);
|
||||
cores = maxcpus / (sockets * dies * clusters * threads);
|
||||
} else if (sockets == 0) {
|
||||
threads = threads > 0 ? threads : 1;
|
||||
sockets = maxcpus / (dies * cores * threads);
|
||||
sockets = maxcpus / (dies * clusters * cores * threads);
|
||||
}
|
||||
}
|
||||
|
||||
/* try to calculate omitted threads at last */
|
||||
if (threads == 0) {
|
||||
threads = maxcpus / (sockets * dies * cores);
|
||||
threads = maxcpus / (sockets * dies * clusters * cores);
|
||||
}
|
||||
}
|
||||
|
||||
maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads;
|
||||
maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * clusters * cores * threads;
|
||||
cpus = cpus > 0 ? cpus : maxcpus;
|
||||
|
||||
ms->smp.cpus = cpus;
|
||||
ms->smp.sockets = sockets;
|
||||
ms->smp.dies = dies;
|
||||
ms->smp.clusters = clusters;
|
||||
ms->smp.cores = cores;
|
||||
ms->smp.threads = threads;
|
||||
ms->smp.max_cpus = maxcpus;
|
||||
|
||||
/* sanity-check of the computed topology */
|
||||
if (sockets * dies * cores * threads != maxcpus) {
|
||||
if (sockets * dies * clusters * cores * threads != maxcpus) {
|
||||
g_autofree char *topo_msg = cpu_hierarchy_to_string(ms);
|
||||
error_setg(errp, "Invalid CPU topology: "
|
||||
"product of the hierarchy must match maxcpus: "
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue