xref: /openbmc/qemu/hw/core/machine-smp.c (revision fc309207)
1 /*
2  * QEMU Machine core (related to -smp parsing)
3  *
4  * Copyright (c) 2021 Huawei Technologies Co., Ltd
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License,
9  * or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "qemu/osdep.h"
21 #include "hw/boards.h"
22 #include "qapi/error.h"
23 
24 /*
25  * Report information of a machine's supported CPU topology hierarchy.
26  * Topology members will be ordered from the largest to the smallest
27  * in the string.
28  */
29 static char *cpu_hierarchy_to_string(MachineState *ms)
30 {
31     MachineClass *mc = MACHINE_GET_CLASS(ms);
32     GString *s = g_string_new(NULL);
33 
34     g_string_append_printf(s, "sockets (%u)", ms->smp.sockets);
35 
36     if (mc->smp_props.dies_supported) {
37         g_string_append_printf(s, " * dies (%u)", ms->smp.dies);
38     }
39 
40     g_string_append_printf(s, " * cores (%u)", ms->smp.cores);
41     g_string_append_printf(s, " * threads (%u)", ms->smp.threads);
42 
43     return g_string_free(s, false);
44 }
45 
46 /*
47  * smp_parse - Generic function used to parse the given SMP configuration
48  *
49  * Any missing parameter in "cpus/maxcpus/sockets/cores/threads" will be
50  * automatically computed based on the provided ones.
51  *
52  * In the calculation of omitted sockets/cores/threads: we prefer sockets
53  * over cores over threads before 6.2, while preferring cores over sockets
54  * over threads since 6.2.
55  *
56  * In the calculation of cpus/maxcpus: When both maxcpus and cpus are omitted,
57  * maxcpus will be computed from the given parameters and cpus will be set
58  * equal to maxcpus. When only one of maxcpus and cpus is given then the
59  * omitted one will be set to its given counterpart's value. Both maxcpus and
60  * cpus may be specified, but maxcpus must be equal to or greater than cpus.
61  *
62  * For compatibility, apart from the parameters that will be computed, newly
63  * introduced topology members which are likely to be target specific should
64  * be directly set as 1 if they are omitted (e.g. dies for PC since 4.1).
65  */
66 void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
67 {
68     MachineClass *mc = MACHINE_GET_CLASS(ms);
69     unsigned cpus    = config->has_cpus ? config->cpus : 0;
70     unsigned sockets = config->has_sockets ? config->sockets : 0;
71     unsigned dies    = config->has_dies ? config->dies : 0;
72     unsigned cores   = config->has_cores ? config->cores : 0;
73     unsigned threads = config->has_threads ? config->threads : 0;
74     unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
75 
76     /*
77      * Specified CPU topology parameters must be greater than zero,
78      * explicit configuration like "cpus=0" is not allowed.
79      */
80     if ((config->has_cpus && config->cpus == 0) ||
81         (config->has_sockets && config->sockets == 0) ||
82         (config->has_dies && config->dies == 0) ||
83         (config->has_cores && config->cores == 0) ||
84         (config->has_threads && config->threads == 0) ||
85         (config->has_maxcpus && config->maxcpus == 0)) {
86         warn_report("Deprecated CPU topology (considered invalid): "
87                     "CPU topology parameters must be greater than zero");
88     }
89 
90     /*
91      * If not supported by the machine, a topology parameter must be
92      * omitted or specified equal to 1.
93      */
94     if (!mc->smp_props.dies_supported && dies > 1) {
95         error_setg(errp, "dies not supported by this machine's CPU topology");
96         return;
97     }
98 
99     dies = dies > 0 ? dies : 1;
100 
101     /* compute missing values based on the provided ones */
102     if (cpus == 0 && maxcpus == 0) {
103         sockets = sockets > 0 ? sockets : 1;
104         cores = cores > 0 ? cores : 1;
105         threads = threads > 0 ? threads : 1;
106     } else {
107         maxcpus = maxcpus > 0 ? maxcpus : cpus;
108 
109         if (mc->smp_props.prefer_sockets) {
110             /* prefer sockets over cores before 6.2 */
111             if (sockets == 0) {
112                 cores = cores > 0 ? cores : 1;
113                 threads = threads > 0 ? threads : 1;
114                 sockets = maxcpus / (dies * cores * threads);
115             } else if (cores == 0) {
116                 threads = threads > 0 ? threads : 1;
117                 cores = maxcpus / (sockets * dies * threads);
118             }
119         } else {
120             /* prefer cores over sockets since 6.2 */
121             if (cores == 0) {
122                 sockets = sockets > 0 ? sockets : 1;
123                 threads = threads > 0 ? threads : 1;
124                 cores = maxcpus / (sockets * dies * threads);
125             } else if (sockets == 0) {
126                 threads = threads > 0 ? threads : 1;
127                 sockets = maxcpus / (dies * cores * threads);
128             }
129         }
130 
131         /* try to calculate omitted threads at last */
132         if (threads == 0) {
133             threads = maxcpus / (sockets * dies * cores);
134         }
135     }
136 
137     maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * cores * threads;
138     cpus = cpus > 0 ? cpus : maxcpus;
139 
140     ms->smp.cpus = cpus;
141     ms->smp.sockets = sockets;
142     ms->smp.dies = dies;
143     ms->smp.cores = cores;
144     ms->smp.threads = threads;
145     ms->smp.max_cpus = maxcpus;
146 
147     /* sanity-check of the computed topology */
148     if (sockets * dies * cores * threads != maxcpus) {
149         g_autofree char *topo_msg = cpu_hierarchy_to_string(ms);
150         error_setg(errp, "Invalid CPU topology: "
151                    "product of the hierarchy must match maxcpus: "
152                    "%s != maxcpus (%u)",
153                    topo_msg, maxcpus);
154         return;
155     }
156 
157     if (maxcpus < cpus) {
158         g_autofree char *topo_msg = cpu_hierarchy_to_string(ms);
159         error_setg(errp, "Invalid CPU topology: "
160                    "maxcpus must be equal to or greater than smp: "
161                    "%s == maxcpus (%u) < smp_cpus (%u)",
162                    topo_msg, maxcpus, cpus);
163         return;
164     }
165 
166     if (ms->smp.cpus < mc->min_cpus) {
167         error_setg(errp, "Invalid SMP CPUs %d. The min CPUs "
168                    "supported by machine '%s' is %d",
169                    ms->smp.cpus,
170                    mc->name, mc->min_cpus);
171         return;
172     }
173 
174     if (ms->smp.max_cpus > mc->max_cpus) {
175         error_setg(errp, "Invalid SMP CPUs %d. The max CPUs "
176                    "supported by machine '%s' is %d",
177                    ms->smp.max_cpus,
178                    mc->name, mc->max_cpus);
179         return;
180     }
181 }
182